webpack 4.42.0 → 4.44.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.
@@ -709,6 +709,10 @@ export interface ResolveOptions {
709
709
  resolver?: {
710
710
  [k: string]: any;
711
711
  };
712
+ /**
713
+ * A list of directories in which requests that are server-relative URLs (starting with '/') are resolved. On non-windows system these requests are tried to resolve as absolute path first.
714
+ */
715
+ roots?: string[];
712
716
  /**
713
717
  * Enable resolving symlinks to the original location
714
718
  */
@@ -927,6 +931,10 @@ export interface OptimizationSplitChunksOptions {
927
931
  * Ignore minimum size, minimum chunks and maximum requests and always create chunks for this cache group
928
932
  */
929
933
  enforce?: boolean;
934
+ /**
935
+ * Size threshold at which splitting is enforced and other restrictions (maxAsyncRequests, maxInitialRequests) are ignored.
936
+ */
937
+ enforceSizeThreshold?: number;
930
938
  /**
931
939
  * Sets the template for the filename for created chunks (Only works for initial chunks)
932
940
  */
@@ -973,6 +981,10 @@ export interface OptimizationSplitChunksOptions {
973
981
  * Select chunks for determining shared modules (defaults to "async", "initial" and "all" requires adding these chunks to the HTML)
974
982
  */
975
983
  chunks?: ("initial" | "async" | "all") | Function;
984
+ /**
985
+ * Size threshold at which splitting is enforced and other restrictions (maxAsyncRequests, maxInitialRequests) are ignored.
986
+ */
987
+ enforceSizeThreshold?: number;
976
988
  /**
977
989
  * Options for modules not selected by any other cache group
978
990
  */
@@ -109,6 +109,7 @@ module.exports = function() {
109
109
  _declinedDependencies: {},
110
110
  _selfAccepted: false,
111
111
  _selfDeclined: false,
112
+ _selfInvalidated: false,
112
113
  _disposeHandlers: [],
113
114
  _main: hotCurrentChildModule !== moduleId,
114
115
 
@@ -139,6 +140,29 @@ module.exports = function() {
139
140
  var idx = hot._disposeHandlers.indexOf(callback);
140
141
  if (idx >= 0) hot._disposeHandlers.splice(idx, 1);
141
142
  },
143
+ invalidate: function() {
144
+ this._selfInvalidated = true;
145
+ switch (hotStatus) {
146
+ case "idle":
147
+ hotUpdate = {};
148
+ hotUpdate[moduleId] = modules[moduleId];
149
+ hotSetStatus("ready");
150
+ break;
151
+ case "ready":
152
+ hotApplyInvalidatedModule(moduleId);
153
+ break;
154
+ case "prepare":
155
+ case "check":
156
+ case "dispose":
157
+ case "apply":
158
+ (hotQueuedInvalidatedModules =
159
+ hotQueuedInvalidatedModules || []).push(moduleId);
160
+ break;
161
+ default:
162
+ // ignore requests in error states
163
+ break;
164
+ }
165
+ },
142
166
 
143
167
  // Management API
144
168
  check: hotCheck,
@@ -180,7 +204,7 @@ module.exports = function() {
180
204
  var hotDeferred;
181
205
 
182
206
  // The update info
183
- var hotUpdate, hotUpdateNewHash;
207
+ var hotUpdate, hotUpdateNewHash, hotQueuedInvalidatedModules;
184
208
 
185
209
  function toModuleId(id) {
186
210
  var isNumber = +id + "" === id;
@@ -195,7 +219,7 @@ module.exports = function() {
195
219
  hotSetStatus("check");
196
220
  return hotDownloadManifest(hotRequestTimeout).then(function(update) {
197
221
  if (!update) {
198
- hotSetStatus("idle");
222
+ hotSetStatus(hotApplyInvalidatedModules() ? "ready" : "idle");
199
223
  return null;
200
224
  }
201
225
  hotRequestedFilesMap = {};
@@ -288,6 +312,11 @@ module.exports = function() {
288
312
  if (hotStatus !== "ready")
289
313
  throw new Error("apply() is only allowed in ready status");
290
314
  options = options || {};
315
+ return hotApplyInternal(options);
316
+ }
317
+
318
+ function hotApplyInternal(options) {
319
+ hotApplyInvalidatedModules();
291
320
 
292
321
  var cb;
293
322
  var i;
@@ -310,7 +339,11 @@ module.exports = function() {
310
339
  var moduleId = queueItem.id;
311
340
  var chain = queueItem.chain;
312
341
  module = installedModules[moduleId];
313
- if (!module || module.hot._selfAccepted) continue;
342
+ if (
343
+ !module ||
344
+ (module.hot._selfAccepted && !module.hot._selfInvalidated)
345
+ )
346
+ continue;
314
347
  if (module.hot._selfDeclined) {
315
348
  return {
316
349
  type: "self-declined",
@@ -478,10 +511,13 @@ module.exports = function() {
478
511
  installedModules[moduleId] &&
479
512
  installedModules[moduleId].hot._selfAccepted &&
480
513
  // removed self-accepted modules should not be required
481
- appliedUpdate[moduleId] !== warnUnexpectedRequire
514
+ appliedUpdate[moduleId] !== warnUnexpectedRequire &&
515
+ // when called invalidate self-accepting is not possible
516
+ !installedModules[moduleId].hot._selfInvalidated
482
517
  ) {
483
518
  outdatedSelfAcceptedModules.push({
484
519
  module: moduleId,
520
+ parents: installedModules[moduleId].parents.slice(),
485
521
  errorHandler: installedModules[moduleId].hot._selfAccepted
486
522
  });
487
523
  }
@@ -554,7 +590,11 @@ module.exports = function() {
554
590
  // Now in "apply" phase
555
591
  hotSetStatus("apply");
556
592
 
557
- hotCurrentHash = hotUpdateNewHash;
593
+ if (hotUpdateNewHash !== undefined) {
594
+ hotCurrentHash = hotUpdateNewHash;
595
+ hotUpdateNewHash = undefined;
596
+ }
597
+ hotUpdate = undefined;
558
598
 
559
599
  // insert new code
560
600
  for (moduleId in appliedUpdate) {
@@ -607,7 +647,8 @@ module.exports = function() {
607
647
  for (i = 0; i < outdatedSelfAcceptedModules.length; i++) {
608
648
  var item = outdatedSelfAcceptedModules[i];
609
649
  moduleId = item.module;
610
- hotCurrentParents = [moduleId];
650
+ hotCurrentParents = item.parents;
651
+ hotCurrentChildModule = moduleId;
611
652
  try {
612
653
  $require$(moduleId);
613
654
  } catch (err) {
@@ -649,9 +690,32 @@ module.exports = function() {
649
690
  return Promise.reject(error);
650
691
  }
651
692
 
693
+ if (hotQueuedInvalidatedModules) {
694
+ return hotApplyInternal(options).then(function(list) {
695
+ outdatedModules.forEach(function(moduleId) {
696
+ if (list.indexOf(moduleId) < 0) list.push(moduleId);
697
+ });
698
+ return list;
699
+ });
700
+ }
701
+
652
702
  hotSetStatus("idle");
653
703
  return new Promise(function(resolve) {
654
704
  resolve(outdatedModules);
655
705
  });
656
706
  }
707
+
708
+ function hotApplyInvalidatedModules() {
709
+ if (hotQueuedInvalidatedModules) {
710
+ if (!hotUpdate) hotUpdate = {};
711
+ hotQueuedInvalidatedModules.forEach(hotApplyInvalidatedModule);
712
+ hotQueuedInvalidatedModules = undefined;
713
+ return true;
714
+ }
715
+ }
716
+
717
+ function hotApplyInvalidatedModule(moduleId) {
718
+ if (!Object.prototype.hasOwnProperty.call(hotUpdate, moduleId))
719
+ hotUpdate[moduleId] = modules[moduleId];
720
+ }
657
721
  };
@@ -129,28 +129,34 @@ class RecordIdsPlugin {
129
129
  const sources = [];
130
130
  for (const chunkGroup of chunk.groupsIterable) {
131
131
  const index = chunkGroup.chunks.indexOf(chunk);
132
- for (const origin of chunkGroup.origins) {
133
- if (origin.module) {
134
- if (origin.request) {
135
- sources.push(
136
- `${index} ${getModuleIdentifier(origin.module)} ${
137
- origin.request
138
- }`
139
- );
140
- } else if (typeof origin.loc === "string") {
141
- sources.push(
142
- `${index} ${getModuleIdentifier(origin.module)} ${origin.loc}`
143
- );
144
- } else if (
145
- origin.loc &&
146
- typeof origin.loc === "object" &&
147
- origin.loc.start
148
- ) {
149
- sources.push(
150
- `${index} ${getModuleIdentifier(
151
- origin.module
152
- )} ${JSON.stringify(origin.loc.start)}`
153
- );
132
+ if (chunkGroup.name) {
133
+ sources.push(`${index} ${chunkGroup.name}`);
134
+ } else {
135
+ for (const origin of chunkGroup.origins) {
136
+ if (origin.module) {
137
+ if (origin.request) {
138
+ sources.push(
139
+ `${index} ${getModuleIdentifier(origin.module)} ${
140
+ origin.request
141
+ }`
142
+ );
143
+ } else if (typeof origin.loc === "string") {
144
+ sources.push(
145
+ `${index} ${getModuleIdentifier(origin.module)} ${
146
+ origin.loc
147
+ }`
148
+ );
149
+ } else if (
150
+ origin.loc &&
151
+ typeof origin.loc === "object" &&
152
+ origin.loc.start
153
+ ) {
154
+ sources.push(
155
+ `${index} ${getModuleIdentifier(
156
+ origin.module
157
+ )} ${JSON.stringify(origin.loc.start)}`
158
+ );
159
+ }
154
160
  }
155
161
  }
156
162
  }
@@ -362,6 +362,7 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
362
362
  this.set("resolveLoader.mainFields", ["loader", "main"]);
363
363
  this.set("resolveLoader.extensions", [".js", ".json"]);
364
364
  this.set("resolveLoader.mainFiles", ["index"]);
365
+ this.set("resolveLoader.roots", "make", options => [options.context]);
365
366
  this.set("resolveLoader.cacheWithContext", "make", options => {
366
367
  return (
367
368
  Array.isArray(options.resolveLoader.plugins) &&
@@ -27,6 +27,8 @@ class ExportMode {
27
27
  this.name = null;
28
28
  /** @type {Map<string, string>} */
29
29
  this.map = EMPTY_MAP;
30
+ /** @type {Set<string>|null} */
31
+ this.ignored = null;
30
32
  /** @type {Module|null} */
31
33
  this.module = null;
32
34
  /** @type {string|null} */
@@ -212,6 +214,11 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
212
214
 
213
215
  const mode = new ExportMode("dynamic-reexport");
214
216
  mode.module = importedModule;
217
+ mode.ignored = new Set([
218
+ "default",
219
+ ...this.activeExports,
220
+ ...activeFromOtherStarExports
221
+ ]);
215
222
  return mode;
216
223
  }
217
224
 
@@ -580,10 +587,7 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
580
587
  .join("");
581
588
 
582
589
  case "dynamic-reexport": {
583
- const activeExports = new Set([
584
- ...dep.activeExports,
585
- ...dep._discoverActiveExportsFromOtherStartExports()
586
- ]);
590
+ const ignoredExports = mode.ignored;
587
591
  let content =
588
592
  "/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in " +
589
593
  importVar +
@@ -591,10 +595,10 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
591
595
 
592
596
  // Filter out exports which are defined by other exports
593
597
  // and filter out default export because it cannot be reexported with *
594
- if (activeExports.size > 0) {
598
+ if (ignoredExports.size > 0) {
595
599
  content +=
596
600
  "if(" +
597
- JSON.stringify(Array.from(activeExports).concat("default")) +
601
+ JSON.stringify(Array.from(ignoredExports)) +
598
602
  ".indexOf(__WEBPACK_IMPORT_KEY__) < 0) ";
599
603
  } else {
600
604
  content += "if(__WEBPACK_IMPORT_KEY__ !== 'default') ";
@@ -16,8 +16,89 @@ const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportS
16
16
  * @typedef {Object} ExportInModule
17
17
  * @property {Module} module the module
18
18
  * @property {string} exportName the name of the export
19
+ * @property {boolean} checked if the export is conditional
19
20
  */
20
21
 
22
+ /**
23
+ * @typedef {Object} ReexportInfo
24
+ * @property {Map<string, ExportInModule[]>} static
25
+ * @property {Map<Module, Set<string>>} dynamic
26
+ */
27
+
28
+ /**
29
+ * @param {ReexportInfo} info info object
30
+ * @param {string} exportName name of export
31
+ * @returns {ExportInModule | undefined} static export
32
+ */
33
+ const getMappingFromInfo = (info, exportName) => {
34
+ const staticMappings = info.static.get(exportName);
35
+ if (staticMappings !== undefined) {
36
+ if (staticMappings.length === 1) return staticMappings[0];
37
+ return undefined;
38
+ }
39
+ const dynamicMappings = Array.from(info.dynamic).filter(
40
+ ([_, ignored]) => !ignored.has(exportName)
41
+ );
42
+ if (dynamicMappings.length === 1) {
43
+ return {
44
+ module: dynamicMappings[0][0],
45
+ exportName,
46
+ checked: true
47
+ };
48
+ }
49
+ return undefined;
50
+ };
51
+
52
+ /**
53
+ * @param {ReexportInfo} info info object
54
+ * @param {string} exportName name of export of source module
55
+ * @param {Module} module the target module
56
+ * @param {string} innerExportName name of export of target module
57
+ * @param {boolean} checked true, if existence of target module is checked
58
+ */
59
+ const addStaticReexport = (
60
+ info,
61
+ exportName,
62
+ module,
63
+ innerExportName,
64
+ checked
65
+ ) => {
66
+ let mappings = info.static.get(exportName);
67
+ if (mappings !== undefined) {
68
+ for (const mapping of mappings) {
69
+ if (mapping.module === module && mapping.exportName === innerExportName) {
70
+ mapping.checked = mapping.checked && checked;
71
+ return;
72
+ }
73
+ }
74
+ } else {
75
+ mappings = [];
76
+ info.static.set(exportName, mappings);
77
+ }
78
+ mappings.push({
79
+ module,
80
+ exportName: innerExportName,
81
+ checked
82
+ });
83
+ };
84
+
85
+ /**
86
+ * @param {ReexportInfo} info info object
87
+ * @param {Module} module the reexport module
88
+ * @param {Set<string>} ignored ignore list
89
+ * @returns {void}
90
+ */
91
+ const addDynamicReexport = (info, module, ignored) => {
92
+ const existingList = info.dynamic.get(module);
93
+ if (existingList !== undefined) {
94
+ for (const key of existingList) {
95
+ if (!ignored.has(key)) existingList.delete(key);
96
+ }
97
+ } else {
98
+ info.dynamic.set(module, new Set(ignored));
99
+ }
100
+ };
101
+
21
102
  class SideEffectsFlagPlugin {
22
103
  apply(compiler) {
23
104
  compiler.hooks.normalModuleFactory.tap("SideEffectsFlagPlugin", nmf => {
@@ -52,7 +133,7 @@ class SideEffectsFlagPlugin {
52
133
  compilation.hooks.optimizeDependencies.tap(
53
134
  "SideEffectsFlagPlugin",
54
135
  modules => {
55
- /** @type {Map<Module, Map<string, ExportInModule>>} */
136
+ /** @type {Map<Module, ReexportInfo>} */
56
137
  const reexportMaps = new Map();
57
138
 
58
139
  // Capture reexports of sideEffectFree modules
@@ -69,16 +150,66 @@ class SideEffectsFlagPlugin {
69
150
  ) {
70
151
  if (module.factoryMeta.sideEffectFree) {
71
152
  const mode = dep.getMode(true);
72
- if (mode.type === "safe-reexport") {
73
- let map = reexportMaps.get(module);
74
- if (!map) {
75
- reexportMaps.set(module, (map = new Map()));
153
+ if (
154
+ mode.type === "safe-reexport" ||
155
+ mode.type === "checked-reexport" ||
156
+ mode.type === "dynamic-reexport" ||
157
+ mode.type === "reexport-non-harmony-default" ||
158
+ mode.type === "reexport-non-harmony-default-strict" ||
159
+ mode.type === "reexport-named-default"
160
+ ) {
161
+ let info = reexportMaps.get(module);
162
+ if (!info) {
163
+ reexportMaps.set(
164
+ module,
165
+ (info = {
166
+ static: new Map(),
167
+ dynamic: new Map()
168
+ })
169
+ );
76
170
  }
77
- for (const pair of mode.map) {
78
- map.set(pair[0], {
79
- module: mode.module,
80
- exportName: pair[1]
81
- });
171
+ const targetModule = dep._module;
172
+ switch (mode.type) {
173
+ case "safe-reexport":
174
+ for (const [key, id] of mode.map) {
175
+ if (id) {
176
+ addStaticReexport(
177
+ info,
178
+ key,
179
+ targetModule,
180
+ id,
181
+ false
182
+ );
183
+ }
184
+ }
185
+ break;
186
+ case "checked-reexport":
187
+ for (const [key, id] of mode.map) {
188
+ if (id) {
189
+ addStaticReexport(
190
+ info,
191
+ key,
192
+ targetModule,
193
+ id,
194
+ true
195
+ );
196
+ }
197
+ }
198
+ break;
199
+ case "dynamic-reexport":
200
+ addDynamicReexport(info, targetModule, mode.ignored);
201
+ break;
202
+ case "reexport-non-harmony-default":
203
+ case "reexport-non-harmony-default-strict":
204
+ case "reexport-named-default":
205
+ addStaticReexport(
206
+ info,
207
+ mode.name,
208
+ targetModule,
209
+ "default",
210
+ false
211
+ );
212
+ break;
82
213
  }
83
214
  }
84
215
  }
@@ -87,17 +218,68 @@ class SideEffectsFlagPlugin {
87
218
  }
88
219
 
89
220
  // Flatten reexports
90
- for (const map of reexportMaps.values()) {
91
- for (const pair of map) {
92
- let mapping = pair[1];
93
- while (mapping) {
94
- const innerMap = reexportMaps.get(mapping.module);
95
- if (!innerMap) break;
96
- const newMapping = innerMap.get(mapping.exportName);
97
- if (newMapping) {
98
- map.set(pair[0], newMapping);
221
+ for (const info of reexportMaps.values()) {
222
+ const dynamicReexports = info.dynamic;
223
+ info.dynamic = new Map();
224
+ for (const reexport of dynamicReexports) {
225
+ let [targetModule, ignored] = reexport;
226
+ for (;;) {
227
+ const innerInfo = reexportMaps.get(targetModule);
228
+ if (!innerInfo) break;
229
+
230
+ for (const [key, reexports] of innerInfo.static) {
231
+ if (ignored.has(key)) continue;
232
+ for (const { module, exportName, checked } of reexports) {
233
+ addStaticReexport(info, key, module, exportName, checked);
234
+ }
235
+ }
236
+
237
+ // Follow dynamic reexport if there is only one
238
+ if (innerInfo.dynamic.size !== 1) {
239
+ // When there are more then one, we don't know which one
240
+ break;
241
+ }
242
+
243
+ ignored = new Set(ignored);
244
+ for (const [innerModule, innerIgnored] of innerInfo.dynamic) {
245
+ for (const key of innerIgnored) {
246
+ if (ignored.has(key)) continue;
247
+ // This reexports ends here
248
+ addStaticReexport(info, key, targetModule, key, true);
249
+ ignored.add(key);
250
+ }
251
+ targetModule = innerModule;
252
+ }
253
+ }
254
+
255
+ // Update reexport as all other cases has been handled
256
+ addDynamicReexport(info, targetModule, ignored);
257
+ }
258
+ }
259
+
260
+ for (const info of reexportMaps.values()) {
261
+ const staticReexports = info.static;
262
+ info.static = new Map();
263
+ for (const [key, reexports] of staticReexports) {
264
+ for (let mapping of reexports) {
265
+ for (;;) {
266
+ const innerInfo = reexportMaps.get(mapping.module);
267
+ if (!innerInfo) break;
268
+
269
+ const newMapping = getMappingFromInfo(
270
+ innerInfo,
271
+ mapping.exportName
272
+ );
273
+ if (!newMapping) break;
274
+ mapping = newMapping;
99
275
  }
100
- mapping = newMapping;
276
+ addStaticReexport(
277
+ info,
278
+ key,
279
+ mapping.module,
280
+ mapping.exportName,
281
+ mapping.checked
282
+ );
101
283
  }
102
284
  }
103
285
  }
@@ -105,17 +287,18 @@ class SideEffectsFlagPlugin {
105
287
  // Update imports along the reexports from sideEffectFree modules
106
288
  for (const pair of reexportMaps) {
107
289
  const module = pair[0];
108
- const map = pair[1];
290
+ const info = pair[1];
109
291
  let newReasons = undefined;
110
292
  for (let i = 0; i < module.reasons.length; i++) {
111
293
  const reason = module.reasons[i];
112
294
  const dep = reason.dependency;
113
295
  if (
114
- dep instanceof HarmonyExportImportedSpecifierDependency ||
115
- (dep instanceof HarmonyImportSpecifierDependency &&
116
- !dep.namespaceObjectAsContext)
296
+ (dep instanceof HarmonyExportImportedSpecifierDependency ||
297
+ (dep instanceof HarmonyImportSpecifierDependency &&
298
+ !dep.namespaceObjectAsContext)) &&
299
+ dep._id
117
300
  ) {
118
- const mapping = map.get(dep._id);
301
+ const mapping = getMappingFromInfo(info, dep._id);
119
302
  if (mapping) {
120
303
  dep.redirectedModule = mapping.module;
121
304
  dep.redirectedId = mapping.exportName;
@@ -75,12 +75,15 @@ const compareEntries = (a, b) => {
75
75
  const bSizeReduce = b.size * (b.chunks.size - 1);
76
76
  const diffSizeReduce = aSizeReduce - bSizeReduce;
77
77
  if (diffSizeReduce) return diffSizeReduce;
78
- // 4. by number of modules (to be able to compare by identifier)
78
+ // 4. by cache group index
79
+ const indexDiff = a.cacheGroupIndex - b.cacheGroupIndex;
80
+ if (indexDiff) return indexDiff;
81
+ // 5. by number of modules (to be able to compare by identifier)
79
82
  const modulesA = a.modules;
80
83
  const modulesB = b.modules;
81
84
  const diff = modulesA.size - modulesB.size;
82
85
  if (diff) return diff;
83
- // 5. by module identifiers
86
+ // 6. by module identifiers
84
87
  modulesA.sort();
85
88
  modulesB.sort();
86
89
  const aI = modulesA[Symbol.iterator]();
@@ -114,6 +117,7 @@ module.exports = class SplitChunksPlugin {
114
117
  options.chunks || "all"
115
118
  ),
116
119
  minSize: options.minSize || 0,
120
+ enforceSizeThreshold: options.enforceSizeThreshold || 0,
117
121
  maxSize: options.maxSize || 0,
118
122
  minChunks: options.minChunks || 1,
119
123
  maxAsyncRequests: options.maxAsyncRequests || 1,
@@ -286,6 +290,7 @@ module.exports = class SplitChunksPlugin {
286
290
  ),
287
291
  enforce: option.enforce,
288
292
  minSize: option.minSize,
293
+ enforceSizeThreshold: option.enforceSizeThreshold,
289
294
  maxSize: option.maxSize,
290
295
  minChunks: option.minChunks,
291
296
  maxAsyncRequests: option.maxAsyncRequests,
@@ -458,8 +463,8 @@ module.exports = class SplitChunksPlugin {
458
463
  * @typedef {Object} ChunksInfoItem
459
464
  * @property {SortableSet} modules
460
465
  * @property {TODO} cacheGroup
466
+ * @property {number} cacheGroupIndex
461
467
  * @property {string} name
462
- * @property {boolean} validateSize
463
468
  * @property {number} size
464
469
  * @property {Set<Chunk>} chunks
465
470
  * @property {Set<Chunk>} reuseableChunks
@@ -473,6 +478,7 @@ module.exports = class SplitChunksPlugin {
473
478
 
474
479
  /**
475
480
  * @param {TODO} cacheGroup the current cache group
481
+ * @param {number} cacheGroupIndex the index of the cache group of ordering
476
482
  * @param {Chunk[]} selectedChunks chunks selected for this module
477
483
  * @param {string} selectedChunksKey a key of selectedChunks
478
484
  * @param {Module} module the current module
@@ -480,6 +486,7 @@ module.exports = class SplitChunksPlugin {
480
486
  */
481
487
  const addModuleToChunksInfoMap = (
482
488
  cacheGroup,
489
+ cacheGroupIndex,
483
490
  selectedChunks,
484
491
  selectedChunksKey,
485
492
  module
@@ -507,8 +514,8 @@ module.exports = class SplitChunksPlugin {
507
514
  (info = {
508
515
  modules: new SortableSet(undefined, sortByIdentifier),
509
516
  cacheGroup,
517
+ cacheGroupIndex,
510
518
  name,
511
- validateSize: cacheGroup.minSize > 0,
512
519
  size: 0,
513
520
  chunks: new Set(),
514
521
  reuseableChunks: new Set(),
@@ -517,9 +524,7 @@ module.exports = class SplitChunksPlugin {
517
524
  );
518
525
  }
519
526
  info.modules.add(module);
520
- if (info.validateSize) {
521
- info.size += module.size();
522
- }
527
+ info.size += module.size();
523
528
  if (!info.chunksKeys.has(selectedChunksKey)) {
524
529
  info.chunksKeys.add(selectedChunksKey);
525
530
  for (const chunk of selectedChunks) {
@@ -544,22 +549,31 @@ module.exports = class SplitChunksPlugin {
544
549
  combinationsCache.set(chunksKey, combs);
545
550
  }
546
551
 
552
+ let cacheGroupIndex = 0;
547
553
  for (const cacheGroupSource of cacheGroups) {
554
+ const minSize =
555
+ cacheGroupSource.minSize !== undefined
556
+ ? cacheGroupSource.minSize
557
+ : cacheGroupSource.enforce
558
+ ? 0
559
+ : this.options.minSize;
560
+ const enforceSizeThreshold =
561
+ cacheGroupSource.enforceSizeThreshold !== undefined
562
+ ? cacheGroupSource.enforceSizeThreshold
563
+ : cacheGroupSource.enforce
564
+ ? 0
565
+ : this.options.enforceSizeThreshold;
548
566
  const cacheGroup = {
549
567
  key: cacheGroupSource.key,
550
568
  priority: cacheGroupSource.priority || 0,
551
569
  chunksFilter:
552
570
  cacheGroupSource.chunksFilter || this.options.chunksFilter,
553
- minSize:
554
- cacheGroupSource.minSize !== undefined
555
- ? cacheGroupSource.minSize
556
- : cacheGroupSource.enforce
557
- ? 0
558
- : this.options.minSize,
571
+ minSize,
559
572
  minSizeForMaxSize:
560
573
  cacheGroupSource.minSize !== undefined
561
574
  ? cacheGroupSource.minSize
562
575
  : this.options.minSize,
576
+ enforceSizeThreshold,
563
577
  maxSize:
564
578
  cacheGroupSource.maxSize !== undefined
565
579
  ? cacheGroupSource.maxSize
@@ -596,7 +610,9 @@ module.exports = class SplitChunksPlugin {
596
610
  cacheGroupSource.automaticNameDelimiter !== undefined
597
611
  ? cacheGroupSource.automaticNameDelimiter
598
612
  : this.options.automaticNameDelimiter,
599
- reuseExistingChunk: cacheGroupSource.reuseExistingChunk
613
+ reuseExistingChunk: cacheGroupSource.reuseExistingChunk,
614
+ _validateSize: minSize > 0,
615
+ _conditionalEnforce: enforceSizeThreshold > 0
600
616
  };
601
617
  // For all combination of chunk selection
602
618
  for (const chunkCombination of combs) {
@@ -613,18 +629,23 @@ module.exports = class SplitChunksPlugin {
613
629
 
614
630
  addModuleToChunksInfoMap(
615
631
  cacheGroup,
632
+ cacheGroupIndex,
616
633
  selectedChunks,
617
634
  selectedChunksKey,
618
635
  module
619
636
  );
620
637
  }
638
+ cacheGroupIndex++;
621
639
  }
622
640
  }
623
641
 
624
642
  // Filter items were size < minSize
625
643
  for (const pair of chunksInfoMap) {
626
644
  const info = pair[1];
627
- if (info.validateSize && info.size < info.cacheGroup.minSize) {
645
+ if (
646
+ info.cacheGroup._validateSize &&
647
+ info.size < info.cacheGroup.minSize
648
+ ) {
628
649
  chunksInfoMap.delete(pair[0]);
629
650
  }
630
651
  }
@@ -684,24 +705,30 @@ module.exports = class SplitChunksPlugin {
684
705
  }
685
706
  // Check if maxRequests condition can be fulfilled
686
707
 
687
- const usedChunks = Array.from(item.chunks).filter(chunk => {
708
+ const selectedChunks = Array.from(item.chunks).filter(chunk => {
688
709
  // skip if we address ourself
689
710
  return (
690
711
  (!chunkName || chunk.name !== chunkName) && chunk !== newChunk
691
712
  );
692
713
  });
693
714
 
715
+ const enforced =
716
+ item.cacheGroup._conditionalEnforce &&
717
+ item.size >= item.cacheGroup.enforceSizeThreshold;
718
+
694
719
  // Skip when no chunk selected
695
- if (usedChunks.length === 0) continue;
720
+ if (selectedChunks.length === 0) continue;
696
721
 
697
- let validChunks = usedChunks;
722
+ const usedChunks = new Set(selectedChunks);
698
723
 
724
+ // Check if maxRequests condition can be fulfilled
699
725
  if (
700
- Number.isFinite(item.cacheGroup.maxInitialRequests) ||
701
- Number.isFinite(item.cacheGroup.maxAsyncRequests)
726
+ !enforced &&
727
+ (Number.isFinite(item.cacheGroup.maxInitialRequests) ||
728
+ Number.isFinite(item.cacheGroup.maxAsyncRequests))
702
729
  ) {
703
- validChunks = validChunks.filter(chunk => {
704
- // respect max requests when not enforced
730
+ for (const chunk of usedChunks) {
731
+ // respect max requests
705
732
  const maxRequests = chunk.isOnlyInitial()
706
733
  ? item.cacheGroup.maxInitialRequests
707
734
  : chunk.canBeInitial()
@@ -710,26 +737,33 @@ module.exports = class SplitChunksPlugin {
710
737
  item.cacheGroup.maxAsyncRequests
711
738
  )
712
739
  : item.cacheGroup.maxAsyncRequests;
713
- return (
714
- !isFinite(maxRequests) || getRequests(chunk) < maxRequests
715
- );
716
- });
740
+ if (
741
+ isFinite(maxRequests) &&
742
+ getRequests(chunk) >= maxRequests
743
+ ) {
744
+ usedChunks.delete(chunk);
745
+ }
746
+ }
717
747
  }
718
748
 
719
- validChunks = validChunks.filter(chunk => {
749
+ outer: for (const chunk of usedChunks) {
720
750
  for (const module of item.modules) {
721
- if (chunk.containsModule(module)) return true;
751
+ if (chunk.containsModule(module)) continue outer;
722
752
  }
723
- return false;
724
- });
753
+ usedChunks.delete(chunk);
754
+ }
725
755
 
726
- if (validChunks.length < usedChunks.length) {
727
- if (validChunks.length >= item.cacheGroup.minChunks) {
756
+ // Were some (invalid) chunks removed from usedChunks?
757
+ // => readd all modules to the queue, as things could have been changed
758
+ if (usedChunks.size < selectedChunks.length) {
759
+ if (usedChunks.size >= item.cacheGroup.minChunks) {
760
+ const chunksArr = Array.from(usedChunks);
728
761
  for (const module of item.modules) {
729
762
  addModuleToChunksInfoMap(
730
763
  item.cacheGroup,
731
- validChunks,
732
- getKey(validChunks),
764
+ item.cacheGroupIndex,
765
+ chunksArr,
766
+ getKey(usedChunks),
733
767
  module
734
768
  );
735
769
  }
@@ -819,28 +853,24 @@ module.exports = class SplitChunksPlugin {
819
853
 
820
854
  // remove all modules from other entries and update size
821
855
  for (const [key, info] of chunksInfoMap) {
822
- if (isOverlap(info.chunks, item.chunks)) {
823
- if (info.validateSize) {
824
- // update modules and total size
825
- // may remove it from the map when < minSize
826
- const oldSize = info.modules.size;
827
- for (const module of item.modules) {
828
- info.modules.delete(module);
829
- }
856
+ if (isOverlap(info.chunks, usedChunks)) {
857
+ // update modules and total size
858
+ // may remove it from the map when < minSize
859
+ const oldSize = info.modules.size;
860
+ for (const module of item.modules) {
861
+ info.modules.delete(module);
862
+ }
863
+ if (info.modules.size !== oldSize) {
830
864
  if (info.modules.size === 0) {
831
865
  chunksInfoMap.delete(key);
832
866
  continue;
833
867
  }
834
- if (info.modules.size !== oldSize) {
835
- info.size = getModulesSize(info.modules);
836
- if (info.size < info.cacheGroup.minSize) {
837
- chunksInfoMap.delete(key);
838
- }
839
- }
840
- } else {
841
- // only update the modules
842
- for (const module of item.modules) {
843
- info.modules.delete(module);
868
+ info.size = getModulesSize(info.modules);
869
+ if (
870
+ info.cacheGroup._validateSize &&
871
+ info.size < info.cacheGroup.minSize
872
+ ) {
873
+ chunksInfoMap.delete(key);
844
874
  }
845
875
  if (info.modules.size === 0) {
846
876
  chunksInfoMap.delete(key);
package/package.json CHANGED
@@ -1,34 +1,42 @@
1
1
  {
2
2
  "name": "webpack",
3
- "version": "4.42.0",
3
+ "version": "4.44.1",
4
4
  "author": "Tobias Koppers @sokra",
5
5
  "description": "Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.",
6
6
  "license": "MIT",
7
7
  "dependencies": {
8
- "@webassemblyjs/ast": "1.8.5",
9
- "@webassemblyjs/helper-module-context": "1.8.5",
10
- "@webassemblyjs/wasm-edit": "1.8.5",
11
- "@webassemblyjs/wasm-parser": "1.8.5",
12
- "acorn": "^6.2.1",
8
+ "@webassemblyjs/ast": "1.9.0",
9
+ "@webassemblyjs/helper-module-context": "1.9.0",
10
+ "@webassemblyjs/wasm-edit": "1.9.0",
11
+ "@webassemblyjs/wasm-parser": "1.9.0",
12
+ "acorn": "^6.4.1",
13
13
  "ajv": "^6.10.2",
14
14
  "ajv-keywords": "^3.4.1",
15
15
  "chrome-trace-event": "^1.0.2",
16
- "enhanced-resolve": "^4.1.0",
16
+ "enhanced-resolve": "^4.3.0",
17
17
  "eslint-scope": "^4.0.3",
18
18
  "json-parse-better-errors": "^1.0.2",
19
19
  "loader-runner": "^2.4.0",
20
20
  "loader-utils": "^1.2.3",
21
21
  "memory-fs": "^0.4.1",
22
22
  "micromatch": "^3.1.10",
23
- "mkdirp": "^0.5.1",
23
+ "mkdirp": "^0.5.3",
24
24
  "neo-async": "^2.6.1",
25
25
  "node-libs-browser": "^2.2.1",
26
26
  "schema-utils": "^1.0.0",
27
27
  "tapable": "^1.1.3",
28
28
  "terser-webpack-plugin": "^1.4.3",
29
- "watchpack": "^1.6.0",
29
+ "watchpack": "^1.7.4",
30
30
  "webpack-sources": "^1.4.1"
31
31
  },
32
+ "peerDependenciesMeta": {
33
+ "webpack-cli": {
34
+ "optional": true
35
+ },
36
+ "webpack-command": {
37
+ "optional": true
38
+ }
39
+ },
32
40
  "devDependencies": {
33
41
  "@babel/core": "^7.7.2",
34
42
  "@types/node": "^10.12.21",
@@ -80,6 +88,7 @@
80
88
  "vm-browserify": "~1.1.0",
81
89
  "wast-loader": "^1.5.5",
82
90
  "webpack-dev-middleware": "^3.5.1",
91
+ "webassembly-feature": "1.3.0",
83
92
  "worker-loader": "^2.0.0",
84
93
  "xxhashjs": "^0.2.1"
85
94
  },
@@ -612,6 +612,10 @@
612
612
  "description": "Ignore minimum size, minimum chunks and maximum requests and always create chunks for this cache group",
613
613
  "type": "boolean"
614
614
  },
615
+ "enforceSizeThreshold": {
616
+ "description": "Size threshold at which splitting is enforced and other restrictions (maxAsyncRequests, maxInitialRequests) are ignored.",
617
+ "type": "number"
618
+ },
615
619
  "filename": {
616
620
  "description": "Sets the template for the filename for created chunks (Only works for initial chunks)",
617
621
  "type": "string",
@@ -722,6 +726,10 @@
722
726
  }
723
727
  ]
724
728
  },
729
+ "enforceSizeThreshold": {
730
+ "description": "Size threshold at which splitting is enforced and other restrictions (maxAsyncRequests, maxInitialRequests) are ignored.",
731
+ "type": "number"
732
+ },
725
733
  "fallbackCacheGroup": {
726
734
  "description": "Options for modules not selected by any other cache group",
727
735
  "type": "object",
@@ -1221,6 +1229,14 @@
1221
1229
  "resolver": {
1222
1230
  "description": "Custom resolver"
1223
1231
  },
1232
+ "roots": {
1233
+ "description": "A list of directories in which requests that are server-relative URLs (starting with '/') are resolved. On non-windows system these requests are tried to resolve as absolute path first.",
1234
+ "type": "array",
1235
+ "items": {
1236
+ "description": "Directory in which requests that are server-relative URLs (starting with '/') are resolved.",
1237
+ "type": "string"
1238
+ }
1239
+ },
1224
1240
  "symlinks": {
1225
1241
  "description": "Enable resolving symlinks to the original location",
1226
1242
  "type": "boolean"