summaryrefslogtreecommitdiff
path: root/node_modules/webpack/lib/util/cleverMerge.js
diff options
context:
space:
mode:
authorlittleblack111 <littleblack11111@gmail.com>2023-10-31 15:37:35 +0800
committerlittleblack111 <littleblack11111@gmail.com>2023-10-31 15:37:35 +0800
commitd5f14083061f2ce6a42313188443a42e30796320 (patch)
treecc6a9889e08b29595a76a33b9e7b4ef84866468b /node_modules/webpack/lib/util/cleverMerge.js
parentb1a6ea021f29f182b62f9f81024a3d8ac8621b7f (diff)
added make browserlist(autoprefixer) work fixed
Diffstat (limited to 'node_modules/webpack/lib/util/cleverMerge.js')
-rw-r--r--node_modules/webpack/lib/util/cleverMerge.js568
1 files changed, 568 insertions, 0 deletions
diff --git a/node_modules/webpack/lib/util/cleverMerge.js b/node_modules/webpack/lib/util/cleverMerge.js
new file mode 100644
index 00000000..f86934cc
--- /dev/null
+++ b/node_modules/webpack/lib/util/cleverMerge.js
@@ -0,0 +1,568 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+/** @type {WeakMap<object, WeakMap<object, object>>} */
+const mergeCache = new WeakMap();
+/** @type {WeakMap<object, Map<string, Map<string|number|boolean, object>>>} */
+const setPropertyCache = new WeakMap();
+const DELETE = Symbol("DELETE");
+const DYNAMIC_INFO = Symbol("cleverMerge dynamic info");
+
+/**
+ * Merges two given objects and caches the result to avoid computation if same objects passed as arguments again.
+ * @template T
+ * @template O
+ * @example
+ * // performs cleverMerge(first, second), stores the result in WeakMap and returns result
+ * cachedCleverMerge({a: 1}, {a: 2})
+ * {a: 2}
+ * // when same arguments passed, gets the result from WeakMap and returns it.
+ * cachedCleverMerge({a: 1}, {a: 2})
+ * {a: 2}
+ * @param {T} first first object
+ * @param {O} second second object
+ * @returns {T & O | T | O} merged object of first and second object
+ */
+const cachedCleverMerge = (first, second) => {
+ if (second === undefined) return first;
+ if (first === undefined) return second;
+ if (typeof second !== "object" || second === null) return second;
+ if (typeof first !== "object" || first === null) return first;
+
+ let innerCache = mergeCache.get(first);
+ if (innerCache === undefined) {
+ innerCache = new WeakMap();
+ mergeCache.set(first, innerCache);
+ }
+ const prevMerge = innerCache.get(second);
+ if (prevMerge !== undefined) return prevMerge;
+ const newMerge = _cleverMerge(first, second, true);
+ innerCache.set(second, newMerge);
+ return newMerge;
+};
+
+/**
+ * @template T
+ * @param {Partial<T>} obj object
+ * @param {string} property property
+ * @param {string|number|boolean} value assignment value
+ * @returns {T} new object
+ */
+const cachedSetProperty = (obj, property, value) => {
+ let mapByProperty = setPropertyCache.get(obj);
+
+ if (mapByProperty === undefined) {
+ mapByProperty = new Map();
+ setPropertyCache.set(obj, mapByProperty);
+ }
+
+ let mapByValue = mapByProperty.get(property);
+
+ if (mapByValue === undefined) {
+ mapByValue = new Map();
+ mapByProperty.set(property, mapByValue);
+ }
+
+ let result = mapByValue.get(value);
+
+ if (result) return result;
+
+ result = {
+ ...obj,
+ [property]: value
+ };
+ mapByValue.set(value, result);
+
+ return result;
+};
+
+/**
+ * @typedef {Object} ObjectParsedPropertyEntry
+ * @property {any | undefined} base base value
+ * @property {string | undefined} byProperty the name of the selector property
+ * @property {Map<string, any>} byValues value depending on selector property, merged with base
+ */
+
+/**
+ * @typedef {Object} ParsedObject
+ * @property {Map<string, ObjectParsedPropertyEntry>} static static properties (key is property name)
+ * @property {{ byProperty: string, fn: Function } | undefined} dynamic dynamic part
+ */
+
+/** @type {WeakMap<object, ParsedObject>} */
+const parseCache = new WeakMap();
+
+/**
+ * @param {object} obj the object
+ * @returns {ParsedObject} parsed object
+ */
+const cachedParseObject = obj => {
+ const entry = parseCache.get(obj);
+ if (entry !== undefined) return entry;
+ const result = parseObject(obj);
+ parseCache.set(obj, result);
+ return result;
+};
+
+/**
+ * @param {object} obj the object
+ * @returns {ParsedObject} parsed object
+ */
+const parseObject = obj => {
+ const info = new Map();
+ let dynamicInfo;
+ const getInfo = p => {
+ const entry = info.get(p);
+ if (entry !== undefined) return entry;
+ const newEntry = {
+ base: undefined,
+ byProperty: undefined,
+ byValues: undefined
+ };
+ info.set(p, newEntry);
+ return newEntry;
+ };
+ for (const key of Object.keys(obj)) {
+ if (key.startsWith("by")) {
+ const byProperty = key;
+ const byObj = obj[byProperty];
+ if (typeof byObj === "object") {
+ for (const byValue of Object.keys(byObj)) {
+ const obj = byObj[byValue];
+ for (const key of Object.keys(obj)) {
+ const entry = getInfo(key);
+ if (entry.byProperty === undefined) {
+ entry.byProperty = byProperty;
+ entry.byValues = new Map();
+ } else if (entry.byProperty !== byProperty) {
+ throw new Error(
+ `${byProperty} and ${entry.byProperty} for a single property is not supported`
+ );
+ }
+ entry.byValues.set(byValue, obj[key]);
+ if (byValue === "default") {
+ for (const otherByValue of Object.keys(byObj)) {
+ if (!entry.byValues.has(otherByValue))
+ entry.byValues.set(otherByValue, undefined);
+ }
+ }
+ }
+ }
+ } else if (typeof byObj === "function") {
+ if (dynamicInfo === undefined) {
+ dynamicInfo = {
+ byProperty: key,
+ fn: byObj
+ };
+ } else {
+ throw new Error(
+ `${key} and ${dynamicInfo.byProperty} when both are functions is not supported`
+ );
+ }
+ } else {
+ const entry = getInfo(key);
+ entry.base = obj[key];
+ }
+ } else {
+ const entry = getInfo(key);
+ entry.base = obj[key];
+ }
+ }
+ return {
+ static: info,
+ dynamic: dynamicInfo
+ };
+};
+
+/**
+ * @param {Map<string, ObjectParsedPropertyEntry>} info static properties (key is property name)
+ * @param {{ byProperty: string, fn: Function } | undefined} dynamicInfo dynamic part
+ * @returns {object} the object
+ */
+const serializeObject = (info, dynamicInfo) => {
+ const obj = {};
+ // Setup byProperty structure
+ for (const entry of info.values()) {
+ if (entry.byProperty !== undefined) {
+ const byObj = (obj[entry.byProperty] = obj[entry.byProperty] || {});
+ for (const byValue of entry.byValues.keys()) {
+ byObj[byValue] = byObj[byValue] || {};
+ }
+ }
+ }
+ for (const [key, entry] of info) {
+ if (entry.base !== undefined) {
+ obj[key] = entry.base;
+ }
+ // Fill byProperty structure
+ if (entry.byProperty !== undefined) {
+ const byObj = (obj[entry.byProperty] = obj[entry.byProperty] || {});
+ for (const byValue of Object.keys(byObj)) {
+ const value = getFromByValues(entry.byValues, byValue);
+ if (value !== undefined) byObj[byValue][key] = value;
+ }
+ }
+ }
+ if (dynamicInfo !== undefined) {
+ obj[dynamicInfo.byProperty] = dynamicInfo.fn;
+ }
+ return obj;
+};
+
+const VALUE_TYPE_UNDEFINED = 0;
+const VALUE_TYPE_ATOM = 1;
+const VALUE_TYPE_ARRAY_EXTEND = 2;
+const VALUE_TYPE_OBJECT = 3;
+const VALUE_TYPE_DELETE = 4;
+
+/**
+ * @param {any} value a single value
+ * @returns {VALUE_TYPE_UNDEFINED | VALUE_TYPE_ATOM | VALUE_TYPE_ARRAY_EXTEND | VALUE_TYPE_OBJECT | VALUE_TYPE_DELETE} value type
+ */
+const getValueType = value => {
+ if (value === undefined) {
+ return VALUE_TYPE_UNDEFINED;
+ } else if (value === DELETE) {
+ return VALUE_TYPE_DELETE;
+ } else if (Array.isArray(value)) {
+ if (value.lastIndexOf("...") !== -1) return VALUE_TYPE_ARRAY_EXTEND;
+ return VALUE_TYPE_ATOM;
+ } else if (
+ typeof value === "object" &&
+ value !== null &&
+ (!value.constructor || value.constructor === Object)
+ ) {
+ return VALUE_TYPE_OBJECT;
+ }
+ return VALUE_TYPE_ATOM;
+};
+
+/**
+ * Merges two objects. Objects are deeply clever merged.
+ * Arrays might reference the old value with "...".
+ * Non-object values take preference over object values.
+ * @template T
+ * @template O
+ * @param {T} first first object
+ * @param {O} second second object
+ * @returns {T & O | T | O} merged object of first and second object
+ */
+const cleverMerge = (first, second) => {
+ if (second === undefined) return first;
+ if (first === undefined) return second;
+ if (typeof second !== "object" || second === null) return second;
+ if (typeof first !== "object" || first === null) return first;
+
+ return _cleverMerge(first, second, false);
+};
+
+/**
+ * Merges two objects. Objects are deeply clever merged.
+ * @param {object} first first object
+ * @param {object} second second object
+ * @param {boolean} internalCaching should parsing of objects and nested merges be cached
+ * @returns {object} merged object of first and second object
+ */
+const _cleverMerge = (first, second, internalCaching = false) => {
+ const firstObject = internalCaching
+ ? cachedParseObject(first)
+ : parseObject(first);
+ const { static: firstInfo, dynamic: firstDynamicInfo } = firstObject;
+
+ // If the first argument has a dynamic part we modify the dynamic part to merge the second argument
+ if (firstDynamicInfo !== undefined) {
+ let { byProperty, fn } = firstDynamicInfo;
+ const fnInfo = fn[DYNAMIC_INFO];
+ if (fnInfo) {
+ second = internalCaching
+ ? cachedCleverMerge(fnInfo[1], second)
+ : cleverMerge(fnInfo[1], second);
+ fn = fnInfo[0];
+ }
+ const newFn = (...args) => {
+ const fnResult = fn(...args);
+ return internalCaching
+ ? cachedCleverMerge(fnResult, second)
+ : cleverMerge(fnResult, second);
+ };
+ newFn[DYNAMIC_INFO] = [fn, second];
+ return serializeObject(firstObject.static, { byProperty, fn: newFn });
+ }
+
+ // If the first part is static only, we merge the static parts and keep the dynamic part of the second argument
+ const secondObject = internalCaching
+ ? cachedParseObject(second)
+ : parseObject(second);
+ const { static: secondInfo, dynamic: secondDynamicInfo } = secondObject;
+ /** @type {Map<string, ObjectParsedPropertyEntry>} */
+ const resultInfo = new Map();
+ for (const [key, firstEntry] of firstInfo) {
+ const secondEntry = secondInfo.get(key);
+ const entry =
+ secondEntry !== undefined
+ ? mergeEntries(firstEntry, secondEntry, internalCaching)
+ : firstEntry;
+ resultInfo.set(key, entry);
+ }
+ for (const [key, secondEntry] of secondInfo) {
+ if (!firstInfo.has(key)) {
+ resultInfo.set(key, secondEntry);
+ }
+ }
+ return serializeObject(resultInfo, secondDynamicInfo);
+};
+
+/**
+ * @param {ObjectParsedPropertyEntry} firstEntry a
+ * @param {ObjectParsedPropertyEntry} secondEntry b
+ * @param {boolean} internalCaching should parsing of objects and nested merges be cached
+ * @returns {ObjectParsedPropertyEntry} new entry
+ */
+const mergeEntries = (firstEntry, secondEntry, internalCaching) => {
+ switch (getValueType(secondEntry.base)) {
+ case VALUE_TYPE_ATOM:
+ case VALUE_TYPE_DELETE:
+ // No need to consider firstEntry at all
+ // second value override everything
+ // = second.base + second.byProperty
+ return secondEntry;
+ case VALUE_TYPE_UNDEFINED:
+ if (!firstEntry.byProperty) {
+ // = first.base + second.byProperty
+ return {
+ base: firstEntry.base,
+ byProperty: secondEntry.byProperty,
+ byValues: secondEntry.byValues
+ };
+ } else if (firstEntry.byProperty !== secondEntry.byProperty) {
+ throw new Error(
+ `${firstEntry.byProperty} and ${secondEntry.byProperty} for a single property is not supported`
+ );
+ } else {
+ // = first.base + (first.byProperty + second.byProperty)
+ // need to merge first and second byValues
+ const newByValues = new Map(firstEntry.byValues);
+ for (const [key, value] of secondEntry.byValues) {
+ const firstValue = getFromByValues(firstEntry.byValues, key);
+ newByValues.set(
+ key,
+ mergeSingleValue(firstValue, value, internalCaching)
+ );
+ }
+ return {
+ base: firstEntry.base,
+ byProperty: firstEntry.byProperty,
+ byValues: newByValues
+ };
+ }
+ default: {
+ if (!firstEntry.byProperty) {
+ // The simple case
+ // = (first.base + second.base) + second.byProperty
+ return {
+ base: mergeSingleValue(
+ firstEntry.base,
+ secondEntry.base,
+ internalCaching
+ ),
+ byProperty: secondEntry.byProperty,
+ byValues: secondEntry.byValues
+ };
+ }
+ let newBase;
+ const intermediateByValues = new Map(firstEntry.byValues);
+ for (const [key, value] of intermediateByValues) {
+ intermediateByValues.set(
+ key,
+ mergeSingleValue(value, secondEntry.base, internalCaching)
+ );
+ }
+ if (
+ Array.from(firstEntry.byValues.values()).every(value => {
+ const type = getValueType(value);
+ return type === VALUE_TYPE_ATOM || type === VALUE_TYPE_DELETE;
+ })
+ ) {
+ // = (first.base + second.base) + ((first.byProperty + second.base) + second.byProperty)
+ newBase = mergeSingleValue(
+ firstEntry.base,
+ secondEntry.base,
+ internalCaching
+ );
+ } else {
+ // = first.base + ((first.byProperty (+default) + second.base) + second.byProperty)
+ newBase = firstEntry.base;
+ if (!intermediateByValues.has("default"))
+ intermediateByValues.set("default", secondEntry.base);
+ }
+ if (!secondEntry.byProperty) {
+ // = first.base + (first.byProperty + second.base)
+ return {
+ base: newBase,
+ byProperty: firstEntry.byProperty,
+ byValues: intermediateByValues
+ };
+ } else if (firstEntry.byProperty !== secondEntry.byProperty) {
+ throw new Error(
+ `${firstEntry.byProperty} and ${secondEntry.byProperty} for a single property is not supported`
+ );
+ }
+ const newByValues = new Map(intermediateByValues);
+ for (const [key, value] of secondEntry.byValues) {
+ const firstValue = getFromByValues(intermediateByValues, key);
+ newByValues.set(
+ key,
+ mergeSingleValue(firstValue, value, internalCaching)
+ );
+ }
+ return {
+ base: newBase,
+ byProperty: firstEntry.byProperty,
+ byValues: newByValues
+ };
+ }
+ }
+};
+
+/**
+ * @param {Map<string, any>} byValues all values
+ * @param {string} key value of the selector
+ * @returns {any | undefined} value
+ */
+const getFromByValues = (byValues, key) => {
+ if (key !== "default" && byValues.has(key)) {
+ return byValues.get(key);
+ }
+ return byValues.get("default");
+};
+
+/**
+ * @param {any} a value
+ * @param {any} b value
+ * @param {boolean} internalCaching should parsing of objects and nested merges be cached
+ * @returns {any} value
+ */
+const mergeSingleValue = (a, b, internalCaching) => {
+ const bType = getValueType(b);
+ const aType = getValueType(a);
+ switch (bType) {
+ case VALUE_TYPE_DELETE:
+ case VALUE_TYPE_ATOM:
+ return b;
+ case VALUE_TYPE_OBJECT: {
+ return aType !== VALUE_TYPE_OBJECT
+ ? b
+ : internalCaching
+ ? cachedCleverMerge(a, b)
+ : cleverMerge(a, b);
+ }
+ case VALUE_TYPE_UNDEFINED:
+ return a;
+ case VALUE_TYPE_ARRAY_EXTEND:
+ switch (
+ aType !== VALUE_TYPE_ATOM
+ ? aType
+ : Array.isArray(a)
+ ? VALUE_TYPE_ARRAY_EXTEND
+ : VALUE_TYPE_OBJECT
+ ) {
+ case VALUE_TYPE_UNDEFINED:
+ return b;
+ case VALUE_TYPE_DELETE:
+ return b.filter(item => item !== "...");
+ case VALUE_TYPE_ARRAY_EXTEND: {
+ const newArray = [];
+ for (const item of b) {
+ if (item === "...") {
+ for (const item of a) {
+ newArray.push(item);
+ }
+ } else {
+ newArray.push(item);
+ }
+ }
+ return newArray;
+ }
+ case VALUE_TYPE_OBJECT:
+ return b.map(item => (item === "..." ? a : item));
+ default:
+ throw new Error("Not implemented");
+ }
+ default:
+ throw new Error("Not implemented");
+ }
+};
+
+/**
+ * @template T
+ * @param {T} obj the object
+ * @returns {T} the object without operations like "..." or DELETE
+ */
+const removeOperations = obj => {
+ const newObj = /** @type {T} */ ({});
+ for (const key of Object.keys(obj)) {
+ const value = obj[key];
+ const type = getValueType(value);
+ switch (type) {
+ case VALUE_TYPE_UNDEFINED:
+ case VALUE_TYPE_DELETE:
+ break;
+ case VALUE_TYPE_OBJECT:
+ newObj[key] = removeOperations(value);
+ break;
+ case VALUE_TYPE_ARRAY_EXTEND:
+ newObj[key] = value.filter(i => i !== "...");
+ break;
+ default:
+ newObj[key] = value;
+ break;
+ }
+ }
+ return newObj;
+};
+
+/**
+ * @template T
+ * @template {string} P
+ * @param {T} obj the object
+ * @param {P} byProperty the by description
+ * @param {...any} values values
+ * @returns {Omit<T, P>} object with merged byProperty
+ */
+const resolveByProperty = (obj, byProperty, ...values) => {
+ if (typeof obj !== "object" || obj === null || !(byProperty in obj)) {
+ return obj;
+ }
+ const { [byProperty]: _byValue, ..._remaining } = /** @type {object} */ (obj);
+ const remaining = /** @type {T} */ (_remaining);
+ const byValue = /** @type {Record<string, T> | function(...any[]): T} */ (
+ _byValue
+ );
+ if (typeof byValue === "object") {
+ const key = values[0];
+ if (key in byValue) {
+ return cachedCleverMerge(remaining, byValue[key]);
+ } else if ("default" in byValue) {
+ return cachedCleverMerge(remaining, byValue.default);
+ } else {
+ return /** @type {T} */ (remaining);
+ }
+ } else if (typeof byValue === "function") {
+ const result = byValue.apply(null, values);
+ return cachedCleverMerge(
+ remaining,
+ resolveByProperty(result, byProperty, ...values)
+ );
+ }
+};
+
+exports.cachedSetProperty = cachedSetProperty;
+exports.cachedCleverMerge = cachedCleverMerge;
+exports.cleverMerge = cleverMerge;
+exports.resolveByProperty = resolveByProperty;
+exports.removeOperations = removeOperations;
+exports.DELETE = DELETE;