summaryrefslogtreecommitdiff
path: root/node_modules/doiuse/lib/stream/RuleUsageTransform.js
blob: a2a79d4d568684986b409842d15179439af43796 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/* eslint-disable no-underscore-dangle */
import { Transform } from 'node:stream';

import postcss from 'postcss';
import sourcemap from 'source-map';

import DoIUse from '../DoIUse.js';

/** @typedef {import('../../data/features.js').FeatureKeys} FeatureKeys */
/** @typedef {import('./SourceMapTransform.js').SourceMapTransformation} SourceMapTransformation */

export default class RuleUsageTransform extends Transform {
  /** @type {import("postcss").Processor} */
  #processor;

  #skipErrors;

  #filename;

  /**
   * @param {Object} options
   * @param {string} options.browsers
   * @param {FeatureKeys[]} [options.ignore]
   * @param {boolean} [options.skipErrors]
   * @param {string} [filename]  Filename for outputting source code locations.
   */
  constructor(options, filename = '<streaming css input>') {
    super({
      objectMode: true,
    });
    this.#processor = postcss([new DoIUse({
      browsers: options.browsers,
      ignore: options.ignore,
      onFeatureUsage: (usage) => {
        this.push(usage);
      },
    })]);
    this.#filename = filename;
    this.#skipErrors = options.skipErrors;
  }

  /**
   * @param {SourceMapTransformation} rule
   * @param {any} encoding
   * @param {Function} callback
   */
  async _transform(rule, encoding, callback) {
    let caughtError;
    try {
      const mapper = new sourcemap.SourceMapGenerator();
      const lines = rule.content.split('\n');
      let oline = rule.line;
      let ocol = rule.column;
      for (const [lineNumber, content] of lines.entries()) {
        mapper.addMapping({
          generated: { line: lineNumber + 1, column: 1 },
          original: { line: oline, column: ocol },
          source: this.#filename,
        });
        mapper.addMapping({
          generated: { line: lineNumber + 1, column: content.length },
          original: { line: oline, column: ocol + content.length },
          source: this.#filename,
        });
        oline += 1;
        ocol = 1;
      }
      await this.#processor.process(rule.content, {
        from: this.#filename, map: { prev: mapper.toString() },
      });
    } catch (error) {
      if (this.#skipErrors) {
        this.emit('warning', error);
      } else {
        caughtError = error;
      }
    }
    callback(caughtError);
  }
}