comparators.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { getFullModuleName } = require("../ids/IdHelpers");
  7. const { compareRuntime } = require("./runtime");
  8. /** @typedef {import("../Chunk")} Chunk */
  9. /** @typedef {import("../Chunk").ChunkName} ChunkName */
  10. /** @typedef {import("../Chunk").ChunkId} ChunkId */
  11. /** @typedef {import("../ChunkGraph")} ChunkGraph */
  12. /** @typedef {import("../ChunkGraph").ModuleId} ModuleId */
  13. /** @typedef {import("../ChunkGroup")} ChunkGroup */
  14. /** @typedef {import("../Compiler")} Compiler */
  15. /** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
  16. /** @typedef {import("../Dependency")} Dependency */
  17. /** @typedef {import("../dependencies/HarmonyImportSideEffectDependency")} HarmonyImportSideEffectDependency */
  18. /** @typedef {import("../dependencies/HarmonyImportSpecifierDependency")} HarmonyImportSpecifierDependency */
  19. /** @typedef {import("../Module")} Module */
  20. /** @typedef {import("../ModuleGraph")} ModuleGraph */
  21. /** @typedef {import("../dependencies/ModuleDependency")} ModuleDependency */
  22. /**
  23. * Defines the dependency source order type used by this module.
  24. * @typedef {object} DependencySourceOrder
  25. * @property {number} main the main source order
  26. * @property {number} sub the sub source order
  27. */
  28. /**
  29. * Defines the comparator type used by this module.
  30. * @template T
  31. * @typedef {(a: T, b: T) => -1 | 0 | 1} Comparator
  32. */
  33. /**
  34. * Defines the raw parameterized comparator type used by this module.
  35. * @template {object} TArg
  36. * @template T
  37. * @typedef {(tArg: TArg, a: T, b: T) => -1 | 0 | 1} RawParameterizedComparator
  38. */
  39. /**
  40. * Defines the parameterized comparator type used by this module.
  41. * @template {object} TArg
  42. * @template T
  43. * @typedef {(tArg: TArg) => Comparator<T>} ParameterizedComparator
  44. */
  45. /**
  46. * Creates a cached parameterized comparator.
  47. * @template {object} TArg
  48. * @template {object} T
  49. * @param {RawParameterizedComparator<TArg, T>} fn comparator with argument
  50. * @returns {ParameterizedComparator<TArg, T>} comparator
  51. */
  52. const createCachedParameterizedComparator = (fn) => {
  53. /** @type {WeakMap<EXPECTED_OBJECT, Comparator<T>>} */
  54. const map = new WeakMap();
  55. return (arg) => {
  56. const cachedResult = map.get(/** @type {EXPECTED_OBJECT} */ (arg));
  57. if (cachedResult !== undefined) return cachedResult;
  58. /**
  59. * Returns compare result.
  60. * @param {T} a first item
  61. * @param {T} b second item
  62. * @returns {-1 | 0 | 1} compare result
  63. */
  64. const result = fn.bind(null, arg);
  65. map.set(/** @type {EXPECTED_OBJECT} */ (arg), result);
  66. return result;
  67. };
  68. };
  69. /**
  70. * Compares the provided values and returns their ordering.
  71. * @param {string | number} a first id
  72. * @param {string | number} b second id
  73. * @returns {-1 | 0 | 1} compare result
  74. */
  75. const compareIds = (a, b) => {
  76. if (typeof a !== typeof b) {
  77. return typeof a < typeof b ? -1 : 1;
  78. }
  79. if (a < b) return -1;
  80. if (a > b) return 1;
  81. return 0;
  82. };
  83. /**
  84. * Compares iterables.
  85. * @template T
  86. * @param {Comparator<T>} elementComparator comparator for elements
  87. * @returns {Comparator<Iterable<T>>} comparator for iterables of elements
  88. */
  89. const compareIterables = (elementComparator) => {
  90. const cacheEntry = compareIteratorsCache.get(elementComparator);
  91. if (cacheEntry !== undefined) return cacheEntry;
  92. /**
  93. * Returns compare result.
  94. * @param {Iterable<T>} a first value
  95. * @param {Iterable<T>} b second value
  96. * @returns {-1 | 0 | 1} compare result
  97. */
  98. const result = (a, b) => {
  99. const aI = a[Symbol.iterator]();
  100. const bI = b[Symbol.iterator]();
  101. while (true) {
  102. const aItem = aI.next();
  103. const bItem = bI.next();
  104. if (aItem.done) {
  105. return bItem.done ? 0 : -1;
  106. } else if (bItem.done) {
  107. return 1;
  108. }
  109. const res = elementComparator(aItem.value, bItem.value);
  110. if (res !== 0) return res;
  111. }
  112. };
  113. compareIteratorsCache.set(elementComparator, result);
  114. return result;
  115. };
  116. /**
  117. * Compare two locations
  118. * @param {DependencyLocation} a A location node
  119. * @param {DependencyLocation} b A location node
  120. * @returns {-1 | 0 | 1} sorting comparator value
  121. */
  122. const compareLocations = (a, b) => {
  123. const isObjectA = typeof a === "object" && a !== null;
  124. const isObjectB = typeof b === "object" && b !== null;
  125. if (!isObjectA || !isObjectB) {
  126. if (isObjectA) return 1;
  127. if (isObjectB) return -1;
  128. return 0;
  129. }
  130. if ("start" in a) {
  131. if ("start" in b) {
  132. const ap = a.start;
  133. const bp = b.start;
  134. if (ap.line < bp.line) return -1;
  135. if (ap.line > bp.line) return 1;
  136. if (
  137. /** @type {number} */ (ap.column) < /** @type {number} */ (bp.column)
  138. ) {
  139. return -1;
  140. }
  141. if (
  142. /** @type {number} */ (ap.column) > /** @type {number} */ (bp.column)
  143. ) {
  144. return 1;
  145. }
  146. } else {
  147. return -1;
  148. }
  149. } else if ("start" in b) {
  150. return 1;
  151. }
  152. if ("name" in a) {
  153. if ("name" in b) {
  154. if (a.name < b.name) return -1;
  155. if (a.name > b.name) return 1;
  156. } else {
  157. return -1;
  158. }
  159. } else if ("name" in b) {
  160. return 1;
  161. }
  162. if ("index" in a) {
  163. if ("index" in b) {
  164. if (/** @type {number} */ (a.index) < /** @type {number} */ (b.index)) {
  165. return -1;
  166. }
  167. if (/** @type {number} */ (a.index) > /** @type {number} */ (b.index)) {
  168. return 1;
  169. }
  170. } else {
  171. return -1;
  172. }
  173. } else if ("index" in b) {
  174. return 1;
  175. }
  176. return 0;
  177. };
  178. /**
  179. * Compares modules by id.
  180. * @param {ChunkGraph} chunkGraph the chunk graph
  181. * @param {Module} a module
  182. * @param {Module} b module
  183. * @returns {-1 | 0 | 1} compare result
  184. */
  185. const compareModulesById = (chunkGraph, a, b) =>
  186. compareIds(
  187. /** @type {ModuleId} */ (chunkGraph.getModuleId(a)),
  188. /** @type {ModuleId} */ (chunkGraph.getModuleId(b))
  189. );
  190. /**
  191. * Compares the provided values and returns their ordering.
  192. * @param {number} a number
  193. * @param {number} b number
  194. * @returns {-1 | 0 | 1} compare result
  195. */
  196. const compareNumbers = (a, b) => {
  197. if (typeof a !== typeof b) {
  198. return typeof a < typeof b ? -1 : 1;
  199. }
  200. if (a < b) return -1;
  201. if (a > b) return 1;
  202. return 0;
  203. };
  204. /**
  205. * Compares strings numeric.
  206. * @param {string} a string
  207. * @param {string} b string
  208. * @returns {-1 | 0 | 1} compare result
  209. */
  210. const compareStringsNumeric = (a, b) => {
  211. const aLength = a.length;
  212. const bLength = b.length;
  213. let aChar = 0;
  214. let bChar = 0;
  215. let aIsDigit = false;
  216. let bIsDigit = false;
  217. let i = 0;
  218. let j = 0;
  219. while (i < aLength && j < bLength) {
  220. aChar = a.charCodeAt(i);
  221. bChar = b.charCodeAt(j);
  222. aIsDigit = aChar >= 48 && aChar <= 57;
  223. bIsDigit = bChar >= 48 && bChar <= 57;
  224. if (!aIsDigit && !bIsDigit) {
  225. if (aChar < bChar) return -1;
  226. if (aChar > bChar) return 1;
  227. i++;
  228. j++;
  229. } else if (aIsDigit && !bIsDigit) {
  230. // This segment of a is shorter than in b
  231. return 1;
  232. } else if (!aIsDigit && bIsDigit) {
  233. // This segment of b is shorter than in a
  234. return -1;
  235. } else {
  236. let aNumber = aChar - 48;
  237. let bNumber = bChar - 48;
  238. while (++i < aLength) {
  239. aChar = a.charCodeAt(i);
  240. if (aChar < 48 || aChar > 57) break;
  241. aNumber = aNumber * 10 + aChar - 48;
  242. }
  243. while (++j < bLength) {
  244. bChar = b.charCodeAt(j);
  245. if (bChar < 48 || bChar > 57) break;
  246. bNumber = bNumber * 10 + bChar - 48;
  247. }
  248. if (aNumber < bNumber) return -1;
  249. if (aNumber > bNumber) return 1;
  250. }
  251. }
  252. if (j < bLength) {
  253. // a is shorter than b
  254. bChar = b.charCodeAt(j);
  255. bIsDigit = bChar >= 48 && bChar <= 57;
  256. return bIsDigit ? -1 : 1;
  257. }
  258. if (i < aLength) {
  259. // b is shorter than a
  260. aChar = a.charCodeAt(i);
  261. aIsDigit = aChar >= 48 && aChar <= 57;
  262. return aIsDigit ? 1 : -1;
  263. }
  264. return 0;
  265. };
  266. /**
  267. * Compares modules by post order index or identifier.
  268. * @param {ModuleGraph} moduleGraph the module graph
  269. * @param {Module} a module
  270. * @param {Module} b module
  271. * @returns {-1 | 0 | 1} compare result
  272. */
  273. const compareModulesByPostOrderIndexOrIdentifier = (moduleGraph, a, b) => {
  274. const cmp = compareNumbers(
  275. /** @type {number} */ (moduleGraph.getPostOrderIndex(a)),
  276. /** @type {number} */ (moduleGraph.getPostOrderIndex(b))
  277. );
  278. if (cmp !== 0) return cmp;
  279. return compareIds(a.identifier(), b.identifier());
  280. };
  281. /**
  282. * Compares modules by pre order index or identifier.
  283. * @param {ModuleGraph} moduleGraph the module graph
  284. * @param {Module} a module
  285. * @param {Module} b module
  286. * @returns {-1 | 0 | 1} compare result
  287. */
  288. const compareModulesByPreOrderIndexOrIdentifier = (moduleGraph, a, b) => {
  289. const cmp = compareNumbers(
  290. /** @type {number} */ (moduleGraph.getPreOrderIndex(a)),
  291. /** @type {number} */ (moduleGraph.getPreOrderIndex(b))
  292. );
  293. if (cmp !== 0) return cmp;
  294. return compareIds(a.identifier(), b.identifier());
  295. };
  296. /**
  297. * Compares modules by id or identifier.
  298. * @param {ChunkGraph} chunkGraph the chunk graph
  299. * @param {Module} a module
  300. * @param {Module} b module
  301. * @returns {-1 | 0 | 1} compare result
  302. */
  303. const compareModulesByIdOrIdentifier = (chunkGraph, a, b) => {
  304. const cmp = compareIds(
  305. /** @type {ModuleId} */ (chunkGraph.getModuleId(a)),
  306. /** @type {ModuleId} */ (chunkGraph.getModuleId(b))
  307. );
  308. if (cmp !== 0) return cmp;
  309. return compareIds(a.identifier(), b.identifier());
  310. };
  311. /**
  312. * 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.
  313. * @param {{ context: string, root: object }} compiler the compiler, used for context and cache
  314. * @param {Module} a module
  315. * @param {Module} b module
  316. * @returns {-1 | 0 | 1} compare result
  317. */
  318. const compareModulesByFullName = (compiler, a, b) => {
  319. const aName = getFullModuleName(a, compiler.context, compiler.root);
  320. const bName = getFullModuleName(b, compiler.context, compiler.root);
  321. return compareIds(aName, bName);
  322. };
  323. /**
  324. * Compares the provided values and returns their ordering.
  325. * @param {ChunkGraph} chunkGraph the chunk graph
  326. * @param {Chunk} a chunk
  327. * @param {Chunk} b chunk
  328. * @returns {-1 | 0 | 1} compare result
  329. */
  330. const compareChunks = (chunkGraph, a, b) => chunkGraph.compareChunks(a, b);
  331. /**
  332. * Compares the provided values and returns their ordering.
  333. * @param {string} a first string
  334. * @param {string} b second string
  335. * @returns {-1 | 0 | 1} compare result
  336. */
  337. const compareStrings = (a, b) => {
  338. if (a < b) return -1;
  339. if (a > b) return 1;
  340. return 0;
  341. };
  342. /**
  343. * Compares chunk groups by index.
  344. * @param {ChunkGroup} a first chunk group
  345. * @param {ChunkGroup} b second chunk group
  346. * @returns {-1 | 0 | 1} compare result
  347. */
  348. const compareChunkGroupsByIndex = (a, b) =>
  349. /** @type {number} */ (a.index) < /** @type {number} */ (b.index) ? -1 : 1;
  350. /**
  351. * Represents TwoKeyWeakMap.
  352. * @template {EXPECTED_OBJECT} K1
  353. * @template {EXPECTED_OBJECT} K2
  354. * @template T
  355. */
  356. class TwoKeyWeakMap {
  357. constructor() {
  358. /**
  359. * @private
  360. * @type {WeakMap<K1, WeakMap<K2, T | undefined>>}
  361. */
  362. this._map = new WeakMap();
  363. }
  364. /**
  365. * Returns value.
  366. * @param {K1} key1 first key
  367. * @param {K2} key2 second key
  368. * @returns {T | undefined} value
  369. */
  370. get(key1, key2) {
  371. const childMap = this._map.get(key1);
  372. if (childMap === undefined) {
  373. return;
  374. }
  375. return childMap.get(key2);
  376. }
  377. /**
  378. * Updates value using the provided key1.
  379. * @param {K1} key1 first key
  380. * @param {K2} key2 second key
  381. * @param {T | undefined} value new value
  382. * @returns {void}
  383. */
  384. set(key1, key2, value) {
  385. let childMap = this._map.get(key1);
  386. if (childMap === undefined) {
  387. childMap = new WeakMap();
  388. this._map.set(key1, childMap);
  389. }
  390. childMap.set(key2, value);
  391. }
  392. }
  393. /** @type {TwoKeyWeakMap<Comparator<EXPECTED_ANY>, Comparator<EXPECTED_ANY>, Comparator<EXPECTED_ANY>>}} */
  394. const concatComparatorsCache = new TwoKeyWeakMap();
  395. /**
  396. * Concat comparators.
  397. * @template T
  398. * @param {Comparator<T>} c1 comparator
  399. * @param {Comparator<T>} c2 comparator
  400. * @param {Comparator<T>[]} cRest comparators
  401. * @returns {Comparator<T>} comparator
  402. */
  403. const concatComparators = (c1, c2, ...cRest) => {
  404. if (cRest.length > 0) {
  405. const [c3, ...cRest2] = cRest;
  406. return concatComparators(c1, concatComparators(c2, c3, ...cRest2));
  407. }
  408. const cacheEntry = /** @type {Comparator<T>} */ (
  409. concatComparatorsCache.get(c1, c2)
  410. );
  411. if (cacheEntry !== undefined) return cacheEntry;
  412. /**
  413. * Returns compare result.
  414. * @param {T} a first value
  415. * @param {T} b second value
  416. * @returns {-1 | 0 | 1} compare result
  417. */
  418. const result = (a, b) => {
  419. const res = c1(a, b);
  420. if (res !== 0) return res;
  421. return c2(a, b);
  422. };
  423. concatComparatorsCache.set(c1, c2, result);
  424. return result;
  425. };
  426. /**
  427. * Defines the selector type used by this module.
  428. * @template A, B
  429. * @typedef {(input: A) => B | undefined | null} Selector
  430. */
  431. /** @type {TwoKeyWeakMap<Selector<EXPECTED_ANY, EXPECTED_ANY>, Comparator<EXPECTED_ANY>, Comparator<EXPECTED_ANY>>}} */
  432. const compareSelectCache = new TwoKeyWeakMap();
  433. /**
  434. * Compares the provided values and returns their ordering.
  435. * @template T
  436. * @template R
  437. * @param {Selector<T, R>} getter getter for value
  438. * @param {Comparator<R>} comparator comparator
  439. * @returns {Comparator<T>} comparator
  440. */
  441. const compareSelect = (getter, comparator) => {
  442. const cacheEntry = compareSelectCache.get(getter, comparator);
  443. if (cacheEntry !== undefined) return cacheEntry;
  444. /**
  445. * Returns compare result.
  446. * @param {T} a first value
  447. * @param {T} b second value
  448. * @returns {-1 | 0 | 1} compare result
  449. */
  450. const result = (a, b) => {
  451. const aValue = getter(a);
  452. const bValue = getter(b);
  453. if (aValue !== undefined && aValue !== null) {
  454. if (bValue !== undefined && bValue !== null) {
  455. return comparator(aValue, bValue);
  456. }
  457. return -1;
  458. }
  459. if (bValue !== undefined && bValue !== null) {
  460. return 1;
  461. }
  462. return 0;
  463. };
  464. compareSelectCache.set(getter, comparator, result);
  465. return result;
  466. };
  467. /** @type {WeakMap<Comparator<EXPECTED_ANY>, Comparator<Iterable<EXPECTED_ANY>>>} */
  468. const compareIteratorsCache = new WeakMap();
  469. // TODO this is no longer needed when minimum node.js version is >= 12
  470. // since these versions ship with a stable sort function
  471. /**
  472. * Keep original order.
  473. * @template T
  474. * @param {Iterable<T>} iterable original ordered list
  475. * @returns {Comparator<T>} comparator
  476. */
  477. const keepOriginalOrder = (iterable) => {
  478. /** @type {Map<T, number>} */
  479. const map = new Map();
  480. let i = 0;
  481. for (const item of iterable) {
  482. map.set(item, i++);
  483. }
  484. return (a, b) =>
  485. compareNumbers(
  486. /** @type {number} */ (map.get(a)),
  487. /** @type {number} */ (map.get(b))
  488. );
  489. };
  490. /**
  491. * Compares chunks natural.
  492. * @param {ChunkGraph} chunkGraph the chunk graph
  493. * @returns {Comparator<Chunk>} comparator
  494. */
  495. const compareChunksNatural = (chunkGraph) => {
  496. const cmpFn = module.exports.compareModulesById(chunkGraph);
  497. const cmpIterableFn = compareIterables(cmpFn);
  498. return concatComparators(
  499. compareSelect((chunk) => /** @type {ChunkName} */ (chunk.name), compareIds),
  500. compareSelect((chunk) => chunk.runtime, compareRuntime),
  501. compareSelect(
  502. /**
  503. * Handles the callback logic for this hook.
  504. * @param {Chunk} chunk a chunk
  505. * @returns {Iterable<Module>} modules
  506. */
  507. (chunk) => chunkGraph.getOrderedChunkModulesIterable(chunk, cmpFn),
  508. cmpIterableFn
  509. )
  510. );
  511. };
  512. /**
  513. * For HarmonyImportSideEffectDependency and HarmonyImportSpecifierDependency, we should prioritize import order to match the behavior of running modules directly in a JS engine without a bundler.
  514. * For other types like ConstDependency, we can instead prioritize usage order.
  515. * https://github.com/webpack/webpack/pull/19686
  516. * @param {Dependency[]} dependencies dependencies
  517. * @param {WeakMap<Dependency, DependencySourceOrder>} dependencySourceOrderMap dependency source order map
  518. * @param {((dep: Dependency, index: number) => void)=} onDependencyReSort optional callback to set index for each dependency
  519. * @returns {void}
  520. */
  521. const sortWithSourceOrder = (
  522. dependencies,
  523. dependencySourceOrderMap,
  524. onDependencyReSort
  525. ) => {
  526. /** @type {{ dep: Dependency, main: number, sub: number }[]} */
  527. const withSourceOrder = [];
  528. /** @type {number[]} */
  529. const positions = [];
  530. for (let i = 0; i < dependencies.length; i++) {
  531. const dep = dependencies[i];
  532. const cached = dependencySourceOrderMap.get(dep);
  533. if (cached) {
  534. positions.push(i);
  535. withSourceOrder.push({
  536. dep,
  537. main: cached.main,
  538. sub: cached.sub
  539. });
  540. } else {
  541. const sourceOrder = /** @type {number | undefined} */ (
  542. /** @type {ModuleDependency} */ (dep).sourceOrder
  543. );
  544. if (typeof sourceOrder === "number") {
  545. positions.push(i);
  546. withSourceOrder.push({
  547. dep,
  548. main: sourceOrder,
  549. sub: 0
  550. });
  551. }
  552. }
  553. }
  554. if (withSourceOrder.length <= 1) {
  555. return;
  556. }
  557. withSourceOrder.sort((a, b) => {
  558. if (a.main !== b.main) {
  559. return compareNumbers(a.main, b.main);
  560. }
  561. return compareNumbers(a.sub, b.sub);
  562. });
  563. // Second pass: place sorted deps back to original positions
  564. for (let i = 0; i < positions.length; i++) {
  565. const depIndex = positions[i];
  566. dependencies[depIndex] = withSourceOrder[i].dep;
  567. if (onDependencyReSort) {
  568. onDependencyReSort(dependencies[depIndex], depIndex);
  569. }
  570. }
  571. };
  572. module.exports.compareChunkGroupsByIndex = compareChunkGroupsByIndex;
  573. /** @type {ParameterizedComparator<ChunkGraph, Chunk>} */
  574. module.exports.compareChunks =
  575. createCachedParameterizedComparator(compareChunks);
  576. /**
  577. * Returns compare result.
  578. * @param {Chunk} a chunk
  579. * @param {Chunk} b chunk
  580. * @returns {-1 | 0 | 1} compare result
  581. */
  582. module.exports.compareChunksById = (a, b) =>
  583. compareIds(/** @type {ChunkId} */ (a.id), /** @type {ChunkId} */ (b.id));
  584. module.exports.compareChunksNatural = compareChunksNatural;
  585. module.exports.compareIds = compareIds;
  586. module.exports.compareIterables = compareIterables;
  587. module.exports.compareLocations = compareLocations;
  588. /** @type {ParameterizedComparator<Compiler, Module>} */
  589. module.exports.compareModulesByFullName = createCachedParameterizedComparator(
  590. compareModulesByFullName
  591. );
  592. /** @type {ParameterizedComparator<ChunkGraph, Module>} */
  593. module.exports.compareModulesById =
  594. createCachedParameterizedComparator(compareModulesById);
  595. /** @type {ParameterizedComparator<ChunkGraph, Module>} */
  596. module.exports.compareModulesByIdOrIdentifier =
  597. createCachedParameterizedComparator(compareModulesByIdOrIdentifier);
  598. /**
  599. * Returns compare result.
  600. * @param {Module} a module
  601. * @param {Module} b module
  602. * @returns {-1 | 0 | 1} compare result
  603. */
  604. module.exports.compareModulesByIdentifier = (a, b) =>
  605. compareIds(a.identifier(), b.identifier());
  606. /** @type {ParameterizedComparator<ModuleGraph, Module>} */
  607. module.exports.compareModulesByPostOrderIndexOrIdentifier =
  608. createCachedParameterizedComparator(
  609. compareModulesByPostOrderIndexOrIdentifier
  610. );
  611. /** @type {ParameterizedComparator<ModuleGraph, Module>} */
  612. module.exports.compareModulesByPreOrderIndexOrIdentifier =
  613. createCachedParameterizedComparator(
  614. compareModulesByPreOrderIndexOrIdentifier
  615. );
  616. module.exports.compareNumbers = compareNumbers;
  617. module.exports.compareSelect = compareSelect;
  618. module.exports.compareStrings = compareStrings;
  619. module.exports.compareStringsNumeric = compareStringsNumeric;
  620. module.exports.concatComparators = concatComparators;
  621. module.exports.keepOriginalOrder = keepOriginalOrder;
  622. module.exports.sortWithSourceOrder = sortWithSourceOrder;