summaryrefslogtreecommitdiff
path: root/node_modules/webpack/lib/util/compileBooleanMatcher.js
diff options
context:
space:
mode:
authorlittleblack111 <littleblack11111@gmail.com>2023-11-05 17:16:09 +0800
committerlittleblack111 <littleblack11111@gmail.com>2023-11-05 17:16:09 +0800
commitd1d9f00b1bd660842addf5e6b1d1b64c852a9eea (patch)
tree60fb37ae73a1109d95301de5ec9b61b5726a4b61 /node_modules/webpack/lib/util/compileBooleanMatcher.js
parent7c85166c4cbdd862fc4252007d2df5befcbc2a58 (diff)
added hover prefixer
Diffstat (limited to 'node_modules/webpack/lib/util/compileBooleanMatcher.js')
-rw-r--r--node_modules/webpack/lib/util/compileBooleanMatcher.js237
1 files changed, 237 insertions, 0 deletions
diff --git a/node_modules/webpack/lib/util/compileBooleanMatcher.js b/node_modules/webpack/lib/util/compileBooleanMatcher.js
new file mode 100644
index 00000000..9474c6d8
--- /dev/null
+++ b/node_modules/webpack/lib/util/compileBooleanMatcher.js
@@ -0,0 +1,237 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+/**
+ * @param {string} str string
+ * @returns {string} quoted meta
+ */
+const quoteMeta = str => {
+ return str.replace(/[-[\]\\/{}()*+?.^$|]/g, "\\$&");
+};
+
+/**
+ * @param {string} str string
+ * @returns {string} string
+ */
+const toSimpleString = str => {
+ if (`${+str}` === str) {
+ return str;
+ }
+ return JSON.stringify(str);
+};
+
+/**
+ * @param {Record<string|number, boolean>} map value map
+ * @returns {boolean|(function(string): string)} true/false, when unconditionally true/false, or a template function to determine the value at runtime
+ */
+const compileBooleanMatcher = map => {
+ const positiveItems = Object.keys(map).filter(i => map[i]);
+ const negativeItems = Object.keys(map).filter(i => !map[i]);
+ if (positiveItems.length === 0) return false;
+ if (negativeItems.length === 0) return true;
+ return compileBooleanMatcherFromLists(positiveItems, negativeItems);
+};
+
+/**
+ * @param {string[]} positiveItems positive items
+ * @param {string[]} negativeItems negative items
+ * @returns {function(string): string} a template function to determine the value at runtime
+ */
+const compileBooleanMatcherFromLists = (positiveItems, negativeItems) => {
+ if (positiveItems.length === 0) return () => "false";
+ if (negativeItems.length === 0) return () => "true";
+ if (positiveItems.length === 1)
+ return value => `${toSimpleString(positiveItems[0])} == ${value}`;
+ if (negativeItems.length === 1)
+ return value => `${toSimpleString(negativeItems[0])} != ${value}`;
+ const positiveRegexp = itemsToRegexp(positiveItems);
+ const negativeRegexp = itemsToRegexp(negativeItems);
+ if (positiveRegexp.length <= negativeRegexp.length) {
+ return value => `/^${positiveRegexp}$/.test(${value})`;
+ } else {
+ return value => `!/^${negativeRegexp}$/.test(${value})`;
+ }
+};
+
+/**
+ * @param {Set<string>} itemsSet items set
+ * @param {(str: string) => string | false} getKey get key function
+ * @param {(str: Array<string>) => boolean} condition condition
+ * @returns {Array<Array<string>>} list of common items
+ */
+const popCommonItems = (itemsSet, getKey, condition) => {
+ /** @type {Map<string, Array<string>>} */
+ const map = new Map();
+ for (const item of itemsSet) {
+ const key = getKey(item);
+ if (key) {
+ let list = map.get(key);
+ if (list === undefined) {
+ /** @type {Array<string>} */
+ list = [];
+ map.set(key, list);
+ }
+ list.push(item);
+ }
+ }
+ /** @type {Array<Array<string>>} */
+ const result = [];
+ for (const list of map.values()) {
+ if (condition(list)) {
+ for (const item of list) {
+ itemsSet.delete(item);
+ }
+ result.push(list);
+ }
+ }
+ return result;
+};
+
+/**
+ * @param {Array<string>} items items
+ * @returns {string} common prefix
+ */
+const getCommonPrefix = items => {
+ let prefix = items[0];
+ for (let i = 1; i < items.length; i++) {
+ const item = items[i];
+ for (let p = 0; p < prefix.length; p++) {
+ if (item[p] !== prefix[p]) {
+ prefix = prefix.slice(0, p);
+ break;
+ }
+ }
+ }
+ return prefix;
+};
+
+/**
+ * @param {Array<string>} items items
+ * @returns {string} common suffix
+ */
+const getCommonSuffix = items => {
+ let suffix = items[0];
+ for (let i = 1; i < items.length; i++) {
+ const item = items[i];
+ for (let p = item.length - 1, s = suffix.length - 1; s >= 0; p--, s--) {
+ if (item[p] !== suffix[s]) {
+ suffix = suffix.slice(s + 1);
+ break;
+ }
+ }
+ }
+ return suffix;
+};
+
+/**
+ * @param {Array<string>} itemsArr array of items
+ * @returns {string} regexp
+ */
+const itemsToRegexp = itemsArr => {
+ if (itemsArr.length === 1) {
+ return quoteMeta(itemsArr[0]);
+ }
+ /** @type {Array<string>} */
+ const finishedItems = [];
+
+ // merge single char items: (a|b|c|d|ef) => ([abcd]|ef)
+ let countOfSingleCharItems = 0;
+ for (const item of itemsArr) {
+ if (item.length === 1) {
+ countOfSingleCharItems++;
+ }
+ }
+ // special case for only single char items
+ if (countOfSingleCharItems === itemsArr.length) {
+ return `[${quoteMeta(itemsArr.sort().join(""))}]`;
+ }
+ const items = new Set(itemsArr.sort());
+ if (countOfSingleCharItems > 2) {
+ let singleCharItems = "";
+ for (const item of items) {
+ if (item.length === 1) {
+ singleCharItems += item;
+ items.delete(item);
+ }
+ }
+ finishedItems.push(`[${quoteMeta(singleCharItems)}]`);
+ }
+
+ // special case for 2 items with common prefix/suffix
+ if (finishedItems.length === 0 && items.size === 2) {
+ const prefix = getCommonPrefix(itemsArr);
+ const suffix = getCommonSuffix(
+ itemsArr.map(item => item.slice(prefix.length))
+ );
+ if (prefix.length > 0 || suffix.length > 0) {
+ return `${quoteMeta(prefix)}${itemsToRegexp(
+ itemsArr.map(i => i.slice(prefix.length, -suffix.length || undefined))
+ )}${quoteMeta(suffix)}`;
+ }
+ }
+
+ // special case for 2 items with common suffix
+ if (finishedItems.length === 0 && items.size === 2) {
+ /** @type {Iterator<string>} */
+ const it = items[Symbol.iterator]();
+ const a = it.next().value;
+ const b = it.next().value;
+ if (a.length > 0 && b.length > 0 && a.slice(-1) === b.slice(-1)) {
+ return `${itemsToRegexp([a.slice(0, -1), b.slice(0, -1)])}${quoteMeta(
+ a.slice(-1)
+ )}`;
+ }
+ }
+
+ // find common prefix: (a1|a2|a3|a4|b5) => (a(1|2|3|4)|b5)
+ const prefixed = popCommonItems(
+ items,
+ item => (item.length >= 1 ? item[0] : false),
+ list => {
+ if (list.length >= 3) return true;
+ if (list.length <= 1) return false;
+ return list[0][1] === list[1][1];
+ }
+ );
+ for (const prefixedItems of prefixed) {
+ const prefix = getCommonPrefix(prefixedItems);
+ finishedItems.push(
+ `${quoteMeta(prefix)}${itemsToRegexp(
+ prefixedItems.map(i => i.slice(prefix.length))
+ )}`
+ );
+ }
+
+ // find common suffix: (a1|b1|c1|d1|e2) => ((a|b|c|d)1|e2)
+ const suffixed = popCommonItems(
+ items,
+ item => (item.length >= 1 ? item.slice(-1) : false),
+ list => {
+ if (list.length >= 3) return true;
+ if (list.length <= 1) return false;
+ return list[0].slice(-2) === list[1].slice(-2);
+ }
+ );
+ for (const suffixedItems of suffixed) {
+ const suffix = getCommonSuffix(suffixedItems);
+ finishedItems.push(
+ `${itemsToRegexp(
+ suffixedItems.map(i => i.slice(0, -suffix.length))
+ )}${quoteMeta(suffix)}`
+ );
+ }
+
+ // TODO further optimize regexp, i. e.
+ // use ranges: (1|2|3|4|a) => [1-4a]
+ const conditional = finishedItems.concat(Array.from(items, quoteMeta));
+ if (conditional.length === 1) return conditional[0];
+ return `(${conditional.join("|")})`;
+};
+
+compileBooleanMatcher.fromLists = compileBooleanMatcherFromLists;
+compileBooleanMatcher.itemsToRegexp = itemsToRegexp;
+module.exports = compileBooleanMatcher;