webpack 4.20.2 → 4.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +20 -20
  2. package/SECURITY.md +9 -9
  3. package/buildin/amd-define.js +3 -3
  4. package/buildin/amd-options.js +2 -2
  5. package/buildin/global.js +20 -20
  6. package/buildin/module.js +22 -22
  7. package/buildin/system.js +7 -7
  8. package/declarations/WebpackOptions.d.ts +1388 -0
  9. package/declarations/plugins/BannerPlugin.d.ts +51 -0
  10. package/declarations/plugins/DllPlugin.d.ts +28 -0
  11. package/declarations/plugins/DllReferencePlugin.d.ts +126 -0
  12. package/declarations/plugins/HashedModuleIdsPlugin.d.ts +24 -0
  13. package/declarations/plugins/IgnorePlugin.d.ts +27 -0
  14. package/declarations/plugins/LoaderOptionsPlugin.d.ts +27 -0
  15. package/declarations/plugins/SourceMapDevToolPlugin.d.ts +94 -0
  16. package/declarations/plugins/WatchIgnorePlugin.d.ts +10 -0
  17. package/declarations/plugins/debug/ProfilingPlugin.d.ts +12 -0
  18. package/declarations/plugins/optimize/AggressiveSplittingPlugin.d.ts +24 -0
  19. package/declarations/plugins/optimize/LimitChunkCountPlugin.d.ts +16 -0
  20. package/declarations/plugins/optimize/MinChunkSizePlugin.d.ts +12 -0
  21. package/declarations/plugins/optimize/OccurrenceOrderChunkIdsPlugin.d.ts +12 -0
  22. package/declarations/plugins/optimize/OccurrenceOrderModuleIdsPlugin.d.ts +12 -0
  23. package/hot/emitter.js +2 -2
  24. package/lib/AmdMainTemplatePlugin.js +22 -5
  25. package/lib/BasicEvaluatedExpression.js +211 -211
  26. package/lib/Chunk.js +33 -14
  27. package/lib/Compilation.js +99 -52
  28. package/lib/Compiler.js +6 -1
  29. package/lib/ConstPlugin.js +85 -0
  30. package/lib/Entrypoint.js +10 -0
  31. package/lib/ExternalModule.js +3 -2
  32. package/lib/LibraryTemplatePlugin.js +11 -8
  33. package/lib/MainTemplate.js +13 -0
  34. package/lib/Parser.js +9 -1
  35. package/lib/Stats.js +44 -45
  36. package/lib/WatchIgnorePlugin.js +4 -3
  37. package/lib/Watching.js +4 -3
  38. package/lib/dependencies/WebAssemblyExportImportedDependency.js +3 -1
  39. package/lib/node/NodeWatchFileSystem.js +16 -6
  40. package/lib/optimize/LimitChunkCountPlugin.js +11 -5
  41. package/lib/optimize/OccurrenceModuleOrderPlugin.js +5 -1
  42. package/lib/wasm/WasmFinalizeExportsPlugin.js +2 -0
  43. package/lib/wasm/WebAssemblyJavascriptGenerator.js +9 -4
  44. package/lib/wasm/WebAssemblyParser.js +2 -1
  45. package/lib/web/JsonpMainTemplate.runtime.js +1 -1
  46. package/lib/web/JsonpMainTemplatePlugin.js +1 -3
  47. package/package.json +14 -12
  48. package/schemas/WebpackOptions.json +1 -0
  49. package/schemas/ajv.absolutePath.js +55 -55
  50. package/schemas/plugins/DllReferencePlugin.json +1 -0
@@ -88,12 +88,6 @@ const compareLocations = require("./compareLocations");
88
88
  * @property {Dependency[]} dependencies
89
89
  */
90
90
 
91
- /**
92
- * @typedef {Object} AvailableModulesChunkGroupMapping
93
- * @property {ChunkGroup} chunkGroup
94
- * @property {Set<Module>} availableModules
95
- */
96
-
97
91
  /**
98
92
  * @typedef {Object} DependenciesBlockLike
99
93
  * @property {Dependency[]} dependencies
@@ -107,10 +101,11 @@ const compareLocations = require("./compareLocations");
107
101
  * @returns {-1|0|1} sort value
108
102
  */
109
103
  const byId = (a, b) => {
110
- if (a.id !== null && b.id !== null) {
111
- if (a.id < b.id) return -1;
112
- if (a.id > b.id) return 1;
104
+ if (typeof a.id !== typeof b.id) {
105
+ return typeof a.id < typeof b.id ? -1 : 1;
113
106
  }
107
+ if (a.id < b.id) return -1;
108
+ if (a.id > b.id) return 1;
114
109
  return 0;
115
110
  };
116
111
 
@@ -120,6 +115,9 @@ const byId = (a, b) => {
120
115
  * @returns {-1|0|1} sort value
121
116
  */
122
117
  const byIdOrIdentifier = (a, b) => {
118
+ if (typeof a.id !== typeof b.id) {
119
+ return typeof a.id < typeof b.id ? -1 : 1;
120
+ }
123
121
  if (a.id < b.id) return -1;
124
122
  if (a.id > b.id) return 1;
125
123
  const identA = a.identifier();
@@ -157,6 +155,16 @@ const byNameOrHash = (a, b) => {
157
155
  return 0;
158
156
  };
159
157
 
158
+ /**
159
+ * @template T
160
+ * @param {Set<T>} a first set
161
+ * @param {Set<T>} b second set
162
+ * @returns {number} cmp
163
+ */
164
+ const bySetSize = (a, b) => {
165
+ return a.size - b.size;
166
+ };
167
+
160
168
  /**
161
169
  * @param {DependenciesBlockVariable[]} variables DepBlock Variables to iterate over
162
170
  * @param {DepBlockVarDependenciesCallback} fn callback to apply on iterated elements
@@ -644,7 +652,15 @@ class Compilation extends Tapable {
644
652
  war.dependencies = dependencies;
645
653
  this.warnings.push(war);
646
654
  }
647
- module.dependencies.sort((a, b) => compareLocations(a.loc, b.loc));
655
+ const originalMap = module.dependencies.reduce((map, v, i) => {
656
+ map.set(v, i);
657
+ return map;
658
+ }, new Map());
659
+ module.dependencies.sort((a, b) => {
660
+ const cmp = compareLocations(a.loc, b.loc);
661
+ if (cmp) return cmp;
662
+ return originalMap.get(a) - originalMap.get(b);
663
+ });
648
664
  if (error) {
649
665
  this.hooks.failedModule.call(module, error);
650
666
  return callback(error);
@@ -1491,7 +1507,7 @@ class Compilation extends Tapable {
1491
1507
  // eachother and Blocks with Chunks. It stops traversing when all modules
1492
1508
  // for a chunk are already available. So it doesn't connect unneeded chunks.
1493
1509
 
1494
- /** @type {Map<ChunkGroup, {block: AsyncDependenciesBlock, chunkGroup: ChunkGroup}[]>} */
1510
+ /** @type {Map<ChunkGroup, {block: AsyncDependenciesBlock, chunkGroup: ChunkGroup, couldBeFiltered: boolean}[]>} */
1495
1511
  const chunkDependencies = new Map();
1496
1512
  const allCreatedChunkGroups = new Set();
1497
1513
 
@@ -1664,7 +1680,8 @@ class Compilation extends Tapable {
1664
1680
  if (!deps) chunkDependencies.set(chunkGroup, (deps = []));
1665
1681
  deps.push({
1666
1682
  block: b,
1667
- chunkGroup: c
1683
+ chunkGroup: c,
1684
+ couldBeFiltered: true
1668
1685
  });
1669
1686
 
1670
1687
  // 3. We enqueue the DependenciesBlock for traversal
@@ -1777,15 +1794,26 @@ class Compilation extends Tapable {
1777
1794
 
1778
1795
  // PART TWO
1779
1796
  /** @type {Set<Module>} */
1780
- let availableModules;
1781
1797
  let newAvailableModules;
1782
- /** @type {Queue<AvailableModulesChunkGroupMapping>} */
1783
- const queue2 = new Queue(
1784
- inputChunkGroups.map(chunkGroup => ({
1785
- chunkGroup,
1786
- availableModules: new Set()
1787
- }))
1788
- );
1798
+
1799
+ /**
1800
+ * @typedef {Object} ChunkGroupInfo
1801
+ * @property {Set<Module>} minAvailableModules current minimal set of modules available at this point
1802
+ * @property {Set<Module>[]} availableModulesToBeMerged enqueued updates to the minimal set of available modules
1803
+ */
1804
+
1805
+ /** @type {Map<ChunkGroup, ChunkGroupInfo>} */
1806
+ const chunkGroupInfoMap = new Map();
1807
+
1808
+ /** @type {Queue<ChunkGroup>} */
1809
+ const queue2 = new Queue(inputChunkGroups);
1810
+
1811
+ for (const chunkGroup of inputChunkGroups) {
1812
+ chunkGroupInfoMap.set(chunkGroup, {
1813
+ minAvailableModules: undefined,
1814
+ availableModulesToBeMerged: [new Set()]
1815
+ });
1816
+ }
1789
1817
 
1790
1818
  /**
1791
1819
  * Helper function to check if all modules of a chunk are available
@@ -1811,38 +1839,44 @@ class Compilation extends Tapable {
1811
1839
  */
1812
1840
  const filterFn = dep => {
1813
1841
  const depChunkGroup = dep.chunkGroup;
1842
+ if (!dep.couldBeFiltered) return true;
1814
1843
  if (blocksWithNestedBlocks.has(dep.block)) return true;
1815
- if (areModulesAvailable(depChunkGroup, newAvailableModules)) return false; // break all modules are already available
1844
+ if (areModulesAvailable(depChunkGroup, newAvailableModules)) {
1845
+ return false; // break all modules are already available
1846
+ }
1847
+ dep.couldBeFiltered = false;
1816
1848
  return true;
1817
1849
  };
1818
1850
 
1819
- /** @type {Map<ChunkGroup, Set<Module>>} */
1820
- const minAvailableModulesMap = new Map();
1821
-
1822
1851
  // Iterative traversing of the basic chunk graph
1823
1852
  while (queue2.length) {
1824
- const queueItem = queue2.dequeue();
1825
- chunkGroup = queueItem.chunkGroup;
1826
- availableModules = queueItem.availableModules;
1853
+ chunkGroup = queue2.dequeue();
1854
+ const info = chunkGroupInfoMap.get(chunkGroup);
1855
+ const availableModulesToBeMerged = info.availableModulesToBeMerged;
1856
+ let minAvailableModules = info.minAvailableModules;
1827
1857
 
1828
1858
  // 1. Get minimal available modules
1829
1859
  // It doesn't make sense to traverse a chunk again with more available modules.
1830
1860
  // This step calculates the minimal available modules and skips traversal when
1831
1861
  // the list didn't shrink.
1832
- let minAvailableModules = minAvailableModulesMap.get(chunkGroup);
1833
- if (minAvailableModules === undefined) {
1834
- minAvailableModulesMap.set(chunkGroup, new Set(availableModules));
1835
- } else {
1836
- let deletedModules = false;
1837
- for (const m of minAvailableModules) {
1838
- if (!availableModules.has(m)) {
1839
- minAvailableModules.delete(m);
1840
- deletedModules = true;
1862
+ availableModulesToBeMerged.sort(bySetSize);
1863
+ let changed = false;
1864
+ for (const availableModules of availableModulesToBeMerged) {
1865
+ if (minAvailableModules === undefined) {
1866
+ minAvailableModules = new Set(availableModules);
1867
+ info.minAvailableModules = minAvailableModules;
1868
+ changed = true;
1869
+ } else {
1870
+ for (const m of minAvailableModules) {
1871
+ if (!availableModules.has(m)) {
1872
+ minAvailableModules.delete(m);
1873
+ changed = true;
1874
+ }
1841
1875
  }
1842
1876
  }
1843
- if (!deletedModules) continue;
1844
- availableModules = minAvailableModules;
1845
1877
  }
1878
+ availableModulesToBeMerged.length = 0;
1879
+ if (!changed) continue;
1846
1880
 
1847
1881
  // 2. Get the edges at this point of the graph
1848
1882
  const deps = chunkDependencies.get(chunkGroup);
@@ -1850,41 +1884,52 @@ class Compilation extends Tapable {
1850
1884
  if (deps.length === 0) continue;
1851
1885
 
1852
1886
  // 3. Create a new Set of available modules at this points
1853
- newAvailableModules = new Set(availableModules);
1887
+ newAvailableModules = new Set(minAvailableModules);
1854
1888
  for (const chunk of chunkGroup.chunks) {
1855
1889
  for (const m of chunk.modulesIterable) {
1856
1890
  newAvailableModules.add(m);
1857
1891
  }
1858
1892
  }
1859
1893
 
1860
- // 4. Filter edges with available modules
1861
- const filteredDeps = deps.filter(filterFn);
1862
-
1863
- // 5. Foreach remaining edge
1894
+ // 4. Foreach remaining edge
1864
1895
  const nextChunkGroups = new Set();
1865
- for (let i = 0; i < filteredDeps.length; i++) {
1866
- const dep = filteredDeps[i];
1896
+ for (let i = 0; i < deps.length; i++) {
1897
+ const dep = deps[i];
1898
+
1899
+ // Filter inline, rather than creating a new array from `.filter()`
1900
+ if (!filterFn(dep)) {
1901
+ continue;
1902
+ }
1867
1903
  const depChunkGroup = dep.chunkGroup;
1868
1904
  const depBlock = dep.block;
1869
1905
 
1870
- // 6. Connect block with chunk
1906
+ // 5. Connect block with chunk
1871
1907
  GraphHelpers.connectDependenciesBlockAndChunkGroup(
1872
1908
  depBlock,
1873
1909
  depChunkGroup
1874
1910
  );
1875
1911
 
1876
- // 7. Connect chunk with parent
1912
+ // 6. Connect chunk with parent
1877
1913
  GraphHelpers.connectChunkGroupParentAndChild(chunkGroup, depChunkGroup);
1878
1914
 
1879
1915
  nextChunkGroups.add(depChunkGroup);
1880
1916
  }
1881
1917
 
1882
- // 8. Enqueue further traversal
1918
+ // 7. Enqueue further traversal
1883
1919
  for (const nextChunkGroup of nextChunkGroups) {
1884
- queue2.enqueue({
1885
- chunkGroup: nextChunkGroup,
1886
- availableModules: newAvailableModules
1887
- });
1920
+ let nextInfo = chunkGroupInfoMap.get(nextChunkGroup);
1921
+ if (nextInfo === undefined) {
1922
+ nextInfo = {
1923
+ minAvailableModules: undefined,
1924
+ availableModulesToBeMerged: []
1925
+ };
1926
+ chunkGroupInfoMap.set(nextChunkGroup, nextInfo);
1927
+ }
1928
+ nextInfo.availableModulesToBeMerged.push(newAvailableModules);
1929
+
1930
+ // As queue deduplicates enqueued items this makes sure that a ChunkGroup
1931
+ // is not enqueued twice
1932
+ queue2.enqueue(nextChunkGroup);
1888
1933
  }
1889
1934
  }
1890
1935
 
@@ -2113,6 +2158,8 @@ class Compilation extends Tapable {
2113
2158
  for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
2114
2159
  chunks[indexChunk].sortItems();
2115
2160
  }
2161
+
2162
+ chunks.sort((a, b) => a.compareTo(b));
2116
2163
  }
2117
2164
 
2118
2165
  sortItemsWithChunkIds() {
package/lib/Compiler.js CHANGED
@@ -128,6 +128,7 @@ class Compiler extends Tapable {
128
128
  /** @type {string|null} */
129
129
  this.recordsOutputPath = null;
130
130
  this.records = {};
131
+ this.removedFiles = new Set();
131
132
  /** @type {Map<string, number>} */
132
133
  this.fileTimestamps = new Map();
133
134
  /** @type {Map<string, number>} */
@@ -184,14 +185,19 @@ class Compiler extends Tapable {
184
185
 
185
186
  /** @type {boolean} */
186
187
  this.running = false;
188
+
189
+ /** @type {boolean} */
190
+ this.watchMode = false;
187
191
  }
188
192
 
189
193
  watch(watchOptions, handler) {
190
194
  if (this.running) return handler(new ConcurrentCompilationError());
191
195
 
192
196
  this.running = true;
197
+ this.watchMode = true;
193
198
  this.fileTimestamps = new Map();
194
199
  this.contextTimestamps = new Map();
200
+ this.removedFiles = new Set();
195
201
  return new Watching(this, watchOptions, handler);
196
202
  }
197
203
 
@@ -299,7 +305,6 @@ class Compiler extends Tapable {
299
305
 
300
306
  emitAssets(compilation, callback) {
301
307
  let outputPath;
302
-
303
308
  const emitFiles = err => {
304
309
  if (err) return callback(err);
305
310
 
@@ -221,6 +221,91 @@ class ConstPlugin {
221
221
  }
222
222
  }
223
223
  );
224
+ parser.hooks.expressionLogicalOperator.tap(
225
+ "ConstPlugin",
226
+ expression => {
227
+ if (
228
+ expression.operator === "&&" ||
229
+ expression.operator === "||"
230
+ ) {
231
+ const param = parser.evaluateExpression(expression.left);
232
+ const bool = param.asBool();
233
+ if (typeof bool === "boolean") {
234
+ // Expressions do not hoist.
235
+ // It is safe to remove the dead branch.
236
+ //
237
+ // ------------------------------------------
238
+ //
239
+ // Given the following code:
240
+ //
241
+ // falsyExpression() && someExpression();
242
+ //
243
+ // the generated code is:
244
+ //
245
+ // falsyExpression() && false;
246
+ //
247
+ // ------------------------------------------
248
+ //
249
+ // Given the following code:
250
+ //
251
+ // truthyExpression() && someExpression();
252
+ //
253
+ // the generated code is:
254
+ //
255
+ // true && someExpression();
256
+ //
257
+ // ------------------------------------------
258
+ //
259
+ // Given the following code:
260
+ //
261
+ // truthyExpression() || someExpression();
262
+ //
263
+ // the generated code is:
264
+ //
265
+ // truthyExpression() || false;
266
+ //
267
+ // ------------------------------------------
268
+ //
269
+ // Given the following code:
270
+ //
271
+ // falsyExpression() || someExpression();
272
+ //
273
+ // the generated code is:
274
+ //
275
+ // false && someExpression();
276
+ //
277
+ const keepRight =
278
+ (expression.operator === "&&" && bool) ||
279
+ (expression.operator === "||" && !bool);
280
+
281
+ if (param.isBoolean() || keepRight) {
282
+ // for case like
283
+ //
284
+ // return'development'===process.env.NODE_ENV&&'foo'
285
+ //
286
+ // we need a space before the bool to prevent result like
287
+ //
288
+ // returnfalse&&'foo'
289
+ //
290
+ const dep = new ConstDependency(` ${bool}`, param.range);
291
+ dep.loc = expression.loc;
292
+ parser.state.current.addDependency(dep);
293
+ } else {
294
+ parser.walkExpression(expression.left);
295
+ }
296
+ if (!keepRight) {
297
+ const dep = new ConstDependency(
298
+ "false",
299
+ expression.right.range
300
+ );
301
+ dep.loc = expression.loc;
302
+ parser.state.current.addDependency(dep);
303
+ }
304
+ return keepRight;
305
+ }
306
+ }
307
+ }
308
+ );
224
309
  parser.hooks.evaluateIdentifier
225
310
  .for("__resourceQuery")
226
311
  .tap("ConstPlugin", expr => {
package/lib/Entrypoint.js CHANGED
@@ -49,6 +49,16 @@ class Entrypoint extends ChunkGroup {
49
49
  getRuntimeChunk() {
50
50
  return this.runtimeChunk || this.chunks[0];
51
51
  }
52
+
53
+ /**
54
+ * @param {Chunk} oldChunk chunk to be replaced
55
+ * @param {Chunk} newChunk New chunkt that will be replaced
56
+ * @returns {boolean} rerturns true for
57
+ */
58
+ replaceChunk(oldChunk, newChunk) {
59
+ if (this.runtimeChunk === oldChunk) this.runtimeChunk = newChunk;
60
+ return super.replaceChunk(oldChunk, newChunk);
61
+ }
52
62
  }
53
63
 
54
64
  module.exports = Entrypoint;
@@ -127,13 +127,14 @@ class ExternalModule extends Module {
127
127
  );
128
128
  case "global":
129
129
  return this.getSourceForGlobalVariableExternal(
130
- runtime.outputOptions.globalObject,
131
- this.externalType
130
+ request,
131
+ runtime.outputOptions.globalObject
132
132
  );
133
133
  case "commonjs":
134
134
  case "commonjs2":
135
135
  return this.getSourceForCommonJsExternal(request);
136
136
  case "amd":
137
+ case "amd-require":
137
138
  case "umd":
138
139
  case "umd2":
139
140
  return this.getSourceForAmdOrUmdExternal(
@@ -26,7 +26,9 @@ const accessorToObjectAccess = accessor => {
26
26
  */
27
27
  const accessorAccess = (base, accessor, umdProperty, joinWith = "; ") => {
28
28
  const normalizedAccessor =
29
- typeof accessor === "object" ? accessor[umdProperty] : accessor;
29
+ typeof accessor === "object" && !Array.isArray(accessor)
30
+ ? accessor[umdProperty]
31
+ : accessor;
30
32
  const accessors = Array.isArray(normalizedAccessor)
31
33
  ? normalizedAccessor
32
34
  : [normalizedAccessor];
@@ -138,15 +140,16 @@ class LibraryTemplatePlugin {
138
140
  compilation
139
141
  );
140
142
  break;
141
- case "amd": {
143
+ case "amd":
144
+ case "amd-require": {
142
145
  const AmdMainTemplatePlugin = require("./AmdMainTemplatePlugin");
143
- if (this.name) {
144
- if (typeof this.name !== "string")
145
- throw new Error("library name must be a string for amd target");
146
- new AmdMainTemplatePlugin(this.name).apply(compilation);
147
- } else {
148
- new AmdMainTemplatePlugin().apply(compilation);
146
+ if (this.name && typeof this.name !== "string") {
147
+ throw new Error("library name must be a string for amd target");
149
148
  }
149
+ new AmdMainTemplatePlugin({
150
+ name: this.name,
151
+ requireAsWrapper: this.target === "amd-require"
152
+ }).apply(compilation);
150
153
  break;
151
154
  }
152
155
  case "umd":
@@ -247,6 +247,19 @@ module.exports = class MainTemplate extends Tapable {
247
247
  );
248
248
  buf.push(Template.indent("return Promise.all(promises);"));
249
249
  buf.push("};");
250
+ } else if (
251
+ chunk.hasModuleInGraph(m =>
252
+ m.blocks.some(b => b.chunkGroup && b.chunkGroup.chunks.length > 0)
253
+ )
254
+ ) {
255
+ // There async blocks in the graph, so we need to add an empty requireEnsure
256
+ // function anyway. This can happen with multiple entrypoints.
257
+ buf.push("// The chunk loading function for additional chunks");
258
+ buf.push("// Since all referenced chunks are already included");
259
+ buf.push("// in this file, this function is empty here.");
260
+ buf.push(`${this.requireFn}.e = function requireEnsure() {`);
261
+ buf.push(Template.indent("return Promise.resolve();"));
262
+ buf.push("};");
250
263
  }
251
264
  buf.push("");
252
265
  buf.push("// expose the modules object (__webpack_modules__)");
package/lib/Parser.js CHANGED
@@ -97,6 +97,7 @@ class Parser extends Tapable {
97
97
  expression: new HookMap(() => new SyncBailHook(["expression"])),
98
98
  expressionAnyMember: new HookMap(() => new SyncBailHook(["expression"])),
99
99
  expressionConditionalOperator: new SyncBailHook(["expression"]),
100
+ expressionLogicalOperator: new SyncBailHook(["expression"]),
100
101
  program: new SyncBailHook(["ast", "comments"])
101
102
  };
102
103
  const HOOK_MAP_COMPAT_CONFIG = {
@@ -1598,7 +1599,14 @@ class Parser extends Tapable {
1598
1599
  }
1599
1600
 
1600
1601
  walkLogicalExpression(expression) {
1601
- this.walkLeftRightExpression(expression);
1602
+ const result = this.hooks.expressionLogicalOperator.call(expression);
1603
+ if (result === undefined) {
1604
+ this.walkLeftRightExpression(expression);
1605
+ } else {
1606
+ if (result) {
1607
+ this.walkExpression(expression.right);
1608
+ }
1609
+ }
1602
1610
  }
1603
1611
 
1604
1612
  walkAssignmentExpression(expression) {
package/lib/Stats.js CHANGED
@@ -17,6 +17,9 @@ const optionsOrFallback = (...args) => {
17
17
  };
18
18
 
19
19
  const compareId = (a, b) => {
20
+ if (typeof a !== typeof b) {
21
+ return typeof a < typeof b ? -1 : 1;
22
+ }
20
23
  if (a < b) return -1;
21
24
  if (a > b) return 1;
22
25
  return 0;
@@ -247,24 +250,32 @@ class Stats {
247
250
  if (a[fieldKey] === null) return 1;
248
251
  if (b[fieldKey] === null) return -1;
249
252
  if (a[fieldKey] === b[fieldKey]) return 0;
253
+ if (typeof a[fieldKey] !== typeof b[fieldKey])
254
+ return typeof a[fieldKey] < typeof b[fieldKey] ? -1 : 1;
250
255
  return a[fieldKey] < b[fieldKey] ? -1 : 1;
251
256
  };
252
257
 
253
- const sortByField = field => (a, b) => {
254
- if (!field) {
255
- return 0;
256
- }
257
-
258
- const fieldKey = this.normalizeFieldKey(field);
259
-
260
- // if a field is prefixed with a "!" the sort is reversed!
261
- const sortIsRegular = this.sortOrderRegular(field);
262
-
263
- return sortByFieldAndOrder(
264
- fieldKey,
265
- sortIsRegular ? a : b,
266
- sortIsRegular ? b : a
267
- );
258
+ const sortByField = (field, originalArray) => {
259
+ const originalMap = originalArray.reduce((map, v, i) => {
260
+ map.set(v, i);
261
+ return map;
262
+ }, new Map());
263
+ return (a, b) => {
264
+ if (field) {
265
+ const fieldKey = this.normalizeFieldKey(field);
266
+
267
+ // if a field is prefixed with a "!" the sort is reversed!
268
+ const sortIsRegular = this.sortOrderRegular(field);
269
+
270
+ const cmp = sortByFieldAndOrder(
271
+ fieldKey,
272
+ sortIsRegular ? a : b,
273
+ sortIsRegular ? b : a
274
+ );
275
+ if (cmp) return cmp;
276
+ }
277
+ return originalMap.get(a) - originalMap.get(b);
278
+ };
268
279
  };
269
280
 
270
281
  const formatError = e => {
@@ -380,7 +391,7 @@ class Stats {
380
391
  }
381
392
  if (showAssets) {
382
393
  const assetsByFile = {};
383
- const compilationAssets = Object.keys(compilation.assets);
394
+ const compilationAssets = Object.keys(compilation.assets).sort();
384
395
  obj.assetsByChunkName = {};
385
396
  obj.assets = compilationAssets
386
397
  .map(asset => {
@@ -421,7 +432,7 @@ class Stats {
421
432
  }
422
433
  }
423
434
  }
424
- obj.assets.sort(sortByField(sortAssets));
435
+ obj.assets.sort(sortByField(sortAssets, obj.assets));
425
436
  }
426
437
 
427
438
  const fnChunkGroup = groupMap => {
@@ -589,11 +600,11 @@ class Stats {
589
600
  if (module.modules) {
590
601
  const modules = module.modules;
591
602
  obj.modules = modules
592
- .sort(sortByField("depth"))
603
+ .sort(sortByField("depth", modules))
593
604
  .filter(createModuleFilter())
594
605
  .map(fnModule);
595
606
  obj.filteredModules = modules.length - obj.modules.length;
596
- obj.modules.sort(sortByField(sortModules));
607
+ obj.modules.sort(sortByField(sortModules, obj.modules));
597
608
  }
598
609
  }
599
610
  if (showSource && module._source) {
@@ -639,13 +650,14 @@ class Stats {
639
650
  childrenByOrder: childIdByOrder
640
651
  };
641
652
  if (showChunkModules) {
642
- obj.modules = chunk
643
- .getModules()
644
- .sort(sortByField("depth"))
653
+ const modules = chunk.getModules();
654
+ obj.modules = modules
655
+ .slice()
656
+ .sort(sortByField("depth", modules))
645
657
  .filter(createModuleFilter())
646
658
  .map(fnModule);
647
659
  obj.filteredModules = chunk.getNumberOfModules() - obj.modules.length;
648
- obj.modules.sort(sortByField(sortModules));
660
+ obj.modules.sort(sortByField(sortModules, obj.modules));
649
661
  }
650
662
  if (showChunkOrigins) {
651
663
  obj.origins = Array.from(chunk.groupsIterable, g => g.origins)
@@ -662,40 +674,27 @@ class Stats {
662
674
  reasons: origin.reasons || []
663
675
  }))
664
676
  .sort((a, b) => {
665
- if (
666
- typeof a.moduleId === "number" &&
667
- typeof b.moduleId !== "number"
668
- )
669
- return 1;
670
- if (
671
- typeof a.moduleId !== "number" &&
672
- typeof b.moduleId === "number"
673
- )
674
- return -1;
675
- if (
676
- typeof a.moduleId === "number" &&
677
- typeof b.moduleId === "number"
678
- ) {
679
- const diffId = a.moduleId - b.moduleId;
680
- if (diffId !== 0) return diffId;
681
- }
682
- if (a.loc < b.loc) return -1;
683
- if (a.loc > b.loc) return 1;
677
+ const cmp1 = compareId(a.moduleId, b.moduleId);
678
+ if (cmp1) return cmp1;
679
+ const cmp2 = compareId(a.loc, b.loc);
680
+ if (cmp2) return cmp2;
681
+ const cmp3 = compareId(a.request, b.request);
682
+ if (cmp3) return cmp3;
684
683
  return 0;
685
684
  });
686
685
  }
687
686
  return obj;
688
687
  });
689
- obj.chunks.sort(sortByField(sortChunks));
688
+ obj.chunks.sort(sortByField(sortChunks, obj.chunks));
690
689
  }
691
690
  if (showModules) {
692
691
  obj.modules = compilation.modules
693
692
  .slice()
694
- .sort(sortByField("depth"))
693
+ .sort(sortByField("depth", compilation.modules))
695
694
  .filter(createModuleFilter())
696
695
  .map(fnModule);
697
696
  obj.filteredModules = compilation.modules.length - obj.modules.length;
698
- obj.modules.sort(sortByField(sortModules));
697
+ obj.modules.sort(sortByField(sortModules, obj.modules));
699
698
  }
700
699
  if (showChildren) {
701
700
  obj.children = compilation.children.map((child, idx) => {
@@ -38,10 +38,10 @@ class IgnoringWatchFileSystem {
38
38
  dirsModified,
39
39
  missingModified,
40
40
  fileTimestamps,
41
- dirTimestamps
41
+ dirTimestamps,
42
+ removedFiles
42
43
  ) => {
43
44
  if (err) return callback(err);
44
-
45
45
  for (const path of ignoredFiles) {
46
46
  fileTimestamps.set(path, 1);
47
47
  }
@@ -56,7 +56,8 @@ class IgnoringWatchFileSystem {
56
56
  dirsModified,
57
57
  missingModified,
58
58
  fileTimestamps,
59
- dirTimestamps
59
+ dirTimestamps,
60
+ removedFiles
60
61
  );
61
62
  },
62
63
  callbackUndelayed