webpack 5.58.2 → 5.59.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.

@@ -1883,7 +1883,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1883
1883
  creatingModuleDuringBuildSet
1884
1884
  );
1885
1885
  }
1886
- creatingModuleDuringBuildSet.add(originModule);
1886
+ creatingModuleDuringBuildSet.add(module);
1887
1887
 
1888
1888
  // When building is blocked by another module
1889
1889
  // search for a cycle, cancel the cycle by throwing
@@ -4687,21 +4687,18 @@ This prevents using hashes of each other and should be avoided.`);
4687
4687
  * @returns {void}
4688
4688
  */
4689
4689
  (module, push, callback) => {
4690
- this.addModuleQueue.waitFor(module, err => {
4690
+ this.buildQueue.waitFor(module, err => {
4691
4691
  if (err) return callback(err);
4692
- this.buildQueue.waitFor(module, err => {
4692
+ this.processDependenciesQueue.waitFor(module, err => {
4693
4693
  if (err) return callback(err);
4694
- this.processDependenciesQueue.waitFor(module, err => {
4695
- if (err) return callback(err);
4696
- for (const {
4697
- module: m
4698
- } of this.moduleGraph.getOutgoingConnections(module)) {
4699
- const size = modules.size;
4700
- modules.add(m);
4701
- if (modules.size !== size) push(m);
4702
- }
4703
- callback();
4704
- });
4694
+ for (const { module: m } of this.moduleGraph.getOutgoingConnections(
4695
+ module
4696
+ )) {
4697
+ const size = modules.size;
4698
+ modules.add(m);
4699
+ if (modules.size !== size) push(m);
4700
+ }
4701
+ callback();
4705
4702
  });
4706
4703
  });
4707
4704
  },
package/lib/Compiler.js CHANGED
@@ -219,9 +219,9 @@ class Compiler {
219
219
  /** @type {string|null} */
220
220
  this.recordsOutputPath = null;
221
221
  this.records = {};
222
- /** @type {Set<string>} */
222
+ /** @type {Set<string | RegExp>} */
223
223
  this.managedPaths = new Set();
224
- /** @type {Set<string>} */
224
+ /** @type {Set<string | RegExp>} */
225
225
  this.immutablePaths = new Set();
226
226
 
227
227
  /** @type {ReadonlySet<string>} */
@@ -151,7 +151,7 @@ const stringifyObj = (
151
151
  case false:
152
152
  return arr ? `;${code}` : `;(${code})`;
153
153
  default:
154
- return `Object(${code})`;
154
+ return `/*#__PURE__*/Object(${code})`;
155
155
  }
156
156
  };
157
157
 
@@ -853,8 +853,8 @@ class FileSystemInfo {
853
853
  /**
854
854
  * @param {InputFileSystem} fs file system
855
855
  * @param {Object} options options
856
- * @param {Iterable<string>=} options.managedPaths paths that are only managed by a package manager
857
- * @param {Iterable<string>=} options.immutablePaths paths that are immutable
856
+ * @param {Iterable<string | RegExp>=} options.managedPaths paths that are only managed by a package manager
857
+ * @param {Iterable<string | RegExp>=} options.immutablePaths paths that are immutable
858
858
  * @param {Logger=} options.logger logger used to log invalid snapshots
859
859
  * @param {string | Hash=} options.hashFunction the hash function to use
860
860
  */
@@ -996,12 +996,19 @@ class FileSystemInfo {
996
996
  processor: this._getManagedItemDirectoryInfo.bind(this)
997
997
  });
998
998
  this.managedPaths = Array.from(managedPaths);
999
- this.managedPathsWithSlash = this.managedPaths.map(p =>
1000
- join(fs, p, "_").slice(0, -1)
999
+ this.managedPathsWithSlash = /** @type {string[]} */ (
1000
+ this.managedPaths.filter(p => typeof p === "string")
1001
+ ).map(p => join(fs, p, "_").slice(0, -1));
1002
+
1003
+ this.managedPathsRegExps = /** @type {RegExp[]} */ (
1004
+ this.managedPaths.filter(p => typeof p !== "string")
1001
1005
  );
1002
1006
  this.immutablePaths = Array.from(immutablePaths);
1003
- this.immutablePathsWithSlash = this.immutablePaths.map(p =>
1004
- join(fs, p, "_").slice(0, -1)
1007
+ this.immutablePathsWithSlash = /** @type {string[]} */ (
1008
+ this.immutablePaths.filter(p => typeof p === "string")
1009
+ ).map(p => join(fs, p, "_").slice(0, -1));
1010
+ this.immutablePathsRegExps = /** @type {RegExp[]} */ (
1011
+ this.immutablePaths.filter(p => typeof p !== "string")
1005
1012
  );
1006
1013
 
1007
1014
  this._cachedDeprecatedFileTimestamps = undefined;
@@ -1966,12 +1973,29 @@ class FileSystemInfo {
1966
1973
  }
1967
1974
  };
1968
1975
  const checkManaged = (path, managedSet) => {
1976
+ for (const immutablePath of this.immutablePathsRegExps) {
1977
+ if (immutablePath.test(path)) {
1978
+ managedSet.add(path);
1979
+ return true;
1980
+ }
1981
+ }
1969
1982
  for (const immutablePath of this.immutablePathsWithSlash) {
1970
1983
  if (path.startsWith(immutablePath)) {
1971
1984
  managedSet.add(path);
1972
1985
  return true;
1973
1986
  }
1974
1987
  }
1988
+ for (const managedPath of this.managedPathsRegExps) {
1989
+ const match = managedPath.exec(path);
1990
+ if (match) {
1991
+ const managedItem = getManagedItem(managedPath[1], path);
1992
+ if (managedItem) {
1993
+ managedItems.add(managedItem);
1994
+ managedSet.add(path);
1995
+ return true;
1996
+ }
1997
+ }
1998
+ }
1975
1999
  for (const managedPath of this.managedPathsWithSlash) {
1976
2000
  if (path.startsWith(managedPath)) {
1977
2001
  const managedItem = getManagedItem(managedPath, path);
@@ -2923,10 +2947,29 @@ class FileSystemInfo {
2923
2947
  files,
2924
2948
  (file, callback) => {
2925
2949
  const child = join(this.fs, path, file);
2950
+ for (const immutablePath of this.immutablePathsRegExps) {
2951
+ if (immutablePath.test(path)) {
2952
+ // ignore any immutable path for timestamping
2953
+ return callback(null, fromImmutablePath(path));
2954
+ }
2955
+ }
2926
2956
  for (const immutablePath of this.immutablePathsWithSlash) {
2927
2957
  if (path.startsWith(immutablePath)) {
2928
2958
  // ignore any immutable path for timestamping
2929
- return callback(null, fromImmutablePath(immutablePath));
2959
+ return callback(null, fromImmutablePath(path));
2960
+ }
2961
+ }
2962
+ for (const managedPath of this.managedPathsRegExps) {
2963
+ const match = managedPath.exec(path);
2964
+ if (match) {
2965
+ const managedItem = getManagedItem(managedPath[1], path);
2966
+ if (managedItem) {
2967
+ // construct timestampHash from managed info
2968
+ return this.managedItemQueue.add(managedItem, (err, info) => {
2969
+ if (err) return callback(err);
2970
+ return callback(null, fromManagedItem(info));
2971
+ });
2972
+ }
2930
2973
  }
2931
2974
  }
2932
2975
  for (const managedPath of this.managedPathsWithSlash) {
@@ -187,6 +187,8 @@ makeSerializable(
187
187
  * @typedef {Object} NormalModuleCompilationHooks
188
188
  * @property {SyncHook<[object, NormalModule]>} loader
189
189
  * @property {SyncHook<[LoaderItem[], NormalModule, object]>} beforeLoaders
190
+ * @property {SyncHook<[NormalModule]>} beforeParse
191
+ * @property {SyncHook<[NormalModule]>} beforeSnapshot
190
192
  * @property {HookMap<AsyncSeriesBailHook<[string, NormalModule], string | Buffer>>} readResourceForScheme
191
193
  * @property {HookMap<AsyncSeriesBailHook<[object], string | Buffer>>} readResource
192
194
  * @property {AsyncSeriesBailHook<[NormalModule, NeedBuildContext], boolean>} needBuild
@@ -211,6 +213,8 @@ class NormalModule extends Module {
211
213
  hooks = {
212
214
  loader: new SyncHook(["loaderContext", "module"]),
213
215
  beforeLoaders: new SyncHook(["loaders", "module", "loaderContext"]),
216
+ beforeParse: new SyncHook(["module"]),
217
+ beforeSnapshot: new SyncHook(["module"]),
214
218
  // TODO webpack 6 deprecate
215
219
  readResourceForScheme: new HookMap(scheme => {
216
220
  const hook = hooks.readResource.for(scheme);
@@ -387,6 +391,7 @@ class NormalModule extends Module {
387
391
  this.generator = m.generator;
388
392
  this.generatorOptions = m.generatorOptions;
389
393
  this.resource = m.resource;
394
+ this.resourceResolveData = m.resourceResolveData;
390
395
  this.context = m.context;
391
396
  this.matchResource = m.matchResource;
392
397
  this.loaders = m.loaders;
@@ -488,9 +493,10 @@ class NormalModule extends Module {
488
493
  * @param {WebpackOptions} options webpack options
489
494
  * @param {Compilation} compilation the compilation
490
495
  * @param {InputFileSystem} fs file system from reading
496
+ * @param {NormalModuleCompilationHooks} hooks the hooks
491
497
  * @returns {NormalModuleLoaderContext} loader context
492
498
  */
493
- createLoaderContext(resolver, options, compilation, fs) {
499
+ _createLoaderContext(resolver, options, compilation, fs, hooks) {
494
500
  const { requestShortener } = compilation.runtimeTemplate;
495
501
  const getCurrentLoaderName = () => {
496
502
  const currentLoader = this.getCurrentLoader(loaderContext);
@@ -659,10 +665,7 @@ class NormalModule extends Module {
659
665
 
660
666
  Object.assign(loaderContext, options.loader);
661
667
 
662
- NormalModule.getCompilationHooks(compilation).loader.call(
663
- loaderContext,
664
- this
665
- );
668
+ hooks.loader.call(loaderContext, this);
666
669
 
667
670
  return loaderContext;
668
671
  }
@@ -723,15 +726,17 @@ class NormalModule extends Module {
723
726
  * @param {Compilation} compilation the compilation
724
727
  * @param {ResolverWithOptions} resolver the resolver
725
728
  * @param {InputFileSystem} fs the file system
729
+ * @param {NormalModuleCompilationHooks} hooks the hooks
726
730
  * @param {function(WebpackError=): void} callback callback function
727
731
  * @returns {void}
728
732
  */
729
- doBuild(options, compilation, resolver, fs, callback) {
730
- const loaderContext = this.createLoaderContext(
733
+ _doBuild(options, compilation, resolver, fs, hooks, callback) {
734
+ const loaderContext = this._createLoaderContext(
731
735
  resolver,
732
736
  options,
733
737
  compilation,
734
- fs
738
+ fs,
739
+ hooks
735
740
  );
736
741
 
737
742
  const processResult = (err, result) => {
@@ -785,8 +790,6 @@ class NormalModule extends Module {
785
790
  return callback();
786
791
  };
787
792
 
788
- const hooks = NormalModule.getCompilationHooks(compilation);
789
-
790
793
  this.buildInfo.fileDependencies = new LazySet();
791
794
  this.buildInfo.contextDependencies = new LazySet();
792
795
  this.buildInfo.missingDependencies = new LazySet();
@@ -942,7 +945,9 @@ class NormalModule extends Module {
942
945
 
943
946
  const startTime = compilation.compiler.fsStartTime || Date.now();
944
947
 
945
- return this.doBuild(options, compilation, resolver, fs, err => {
948
+ const hooks = NormalModule.getCompilationHooks(compilation);
949
+
950
+ return this._doBuild(options, compilation, resolver, fs, hooks, err => {
946
951
  // if we have an error mark module as failed and exit
947
952
  if (err) {
948
953
  this.markModuleAsErrored(err);
@@ -974,6 +979,13 @@ class NormalModule extends Module {
974
979
  };
975
980
 
976
981
  const handleBuildDone = () => {
982
+ try {
983
+ hooks.beforeSnapshot.call(this);
984
+ } catch (err) {
985
+ this.markModuleAsErrored(err);
986
+ return callback();
987
+ }
988
+
977
989
  const snapshotOptions = compilation.options.snapshot.module;
978
990
  if (!this.buildInfo.cacheable || !snapshotOptions) {
979
991
  return callback();
@@ -1038,6 +1050,14 @@ class NormalModule extends Module {
1038
1050
  );
1039
1051
  };
1040
1052
 
1053
+ try {
1054
+ hooks.beforeParse.call(this);
1055
+ } catch (err) {
1056
+ this.markModuleAsErrored(err);
1057
+ this._initBuildHash(compilation);
1058
+ return callback();
1059
+ }
1060
+
1041
1061
  // check if this module should !not! be parsed.
1042
1062
  // if so, exit here;
1043
1063
  const noParseRule = options.module && options.module.noParse;
@@ -807,7 +807,7 @@ class RuntimeTemplate {
807
807
  ? `(0,${access})`
808
808
  : asiSafe === false
809
809
  ? `;(0,${access})`
810
- : `Object(${access})`;
810
+ : `/*#__PURE__*/Object(${access})`;
811
811
  }
812
812
  return access;
813
813
  } else {
@@ -279,8 +279,6 @@ class WebpackOptionsApply extends OptionsApply {
279
279
  if (options.experiments.buildHttp) {
280
280
  const HttpUriPlugin = require("./schemes/HttpUriPlugin");
281
281
  const httpOptions = options.experiments.buildHttp;
282
- if (httpOptions === true)
283
- throw new Error("Unexpected due to normalization");
284
282
  new HttpUriPlugin(httpOptions).apply(compiler);
285
283
  }
286
284
 
@@ -9,8 +9,8 @@
9
9
 
10
10
  class AddManagedPathsPlugin {
11
11
  /**
12
- * @param {Iterable<string>} managedPaths list of managed paths
13
- * @param {Iterable<string>} immutablePaths list of immutable paths
12
+ * @param {Iterable<string | RegExp>} managedPaths list of managed paths
13
+ * @param {Iterable<string | RegExp>} immutablePaths list of immutable paths
14
14
  */
15
15
  constructor(managedPaths, immutablePaths) {
16
16
  this.managedPaths = new Set(managedPaths);
@@ -370,8 +370,8 @@ class Pack {
370
370
  for (const identifier of content.items) {
371
371
  mergedItems.add(identifier);
372
372
  }
373
- for (const identifer of content.used) {
374
- mergedUsedItems.add(identifer);
373
+ for (const identifier of content.used) {
374
+ mergedUsedItems.add(identifier);
375
375
  }
376
376
  addToMergedMap.push(async map => {
377
377
  // unpack existing content
@@ -19,6 +19,7 @@ const {
19
19
  /** @typedef {import("../../declarations/WebpackOptions").EntryDescription} EntryDescription */
20
20
  /** @typedef {import("../../declarations/WebpackOptions").EntryNormalized} Entry */
21
21
  /** @typedef {import("../../declarations/WebpackOptions").Experiments} Experiments */
22
+ /** @typedef {import("../../declarations/WebpackOptions").ExperimentsNormalized} ExperimentsNormalized */
22
23
  /** @typedef {import("../../declarations/WebpackOptions").ExternalsPresets} ExternalsPresets */
23
24
  /** @typedef {import("../../declarations/WebpackOptions").ExternalsType} ExternalsType */
24
25
  /** @typedef {import("../../declarations/WebpackOptions").InfrastructureLogging} InfrastructureLogging */
@@ -161,6 +162,8 @@ const applyWebpackOptionsDefaults = options => {
161
162
 
162
163
  applyExperimentsDefaults(options.experiments, { production, development });
163
164
 
165
+ const futureDefaults = options.experiments.futureDefaults;
166
+
164
167
  F(options, "cache", () =>
165
168
  development ? { type: /** @type {"memory"} */ ("memory") } : false
166
169
  );
@@ -172,7 +175,10 @@ const applyWebpackOptionsDefaults = options => {
172
175
  });
173
176
  const cache = !!options.cache;
174
177
 
175
- applySnapshotDefaults(options.snapshot, { production });
178
+ applySnapshotDefaults(options.snapshot, {
179
+ production,
180
+ futureDefaults
181
+ });
176
182
 
177
183
  applyModuleDefaults(options.module, {
178
184
  cache,
@@ -192,7 +198,7 @@ const applyWebpackOptionsDefaults = options => {
192
198
  development,
193
199
  entry: options.entry,
194
200
  module: options.module,
195
- futureDefaults: options.experiments.futureDefaults
201
+ futureDefaults
196
202
  });
197
203
 
198
204
  applyExternalsPresetsDefaults(options.externalsPresets, {
@@ -252,7 +258,7 @@ const applyWebpackOptionsDefaults = options => {
252
258
  };
253
259
 
254
260
  /**
255
- * @param {Experiments} experiments options
261
+ * @param {ExperimentsNormalized} experiments options
256
262
  * @param {Object} options options
257
263
  * @param {boolean} options.production is production
258
264
  * @param {boolean} options.development is development mode
@@ -265,14 +271,14 @@ const applyExperimentsDefaults = (experiments, { production, development }) => {
265
271
  D(experiments, "outputModule", false);
266
272
  D(experiments, "asset", false);
267
273
  D(experiments, "layers", false);
268
- D(experiments, "lazyCompilation", false);
269
- D(experiments, "buildHttp", false);
274
+ D(experiments, "lazyCompilation", undefined);
275
+ D(experiments, "buildHttp", undefined);
270
276
  D(experiments, "futureDefaults", false);
271
277
  D(experiments, "cacheUnaffected", experiments.futureDefaults);
272
278
 
273
279
  if (typeof experiments.buildHttp === "object") {
274
280
  D(experiments.buildHttp, "frozen", production);
275
- D(experiments.buildHttp, "upgrade", development);
281
+ D(experiments.buildHttp, "upgrade", false);
276
282
  }
277
283
  };
278
284
 
@@ -348,49 +354,65 @@ const applyCacheDefaults = (
348
354
  * @param {SnapshotOptions} snapshot options
349
355
  * @param {Object} options options
350
356
  * @param {boolean} options.production is production
357
+ * @param {boolean} options.futureDefaults is future defaults enabled
351
358
  * @returns {void}
352
359
  */
353
- const applySnapshotDefaults = (snapshot, { production }) => {
354
- A(snapshot, "managedPaths", () => {
355
- if (process.versions.pnp === "3") {
356
- const match =
357
- /^(.+?)[\\/]cache[\\/]watchpack-npm-[^\\/]+\.zip[\\/]node_modules[\\/]/.exec(
358
- require.resolve("watchpack")
359
- );
360
- if (match) {
361
- return [path.resolve(match[1], "unplugged")];
362
- }
363
- } else {
364
- const match = /^(.+?[\\/]node_modules)[\\/]/.exec(
365
- // eslint-disable-next-line node/no-extraneous-require
366
- require.resolve("watchpack")
367
- );
368
- if (match) {
369
- return [match[1]];
370
- }
371
- }
372
- return [];
373
- });
374
- A(snapshot, "immutablePaths", () => {
375
- if (process.versions.pnp === "1") {
376
- const match =
377
- /^(.+?[\\/]v4)[\\/]npm-watchpack-[^\\/]+-[\da-f]{40}[\\/]node_modules[\\/]/.exec(
360
+ const applySnapshotDefaults = (snapshot, { production, futureDefaults }) => {
361
+ if (futureDefaults) {
362
+ F(snapshot, "managedPaths", () =>
363
+ process.versions.pnp === "3"
364
+ ? [
365
+ /^(.+?(?:[\\/]\.yarn[\\/]unplugged[\\/][^\\/]+)?[\\/]node_modules[\\/])/
366
+ ]
367
+ : [/^(.+?[\\/]node_modules[\\/])/]
368
+ );
369
+ F(snapshot, "immutablePaths", () =>
370
+ process.versions.pnp === "3"
371
+ ? [/^(.+?[\\/]cache[\\/][^\\/]+\.zip[\\/]node_modules[\\/])/]
372
+ : []
373
+ );
374
+ } else {
375
+ A(snapshot, "managedPaths", () => {
376
+ if (process.versions.pnp === "3") {
377
+ const match =
378
+ /^(.+?)[\\/]cache[\\/]watchpack-npm-[^\\/]+\.zip[\\/]node_modules[\\/]/.exec(
379
+ require.resolve("watchpack")
380
+ );
381
+ if (match) {
382
+ return [path.resolve(match[1], "unplugged")];
383
+ }
384
+ } else {
385
+ const match = /^(.+?[\\/]node_modules)[\\/]/.exec(
386
+ // eslint-disable-next-line node/no-extraneous-require
378
387
  require.resolve("watchpack")
379
388
  );
380
- if (match) {
381
- return [match[1]];
389
+ if (match) {
390
+ return [match[1]];
391
+ }
382
392
  }
383
- } else if (process.versions.pnp === "3") {
384
- const match =
385
- /^(.+?)[\\/]watchpack-npm-[^\\/]+\.zip[\\/]node_modules[\\/]/.exec(
386
- require.resolve("watchpack")
387
- );
388
- if (match) {
389
- return [match[1]];
393
+ return [];
394
+ });
395
+ A(snapshot, "immutablePaths", () => {
396
+ if (process.versions.pnp === "1") {
397
+ const match =
398
+ /^(.+?[\\/]v4)[\\/]npm-watchpack-[^\\/]+-[\da-f]{40}[\\/]node_modules[\\/]/.exec(
399
+ require.resolve("watchpack")
400
+ );
401
+ if (match) {
402
+ return [match[1]];
403
+ }
404
+ } else if (process.versions.pnp === "3") {
405
+ const match =
406
+ /^(.+?)[\\/]watchpack-npm-[^\\/]+\.zip[\\/]node_modules[\\/]/.exec(
407
+ require.resolve("watchpack")
408
+ );
409
+ if (match) {
410
+ return [match[1]];
411
+ }
390
412
  }
391
- }
392
- return [];
393
- });
413
+ return [];
414
+ });
415
+ }
394
416
  F(snapshot, "resolveBuildDependencies", () => ({
395
417
  timestamp: true,
396
418
  hash: true
@@ -174,7 +174,12 @@ const getNormalizedWebpackOptions = config => {
174
174
  experiments: nestedConfig(config.experiments, experiments => ({
175
175
  ...experiments,
176
176
  buildHttp: optionalNestedConfig(experiments.buildHttp, options =>
177
- options === true ? {} : options
177
+ Array.isArray(options) ? { allowedUris: options } : options
178
+ ),
179
+ lazyCompilation: optionalNestedConfig(
180
+ experiments.lazyCompilation,
181
+ options =>
182
+ options === true ? {} : options === false ? undefined : options
178
183
  )
179
184
  })),
180
185
  externals: config.externals,
@@ -564,7 +564,7 @@ const getFinalName = (
564
564
  ? `(0,${reference})`
565
565
  : asiSafe === false
566
566
  ? `;(0,${reference})`
567
- : `Object(${reference})`;
567
+ : `/*#__PURE__*/Object(${reference})`;
568
568
  }
569
569
  return reference;
570
570
  }
@@ -59,6 +59,7 @@ const MinMaxSizeWarning = require("./MinMaxSizeWarning");
59
59
  * @property {ChunkFilterFunction=} chunksFilter
60
60
  * @property {boolean=} enforce
61
61
  * @property {SplitChunksSizes} minSize
62
+ * @property {SplitChunksSizes} minSizeReduction
62
63
  * @property {SplitChunksSizes} minRemainingSize
63
64
  * @property {SplitChunksSizes} enforceSizeThreshold
64
65
  * @property {SplitChunksSizes} maxAsyncSize
@@ -80,6 +81,7 @@ const MinMaxSizeWarning = require("./MinMaxSizeWarning");
80
81
  * @property {GetName=} getName
81
82
  * @property {ChunkFilterFunction=} chunksFilter
82
83
  * @property {SplitChunksSizes} minSize
84
+ * @property {SplitChunksSizes} minSizeReduction
83
85
  * @property {SplitChunksSizes} minRemainingSize
84
86
  * @property {SplitChunksSizes} enforceSizeThreshold
85
87
  * @property {SplitChunksSizes} maxAsyncSize
@@ -132,6 +134,7 @@ const MinMaxSizeWarning = require("./MinMaxSizeWarning");
132
134
  * @property {ChunkFilterFunction} chunksFilter
133
135
  * @property {string[]} defaultSizeTypes
134
136
  * @property {SplitChunksSizes} minSize
137
+ * @property {SplitChunksSizes} minSizeReduction
135
138
  * @property {SplitChunksSizes} minRemainingSize
136
139
  * @property {SplitChunksSizes} enforceSizeThreshold
137
140
  * @property {SplitChunksSizes} maxInitialSize
@@ -336,6 +339,21 @@ const checkMinSize = (sizes, minSize) => {
336
339
  return true;
337
340
  };
338
341
 
342
+ /**
343
+ * @param {SplitChunksSizes} sizes the sizes
344
+ * @param {SplitChunksSizes} minSizeReduction the min sizes
345
+ * @param {number} chunkCount number of chunks
346
+ * @returns {boolean} true if there are sizes and all existing sizes are at least `minSizeReduction`
347
+ */
348
+ const checkMinSizeReduction = (sizes, minSizeReduction, chunkCount) => {
349
+ for (const key of Object.keys(minSizeReduction)) {
350
+ const size = sizes[key];
351
+ if (size === undefined || size === 0) continue;
352
+ if (size * chunkCount < minSizeReduction[key]) return false;
353
+ }
354
+ return true;
355
+ };
356
+
339
357
  /**
340
358
  * @param {SplitChunksSizes} sizes the sizes
341
359
  * @param {SplitChunksSizes} minSize the min sizes
@@ -548,6 +566,10 @@ const checkModuleLayer = (test, module) => {
548
566
  */
549
567
  const createCacheGroupSource = (options, key, defaultSizeTypes) => {
550
568
  const minSize = normalizeSizes(options.minSize, defaultSizeTypes);
569
+ const minSizeReduction = normalizeSizes(
570
+ options.minSizeReduction,
571
+ defaultSizeTypes
572
+ );
551
573
  const maxSize = normalizeSizes(options.maxSize, defaultSizeTypes);
552
574
  return {
553
575
  key,
@@ -556,6 +578,7 @@ const createCacheGroupSource = (options, key, defaultSizeTypes) => {
556
578
  chunksFilter: normalizeChunksFilter(options.chunks),
557
579
  enforce: options.enforce,
558
580
  minSize,
581
+ minSizeReduction,
559
582
  minRemainingSize: mergeSizes(
560
583
  normalizeSizes(options.minRemainingSize, defaultSizeTypes),
561
584
  minSize
@@ -594,6 +617,10 @@ module.exports = class SplitChunksPlugin {
594
617
  ];
595
618
  const fallbackCacheGroup = options.fallbackCacheGroup || {};
596
619
  const minSize = normalizeSizes(options.minSize, defaultSizeTypes);
620
+ const minSizeReduction = normalizeSizes(
621
+ options.minSizeReduction,
622
+ defaultSizeTypes
623
+ );
597
624
  const maxSize = normalizeSizes(options.maxSize, defaultSizeTypes);
598
625
 
599
626
  /** @type {SplitChunksOptions} */
@@ -601,6 +628,7 @@ module.exports = class SplitChunksPlugin {
601
628
  chunksFilter: normalizeChunksFilter(options.chunks || "all"),
602
629
  defaultSizeTypes,
603
630
  minSize,
631
+ minSizeReduction,
604
632
  minRemainingSize: mergeSizes(
605
633
  normalizeSizes(options.minRemainingSize, defaultSizeTypes),
606
634
  minSize
@@ -668,6 +696,10 @@ module.exports = class SplitChunksPlugin {
668
696
  cacheGroupSource.minSize,
669
697
  cacheGroupSource.enforce ? undefined : this.options.minSize
670
698
  );
699
+ const minSizeReduction = mergeSizes(
700
+ cacheGroupSource.minSizeReduction,
701
+ cacheGroupSource.enforce ? undefined : this.options.minSizeReduction
702
+ );
671
703
  const minRemainingSize = mergeSizes(
672
704
  cacheGroupSource.minRemainingSize,
673
705
  cacheGroupSource.enforce ? undefined : this.options.minRemainingSize
@@ -681,6 +713,7 @@ module.exports = class SplitChunksPlugin {
681
713
  priority: cacheGroupSource.priority || 0,
682
714
  chunksFilter: cacheGroupSource.chunksFilter || this.options.chunksFilter,
683
715
  minSize,
716
+ minSizeReduction,
684
717
  minRemainingSize,
685
718
  enforceSizeThreshold,
686
719
  maxAsyncSize: mergeSizes(
@@ -1244,6 +1277,14 @@ module.exports = class SplitChunksPlugin {
1244
1277
  for (const [key, info] of chunksInfoMap) {
1245
1278
  if (removeMinSizeViolatingModules(info)) {
1246
1279
  chunksInfoMap.delete(key);
1280
+ } else if (
1281
+ !checkMinSizeReduction(
1282
+ info.sizes,
1283
+ info.cacheGroup.minSizeReduction,
1284
+ info.chunks.size
1285
+ )
1286
+ ) {
1287
+ chunksInfoMap.delete(key);
1247
1288
  }
1248
1289
  }
1249
1290
 
@@ -1531,7 +1572,14 @@ module.exports = class SplitChunksPlugin {
1531
1572
  chunksInfoMap.delete(key);
1532
1573
  continue;
1533
1574
  }
1534
- if (removeMinSizeViolatingModules(info)) {
1575
+ if (
1576
+ removeMinSizeViolatingModules(info) ||
1577
+ !checkMinSizeReduction(
1578
+ info.sizes,
1579
+ info.cacheGroup.minSizeReduction,
1580
+ info.chunks.size
1581
+ )
1582
+ ) {
1535
1583
  chunksInfoMap.delete(key);
1536
1584
  continue;
1537
1585
  }