summaryrefslogtreecommitdiff
path: root/node_modules/doiuse/lib
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/doiuse/lib')
-rw-r--r--node_modules/doiuse/lib/BrowserSelection.js181
-rw-r--r--node_modules/doiuse/lib/Detector.js157
-rw-r--r--node_modules/doiuse/lib/DoIUse.js130
-rw-r--r--node_modules/doiuse/lib/stream/CssUsageDuplex.js36
-rw-r--r--node_modules/doiuse/lib/stream/RuleUsageTransform.js80
-rw-r--r--node_modules/doiuse/lib/stream/SourceMapTransform.js74
6 files changed, 0 insertions, 658 deletions
diff --git a/node_modules/doiuse/lib/BrowserSelection.js b/node_modules/doiuse/lib/BrowserSelection.js
deleted file mode 100644
index 17467548..00000000
--- a/node_modules/doiuse/lib/BrowserSelection.js
+++ /dev/null
@@ -1,181 +0,0 @@
-import browserslist from 'browserslist';
-import * as caniuse from 'caniuse-lite';
-
-import FEATURES from '../data/features.js';
-import { formatBrowserName } from '../utils/util.js';
-
-/**
- * @template T
- * @typedef {{missing: T, partial: T}} Filter
- */
-
-/**
- * @typedef {Object} MissingSupportResultStats
- * @prop {string} title
- * @prop {caniuse.Feature} caniuseData
- * @prop {string} [missing]
- * @prop {Record<string, Record<string, string>>} [missingData]
- * @prop {string} [partial]
- * @prop {Record<string, Record<string, string>>} [partialData]
- */
-
-/**
- * @typedef {Partial<Record<keyof FEATURES, MissingSupportResultStats>>} BrowserSupportStats
- */
-
-/**
- * @typedef {Object} MissingSupportResult
- * @prop {string[][]} browsers
- * @prop {Partial<Record<keyof FEATURES, MissingSupportResultStats>>} features
- */
-
-export default class BrowserSelection {
- #list;
-
- /**
- * @param {string | string[]} [query]
- * @param {string | false} [from]
- */
- constructor(query, from) {
- this.browsersRequest = query;
- const options = from ? { path: from } : {};
- this.#list = browserslist(this.browsersRequest, options).map((browser) => browser.split(' '));
- }
-
- /**
- * @param {string} browser
- * @param {string} version
- * @return {string[]|undefined}
- */
- test(browser, version) {
- const versions = version.split('-');
-
- if (versions.length === 1) {
- versions.push(versions[0]);
- }
-
- return this.#list.find(([b, v]) => b === browser && v >= versions[0] && v <= versions[1]);
- }
-
- list() {
- return [...this.#list];
- }
-
- /**
- * @param {Record<string, Record<string, string>>} browserStats
- * @return {string}
- */
- static lackingBrowsers(browserStats) {
- const result = [];
- for (const browser of Object.keys(browserStats)) {
- result.push(formatBrowserName(browser, Object.keys(browserStats[browser])));
- }
- return result.join(', ');
- }
-
- /**
- * @param {import('caniuse-lite').StatsByAgentID} stats
- * @return {Filter<Record<string,Record<string, string>>>}
- */
- filterStats(stats) {
- /** @type {Filter<Record<string,Record<string, string>>>} */
- const result = { missing: {}, partial: {} };
- for (const [browser, versions] of Object.entries(stats)) {
- /** @type {Filter<Record<string,string>>} */
- const feature = { missing: {}, partial: {} };
- for (const [version, support] of Object.entries(versions)) {
- const selected = this.test(browser, version);
-
- // check if browser is NOT fully (i.e., don't have 'y' in their stats) supported
- if (!selected) continue;
- if ((/(^|\s)y($|\s)/.test(support))) continue;
- // when it's not partially supported ('a'), it's missing
- const type = (/(^|\s)a($|\s)/.test(support) ? 'partial' : 'missing');
-
- if (!feature[type]) {
- feature[type] = {};
- }
-
- feature[type][selected[1]] = support;
- }
- if (Object.keys(feature.missing).length > 0) {
- result.missing[browser] = feature.missing;
- }
-
- if (Object.keys(feature.partial).length > 0) {
- result.partial[browser] = feature.partial;
- }
- }
- return result;
- }
-
- /**
- * Get data on CSS features not supported by the given autoprefixer-like
- * browser selection.
- * @return {BrowserSupportStats} `features` is an array of:
- * ```
- * {
- * 'feature-name': {
- * title: 'Title of feature'
- * missing: "IE (8), Chrome (31)"
- * missingData: {
- * // map of browser -> version -> (lack of)support code
- * ie: { '8': 'n' },
- * chrome: { '31': 'n' }
- * }
- * partialData: {
- * // map of browser -> version -> (partial)support code
- * ie: { '7': 'a' },
- * ff: { '29': 'a #1' }
- * }
- * caniuseData: {
- * // caniuse-db json data for this feature
- * }
- * },
- * 'feature-name-2': {} // etc.
- * }
- * ```
- *
- * `feature-name` is a caniuse-db slug.
- */
- compileBrowserSupport() {
- /** @type {Partial<Record<keyof FEATURES, MissingSupportResultStats>>} */
- const result = {};
-
- for (const key of Object.keys(FEATURES)) {
- const feature = /** @type {keyof FEATURES} */ (key);
- const packedFeature = caniuse.features[feature];
- const featureData = caniuse.feature(packedFeature);
- const lackData = this.filterStats(featureData.stats);
- const missingData = lackData.missing;
- const partialData = lackData.partial;
- // browsers with missing or partial support for this feature
- const missing = BrowserSelection.lackingBrowsers(missingData);
- const partial = BrowserSelection.lackingBrowsers(partialData);
-
- if (missing.length > 0 || partial.length > 0) {
- result[feature] = {
- title: featureData.title,
- caniuseData: featureData,
- ...(missing.length > 0 ? { missingData, missing } : null),
- ...(partial.length > 0 ? { partialData, partial } : null),
- };
- }
- }
-
- return result;
- }
-
- /**
- * @see BrowserSelection.compileBrowserSupport
- * @param {ConstructorParameters<typeof this>} constructorParameters
- * @return {MissingSupportResult} `{browsers, features}`
- */
- static missingSupport(...constructorParameters) {
- const selection = new BrowserSelection(...constructorParameters);
- return {
- browsers: selection.list(),
- features: selection.compileBrowserSupport(),
- };
- }
-}
diff --git a/node_modules/doiuse/lib/Detector.js b/node_modules/doiuse/lib/Detector.js
deleted file mode 100644
index 2858bca9..00000000
--- a/node_modules/doiuse/lib/Detector.js
+++ /dev/null
@@ -1,157 +0,0 @@
-import FEATURES from '../data/features.js';
-import { performFeatureCheck } from '../utils/util.js';
-
-/**
- * @typedef DetectorCallbackArgument
- * @prop {!import('postcss').ChildNode} usage
- * @prop {keyof FEATURES} feature
- * @prop {(keyof FEATURES & string)[]} ignore
- */
-
-/**
- * @callback DetectorCallback
- * @param {DetectorCallbackArgument} result
- * @return {any}
- */
-
-const PLUGIN_OPTION_COMMENT = 'doiuse-';
-const DISABLE_FEATURE_COMMENT = `${PLUGIN_OPTION_COMMENT}disable`;
-const ENABLE_FEATURE_COMMENT = `${PLUGIN_OPTION_COMMENT}enable`;
-
-/**
- * Strip the contents of url literals so they aren't matched
- * by our naive substring matching.
- * @param {string} input
- * @return {string}
- */
-function stripUrls(input) {
- return input.replaceAll(/url\([^)]*\)/g, 'url()');
-}
-
-/**
- * Detect the use of any of a given list of CSS features.
- * ```
- * var detector = new Detector(featureList)
- * detector.process(css, cb)
- * ```
- *
- * `featureList`: an array of feature slugs (see caniuse-db)
- * `cb`: a callback that gets called for each usage of one of the given features,
- * called with an argument like:
- * ```
- * {
- * usage: {} // postcss node where usage was found
- * feature: {} // caniuse-db feature slug
- * ignore: {} // caniuse-db feature to ignore in current file
- * }
- * ```
- */
-export default class Detector {
- /**
- * @param {(keyof FEATURES & string)[]} featureList an array of feature slugs (see caniuse-db)
- */
- constructor(featureList) {
- /** @type {Partial<FEATURES>} */
- this.features = {};
- for (const feature of featureList) {
- if (FEATURES[feature]) {
- this.features[feature] = FEATURES[feature];
- }
- }
- /** @type {(keyof FEATURES & string)[]} */
- this.ignore = [];
- }
-
- /**
- * @param {import('postcss').Comment} comment
- * @return {void}
- */
- comment(comment) {
- const text = comment.text.toLowerCase();
-
- if (!text.startsWith(PLUGIN_OPTION_COMMENT)) return;
- const option = text.split(' ', 1)[0];
- const value = text.replace(option, '').trim();
-
- switch (option) {
- case DISABLE_FEATURE_COMMENT: {
- if (value === '') {
- // @ts-expect-error Skip cast
- this.ignore = Object.keys(this.features);
- } else {
- for (const feat of value.split(',')) {
- /** @type {any} */
- const f = feat.trim();
- if (!this.ignore.includes(f)) {
- this.ignore.push(f);
- }
- }
- }
- break;
- }
- case ENABLE_FEATURE_COMMENT: {
- if (value === '') {
- this.ignore = [];
- } else {
- const without = new Set(value.split(',').map((feat) => feat.trim()));
- this.ignore = this.ignore.filter((index) => !without.has(index));
- }
- break;
- }
- default:
- }
- }
-
- /**
- * @param {import('postcss').Container} node
- * @param {DetectorCallback} callback
- * @return {void}
- */
- node(node, callback) {
- node.each((child) => {
- if (child.type === 'comment') {
- this.comment(child);
- return;
- }
-
- for (const [feat] of Object.entries(this.features).filter(([, featValue]) => {
- if (!featValue) return false;
- if (typeof featValue === 'function') {
- return featValue(child);
- }
- if (Array.isArray(featValue)) {
- return featValue.some((function_) => function_(child));
- }
- if (child.type !== 'decl') {
- return false;
- }
-
- return Object.entries(featValue).some(([property, value]) => {
- if (property !== '' && property !== child.prop) return false;
- if (value === true) return true;
- if (value === false) return false;
- return performFeatureCheck(value, stripUrls(child.value));
- });
- })) {
- const feature = /** @type {keyof FEATURES} */ (feat);
- callback({ usage: child, feature, ignore: this.ignore });
- }
- if (child.type !== 'decl') {
- this.node(child, callback);
- }
- });
- }
-
- /**
- * @param {import('postcss').Root} node
- * @param {DetectorCallback} callback
- * @return {void}
- */
- process(node, callback) {
- // Reset ignoring rules specified by inline comments per each file
- this.ignore = [];
-
- // Recursively walk nodes in file
- this.node(node, callback);
- }
-}
diff --git a/node_modules/doiuse/lib/DoIUse.js b/node_modules/doiuse/lib/DoIUse.js
deleted file mode 100644
index 78db53c8..00000000
--- a/node_modules/doiuse/lib/DoIUse.js
+++ /dev/null
@@ -1,130 +0,0 @@
-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,
- });
- }
- });
- }
-}
diff --git a/node_modules/doiuse/lib/stream/CssUsageDuplex.js b/node_modules/doiuse/lib/stream/CssUsageDuplex.js
deleted file mode 100644
index 063ed1dc..00000000
--- a/node_modules/doiuse/lib/stream/CssUsageDuplex.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/* 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
deleted file mode 100644
index a2a79d4d..00000000
--- a/node_modules/doiuse/lib/stream/RuleUsageTransform.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/* 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
deleted file mode 100644
index 6295b1d3..00000000
--- a/node_modules/doiuse/lib/stream/SourceMapTransform.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/* 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();
- }
-}