diff options
Diffstat (limited to 'node_modules/doiuse/lib/DoIUse.js')
| -rw-r--r-- | node_modules/doiuse/lib/DoIUse.js | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/node_modules/doiuse/lib/DoIUse.js b/node_modules/doiuse/lib/DoIUse.js new file mode 100644 index 00000000..78db53c8 --- /dev/null +++ b/node_modules/doiuse/lib/DoIUse.js @@ -0,0 +1,130 @@ +import multimatch from 'multimatch'; + +import BrowserSelection from './BrowserSelection.js'; +import Detector from './Detector.js'; + +/** @typedef {import('../data/features.js').FeatureKeys} FeatureKeys */ + +/** + * @typedef {Object} OnFeatureUsageArguments + * @prop {FeatureKeys} feature + * @prop {import('./BrowserSelection.js').MissingSupportResultStats|undefined} featureData + * @prop {import('postcss').Node} usage + * @prop {string} message + */ + +/** + * @callback OnFeatureUsage + * @param {OnFeatureUsageArguments} result + * @return {any} + */ + +/** + * @typedef {Object} DoIUseOptions + * @prop {ConstructorParameters<typeof BrowserSelection>[0]} [browsers] + * @prop {FeatureKeys[]} [ignore] + * @prop {OnFeatureUsage} [onFeatureUsage] + * @prop {string[]} [ignoreFiles] + */ + +export default class DoIUse { + static default = null; + + /** + * @param {DoIUseOptions} [optionsOrBrowserQuery] + */ + constructor(optionsOrBrowserQuery) { + const options = (typeof optionsOrBrowserQuery === 'string') + ? { browsers: optionsOrBrowserQuery } + : { ...optionsOrBrowserQuery }; + this.browserQuery = options.browsers; + this.onFeatureUsage = options.onFeatureUsage; + this.ignoreOptions = options.ignore; + this.ignoreFiles = options.ignoreFiles; + this.info = this.info.bind(this); + this.postcss = this.postcss.bind(this); + } + + /** + * @param {Object} [options] + * @param {ConstructorParameters<typeof BrowserSelection>[1]} [options.from] + */ + info(options = {}) { + const { browsers, features } = BrowserSelection.missingSupport(this.browserQuery, options.from); + + return { + browsers, + features, + }; + } + + /** @type {import('postcss').TransformCallback} */ + postcss(css, result) { + let from; + if (css.source && css.source.input) { + from = css.source.input.file; + } + const { features } = BrowserSelection.missingSupport(this.browserQuery, from); + // @ts-expect-error Needs cast + const detector = new Detector(Object.keys(features)); + + return detector.process(css, ({ feature, usage, ignore }) => { + if (ignore && ignore.includes(feature)) { + return; + } + + if (this.ignoreOptions && this.ignoreOptions.includes(feature)) { + return; + } + + if (!usage.source) { + throw new Error('No source?'); + } + if (this.ignoreFiles && multimatch(usage.source.input.from, this.ignoreFiles).length > 0) { + return; + } + + const data = features[feature]; + if (!data) { + throw new Error('No feature data?'); + } + const messages = []; + if (data.missing) { + messages.push(`not supported by: ${data.missing}`); + } + if (data.partial) { + messages.push(`only partially supported by: ${data.partial}`); + } + + let message = `${data.title} ${messages.join(' and ')} (${feature})`; + + result.warn(message, { node: usage, plugin: 'doiuse' }); + + if (this.onFeatureUsage) { + if (!usage.source) { + throw new Error('No usage source?'); + } + const { start, input } = usage.source; + if (!start) { + throw new Error('No usage source start?'); + } + + const map = css.source && css.source.input.map; + const mappedStart = map && map.consumer().originalPositionFor(start); + + const file = (mappedStart && mappedStart.source) || input.file || input.from; + const line = (mappedStart && mappedStart.line) || start.line; + const column = (mappedStart && mappedStart.column) || start.column; + + message = `${file}:${line}:${column}: ${message}`; + + this.onFeatureUsage({ + feature, + featureData: features[feature], + usage, + message, + }); + } + }); + } +} |
