diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2023-10-14 15:50:21 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2023-10-14 15:50:21 +0800 |
| commit | 0379526e20e1ff212df23e6326741aa9c06df204 (patch) | |
| tree | 340143b0c4dc4280c88ba1591d137b53477005b3 /node_modules/doiuse/lib/stream | |
| parent | 7aba14fb7c970c6e6af82c805db2aef6d70bfe2b (diff) | |
added doiuse plugin for postcss
Diffstat (limited to 'node_modules/doiuse/lib/stream')
| -rw-r--r-- | node_modules/doiuse/lib/stream/CssUsageDuplex.js | 36 | ||||
| -rw-r--r-- | node_modules/doiuse/lib/stream/RuleUsageTransform.js | 80 | ||||
| -rw-r--r-- | node_modules/doiuse/lib/stream/SourceMapTransform.js | 74 |
3 files changed, 190 insertions, 0 deletions
diff --git a/node_modules/doiuse/lib/stream/CssUsageDuplex.js b/node_modules/doiuse/lib/stream/CssUsageDuplex.js new file mode 100644 index 00000000..063ed1dc --- /dev/null +++ b/node_modules/doiuse/lib/stream/CssUsageDuplex.js @@ -0,0 +1,36 @@ +/* eslint-disable no-underscore-dangle */ + +import { pipeline } from 'node:stream'; + +import Tokenize from 'css-tokenize'; +import Duplexify from 'duplexify'; + +import RuleUsageTransform from './RuleUsageTransform.js'; +import SourceMapTransform from './SourceMapTransform.js'; + +/** @typedef {import('../../data/features.js').FeatureKeys} FeatureKeys */ + +export default class CssUsageDuplex extends Duplexify { + /** + * @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) { + const streams = [ + new Tokenize(), + new SourceMapTransform(), + new RuleUsageTransform(options, filename), + ]; + pipeline( + // @ts-expect-error Bad typings + ...streams, + () => { + // noop (Error handled by Duplexify) + }, + ); + super(streams[0], streams.at(-1), { objectMode: true }); + } +} diff --git a/node_modules/doiuse/lib/stream/RuleUsageTransform.js b/node_modules/doiuse/lib/stream/RuleUsageTransform.js new file mode 100644 index 00000000..a2a79d4d --- /dev/null +++ b/node_modules/doiuse/lib/stream/RuleUsageTransform.js @@ -0,0 +1,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); + } +} diff --git a/node_modules/doiuse/lib/stream/SourceMapTransform.js b/node_modules/doiuse/lib/stream/SourceMapTransform.js new file mode 100644 index 00000000..6295b1d3 --- /dev/null +++ b/node_modules/doiuse/lib/stream/SourceMapTransform.js @@ -0,0 +1,74 @@ +/* eslint-disable no-underscore-dangle */ +import { Transform } from 'node:stream'; + +const NEW_LINE_CHAR = '\n'.codePointAt(0); + +/** + * @typedef {Object} SourceMapTransformation + * @prop {number} line + * @prop {number} column + * @prop {string} content + */ + +export default class SourceMapTransform extends Transform { + constructor() { + super({ objectMode: true }); + } + + /** + * @type {?{location:[number,number], buffers: Buffer[]}} + * buffer for the current incoming rule. + */ + #current = null; + + /** track depth to handle rules nested in at-rules. */ + #depth = 0; + + /** track this and pass it downstream for source mapping. */ + #line = 1; + + /** track this and pass it downstream for source mapping. */ + #column = 1; + + /** + * @param {[string,Buffer]} chunk + * @param {any} encoding + * @param {Function} callback + */ + _transform([type, buf], encoding, callback) { + if ((type === 'rule_start' || type === 'atrule_start')) { + this.#depth += 1; + } + if (this.#depth > 0 && !this.#current) { + this.#current = { location: [this.#line, this.#column], buffers: [] }; + } + if (type === 'rule_end' || type === 'atrule_end') { + this.#depth -= 1; + } + + if (this.#current) { + this.#current.buffers.push(buf); + if (this.#depth === 0) { + // Push rule + this.push({ + line: this.#current.location[0], + column: this.#current.location[1], + content: Buffer.concat(this.#current.buffers).toString(), + }); + this.#current = null; + } + } + + // Update position + for (const char of buf) { + if (char === NEW_LINE_CHAR) { + this.#line += 1; + this.#column = 1; + } else { + this.#column += 1; + } + } + + callback(); + } +} |
