webpack 5.66.0 → 5.69.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.

Potentially problematic release.


This version of webpack might be problematic. Click here for more details.

Files changed (97) hide show
  1. package/README.md +1 -1
  2. package/lib/APIPlugin.js +33 -0
  3. package/lib/Cache.js +1 -1
  4. package/lib/CacheFacade.js +2 -2
  5. package/lib/ChunkGraph.js +1 -2
  6. package/lib/CleanPlugin.js +1 -1
  7. package/lib/Compilation.js +14 -9
  8. package/lib/Compiler.js +57 -3
  9. package/lib/ContextModule.js +21 -17
  10. package/lib/DelegatedModule.js +1 -1
  11. package/lib/DependencyTemplates.js +1 -1
  12. package/lib/DllModule.js +1 -1
  13. package/lib/EvalDevToolModulePlugin.js +16 -1
  14. package/lib/EvalSourceMapDevToolPlugin.js +18 -1
  15. package/lib/ExportsInfo.js +4 -4
  16. package/lib/ExternalModule.js +1 -1
  17. package/lib/ExternalModuleFactoryPlugin.js +1 -1
  18. package/lib/FileSystemInfo.js +29 -25
  19. package/lib/HookWebpackError.js +1 -1
  20. package/lib/Module.js +1 -3
  21. package/lib/MultiCompiler.js +1 -1
  22. package/lib/MultiWatching.js +1 -1
  23. package/lib/NormalModule.js +6 -4
  24. package/lib/NormalModuleFactory.js +25 -27
  25. package/lib/ProgressPlugin.js +1 -1
  26. package/lib/RawModule.js +1 -1
  27. package/lib/RuntimeGlobals.js +18 -0
  28. package/lib/RuntimeModule.js +1 -1
  29. package/lib/RuntimePlugin.js +28 -3
  30. package/lib/RuntimeTemplate.js +1 -1
  31. package/lib/TemplatedPathPlugin.js +48 -23
  32. package/lib/Watching.js +1 -1
  33. package/lib/WebpackOptionsApply.js +1 -1
  34. package/lib/asset/AssetGenerator.js +65 -26
  35. package/lib/asset/AssetModulesPlugin.js +3 -0
  36. package/lib/asset/RawDataUrlModule.js +8 -5
  37. package/lib/async-modules/AwaitDependenciesInitFragment.js +4 -4
  38. package/lib/buildChunkGraph.js +1 -1
  39. package/lib/cache/ResolverCachePlugin.js +1 -1
  40. package/lib/cli.js +44 -3
  41. package/lib/config/defaults.js +30 -7
  42. package/lib/config/normalization.js +5 -0
  43. package/lib/container/ContainerEntryModule.js +4 -2
  44. package/lib/container/FallbackModule.js +4 -4
  45. package/lib/container/RemoteModule.js +4 -2
  46. package/lib/css/CssExportsGenerator.js +139 -0
  47. package/lib/css/CssGenerator.js +3 -0
  48. package/lib/css/CssLoadingRuntimeModule.js +201 -154
  49. package/lib/css/CssModulesPlugin.js +22 -4
  50. package/lib/debug/ProfilingPlugin.js +15 -14
  51. package/lib/dependencies/ContextElementDependency.js +8 -2
  52. package/lib/dependencies/CreateScriptUrlDependency.js +12 -0
  53. package/lib/dependencies/ExportsInfoDependency.js +6 -0
  54. package/lib/dependencies/HarmonyCompatibilityDependency.js +5 -5
  55. package/lib/dependencies/ImportMetaPlugin.js +22 -3
  56. package/lib/dependencies/LoaderPlugin.js +2 -2
  57. package/lib/hmr/LazyCompilationPlugin.js +45 -21
  58. package/lib/hmr/lazyCompilationBackend.js +1 -1
  59. package/lib/ids/DeterministicModuleIdsPlugin.js +55 -35
  60. package/lib/ids/HashedModuleIdsPlugin.js +9 -12
  61. package/lib/ids/IdHelpers.js +24 -10
  62. package/lib/ids/NamedModuleIdsPlugin.js +6 -9
  63. package/lib/ids/NaturalModuleIdsPlugin.js +10 -13
  64. package/lib/ids/OccurrenceModuleIdsPlugin.js +13 -10
  65. package/lib/ids/SyncModuleIdsPlugin.js +140 -0
  66. package/lib/index.js +10 -0
  67. package/lib/javascript/JavascriptModulesPlugin.js +27 -2
  68. package/lib/javascript/StartupHelpers.js +3 -2
  69. package/lib/library/AssignLibraryPlugin.js +8 -2
  70. package/lib/node/NodeTargetPlugin.js +1 -0
  71. package/lib/optimize/ConcatenatedModule.js +11 -5
  72. package/lib/runtime/AsyncModuleRuntimeModule.js +25 -15
  73. package/lib/runtime/CreateScriptRuntimeModule.js +36 -0
  74. package/lib/runtime/CreateScriptUrlRuntimeModule.js +9 -34
  75. package/lib/runtime/GetTrustedTypesPolicyRuntimeModule.js +76 -0
  76. package/lib/schemes/HttpUriPlugin.js +32 -11
  77. package/lib/serialization/FileMiddleware.js +44 -9
  78. package/lib/sharing/ConsumeSharedModule.js +4 -2
  79. package/lib/sharing/ProvideSharedModule.js +4 -2
  80. package/lib/sharing/utils.js +1 -1
  81. package/lib/stats/DefaultStatsFactoryPlugin.js +112 -67
  82. package/lib/stats/DefaultStatsPrinterPlugin.js +88 -23
  83. package/lib/util/ArrayHelpers.js +18 -4
  84. package/lib/util/AsyncQueue.js +1 -1
  85. package/lib/util/compileBooleanMatcher.js +1 -1
  86. package/lib/util/deterministicGrouping.js +1 -1
  87. package/lib/util/identifier.js +65 -44
  88. package/lib/util/nonNumericOnlyHash.js +22 -0
  89. package/lib/util/semver.js +17 -10
  90. package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +9 -3
  91. package/lib/webworker/ImportScriptsChunkLoadingPlugin.js +3 -11
  92. package/package.json +14 -21
  93. package/schemas/WebpackOptions.check.js +1 -1
  94. package/schemas/WebpackOptions.json +47 -6
  95. package/schemas/plugins/asset/AssetGeneratorOptions.check.js +1 -1
  96. package/schemas/plugins/asset/AssetResourceGeneratorOptions.check.js +1 -1
  97. package/types.d.ts +207 -60
@@ -19,10 +19,13 @@ const { compareModulesByIdentifier } = require("../util/comparators");
19
19
  const createSchemaValidation = require("../util/create-schema-validation");
20
20
  const createHash = require("../util/createHash");
21
21
  const memoize = require("../util/memoize");
22
+ const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
23
+ const CssExportsGenerator = require("./CssExportsGenerator");
22
24
  const CssGenerator = require("./CssGenerator");
23
25
  const CssParser = require("./CssParser");
24
26
 
25
27
  /** @typedef {import("webpack-sources").Source} Source */
28
+ /** @typedef {import("../../declarations/WebpackOptions").CssExperimentOptions} CssExperimentOptions */
26
29
  /** @typedef {import("../Chunk")} Chunk */
27
30
  /** @typedef {import("../Compiler")} Compiler */
28
31
  /** @typedef {import("../Module")} Module */
@@ -70,6 +73,12 @@ const escapeCss = (str, omitOptionalUnderscore) => {
70
73
  const plugin = "CssModulesPlugin";
71
74
 
72
75
  class CssModulesPlugin {
76
+ /**
77
+ * @param {CssExperimentOptions} options options
78
+ */
79
+ constructor({ exportsOnly = false }) {
80
+ this._exportsOnly = exportsOnly;
81
+ }
73
82
  /**
74
83
  * Apply the plugin
75
84
  * @param {Compiler} compiler the compiler instance
@@ -143,19 +152,25 @@ class CssModulesPlugin {
143
152
  .for("css")
144
153
  .tap(plugin, generatorOptions => {
145
154
  validateGeneratorOptions(generatorOptions);
146
- return new CssGenerator();
155
+ return this._exportsOnly
156
+ ? new CssExportsGenerator()
157
+ : new CssGenerator();
147
158
  });
148
159
  normalModuleFactory.hooks.createGenerator
149
160
  .for("css/global")
150
161
  .tap(plugin, generatorOptions => {
151
162
  validateGeneratorOptions(generatorOptions);
152
- return new CssGenerator();
163
+ return this._exportsOnly
164
+ ? new CssExportsGenerator()
165
+ : new CssGenerator();
153
166
  });
154
167
  normalModuleFactory.hooks.createGenerator
155
168
  .for("css/module")
156
169
  .tap(plugin, generatorOptions => {
157
170
  validateGeneratorOptions(generatorOptions);
158
- return new CssGenerator();
171
+ return this._exportsOnly
172
+ ? new CssExportsGenerator()
173
+ : new CssGenerator();
159
174
  });
160
175
  const orderedCssModulesPerChunk = new WeakMap();
161
176
  compilation.hooks.afterCodeGeneration.tap("CssModulesPlugin", () => {
@@ -187,7 +202,7 @@ class CssModulesPlugin {
187
202
  hash.update(chunkGraph.getModuleHash(module, chunk.runtime));
188
203
  }
189
204
  const digest = /** @type {string} */ (hash.digest(hashDigest));
190
- chunk.contentHash.css = digest.substr(0, hashDigestLength);
205
+ chunk.contentHash.css = nonNumericOnlyHash(digest, hashDigestLength);
191
206
  });
192
207
  compilation.hooks.renderManifest.tap(plugin, (result, options) => {
193
208
  const { chunkGraph } = compilation;
@@ -238,6 +253,9 @@ class CssModulesPlugin {
238
253
  const CssLoadingRuntimeModule = getCssLoadingRuntimeModule();
239
254
  compilation.addRuntimeModule(chunk, new CssLoadingRuntimeModule(set));
240
255
  };
256
+ compilation.hooks.runtimeRequirementInTree
257
+ .for(RuntimeGlobals.hasCssModules)
258
+ .tap(plugin, handler);
241
259
  compilation.hooks.runtimeRequirementInTree
242
260
  .for(RuntimeGlobals.ensureChunkHandlers)
243
261
  .tap(plugin, handler);
@@ -123,9 +123,7 @@ class Profiler {
123
123
  * @returns {Trace} The trace object
124
124
  */
125
125
  const createTrace = (fs, outputPath) => {
126
- const trace = new Tracer({
127
- noStream: true
128
- });
126
+ const trace = new Tracer();
129
127
  const profiler = new Profiler(inspector);
130
128
  if (/\/|\\/.test(outputPath)) {
131
129
  const dirPath = dirname(fs, outputPath);
@@ -173,6 +171,7 @@ const createTrace = (fs, outputPath) => {
173
171
  counter,
174
172
  profiler,
175
173
  end: callback => {
174
+ trace.push("]");
176
175
  // Wait until the write stream finishes.
177
176
  fsStream.on("close", () => {
178
177
  callback();
@@ -242,10 +241,10 @@ class ProfilingPlugin {
242
241
  stage: Infinity
243
242
  },
244
243
  (stats, callback) => {
244
+ if (compiler.watchMode) return callback();
245
245
  tracer.profiler.stopProfiling().then(parsedResults => {
246
246
  if (parsedResults === undefined) {
247
247
  tracer.profiler.destroy();
248
- tracer.trace.flush();
249
248
  tracer.end(callback);
250
249
  return;
251
250
  }
@@ -293,7 +292,6 @@ class ProfilingPlugin {
293
292
  });
294
293
 
295
294
  tracer.profiler.destroy();
296
- tracer.trace.flush();
297
295
  tracer.end(callback);
298
296
  });
299
297
  }
@@ -345,16 +343,19 @@ const interceptAllJavascriptModulesPluginHooks = (compilation, tracer) => {
345
343
  };
346
344
 
347
345
  const makeInterceptorFor = (instance, tracer) => hookName => ({
348
- register: ({ name, type, context, fn }) => {
349
- const newFn = makeNewProfiledTapFn(hookName, tracer, {
350
- name,
351
- type,
352
- fn
353
- });
346
+ register: tapInfo => {
347
+ const { name, type, fn } = tapInfo;
348
+ const newFn =
349
+ // Don't tap our own hooks to ensure stream can close cleanly
350
+ name === pluginName
351
+ ? fn
352
+ : makeNewProfiledTapFn(hookName, tracer, {
353
+ name,
354
+ type,
355
+ fn
356
+ });
354
357
  return {
355
- name,
356
- type,
357
- context,
358
+ ...tapInfo,
358
359
  fn: newFn
359
360
  };
360
361
  }
@@ -53,12 +53,18 @@ class ContextElementDependency extends ModuleDependency {
53
53
  }
54
54
 
55
55
  serialize(context) {
56
- context.write(this.referencedExports);
56
+ const { write } = context;
57
+ write(this._typePrefix);
58
+ write(this._category);
59
+ write(this.referencedExports);
57
60
  super.serialize(context);
58
61
  }
59
62
 
60
63
  deserialize(context) {
61
- this.referencedExports = context.read();
64
+ const { read } = context;
65
+ this._typePrefix = read();
66
+ this._category = read();
67
+ this.referencedExports = read();
62
68
  super.deserialize(context);
63
69
  }
64
70
  }
@@ -25,6 +25,18 @@ class CreateScriptUrlDependency extends NullDependency {
25
25
  get type() {
26
26
  return "create script url";
27
27
  }
28
+
29
+ serialize(context) {
30
+ const { write } = context;
31
+ write(this.range);
32
+ super.serialize(context);
33
+ }
34
+
35
+ deserialize(context) {
36
+ const { read } = context;
37
+ this.range = read();
38
+ super.deserialize(context);
39
+ }
28
40
  }
29
41
 
30
42
  CreateScriptUrlDependency.Template = class CreateScriptUrlDependencyTemplate extends (
@@ -46,6 +46,12 @@ const getProperty = (moduleGraph, module, exportName, property, runtime) => {
46
46
  }
47
47
  }
48
48
  switch (property) {
49
+ case "canMangle": {
50
+ const exportsInfo = moduleGraph.getExportsInfo(module);
51
+ const exportInfo = exportsInfo.getExportInfo(exportName);
52
+ if (exportInfo) return exportInfo.canMangle;
53
+ return exportsInfo.otherExportsInfo.canMangle;
54
+ }
49
55
  case "used":
50
56
  return (
51
57
  moduleGraph.getExportsInfo(module).getUsed(exportName, runtime) !==
@@ -74,14 +74,14 @@ HarmonyCompatibilityDependency.Template = class HarmonyExportDependencyTemplate
74
74
  initFragments.push(
75
75
  new InitFragment(
76
76
  runtimeTemplate.supportsArrowFunction()
77
- ? `${RuntimeGlobals.asyncModule}(${module.moduleArgument}, async (__webpack_handle_async_dependencies__) => {\n`
78
- : `${RuntimeGlobals.asyncModule}(${module.moduleArgument}, async function (__webpack_handle_async_dependencies__) {\n`,
77
+ ? `${RuntimeGlobals.asyncModule}(${module.moduleArgument}, async (__webpack_handle_async_dependencies__, __webpack_async_result__) => { try {\n`
78
+ : `${RuntimeGlobals.asyncModule}(${module.moduleArgument}, async function (__webpack_handle_async_dependencies__, __webpack_async_result__) { try {\n`,
79
79
  InitFragment.STAGE_ASYNC_BOUNDARY,
80
80
  0,
81
81
  undefined,
82
- module.buildMeta.async
83
- ? `\n__webpack_handle_async_dependencies__();\n}, 1);`
84
- : "\n});"
82
+ `\n__webpack_async_result__();\n} catch(e) { __webpack_async_result__(e); } }${
83
+ module.buildMeta.async ? ", 1" : ""
84
+ });`
85
85
  )
86
86
  );
87
87
  }
@@ -20,6 +20,7 @@ const propertyAccess = require("../util/propertyAccess");
20
20
  const ConstDependency = require("./ConstDependency");
21
21
 
22
22
  /** @typedef {import("estree").MemberExpression} MemberExpression */
23
+ /** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
23
24
  /** @typedef {import("../Compiler")} Compiler */
24
25
  /** @typedef {import("../NormalModule")} NormalModule */
25
26
  /** @typedef {import("../javascript/JavascriptParser")} Parser */
@@ -44,11 +45,29 @@ class ImportMetaPlugin {
44
45
  return pathToFileURL(module.resource).toString();
45
46
  };
46
47
  /**
47
- * @param {Parser} parser parser
48
- * @param {Object} parserOptions parserOptions
48
+ * @param {Parser} parser parser parser
49
+ * @param {JavascriptParserOptions} parserOptions parserOptions
49
50
  * @returns {void}
50
51
  */
51
- const parserHandler = (parser, parserOptions) => {
52
+ const parserHandler = (parser, { importMeta }) => {
53
+ if (importMeta === false) {
54
+ const { importMetaName } = compilation.outputOptions;
55
+ if (importMetaName === "import.meta") return;
56
+
57
+ parser.hooks.expression
58
+ .for("import.meta")
59
+ .tap("ImportMetaPlugin", metaProperty => {
60
+ const dep = new ConstDependency(
61
+ importMetaName,
62
+ metaProperty.range
63
+ );
64
+ dep.loc = metaProperty.loc;
65
+ parser.state.module.addPresentationalDependency(dep);
66
+ return true;
67
+ });
68
+ return;
69
+ }
70
+
52
71
  /// import.meta direct ///
53
72
  parser.hooks.typeof
54
73
  .for("import.meta")
@@ -16,7 +16,7 @@ const LoaderImportDependency = require("./LoaderImportDependency");
16
16
 
17
17
  /**
18
18
  * @callback LoadModuleCallback
19
- * @param {Error=} err error object
19
+ * @param {(Error | null)=} err error object
20
20
  * @param {string | Buffer=} source source code
21
21
  * @param {object=} map source map
22
22
  * @param {Module=} module loaded module if successful
@@ -24,7 +24,7 @@ const LoaderImportDependency = require("./LoaderImportDependency");
24
24
 
25
25
  /**
26
26
  * @callback ImportModuleCallback
27
- * @param {Error=} err error object
27
+ * @param {(Error | null)=} err error object
28
28
  * @param {any=} exports exports of the evaluated module
29
29
  */
30
30
 
@@ -29,6 +29,7 @@ const { registerNotSerializable } = require("../util/serialization");
29
29
  /** @typedef {import("../RequestShortener")} RequestShortener */
30
30
  /** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
31
31
  /** @typedef {import("../WebpackError")} WebpackError */
32
+ /** @typedef {import("../dependencies/HarmonyImportDependency")} HarmonyImportDependency */
32
33
  /** @typedef {import("../util/Hash")} Hash */
33
34
  /** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
34
35
 
@@ -38,7 +39,7 @@ const { registerNotSerializable } = require("../util/serialization");
38
39
  * @property {function(Module): { client: string, data: string, active: boolean }} module
39
40
  */
40
41
 
41
- const IGNORED_DEPENDENCY_TYPES = new Set([
42
+ const HMR_DEPENDENCY_TYPES = new Set([
42
43
  "import.meta.webpackHot.accept",
43
44
  "import.meta.webpackHot.decline",
44
45
  "module.hot.accept",
@@ -146,7 +147,7 @@ class LazyCompilationProxyModule extends Module {
146
147
 
147
148
  /**
148
149
  * @param {NeedBuildContext} context context info
149
- * @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
150
+ * @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
150
151
  * @returns {void}
151
152
  */
152
153
  needBuild(context, callback) {
@@ -351,32 +352,55 @@ class LazyCompilationPlugin {
351
352
  "LazyCompilationPlugin",
352
353
  (originalModule, createData, resolveData) => {
353
354
  if (
354
- resolveData.dependencies.every(
355
+ resolveData.dependencies.every(dep =>
356
+ HMR_DEPENDENCY_TYPES.has(dep.type)
357
+ )
358
+ ) {
359
+ // for HMR only resolving, try to determine if the HMR accept/decline refers to
360
+ // an import() or not
361
+ const hmrDep = resolveData.dependencies[0];
362
+ const originModule =
363
+ compilation.moduleGraph.getParentModule(hmrDep);
364
+ const isReferringToDynamicImport = originModule.blocks.some(
365
+ block =>
366
+ block.dependencies.some(
367
+ dep =>
368
+ dep.type === "import()" &&
369
+ /** @type {HarmonyImportDependency} */ (dep).request ===
370
+ hmrDep.request
371
+ )
372
+ );
373
+ if (!isReferringToDynamicImport) return;
374
+ } else if (
375
+ !resolveData.dependencies.every(
355
376
  dep =>
356
- IGNORED_DEPENDENCY_TYPES.has(dep.type) ||
377
+ HMR_DEPENDENCY_TYPES.has(dep.type) ||
357
378
  (this.imports &&
358
379
  (dep.type === "import()" ||
359
380
  dep.type === "import() context element")) ||
360
381
  (this.entries && dep.type === "entry")
361
- ) &&
362
- !/webpack[/\\]hot[/\\]|webpack-dev-server[/\\]client/.test(
382
+ )
383
+ )
384
+ return;
385
+ if (
386
+ /webpack[/\\]hot[/\\]|webpack-dev-server[/\\]client|webpack-hot-middleware[/\\]client/.test(
363
387
  resolveData.request
364
- ) &&
365
- checkTest(this.test, originalModule)
366
- ) {
367
- const moduleInfo = backend.module(originalModule);
368
- if (!moduleInfo) return;
369
- const { client, data, active } = moduleInfo;
388
+ ) ||
389
+ !checkTest(this.test, originalModule)
390
+ )
391
+ return;
392
+ const moduleInfo = backend.module(originalModule);
393
+ if (!moduleInfo) return;
394
+ const { client, data, active } = moduleInfo;
370
395
 
371
- return new LazyCompilationProxyModule(
372
- compiler.context,
373
- originalModule,
374
- resolveData.request,
375
- client,
376
- data,
377
- active
378
- );
379
- }
396
+ return new LazyCompilationProxyModule(
397
+ compiler.context,
398
+ originalModule,
399
+ resolveData.request,
400
+ client,
401
+ data,
402
+ active
403
+ );
380
404
  }
381
405
  );
382
406
  compilation.dependencyFactories.set(
@@ -13,7 +13,7 @@
13
13
  /**
14
14
  * @callback BackendHandler
15
15
  * @param {Compiler} compiler compiler
16
- * @param {function(Error?, any?): void} callback callback
16
+ * @param {function((Error | null)=, any=): void} callback callback
17
17
  * @returns {void}
18
18
  */
19
19
 
@@ -9,7 +9,7 @@ const {
9
9
  compareModulesByPreOrderIndexOrIdentifier
10
10
  } = require("../util/comparators");
11
11
  const {
12
- getUsedModuleIds,
12
+ getUsedModuleIdsAndModules,
13
13
  getFullModuleName,
14
14
  assignDeterministicIds
15
15
  } = require("./IdHelpers");
@@ -18,8 +18,17 @@ const {
18
18
  /** @typedef {import("../Module")} Module */
19
19
 
20
20
  class DeterministicModuleIdsPlugin {
21
- constructor(options) {
22
- this.options = options || {};
21
+ /**
22
+ * @param {Object} options options
23
+ * @param {string=} options.context context relative to which module identifiers are computed
24
+ * @param {function(Module): boolean=} options.test selector function for modules
25
+ * @param {number=} options.maxLength maximum id length in digits (used as starting point)
26
+ * @param {number=} options.salt hash salt for ids
27
+ * @param {boolean=} options.fixedLength do not increase the maxLength to find an optimal id space size
28
+ * @param {boolean=} options.failOnConflict throw an error when id conflicts occur (instead of rehashing)
29
+ */
30
+ constructor(options = {}) {
31
+ this.options = options;
23
32
  }
24
33
 
25
34
  /**
@@ -31,40 +40,51 @@ class DeterministicModuleIdsPlugin {
31
40
  compiler.hooks.compilation.tap(
32
41
  "DeterministicModuleIdsPlugin",
33
42
  compilation => {
34
- compilation.hooks.moduleIds.tap(
35
- "DeterministicModuleIdsPlugin",
36
- modules => {
37
- const chunkGraph = compilation.chunkGraph;
38
- const context = this.options.context
39
- ? this.options.context
40
- : compiler.context;
41
- const maxLength = this.options.maxLength || 3;
43
+ compilation.hooks.moduleIds.tap("DeterministicModuleIdsPlugin", () => {
44
+ const chunkGraph = compilation.chunkGraph;
45
+ const context = this.options.context
46
+ ? this.options.context
47
+ : compiler.context;
48
+ const maxLength = this.options.maxLength || 3;
49
+ const failOnConflict = this.options.failOnConflict || false;
50
+ const fixedLength = this.options.fixedLength || false;
51
+ const salt = this.options.salt || 0;
52
+ let conflicts = 0;
42
53
 
43
- const usedIds = getUsedModuleIds(compilation);
44
- assignDeterministicIds(
45
- Array.from(modules).filter(module => {
46
- if (!module.needId) return false;
47
- if (chunkGraph.getNumberOfModuleChunks(module) === 0)
48
- return false;
49
- return chunkGraph.getModuleId(module) === null;
50
- }),
51
- module => getFullModuleName(module, context, compiler.root),
52
- compareModulesByPreOrderIndexOrIdentifier(
53
- compilation.moduleGraph
54
- ),
55
- (module, id) => {
56
- const size = usedIds.size;
57
- usedIds.add(`${id}`);
58
- if (size === usedIds.size) return false;
59
- chunkGraph.setModuleId(module, id);
60
- return true;
61
- },
62
- [Math.pow(10, maxLength)],
63
- 10,
64
- usedIds.size
54
+ const [usedIds, modules] = getUsedModuleIdsAndModules(
55
+ compilation,
56
+ this.options.test
57
+ );
58
+ assignDeterministicIds(
59
+ modules,
60
+ module => getFullModuleName(module, context, compiler.root),
61
+ failOnConflict
62
+ ? () => 0
63
+ : compareModulesByPreOrderIndexOrIdentifier(
64
+ compilation.moduleGraph
65
+ ),
66
+ (module, id) => {
67
+ const size = usedIds.size;
68
+ usedIds.add(`${id}`);
69
+ if (size === usedIds.size) {
70
+ conflicts++;
71
+ return false;
72
+ }
73
+ chunkGraph.setModuleId(module, id);
74
+ return true;
75
+ },
76
+ [Math.pow(10, maxLength)],
77
+ fixedLength ? 0 : 10,
78
+ usedIds.size,
79
+ salt
80
+ );
81
+ if (failOnConflict && conflicts)
82
+ throw new Error(
83
+ `Assigning deterministic module ids has lead to ${conflicts} conflict${
84
+ conflicts > 1 ? "s" : ""
85
+ }.\nIncrease the 'maxLength' to increase the id space and make conflicts less likely (recommended when there are many conflicts or application is expected to grow), or add an 'salt' number to try another hash starting value in the same id space (recommended when there is only a single conflict).`
65
86
  );
66
- }
67
- );
87
+ });
68
88
  }
69
89
  );
70
90
  }
@@ -10,7 +10,10 @@ const {
10
10
  } = require("../util/comparators");
11
11
  const createSchemaValidation = require("../util/create-schema-validation");
12
12
  const createHash = require("../util/createHash");
13
- const { getUsedModuleIds, getFullModuleName } = require("./IdHelpers");
13
+ const {
14
+ getUsedModuleIdsAndModules,
15
+ getFullModuleName
16
+ } = require("./IdHelpers");
14
17
 
15
18
  /** @typedef {import("../../declarations/plugins/HashedModuleIdsPlugin").HashedModuleIdsPluginOptions} HashedModuleIdsPluginOptions */
16
19
 
@@ -43,22 +46,16 @@ class HashedModuleIdsPlugin {
43
46
  apply(compiler) {
44
47
  const options = this.options;
45
48
  compiler.hooks.compilation.tap("HashedModuleIdsPlugin", compilation => {
46
- compilation.hooks.moduleIds.tap("HashedModuleIdsPlugin", modules => {
49
+ compilation.hooks.moduleIds.tap("HashedModuleIdsPlugin", () => {
47
50
  const chunkGraph = compilation.chunkGraph;
48
51
  const context = this.options.context
49
52
  ? this.options.context
50
53
  : compiler.context;
51
54
 
52
- const usedIds = getUsedModuleIds(compilation);
53
- const modulesInNaturalOrder = Array.from(modules)
54
- .filter(m => {
55
- if (!m.needId) return false;
56
- if (chunkGraph.getNumberOfModuleChunks(m) === 0) return false;
57
- return chunkGraph.getModuleId(module) === null;
58
- })
59
- .sort(
60
- compareModulesByPreOrderIndexOrIdentifier(compilation.moduleGraph)
61
- );
55
+ const [usedIds, modules] = getUsedModuleIdsAndModules(compilation);
56
+ const modulesInNaturalOrder = modules.sort(
57
+ compareModulesByPreOrderIndexOrIdentifier(compilation.moduleGraph)
58
+ );
62
59
  for (const module of modulesInNaturalOrder) {
63
60
  const ident = getFullModuleName(module, context, compiler.root);
64
61
  const hash = createHash(options.hashFunction);
@@ -234,11 +234,14 @@ const addToMapOfItems = (map, key, value) => {
234
234
 
235
235
  /**
236
236
  * @param {Compilation} compilation the compilation
237
- * @returns {Set<string>} used module ids as strings
237
+ * @param {function(Module): boolean=} filter filter modules
238
+ * @returns {[Set<string>, Module[]]} used module ids as strings and modules without id matching the filter
238
239
  */
239
- const getUsedModuleIds = compilation => {
240
+ const getUsedModuleIdsAndModules = (compilation, filter) => {
240
241
  const chunkGraph = compilation.chunkGraph;
241
242
 
243
+ const modules = [];
244
+
242
245
  /** @type {Set<string>} */
243
246
  const usedIds = new Set();
244
247
  if (compilation.usedModuleIds) {
@@ -248,15 +251,23 @@ const getUsedModuleIds = compilation => {
248
251
  }
249
252
 
250
253
  for (const module of compilation.modules) {
254
+ if (!module.needId) continue;
251
255
  const moduleId = chunkGraph.getModuleId(module);
252
256
  if (moduleId !== null) {
253
257
  usedIds.add(moduleId + "");
258
+ } else {
259
+ if (
260
+ (!filter || filter(module)) &&
261
+ chunkGraph.getNumberOfModuleChunks(module) !== 0
262
+ ) {
263
+ modules.push(module);
264
+ }
254
265
  }
255
266
  }
256
267
 
257
- return usedIds;
268
+ return [usedIds, modules];
258
269
  };
259
- exports.getUsedModuleIds = getUsedModuleIds;
270
+ exports.getUsedModuleIdsAndModules = getUsedModuleIdsAndModules;
260
271
 
261
272
  /**
262
273
  * @param {Compilation} compilation the compilation
@@ -359,6 +370,7 @@ exports.assignNames = assignNames;
359
370
  * @param {number[]} ranges usable ranges for ids
360
371
  * @param {number} expandFactor factor to create more ranges
361
372
  * @param {number} extraSpace extra space to allocate, i. e. when some ids are already used
373
+ * @param {number} salt salting number to initialize hashing
362
374
  * @returns {void}
363
375
  */
364
376
  const assignDeterministicIds = (
@@ -368,7 +380,8 @@ const assignDeterministicIds = (
368
380
  assignId,
369
381
  ranges = [10],
370
382
  expandFactor = 10,
371
- extraSpace = 0
383
+ extraSpace = 0,
384
+ salt = 0
372
385
  ) => {
373
386
  items.sort(comparator);
374
387
 
@@ -384,15 +397,17 @@ const assignDeterministicIds = (
384
397
  i++;
385
398
  if (i < ranges.length) {
386
399
  range = Math.min(ranges[i], Number.MAX_SAFE_INTEGER);
387
- } else {
400
+ } else if (expandFactor) {
388
401
  range = Math.min(range * expandFactor, Number.MAX_SAFE_INTEGER);
402
+ } else {
403
+ break;
389
404
  }
390
405
  }
391
406
 
392
407
  for (const item of items) {
393
408
  const ident = getName(item);
394
409
  let id;
395
- let i = 0;
410
+ let i = salt;
396
411
  do {
397
412
  id = numberHash(ident + i++, range);
398
413
  } while (!assignId(item, id));
@@ -401,15 +416,14 @@ const assignDeterministicIds = (
401
416
  exports.assignDeterministicIds = assignDeterministicIds;
402
417
 
403
418
  /**
419
+ * @param {Set<string>} usedIds used ids
404
420
  * @param {Iterable<Module>} modules the modules
405
421
  * @param {Compilation} compilation the compilation
406
422
  * @returns {void}
407
423
  */
408
- const assignAscendingModuleIds = (modules, compilation) => {
424
+ const assignAscendingModuleIds = (usedIds, modules, compilation) => {
409
425
  const chunkGraph = compilation.chunkGraph;
410
426
 
411
- const usedIds = getUsedModuleIds(compilation);
412
-
413
427
  let nextId = 0;
414
428
  let assignId;
415
429
  if (usedIds.size > 0) {