| | |
| | | |
| | | "use strict"; |
| | | |
| | | const { getFullModuleName } = require("../ids/IdHelpers"); |
| | | const { compareRuntime } = require("./runtime"); |
| | | |
| | | /** @typedef {import("../Chunk")} Chunk */ |
| | |
| | | /** @typedef {import("../ChunkGraph")} ChunkGraph */ |
| | | /** @typedef {import("../ChunkGraph").ModuleId} ModuleId */ |
| | | /** @typedef {import("../ChunkGroup")} ChunkGroup */ |
| | | /** @typedef {import("../Compiler")} Compiler */ |
| | | /** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */ |
| | | /** @typedef {import("../Dependency")} Dependency */ |
| | | /** @typedef {import("../dependencies/HarmonyImportSideEffectDependency")} HarmonyImportSideEffectDependency */ |
| | |
| | | /** @typedef {import("../dependencies/ModuleDependency")} ModuleDependency */ |
| | | |
| | | /** |
| | | * Defines the dependency source order type used by this module. |
| | | * @typedef {object} DependencySourceOrder |
| | | * @property {number} main the main source order |
| | | * @property {number} sub the sub source order |
| | | */ |
| | | |
| | | /** |
| | | * Defines the comparator type used by this module. |
| | | * @template T |
| | | * @typedef {(a: T, b: T) => -1 | 0 | 1} Comparator |
| | | */ |
| | | /** |
| | | * Defines the raw parameterized comparator type used by this module. |
| | | * @template {object} TArg |
| | | * @template T |
| | | * @typedef {(tArg: TArg, a: T, b: T) => -1 | 0 | 1} RawParameterizedComparator |
| | | */ |
| | | /** |
| | | * Defines the parameterized comparator type used by this module. |
| | | * @template {object} TArg |
| | | * @template T |
| | | * @typedef {(tArg: TArg) => Comparator<T>} ParameterizedComparator |
| | | */ |
| | | |
| | | /** |
| | | * Creates a cached parameterized comparator. |
| | | * @template {object} TArg |
| | | * @template {object} T |
| | | * @param {RawParameterizedComparator<TArg, T>} fn comparator with argument |
| | |
| | | const cachedResult = map.get(/** @type {EXPECTED_OBJECT} */ (arg)); |
| | | if (cachedResult !== undefined) return cachedResult; |
| | | /** |
| | | * Returns compare result. |
| | | * @param {T} a first item |
| | | * @param {T} b second item |
| | | * @returns {-1|0|1} compare result |
| | | * @returns {-1 | 0 | 1} compare result |
| | | */ |
| | | const result = fn.bind(null, arg); |
| | | map.set(/** @type {EXPECTED_OBJECT} */ (arg), result); |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Compares the provided values and returns their ordering. |
| | | * @param {string | number} a first id |
| | | * @param {string | number} b second id |
| | | * @returns {-1 | 0 | 1} compare result |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Compares iterables. |
| | | * @template T |
| | | * @param {Comparator<T>} elementComparator comparator for elements |
| | | * @returns {Comparator<Iterable<T>>} comparator for iterables of elements |
| | |
| | | const cacheEntry = compareIteratorsCache.get(elementComparator); |
| | | if (cacheEntry !== undefined) return cacheEntry; |
| | | /** |
| | | * Returns compare result. |
| | | * @param {Iterable<T>} a first value |
| | | * @param {Iterable<T>} b second value |
| | | * @returns {-1|0|1} compare result |
| | | * @returns {-1 | 0 | 1} compare result |
| | | */ |
| | | const result = (a, b) => { |
| | | const aI = a[Symbol.iterator](); |
| | |
| | | * Compare two locations |
| | | * @param {DependencyLocation} a A location node |
| | | * @param {DependencyLocation} b A location node |
| | | * @returns {-1|0|1} sorting comparator value |
| | | * @returns {-1 | 0 | 1} sorting comparator value |
| | | */ |
| | | const compareLocations = (a, b) => { |
| | | const isObjectA = typeof a === "object" && a !== null; |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Compares modules by id. |
| | | * @param {ChunkGraph} chunkGraph the chunk graph |
| | | * @param {Module} a module |
| | | * @param {Module} b module |
| | | * @returns {-1|0|1} compare result |
| | | * @returns {-1 | 0 | 1} compare result |
| | | */ |
| | | const compareModulesById = (chunkGraph, a, b) => |
| | | compareIds( |
| | |
| | | ); |
| | | |
| | | /** |
| | | * Compares the provided values and returns their ordering. |
| | | * @param {number} a number |
| | | * @param {number} b number |
| | | * @returns {-1|0|1} compare result |
| | | * @returns {-1 | 0 | 1} compare result |
| | | */ |
| | | const compareNumbers = (a, b) => { |
| | | if (typeof a !== typeof b) { |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Compares strings numeric. |
| | | * @param {string} a string |
| | | * @param {string} b string |
| | | * @returns {-1|0|1} compare result |
| | | * @returns {-1 | 0 | 1} compare result |
| | | */ |
| | | const compareStringsNumeric = (a, b) => { |
| | | const aLength = a.length; |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Compares modules by post order index or identifier. |
| | | * @param {ModuleGraph} moduleGraph the module graph |
| | | * @param {Module} a module |
| | | * @param {Module} b module |
| | | * @returns {-1|0|1} compare result |
| | | * @returns {-1 | 0 | 1} compare result |
| | | */ |
| | | const compareModulesByPostOrderIndexOrIdentifier = (moduleGraph, a, b) => { |
| | | const cmp = compareNumbers( |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Compares modules by pre order index or identifier. |
| | | * @param {ModuleGraph} moduleGraph the module graph |
| | | * @param {Module} a module |
| | | * @param {Module} b module |
| | | * @returns {-1|0|1} compare result |
| | | * @returns {-1 | 0 | 1} compare result |
| | | */ |
| | | const compareModulesByPreOrderIndexOrIdentifier = (moduleGraph, a, b) => { |
| | | const cmp = compareNumbers( |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Compares modules by id or identifier. |
| | | * @param {ChunkGraph} chunkGraph the chunk graph |
| | | * @param {Module} a module |
| | | * @param {Module} b module |
| | | * @returns {-1|0|1} compare result |
| | | * @returns {-1 | 0 | 1} compare result |
| | | */ |
| | | const compareModulesByIdOrIdentifier = (chunkGraph, a, b) => { |
| | | const cmp = compareIds( |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Compare modules by their full name. This differs from comparing by identifier in that the values have been normalized to be relative to the compiler context. |
| | | * @param {{ context: string, root: object }} compiler the compiler, used for context and cache |
| | | * @param {Module} a module |
| | | * @param {Module} b module |
| | | * @returns {-1 | 0 | 1} compare result |
| | | */ |
| | | const compareModulesByFullName = (compiler, a, b) => { |
| | | const aName = getFullModuleName(a, compiler.context, compiler.root); |
| | | const bName = getFullModuleName(b, compiler.context, compiler.root); |
| | | return compareIds(aName, bName); |
| | | }; |
| | | |
| | | /** |
| | | * Compares the provided values and returns their ordering. |
| | | * @param {ChunkGraph} chunkGraph the chunk graph |
| | | * @param {Chunk} a chunk |
| | | * @param {Chunk} b chunk |
| | |
| | | const compareChunks = (chunkGraph, a, b) => chunkGraph.compareChunks(a, b); |
| | | |
| | | /** |
| | | * Compares the provided values and returns their ordering. |
| | | * @param {string} a first string |
| | | * @param {string} b second string |
| | | * @returns {-1|0|1} compare result |
| | | * @returns {-1 | 0 | 1} compare result |
| | | */ |
| | | const compareStrings = (a, b) => { |
| | | if (a < b) return -1; |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Compares chunk groups by index. |
| | | * @param {ChunkGroup} a first chunk group |
| | | * @param {ChunkGroup} b second chunk group |
| | | * @returns {-1 | 0 | 1} compare result |
| | |
| | | /** @type {number} */ (a.index) < /** @type {number} */ (b.index) ? -1 : 1; |
| | | |
| | | /** |
| | | * Represents TwoKeyWeakMap. |
| | | * @template {EXPECTED_OBJECT} K1 |
| | | * @template {EXPECTED_OBJECT} K2 |
| | | * @template T |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns value. |
| | | * @param {K1} key1 first key |
| | | * @param {K2} key2 second key |
| | | * @returns {T | undefined} value |
| | |
| | | } |
| | | |
| | | /** |
| | | * Updates value using the provided key1. |
| | | * @param {K1} key1 first key |
| | | * @param {K2} key2 second key |
| | | * @param {T | undefined} value new value |
| | |
| | | const concatComparatorsCache = new TwoKeyWeakMap(); |
| | | |
| | | /** |
| | | * Concat comparators. |
| | | * @template T |
| | | * @param {Comparator<T>} c1 comparator |
| | | * @param {Comparator<T>} c2 comparator |
| | |
| | | ); |
| | | if (cacheEntry !== undefined) return cacheEntry; |
| | | /** |
| | | * Returns compare result. |
| | | * @param {T} a first value |
| | | * @param {T} b second value |
| | | * @returns {-1|0|1} compare result |
| | | * @returns {-1 | 0 | 1} compare result |
| | | */ |
| | | const result = (a, b) => { |
| | | const res = c1(a, b); |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Defines the selector type used by this module. |
| | | * @template A, B |
| | | * @typedef {(input: A) => B | undefined | null} Selector |
| | | */ |
| | |
| | | const compareSelectCache = new TwoKeyWeakMap(); |
| | | |
| | | /** |
| | | * Compares the provided values and returns their ordering. |
| | | * @template T |
| | | * @template R |
| | | * @param {Selector<T, R>} getter getter for value |
| | |
| | | const cacheEntry = compareSelectCache.get(getter, comparator); |
| | | if (cacheEntry !== undefined) return cacheEntry; |
| | | /** |
| | | * Returns compare result. |
| | | * @param {T} a first value |
| | | * @param {T} b second value |
| | | * @returns {-1|0|1} compare result |
| | | * @returns {-1 | 0 | 1} compare result |
| | | */ |
| | | const result = (a, b) => { |
| | | const aValue = getter(a); |
| | |
| | | // TODO this is no longer needed when minimum node.js version is >= 12 |
| | | // since these versions ship with a stable sort function |
| | | /** |
| | | * Keep original order. |
| | | * @template T |
| | | * @param {Iterable<T>} iterable original ordered list |
| | | * @returns {Comparator<T>} comparator |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Compares chunks natural. |
| | | * @param {ChunkGraph} chunkGraph the chunk graph |
| | | * @returns {Comparator<Chunk>} comparator |
| | | */ |
| | |
| | | compareSelect((chunk) => chunk.runtime, compareRuntime), |
| | | compareSelect( |
| | | /** |
| | | * Handles the callback logic for this hook. |
| | | * @param {Chunk} chunk a chunk |
| | | * @returns {Iterable<Module>} modules |
| | | */ |
| | |
| | | * https://github.com/webpack/webpack/pull/19686 |
| | | * @param {Dependency[]} dependencies dependencies |
| | | * @param {WeakMap<Dependency, DependencySourceOrder>} dependencySourceOrderMap dependency source order map |
| | | * @param {((dep: Dependency, index: number) => void)=} onDependencyReSort optional callback to set index for each dependency |
| | | * @returns {void} |
| | | */ |
| | | const sortWithSourceOrder = (dependencies, dependencySourceOrderMap) => { |
| | | /** |
| | | * @param {Dependency} dep dependency |
| | | * @returns {number} source order |
| | | */ |
| | | const getSourceOrder = (dep) => { |
| | | if (dependencySourceOrderMap.has(dep)) { |
| | | const { main } = /** @type {DependencySourceOrder} */ ( |
| | | dependencySourceOrderMap.get(dep) |
| | | ); |
| | | return main; |
| | | } |
| | | return /** @type {number} */ ( |
| | | /** @type {ModuleDependency} */ (dep).sourceOrder |
| | | ); |
| | | }; |
| | | |
| | | /** |
| | | * If the sourceOrder is a number, it means the dependency needs to be sorted. |
| | | * @param {number | undefined} sourceOrder sourceOrder |
| | | * @returns {boolean} needReSort |
| | | */ |
| | | const needReSort = (sourceOrder) => { |
| | | if (typeof sourceOrder === "number") { |
| | | return true; |
| | | } |
| | | return false; |
| | | }; |
| | | |
| | | // Extract dependencies with sourceOrder and sort them |
| | | const sortWithSourceOrder = ( |
| | | dependencies, |
| | | dependencySourceOrderMap, |
| | | onDependencyReSort |
| | | ) => { |
| | | /** @type {{ dep: Dependency, main: number, sub: number }[]} */ |
| | | const withSourceOrder = []; |
| | | /** @type {number[]} */ |
| | | const positions = []; |
| | | |
| | | // First pass: collect dependencies with sourceOrder |
| | | for (let i = 0; i < dependencies.length; i++) { |
| | | const dep = dependencies[i]; |
| | | const sourceOrder = getSourceOrder(dep); |
| | | const cached = dependencySourceOrderMap.get(dep); |
| | | |
| | | if (needReSort(sourceOrder)) { |
| | | withSourceOrder.push({ dep, sourceOrder, originalIndex: i }); |
| | | if (cached) { |
| | | positions.push(i); |
| | | withSourceOrder.push({ |
| | | dep, |
| | | main: cached.main, |
| | | sub: cached.sub |
| | | }); |
| | | } else { |
| | | const sourceOrder = /** @type {number | undefined} */ ( |
| | | /** @type {ModuleDependency} */ (dep).sourceOrder |
| | | ); |
| | | if (typeof sourceOrder === "number") { |
| | | positions.push(i); |
| | | withSourceOrder.push({ |
| | | dep, |
| | | main: sourceOrder, |
| | | sub: 0 |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | return; |
| | | } |
| | | |
| | | // Sort dependencies with sourceOrder |
| | | withSourceOrder.sort((a, b) => { |
| | | // Handle both dependencies in map case |
| | | if ( |
| | | dependencySourceOrderMap.has(a.dep) && |
| | | dependencySourceOrderMap.has(b.dep) |
| | | ) { |
| | | const { main: mainA, sub: subA } = /** @type {DependencySourceOrder} */ ( |
| | | dependencySourceOrderMap.get(a.dep) |
| | | ); |
| | | const { main: mainB, sub: subB } = /** @type {DependencySourceOrder} */ ( |
| | | dependencySourceOrderMap.get(b.dep) |
| | | ); |
| | | if (mainA === mainB) { |
| | | return compareNumbers(subA, subB); |
| | | } |
| | | return compareNumbers(mainA, mainB); |
| | | if (a.main !== b.main) { |
| | | return compareNumbers(a.main, b.main); |
| | | } |
| | | |
| | | return compareNumbers(a.sourceOrder, b.sourceOrder); |
| | | return compareNumbers(a.sub, b.sub); |
| | | }); |
| | | |
| | | // Second pass: build result array |
| | | let sortedIndex = 0; |
| | | for (let i = 0; i < dependencies.length; i++) { |
| | | const dep = dependencies[i]; |
| | | const sourceOrder = getSourceOrder(dep); |
| | | |
| | | if (needReSort(sourceOrder)) { |
| | | dependencies[i] = withSourceOrder[sortedIndex].dep; |
| | | sortedIndex++; |
| | | // Second pass: place sorted deps back to original positions |
| | | for (let i = 0; i < positions.length; i++) { |
| | | const depIndex = positions[i]; |
| | | dependencies[depIndex] = withSourceOrder[i].dep; |
| | | if (onDependencyReSort) { |
| | | onDependencyReSort(dependencies[depIndex], depIndex); |
| | | } |
| | | } |
| | | }; |
| | |
| | | module.exports.compareChunks = |
| | | createCachedParameterizedComparator(compareChunks); |
| | | /** |
| | | * Returns compare result. |
| | | * @param {Chunk} a chunk |
| | | * @param {Chunk} b chunk |
| | | * @returns {-1|0|1} compare result |
| | | * @returns {-1 | 0 | 1} compare result |
| | | */ |
| | | module.exports.compareChunksById = (a, b) => |
| | | compareIds(/** @type {ChunkId} */ (a.id), /** @type {ChunkId} */ (b.id)); |
| | |
| | | |
| | | module.exports.compareLocations = compareLocations; |
| | | |
| | | /** @type {ParameterizedComparator<Compiler, Module>} */ |
| | | module.exports.compareModulesByFullName = createCachedParameterizedComparator( |
| | | compareModulesByFullName |
| | | ); |
| | | |
| | | /** @type {ParameterizedComparator<ChunkGraph, Module>} */ |
| | | module.exports.compareModulesById = |
| | | createCachedParameterizedComparator(compareModulesById); |
| | |
| | | module.exports.compareModulesByIdOrIdentifier = |
| | | createCachedParameterizedComparator(compareModulesByIdOrIdentifier); |
| | | /** |
| | | * Returns compare result. |
| | | * @param {Module} a module |
| | | * @param {Module} b module |
| | | * @returns {-1|0|1} compare result |
| | | * @returns {-1 | 0 | 1} compare result |
| | | */ |
| | | module.exports.compareModulesByIdentifier = (a, b) => |
| | | compareIds(a.identifier(), b.identifier()); |