webpack 5.5.1 → 5.9.0

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 (63) hide show
  1. package/bin/webpack.js +10 -3
  2. package/lib/Compilation.js +132 -17
  3. package/lib/Compiler.js +2 -2
  4. package/lib/ExportsInfo.js +1 -1
  5. package/lib/FlagDependencyUsagePlugin.js +4 -2
  6. package/lib/HotModuleReplacementPlugin.js +170 -36
  7. package/lib/NormalModule.js +13 -1
  8. package/lib/RuntimeTemplate.js +0 -3
  9. package/lib/SourceMapDevToolPlugin.js +2 -1
  10. package/lib/Template.js +0 -1
  11. package/lib/TemplatedPathPlugin.js +8 -0
  12. package/lib/Watching.js +0 -1
  13. package/lib/cache/ResolverCachePlugin.js +0 -1
  14. package/lib/config/defaults.js +1 -1
  15. package/lib/dependencies/AMDDefineDependency.js +3 -1
  16. package/lib/dependencies/AMDRequireArrayDependency.js +3 -1
  17. package/lib/dependencies/AMDRequireDependency.js +3 -1
  18. package/lib/dependencies/CachedConstDependency.js +3 -1
  19. package/lib/dependencies/CommonJsExportRequireDependency.js +3 -2
  20. package/lib/dependencies/CommonJsExportsDependency.js +3 -1
  21. package/lib/dependencies/CommonJsFullRequireDependency.js +3 -1
  22. package/lib/dependencies/CommonJsSelfReferenceDependency.js +3 -1
  23. package/lib/dependencies/ConstDependency.js +3 -1
  24. package/lib/dependencies/ExportsInfoDependency.js +3 -1
  25. package/lib/dependencies/HarmonyAcceptDependency.js +3 -1
  26. package/lib/dependencies/HarmonyAcceptImportDependency.js +3 -1
  27. package/lib/dependencies/HarmonyCompatibilityDependency.js +3 -1
  28. package/lib/dependencies/HarmonyExportExpressionDependency.js +3 -1
  29. package/lib/dependencies/HarmonyExportHeaderDependency.js +3 -1
  30. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +11 -2
  31. package/lib/dependencies/HarmonyExportSpecifierDependency.js +3 -1
  32. package/lib/dependencies/HarmonyImportDependency.js +50 -46
  33. package/lib/dependencies/HarmonyImportSideEffectDependency.js +3 -1
  34. package/lib/dependencies/HarmonyImportSpecifierDependency.js +11 -1
  35. package/lib/dependencies/ImportDependency.js +3 -2
  36. package/lib/dependencies/ImportEagerDependency.js +3 -1
  37. package/lib/dependencies/ImportWeakDependency.js +3 -1
  38. package/lib/dependencies/LocalModuleDependency.js +3 -1
  39. package/lib/dependencies/ModuleDecoratorDependency.js +3 -2
  40. package/lib/dependencies/NullDependency.js +3 -2
  41. package/lib/dependencies/PureExpressionDependency.js +3 -1
  42. package/lib/dependencies/RequireEnsureDependency.js +3 -1
  43. package/lib/dependencies/RequireHeaderDependency.js +3 -1
  44. package/lib/dependencies/RequireIncludeDependency.js +3 -2
  45. package/lib/dependencies/RequireResolveHeaderDependency.js +3 -1
  46. package/lib/dependencies/RuntimeRequirementsDependency.js +3 -1
  47. package/lib/dependencies/URLDependency.js +3 -1
  48. package/lib/dependencies/UnsupportedDependency.js +3 -1
  49. package/lib/dependencies/WorkerDependency.js +3 -2
  50. package/lib/hmr/HotModuleReplacement.runtime.js +1 -0
  51. package/lib/index.js +3 -0
  52. package/lib/javascript/JavascriptParser.js +29 -11
  53. package/lib/optimize/RealContentHashPlugin.js +127 -32
  54. package/lib/optimize/SideEffectsFlagPlugin.js +224 -204
  55. package/lib/optimize/SplitChunksPlugin.js +0 -1
  56. package/lib/runtime/GetMainFilenameRuntimeModule.js +4 -2
  57. package/lib/runtime/LoadScriptRuntimeModule.js +0 -1
  58. package/lib/serialization/FileMiddleware.js +4 -2
  59. package/lib/util/runtime.js +4 -0
  60. package/lib/wasm-async/AsyncWebAssemblyModulesPlugin.js +0 -1
  61. package/lib/webpack.js +0 -2
  62. package/package.json +14 -14
  63. package/types.d.ts +179 -149
package/bin/webpack.js CHANGED
@@ -82,10 +82,17 @@ if (!cli.installed) {
82
82
 
83
83
  console.error(notify);
84
84
 
85
- const isYarn = fs.existsSync(path.resolve(process.cwd(), "yarn.lock"));
85
+ let packageManager;
86
+
87
+ if (fs.existsSync(path.resolve(process.cwd(), "yarn.lock"))) {
88
+ packageManager = "yarn";
89
+ } else if (fs.existsSync(path.resolve(process.cwd(), "pnpm-lock.yaml"))) {
90
+ packageManager = "pnpm";
91
+ } else {
92
+ packageManager = "npm";
93
+ }
86
94
 
87
- const packageManager = isYarn ? "yarn" : "npm";
88
- const installOptions = [isYarn ? "add" : "install", "-D"];
95
+ const installOptions = [packageManager === "yarn" ? "add" : "install", "-D"];
89
96
 
90
97
  console.error(
91
98
  `We will use "${packageManager}" to install the CLI via "${packageManager} ${installOptions.join(
@@ -76,6 +76,8 @@ const { getRuntimeKey } = require("./util/runtime");
76
76
  /** @typedef {import("webpack-sources").Source} Source */
77
77
  /** @typedef {import("../declarations/WebpackOptions").EntryDescriptionNormalized} EntryDescription */
78
78
  /** @typedef {import("../declarations/WebpackOptions").OutputNormalized} OutputOptions */
79
+ /** @typedef {import("../declarations/WebpackOptions").WebpackPluginFunction} WebpackPluginFunction */
80
+ /** @typedef {import("../declarations/WebpackOptions").WebpackPluginInstance} WebpackPluginInstance */
79
81
  /** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
80
82
  /** @typedef {import("./Cache")} Cache */
81
83
  /** @typedef {import("./CacheFacade")} CacheFacade */
@@ -87,15 +89,11 @@ const { getRuntimeKey } = require("./util/runtime");
87
89
  /** @typedef {import("./Dependency").ReferencedExport} ReferencedExport */
88
90
  /** @typedef {import("./DependencyTemplate")} DependencyTemplate */
89
91
  /** @typedef {import("./Entrypoint").EntryOptions} EntryOptions */
90
- /** @typedef {import("./Module")} Module */
91
92
  /** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */
92
93
  /** @typedef {import("./ModuleFactory")} ModuleFactory */
93
94
  /** @typedef {import("./RuntimeModule")} RuntimeModule */
94
95
  /** @typedef {import("./Template").RenderManifestEntry} RenderManifestEntry */
95
96
  /** @typedef {import("./Template").RenderManifestOptions} RenderManifestOptions */
96
- /** @typedef {import("./WebpackError")} WebpackError */
97
- /** @typedef {import("./stats/StatsFactory")} StatsFactory */
98
- /** @typedef {import("./stats/StatsPrinter")} StatsPrinter */
99
97
  /** @typedef {import("./util/Hash")} Hash */
100
98
  /** @template T @typedef {import("./util/deprecation").FakeHook<T>} FakeHook<T> */
101
99
  /** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
@@ -119,11 +117,6 @@ const { getRuntimeKey } = require("./util/runtime");
119
117
  * @returns {any}
120
118
  */
121
119
 
122
- /**
123
- * @typedef {Object} Plugin
124
- * @property {() => void} apply
125
- */
126
-
127
120
  /** @typedef {new (...args: any[]) => Dependency} DepConstructor */
128
121
  /** @typedef {Record<string, Source>} CompilationAssets */
129
122
 
@@ -296,10 +289,122 @@ class Compilation {
296
289
  */
297
290
  constructor(compiler) {
298
291
  const getNormalModuleLoader = () => deprecatedNormalModuleLoaderHook(this);
299
- /** @type {AsyncSeriesHook<[CompilationAssets]>} */
292
+ /** @typedef {{ additionalAssets?: true | Function }} ProcessAssetsAdditionalOptions */
293
+ /** @type {AsyncSeriesHook<[CompilationAssets], ProcessAssetsAdditionalOptions>} */
300
294
  const processAssetsHook = new AsyncSeriesHook(["assets"]);
295
+
296
+ let savedAssets = new Set();
297
+ const popNewAssets = assets => {
298
+ let newAssets = undefined;
299
+ for (const file of Object.keys(assets)) {
300
+ if (savedAssets.has(file)) continue;
301
+ if (newAssets === undefined) {
302
+ newAssets = Object.create(null);
303
+ }
304
+ newAssets[file] = assets[file];
305
+ savedAssets.add(file);
306
+ }
307
+ return newAssets;
308
+ };
309
+ processAssetsHook.intercept({
310
+ name: "Compilation",
311
+ call: () => {
312
+ savedAssets.clear();
313
+ },
314
+ register: tap => {
315
+ const { type, name } = tap;
316
+ const { fn, additionalAssets, ...remainingTap } = tap;
317
+ const additionalAssetsFn =
318
+ additionalAssets === true ? fn : additionalAssets;
319
+ let processedAssets = undefined;
320
+ switch (type) {
321
+ case "sync":
322
+ if (additionalAssetsFn) {
323
+ this.hooks.processAdditionalAssets.tap(name, assets => {
324
+ if (processedAssets === this.assets) additionalAssetsFn(assets);
325
+ });
326
+ }
327
+ return {
328
+ ...remainingTap,
329
+ type: "async",
330
+ fn: (assets, callback) => {
331
+ try {
332
+ fn(assets);
333
+ } catch (e) {
334
+ return callback(e);
335
+ }
336
+ processedAssets = this.assets;
337
+ const newAssets = popNewAssets(assets);
338
+ if (newAssets !== undefined) {
339
+ this.hooks.processAdditionalAssets.callAsync(
340
+ newAssets,
341
+ callback
342
+ );
343
+ return;
344
+ }
345
+ callback();
346
+ }
347
+ };
348
+ case "async":
349
+ if (additionalAssetsFn) {
350
+ this.hooks.processAdditionalAssets.tapAsync(
351
+ name,
352
+ (assets, callback) => {
353
+ if (processedAssets === this.assets)
354
+ return additionalAssetsFn(assets, callback);
355
+ callback();
356
+ }
357
+ );
358
+ }
359
+ return {
360
+ ...remainingTap,
361
+ fn: (assets, callback) => {
362
+ fn(assets, err => {
363
+ if (err) return callback(err);
364
+ processedAssets = this.assets;
365
+ const newAssets = popNewAssets(assets);
366
+ if (newAssets !== undefined) {
367
+ this.hooks.processAdditionalAssets.callAsync(
368
+ newAssets,
369
+ callback
370
+ );
371
+ return;
372
+ }
373
+ callback();
374
+ });
375
+ }
376
+ };
377
+ case "promise":
378
+ if (additionalAssetsFn) {
379
+ this.hooks.processAdditionalAssets.tapPromise(name, assets => {
380
+ if (processedAssets === this.assets)
381
+ return additionalAssetsFn(assets);
382
+ return Promise.resolve();
383
+ });
384
+ }
385
+ return {
386
+ ...remainingTap,
387
+ fn: assets => {
388
+ const p = fn(assets);
389
+ if (!p || !p.then) return p;
390
+ return p.then(() => {
391
+ processedAssets = this.assets;
392
+ const newAssets = popNewAssets(assets);
393
+ if (newAssets !== undefined) {
394
+ return this.hooks.processAdditionalAssets.promise(
395
+ newAssets
396
+ );
397
+ }
398
+ });
399
+ }
400
+ };
401
+ }
402
+ }
403
+ });
404
+
301
405
  /** @type {SyncHook<[CompilationAssets]>} */
302
406
  const afterProcessAssetsHook = new SyncHook(["assets"]);
407
+
303
408
  /**
304
409
  * @template T
305
410
  * @param {string} name name of the hook
@@ -551,6 +656,8 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
551
656
 
552
657
  processAssets: processAssetsHook,
553
658
  afterProcessAssets: afterProcessAssetsHook,
659
+ /** @type {AsyncSeriesHook<[CompilationAssets]>} */
660
+ processAdditionalAssets: new AsyncSeriesHook(["assets"]),
554
661
 
555
662
  /** @type {SyncBailHook<[], boolean>} */
556
663
  needAdditionalSeal: new SyncBailHook([]),
@@ -3325,7 +3432,7 @@ This prevents using hashes of each other and should be avoided.`
3325
3432
  *
3326
3433
  * @param {string} name name of the child compiler
3327
3434
  * @param {OutputOptions} outputOptions // Need to convert config schema to types for this
3328
- * @param {Plugin[]} plugins webpack plugins that will be applied
3435
+ * @param {Array<WebpackPluginInstance | WebpackPluginFunction>} plugins webpack plugins that will be applied
3329
3436
  * @returns {Compiler} creates a child Compiler instance
3330
3437
  */
3331
3438
  createChildCompiler(name, outputOptions, plugins) {
@@ -3446,6 +3553,8 @@ Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE = 100;
3446
3553
 
3447
3554
  /**
3448
3555
  * Optimize the count of existing assets, e. g. by merging them.
3556
+ * Only assets of the same type should be merged.
3557
+ * For assets of different types see PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE.
3449
3558
  */
3450
3559
  Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_COUNT = 200;
3451
3560
 
@@ -3460,16 +3569,22 @@ Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_COMPATIBILITY = 300;
3460
3569
  Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE = 400;
3461
3570
 
3462
3571
  /**
3463
- * Summarize the list of existing assets.
3464
- * When creating new assets from this they should be fully optimized.
3465
- * e. g. creating an assets manifest of Service Workers.
3572
+ * Add development tooling to assets, e. g. by extracting a SourceMap.
3466
3573
  */
3467
- Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE = 1000;
3574
+ Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING = 500;
3468
3575
 
3469
3576
  /**
3470
- * Add development tooling to assets, e. g. by extracting a SourceMap.
3577
+ * Optimize the count of existing assets, e. g. by inlining assets of into other assets.
3578
+ * Only assets of different types should be inlined.
3579
+ * For assets of the same type see PROCESS_ASSETS_STAGE_OPTIMIZE_COUNT.
3471
3580
  */
3472
- Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING = 2000;
3581
+ Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE = 700;
3582
+
3583
+ /**
3584
+ * Summarize the list of existing assets
3585
+ * e. g. creating an assets manifest of Service Workers.
3586
+ */
3587
+ Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE = 1000;
3473
3588
 
3474
3589
  /**
3475
3590
  * Optimize the hashes of the assets, e. g. by generating real hashes of the asset content.
package/lib/Compiler.js CHANGED
@@ -152,9 +152,9 @@ class Compiler {
152
152
  beforeCompile: new AsyncSeriesHook(["params"]),
153
153
  /** @type {SyncHook<[CompilationParams]>} */
154
154
  compile: new SyncHook(["params"]),
155
- /** @type {AsyncParallelHook<[Compilation], Module>} */
155
+ /** @type {AsyncParallelHook<[Compilation]>} */
156
156
  make: new AsyncParallelHook(["compilation"]),
157
- /** @type {AsyncParallelHook<[Compilation], Module>} */
157
+ /** @type {AsyncParallelHook<[Compilation]>} */
158
158
  finishMake: new AsyncSeriesHook(["compilation"]),
159
159
  /** @type {AsyncSeriesHook<[Compilation]>} */
160
160
  afterCompile: new AsyncSeriesHook(["compilation"]),
@@ -1085,7 +1085,7 @@ class ExportInfo {
1085
1085
 
1086
1086
  /**
1087
1087
  * get used name
1088
- * @param {string=} fallbackName fallback name for used exports with no name
1088
+ * @param {string | undefined} fallbackName fallback name for used exports with no name
1089
1089
  * @param {RuntimeSpec} runtime check usage for this runtime only
1090
1090
  * @returns {string | false} used name
1091
1091
  */
@@ -16,7 +16,6 @@ const { getEntryRuntime, mergeRuntimeOwned } = require("./util/runtime");
16
16
  /** @typedef {import("./ChunkGroup")} ChunkGroup */
17
17
  /** @typedef {import("./Compiler")} Compiler */
18
18
  /** @typedef {import("./DependenciesBlock")} DependenciesBlock */
19
- /** @typedef {import("./Dependency")} Dependency */
20
19
  /** @typedef {import("./Dependency").ReferencedExport} ReferencedExport */
21
20
  /** @typedef {import("./ExportsInfo")} ExportsInfo */
22
21
  /** @typedef {import("./Module")} Module */
@@ -207,7 +206,10 @@ class FlagDependencyUsagePlugin {
207
206
  referencedExports === EXPORTS_OBJECT_REFERENCED
208
207
  ) {
209
208
  map.set(module, referencedExports);
210
- } else if (oldReferencedExports === NO_EXPORTS_REFERENCED) {
209
+ } else if (
210
+ oldReferencedExports !== undefined &&
211
+ referencedExports === NO_EXPORTS_REFERENCED
212
+ ) {
211
213
  continue;
212
214
  } else {
213
215
  let exportsMap;
@@ -12,6 +12,7 @@ const Compilation = require("./Compilation");
12
12
  const HotUpdateChunk = require("./HotUpdateChunk");
13
13
  const NormalModule = require("./NormalModule");
14
14
  const RuntimeGlobals = require("./RuntimeGlobals");
15
+ const WebpackError = require("./WebpackError");
15
16
  const ConstDependency = require("./dependencies/ConstDependency");
16
17
  const ImportMetaHotAcceptDependency = require("./dependencies/ImportMetaHotAcceptDependency");
17
18
  const ImportMetaHotDeclineDependency = require("./dependencies/ImportMetaHotDeclineDependency");
@@ -22,15 +23,22 @@ const JavascriptParser = require("./javascript/JavascriptParser");
22
23
  const {
23
24
  evaluateToIdentifier
24
25
  } = require("./javascript/JavascriptParserHelpers");
25
- const { find } = require("./util/SetHelpers");
26
+ const { find, isSubset } = require("./util/SetHelpers");
26
27
  const TupleSet = require("./util/TupleSet");
27
28
  const { compareModulesById } = require("./util/comparators");
28
- const { getRuntimeKey, keyToRuntime } = require("./util/runtime");
29
+ const {
30
+ getRuntimeKey,
31
+ keyToRuntime,
32
+ forEachRuntime,
33
+ mergeRuntimeOwned,
34
+ subtractRuntime
35
+ } = require("./util/runtime");
29
36
 
30
37
  /** @typedef {import("./Chunk")} Chunk */
31
38
  /** @typedef {import("./Compilation").AssetInfo} AssetInfo */
32
39
  /** @typedef {import("./Compiler")} Compiler */
33
40
  /** @typedef {import("./Module")} Module */
41
+ /** @typedef {import("./RuntimeModule")} RuntimeModule */
34
42
 
35
43
  /**
36
44
  * @typedef {Object} HMRJavascriptParserHooks
@@ -388,27 +396,58 @@ class HotModuleReplacementPlugin {
388
396
  }
389
397
  chunkModuleHashes[key] = hash;
390
398
  }
391
- const hotUpdateMainContent = {
392
- c: [],
393
- r: [],
394
- m: undefined
395
- };
399
+
400
+ /** @type {Map<string, { updatedChunkIds: Set<string|number>, removedChunkIds: Set<string|number>, removedModules: Set<Module>, filename: string, assetInfo: AssetInfo }>} */
401
+ const hotUpdateMainContentByRuntime = new Map();
402
+ let allOldRuntime;
403
+ for (const key of Object.keys(records.chunkRuntime)) {
404
+ const runtime = keyToRuntime(records.chunkRuntime[key]);
405
+ allOldRuntime = mergeRuntimeOwned(allOldRuntime, runtime);
406
+ }
407
+ forEachRuntime(allOldRuntime, runtime => {
408
+ const {
409
+ path: filename,
410
+ info: assetInfo
411
+ } = compilation.getPathWithInfo(
412
+ compilation.outputOptions.hotUpdateMainFilename,
413
+ {
414
+ hash: records.hash,
415
+ runtime
416
+ }
417
+ );
418
+ hotUpdateMainContentByRuntime.set(runtime, {
419
+ updatedChunkIds: new Set(),
420
+ removedChunkIds: new Set(),
421
+ removedModules: new Set(),
422
+ filename,
423
+ assetInfo
424
+ });
425
+ });
426
+ if (hotUpdateMainContentByRuntime.size === 0) return;
396
427
 
397
428
  // Create a list of all active modules to verify which modules are removed completely
398
429
  /** @type {Map<number|string, Module>} */
399
430
  const allModules = new Map();
400
431
  for (const module of compilation.modules) {
401
- allModules.set(chunkGraph.getModuleId(module), module);
432
+ const id = chunkGraph.getModuleId(module);
433
+ allModules.set(id, module);
402
434
  }
403
435
 
404
436
  // List of completely removed modules
405
- const allRemovedModules = new Set();
437
+ /** @type {Set<string | number>} */
438
+ const completelyRemovedModules = new Set();
406
439
 
407
440
  for (const key of Object.keys(records.chunkHashs)) {
408
- // Check which modules are completely removed
441
+ const oldRuntime = keyToRuntime(records.chunkRuntime[key]);
442
+ /** @type {Module[]} */
443
+ const remainingModules = [];
444
+ // Check which modules are removed
409
445
  for (const id of records.chunkModuleIds[key]) {
410
- if (!allModules.has(id)) {
411
- allRemovedModules.add(id);
446
+ const module = allModules.get(id);
447
+ if (module === undefined) {
448
+ completelyRemovedModules.add(id);
449
+ } else {
450
+ remainingModules.push(module);
412
451
  }
413
452
  }
414
453
 
@@ -417,6 +456,7 @@ class HotModuleReplacementPlugin {
417
456
  let newRuntimeModules;
418
457
  let newFullHashModules;
419
458
  let newRuntime;
459
+ let removedFromRuntime;
420
460
  const currentChunk = find(
421
461
  compilation.chunks,
422
462
  chunk => `${chunk.id}` === key
@@ -438,18 +478,61 @@ class HotModuleReplacementPlugin {
438
478
  Array.from(fullHashModules).filter(module =>
439
479
  updatedModules.has(module, currentChunk)
440
480
  );
481
+ removedFromRuntime = subtractRuntime(oldRuntime, newRuntime);
441
482
  } else {
483
+ // chunk has completely removed
442
484
  chunkId = `${+key}` === key ? +key : key;
443
- hotUpdateMainContent.r.push(chunkId);
444
- const runtime = keyToRuntime(records.chunkRuntime[key]);
445
- for (const id of records.chunkModuleIds[key]) {
446
- const module = allModules.get(id);
447
- if (!module) continue;
448
- const hash = chunkGraph.getModuleHash(module, runtime);
485
+ removedFromRuntime = oldRuntime;
486
+ newRuntime = oldRuntime;
487
+ }
488
+ if (removedFromRuntime) {
489
+ // chunk was removed from some runtimes
490
+ forEachRuntime(removedFromRuntime, runtime => {
491
+ hotUpdateMainContentByRuntime
492
+ .get(runtime)
493
+ .removedChunkIds.add(chunkId);
494
+ });
495
+ // dispose modules from the chunk in these runtimes
496
+ // where they are no longer in this runtime
497
+ for (const module of remainingModules) {
449
498
  const moduleKey = `${key}|${module.identifier()}`;
450
- if (hash !== records.chunkModuleHashes[moduleKey]) {
451
- newModules = newModules || [];
452
- newModules.push(module);
499
+ const oldHash = records.chunkModuleHashes[moduleKey];
500
+ const runtimes = chunkGraph.getModuleRuntimes(module);
501
+ if (oldRuntime === newRuntime && runtimes.has(newRuntime)) {
502
+ // Module is still in the same runtime combination
503
+ const hash = chunkGraph.getModuleHash(module, newRuntime);
504
+ if (hash !== oldHash) {
505
+ if (module.type === "runtime") {
506
+ newRuntimeModules = newRuntimeModules || [];
507
+ newRuntimeModules.push(
508
+ /** @type {RuntimeModule} */ (module)
509
+ );
510
+ } else {
511
+ newModules = newModules || [];
512
+ newModules.push(module);
513
+ }
514
+ }
515
+ } else {
516
+ // module is no longer in this runtime combination
517
+ // We (incorrectly) assume that it's not in an overlapping runtime combination
518
+ // and dispose it from the main runtimes the chunk was removed from
519
+ forEachRuntime(removedFromRuntime, runtime => {
520
+ // If the module is still used in this runtime, do not dispose it
521
+ // This could create a bad runtime state where the module is still loaded,
522
+ // but no chunk which contains it. This means we don't receive further HMR updates
523
+ // to this module and that's bad.
524
+ // TODO force load one of the chunks which contains the module
525
+ for (const moduleRuntime of runtimes) {
526
+ if (typeof moduleRuntime === "string") {
527
+ if (moduleRuntime === runtime) return;
528
+ } else if (moduleRuntime !== undefined) {
529
+ if (moduleRuntime.has(runtime)) return;
530
+ }
531
+ }
532
+ hotUpdateMainContentByRuntime
533
+ .get(runtime)
534
+ .removedModules.add(module);
535
+ });
453
536
  }
454
537
  }
455
538
  }
@@ -516,24 +599,75 @@ class HotModuleReplacementPlugin {
516
599
  compilation.hooks.chunkAsset.call(currentChunk, filename);
517
600
  }
518
601
  }
519
- hotUpdateMainContent.c.push(chunkId);
602
+ forEachRuntime(newRuntime, runtime => {
603
+ hotUpdateMainContentByRuntime
604
+ .get(runtime)
605
+ .updatedChunkIds.add(chunkId);
606
+ });
520
607
  }
521
608
  }
522
- hotUpdateMainContent.m = Array.from(allRemovedModules);
523
- const source = new RawSource(JSON.stringify(hotUpdateMainContent));
524
- const {
525
- path: filename,
526
- info: assetInfo
527
- } = compilation.getPathWithInfo(
528
- compilation.outputOptions.hotUpdateMainFilename,
529
- {
530
- hash: records.hash
531
- }
609
+ const completelyRemovedModulesArray = Array.from(
610
+ completelyRemovedModules
532
611
  );
533
- compilation.emitAsset(filename, source, {
534
- hotModuleReplacement: true,
535
- ...assetInfo
536
- });
612
+ const hotUpdateMainContentByFilename = new Map();
613
+ for (const {
614
+ removedChunkIds,
615
+ removedModules,
616
+ updatedChunkIds,
617
+ filename,
618
+ assetInfo
619
+ } of hotUpdateMainContentByRuntime.values()) {
620
+ const old = hotUpdateMainContentByFilename.get(filename);
621
+ if (
622
+ old &&
623
+ (!isSubset(old.removedChunkIds, removedChunkIds) ||
624
+ !isSubset(old.removedModules, removedModules) ||
625
+ !isSubset(old.updatedChunkIds, updatedChunkIds))
626
+ ) {
627
+ compilation.warnings.push(
628
+ new WebpackError(`HotModuleReplacementPlugin
629
+ The configured output.hotUpdateMainFilename doesn't lead to unique filenames per runtime and HMR update differs between runtimes.
630
+ This might lead to incorrect runtime behavior of the applied update.
631
+ To fix this, make sure to include [runtime] in the output.hotUpdateMainFilename option, or use the default config.`)
632
+ );
633
+ for (const chunkId of removedChunkIds)
634
+ old.removedChunkIds.add(chunkId);
635
+ for (const chunkId of removedModules)
636
+ old.removedModules.add(chunkId);
637
+ for (const chunkId of updatedChunkIds)
638
+ old.updatedChunkIds.add(chunkId);
639
+ continue;
640
+ }
641
+ hotUpdateMainContentByFilename.set(filename, {
642
+ removedChunkIds,
643
+ removedModules,
644
+ updatedChunkIds,
645
+ assetInfo
646
+ });
647
+ }
648
+ for (const [
649
+ filename,
650
+ { removedChunkIds, removedModules, updatedChunkIds, assetInfo }
651
+ ] of hotUpdateMainContentByFilename) {
652
+ const hotUpdateMainJson = {
653
+ c: Array.from(updatedChunkIds),
654
+ r: Array.from(removedChunkIds),
655
+ m:
656
+ removedModules.size === 0
657
+ ? completelyRemovedModulesArray
658
+ : completelyRemovedModulesArray.concat(
659
+ Array.from(removedModules, m =>
660
+ chunkGraph.getModuleId(m)
661
+ )
662
+ )
663
+ };
664
+
665
+ const source = new RawSource(JSON.stringify(hotUpdateMainJson));
666
+ compilation.emitAsset(filename, source, {
667
+ hotModuleReplacement: true,
668
+ ...assetInfo
669
+ });
670
+ }
537
671
  }
538
672
  );
539
673
 
@@ -26,6 +26,7 @@ const ModuleWarning = require("./ModuleWarning");
26
26
  const RuntimeGlobals = require("./RuntimeGlobals");
27
27
  const UnhandledSchemeError = require("./UnhandledSchemeError");
28
28
  const WebpackError = require("./WebpackError");
29
+ const formatLocation = require("./formatLocation");
29
30
  const LazySet = require("./util/LazySet");
30
31
  const { getScheme } = require("./util/URLAbsoluteSpecifier");
31
32
  const {
@@ -42,7 +43,6 @@ const makeSerializable = require("./util/makeSerializable");
42
43
  /** @typedef {import("webpack-sources").Source} Source */
43
44
  /** @typedef {import("../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
44
45
  /** @typedef {import("./ChunkGraph")} ChunkGraph */
45
- /** @typedef {import("./Compilation")} Compilation */
46
46
  /** @typedef {import("./Dependency").UpdateHashContext} UpdateHashContext */
47
47
  /** @typedef {import("./DependencyTemplates")} DependencyTemplates */
48
48
  /** @typedef {import("./Generator")} Generator */
@@ -249,6 +249,7 @@ class NormalModule extends Module {
249
249
  this._lastSuccessfulBuildMeta = {};
250
250
  this._forceBuild = true;
251
251
  this._isEvaluatingSideEffects = false;
252
+ this._addedSideEffectsBailout = new WeakSet();
252
253
  }
253
254
 
254
255
  /**
@@ -878,6 +879,17 @@ class NormalModule extends Module {
878
879
  for (const dep of this.dependencies) {
879
880
  const state = dep.getModuleEvaluationSideEffectsState(moduleGraph);
880
881
  if (state === true) {
882
+ if (!this._addedSideEffectsBailout.has(moduleGraph)) {
883
+ this._addedSideEffectsBailout.add(moduleGraph);
884
+ moduleGraph
885
+ .getOptimizationBailout(this)
886
+ .push(
887
+ () =>
888
+ `Dependency (${
889
+ dep.type
890
+ }) with side effects at ${formatLocation(dep.loc)}`
891
+ );
892
+ }
881
893
  this._isEvaluatingSideEffects = false;
882
894
  return true;
883
895
  } else if (state !== ModuleGraphConnection.CIRCULAR_CONNECTION) {
@@ -17,7 +17,6 @@ const { forEachRuntime, subtractRuntime } = require("./util/runtime");
17
17
  /** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
18
18
  /** @typedef {import("./ChunkGraph")} ChunkGraph */
19
19
  /** @typedef {import("./Dependency")} Dependency */
20
- /** @typedef {import("./InitFragment")} InitFragment */
21
20
  /** @typedef {import("./Module")} Module */
22
21
  /** @typedef {import("./ModuleGraph")} ModuleGraph */
23
22
  /** @typedef {import("./RequestShortener")} RequestShortener */
@@ -607,8 +606,6 @@ class RuntimeTemplate {
607
606
  * @param {string} options.importVar name of the import variable
608
607
  * @param {Module} options.originModule module in which the statement is emitted
609
608
  * @param {boolean=} options.weak true, if this is a weak dependency
610
- * @param {RuntimeSpec=} options.runtime runtime for which this code will be generated
611
- * @param {RuntimeSpec | boolean=} options.runtimeCondition only execute the statement in some runtimes
612
609
  * @param {Set<string>} options.runtimeRequirements if set, will be filled with runtime requirements
613
610
  * @returns {[string, string]} the import statement and the compat statement
614
611
  */
@@ -162,7 +162,8 @@ class SourceMapDevToolPlugin {
162
162
  compilation.hooks.processAssets.tapAsync(
163
163
  {
164
164
  name: "SourceMapDevToolPlugin",
165
- stage: Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING
165
+ stage: Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING,
166
+ additionalAssets: true
166
167
  },
167
168
  (assets, callback) => {
168
169
  const chunkGraph = compilation.chunkGraph;
package/lib/Template.js CHANGED
@@ -7,7 +7,6 @@
7
7
 
8
8
  const { ConcatSource, PrefixSource } = require("webpack-sources");
9
9
 
10
- /** @typedef {import("webpack-sources").ConcatSource} ConcatSource */
11
10
  /** @typedef {import("webpack-sources").Source} Source */
12
11
  /** @typedef {import("../declarations/WebpackOptions").Output} OutputOptions */
13
12
  /** @typedef {import("./Chunk")} Chunk */
@@ -274,6 +274,14 @@ const replacePathVariables = (path, data, assetInfo) => {
274
274
  if (data.url) {
275
275
  replacements.set("url", replacer(data.url));
276
276
  }
277
+ if (typeof data.runtime === "string") {
278
+ replacements.set(
279
+ "runtime",
280
+ replacer(() => prepareId(data.runtime))
281
+ );
282
+ } else {
283
+ replacements.set("runtime", replacer("_"));
284
+ }
277
285
 
278
286
  if (typeof path === "function") {
279
287
  path = path(data, assetInfo);
package/lib/Watching.js CHANGED
@@ -10,7 +10,6 @@ const Stats = require("./Stats");
10
10
  /** @typedef {import("../declarations/WebpackOptions").WatchOptions} WatchOptions */
11
11
  /** @typedef {import("./Compilation")} Compilation */
12
12
  /** @typedef {import("./Compiler")} Compiler */
13
- /** @typedef {import("./Stats")} Stats */
14
13
 
15
14
  /**
16
15
  * @template T
@@ -13,7 +13,6 @@ const makeSerializable = require("../util/makeSerializable");
13
13
  /** @typedef {import("../Compiler")} Compiler */
14
14
  /** @typedef {import("../FileSystemInfo")} FileSystemInfo */
15
15
  /** @typedef {import("../FileSystemInfo").Snapshot} Snapshot */
16
- /** @template T @typedef {import("../util/LazySet")<T>} LazySet<T> */
17
16
 
18
17
  class CacheEntry {
19
18
  constructor(result, snapshot) {