webpack 5.69.1 → 5.70.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 (41) hide show
  1. package/lib/BannerPlugin.js +10 -4
  2. package/lib/CleanPlugin.js +64 -18
  3. package/lib/Compilation.js +41 -17
  4. package/lib/ContextModule.js +90 -26
  5. package/lib/ContextModuleFactory.js +65 -21
  6. package/lib/EntryOptionPlugin.js +1 -0
  7. package/lib/Generator.js +1 -0
  8. package/lib/ModuleHashingError.js +29 -0
  9. package/lib/NodeStuffPlugin.js +10 -0
  10. package/lib/NormalModule.js +21 -16
  11. package/lib/NormalModuleFactory.js +15 -8
  12. package/lib/ProgressPlugin.js +3 -4
  13. package/lib/RuntimeTemplate.js +1 -0
  14. package/lib/WebpackOptionsApply.js +2 -0
  15. package/lib/asset/AssetGenerator.js +119 -31
  16. package/lib/cache/ResolverCachePlugin.js +89 -28
  17. package/lib/config/browserslistTargetHandler.js +3 -5
  18. package/lib/config/normalization.js +1 -0
  19. package/lib/dependencies/ContextDependencyHelpers.js +1 -1
  20. package/lib/dependencies/HarmonyAcceptImportDependency.js +5 -3
  21. package/lib/dependencies/HarmonyExportInitFragment.js +4 -1
  22. package/lib/dependencies/ImportContextDependency.js +0 -2
  23. package/lib/dependencies/ImportMetaContextDependency.js +35 -0
  24. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +252 -0
  25. package/lib/dependencies/ImportMetaContextPlugin.js +59 -0
  26. package/lib/dependencies/LoaderPlugin.js +2 -0
  27. package/lib/dependencies/RequireContextDependency.js +0 -16
  28. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +24 -8
  29. package/lib/node/ReadFileChunkLoadingRuntimeModule.js +22 -7
  30. package/lib/node/RequireChunkLoadingRuntimeModule.js +22 -7
  31. package/lib/schemes/HttpUriPlugin.js +44 -3
  32. package/lib/util/internalSerializables.js +2 -0
  33. package/lib/web/JsonpChunkLoadingRuntimeModule.js +15 -5
  34. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +30 -20
  35. package/module.d.ts +15 -0
  36. package/package.json +2 -2
  37. package/schemas/WebpackOptions.check.js +1 -1
  38. package/schemas/WebpackOptions.json +17 -1
  39. package/schemas/plugins/schemes/HttpUriPlugin.check.js +1 -1
  40. package/schemas/plugins/schemes/HttpUriPlugin.json +4 -0
  41. package/types.d.ts +164 -73
@@ -77,6 +77,7 @@ class BannerPlugin {
77
77
  undefined,
78
78
  options
79
79
  );
80
+ const cache = new WeakMap();
80
81
 
81
82
  compiler.hooks.compilation.tap("BannerPlugin", compilation => {
82
83
  compilation.hooks.processAssets.tap(
@@ -102,10 +103,15 @@ class BannerPlugin {
102
103
 
103
104
  const comment = compilation.getPath(banner, data);
104
105
 
105
- compilation.updateAsset(
106
- file,
107
- old => new ConcatSource(comment, "\n", old)
108
- );
106
+ compilation.updateAsset(file, old => {
107
+ let cached = cache.get(old);
108
+ if (!cached || cached.comment !== comment) {
109
+ const source = new ConcatSource(comment, "\n", old);
110
+ cache.set(old, { source, comment });
111
+ return source;
112
+ }
113
+ return cached.source;
114
+ });
109
115
  }
110
116
  }
111
117
  }
@@ -19,6 +19,7 @@ const processAsyncTree = require("./util/processAsyncTree");
19
19
  /** @typedef {import("./util/fs").StatsCallback} StatsCallback */
20
20
 
21
21
  /** @typedef {(function(string):boolean)|RegExp} IgnoreItem */
22
+ /** @typedef {Map<string, number>} Assets */
22
23
  /** @typedef {function(IgnoreItem): void} AddToIgnoreCallback */
23
24
 
24
25
  /**
@@ -40,18 +41,32 @@ const validate = createSchemaValidation(
40
41
  baseDataPath: "options"
41
42
  }
42
43
  );
44
+ const _10sec = 10 * 1000;
45
+
46
+ /**
47
+ * marge assets map 2 into map 1
48
+ * @param {Assets} as1 assets
49
+ * @param {Assets} as2 assets
50
+ * @returns {void}
51
+ */
52
+ const mergeAssets = (as1, as2) => {
53
+ for (const [key, value1] of as2) {
54
+ const value2 = as1.get(key);
55
+ if (!value2 || value1 > value2) as1.set(key, value1);
56
+ }
57
+ };
43
58
 
44
59
  /**
45
60
  * @param {OutputFileSystem} fs filesystem
46
61
  * @param {string} outputPath output path
47
- * @param {Set<string>} currentAssets filename of the current assets (must not start with .. or ., must only use / as path separator)
62
+ * @param {Map<string, number>} currentAssets filename of the current assets (must not start with .. or ., must only use / as path separator)
48
63
  * @param {function((Error | null)=, Set<string>=): void} callback returns the filenames of the assets that shouldn't be there
49
64
  * @returns {void}
50
65
  */
51
66
  const getDiffToFs = (fs, outputPath, currentAssets, callback) => {
52
67
  const directories = new Set();
53
68
  // get directories of assets
54
- for (const asset of currentAssets) {
69
+ for (const [asset] of currentAssets) {
55
70
  directories.add(asset.replace(/(^|\/)[^/]*$/, ""));
56
71
  }
57
72
  // and all parent directories
@@ -91,13 +106,15 @@ const getDiffToFs = (fs, outputPath, currentAssets, callback) => {
91
106
  };
92
107
 
93
108
  /**
94
- * @param {Set<string>} currentAssets assets list
95
- * @param {Set<string>} oldAssets old assets list
109
+ * @param {Assets} currentAssets assets list
110
+ * @param {Assets} oldAssets old assets list
96
111
  * @returns {Set<string>} diff
97
112
  */
98
113
  const getDiffToOldAssets = (currentAssets, oldAssets) => {
99
114
  const diff = new Set();
100
- for (const asset of oldAssets) {
115
+ const now = Date.now();
116
+ for (const [asset, ts] of oldAssets) {
117
+ if (ts >= now) continue;
101
118
  if (!currentAssets.has(asset)) diff.add(asset);
102
119
  }
103
120
  return diff;
@@ -124,7 +141,7 @@ const doStat = (fs, filename, callback) => {
124
141
  * @param {Logger} logger logger
125
142
  * @param {Set<string>} diff filenames of the assets that shouldn't be there
126
143
  * @param {function(string): boolean} isKept check if the entry is ignored
127
- * @param {function(Error=): void} callback callback
144
+ * @param {function(Error=, Assets=): void} callback callback
128
145
  * @returns {void}
129
146
  */
130
147
  const applyDiff = (fs, outputPath, dry, logger, diff, isKept, callback) => {
@@ -137,11 +154,13 @@ const applyDiff = (fs, outputPath, dry, logger, diff, isKept, callback) => {
137
154
  };
138
155
  /** @typedef {{ type: "check" | "unlink" | "rmdir", filename: string, parent: { remaining: number, job: Job } | undefined }} Job */
139
156
  /** @type {Job[]} */
140
- const jobs = Array.from(diff, filename => ({
157
+ const jobs = Array.from(diff.keys(), filename => ({
141
158
  type: "check",
142
159
  filename,
143
160
  parent: undefined
144
161
  }));
162
+ /** @type {Assets} */
163
+ const keptAssets = new Map();
145
164
  processAsyncTree(
146
165
  jobs,
147
166
  10,
@@ -161,6 +180,7 @@ const applyDiff = (fs, outputPath, dry, logger, diff, isKept, callback) => {
161
180
  switch (type) {
162
181
  case "check":
163
182
  if (isKept(filename)) {
183
+ keptAssets.set(filename, 0);
164
184
  // do not decrement parent entry as we don't want to delete the parent
165
185
  log(`${filename} will be kept`);
166
186
  return process.nextTick(callback);
@@ -247,7 +267,10 @@ const applyDiff = (fs, outputPath, dry, logger, diff, isKept, callback) => {
247
267
  break;
248
268
  }
249
269
  },
250
- callback
270
+ err => {
271
+ if (err) return callback(err);
272
+ callback(undefined, keptAssets);
273
+ }
251
274
  );
252
275
  };
253
276
 
@@ -302,6 +325,7 @@ class CleanPlugin {
302
325
  // We assume that no external modification happens while the compiler is active
303
326
  // So we can store the old assets and only diff to them to avoid fs access on
304
327
  // incremental builds
328
+ /** @type {undefined|Assets} */
305
329
  let oldAssets;
306
330
 
307
331
  compiler.hooks.emit.tapAsync(
@@ -322,7 +346,9 @@ class CleanPlugin {
322
346
  );
323
347
  }
324
348
 
325
- const currentAssets = new Set();
349
+ /** @type {Assets} */
350
+ const currentAssets = new Map();
351
+ const now = Date.now();
326
352
  for (const asset of Object.keys(compilation.assets)) {
327
353
  if (/^[A-Za-z]:\\|^\/|^\\\\/.test(asset)) continue;
328
354
  let normalizedAsset;
@@ -335,7 +361,12 @@ class CleanPlugin {
335
361
  );
336
362
  } while (newNormalizedAsset !== normalizedAsset);
337
363
  if (normalizedAsset.startsWith("../")) continue;
338
- currentAssets.add(normalizedAsset);
364
+ const assetInfo = compilation.assetsInfo.get(asset);
365
+ if (assetInfo && assetInfo.hotModuleReplacement) {
366
+ currentAssets.set(normalizedAsset, now + _10sec);
367
+ } else {
368
+ currentAssets.set(normalizedAsset, 0);
369
+ }
339
370
  }
340
371
 
341
372
  const outputPath = compilation.getPath(compiler.outputPath, {});
@@ -346,19 +377,34 @@ class CleanPlugin {
346
377
  return keepFn(path);
347
378
  };
348
379
 
380
+ /**
381
+ * @param {Error=} err err
382
+ * @param {Set<string>=} diff diff
383
+ */
349
384
  const diffCallback = (err, diff) => {
350
385
  if (err) {
351
386
  oldAssets = undefined;
352
- return callback(err);
387
+ callback(err);
388
+ return;
353
389
  }
354
- applyDiff(fs, outputPath, dry, logger, diff, isKept, err => {
355
- if (err) {
356
- oldAssets = undefined;
357
- } else {
358
- oldAssets = currentAssets;
390
+ applyDiff(
391
+ fs,
392
+ outputPath,
393
+ dry,
394
+ logger,
395
+ diff,
396
+ isKept,
397
+ (err, keptAssets) => {
398
+ if (err) {
399
+ oldAssets = undefined;
400
+ } else {
401
+ if (oldAssets) mergeAssets(currentAssets, oldAssets);
402
+ oldAssets = currentAssets;
403
+ if (keptAssets) mergeAssets(oldAssets, keptAssets);
404
+ }
405
+ callback(err);
359
406
  }
360
- callback(err);
361
- });
407
+ );
362
408
  };
363
409
 
364
410
  if (oldAssets) {
@@ -43,6 +43,7 @@ const Module = require("./Module");
43
43
  const ModuleDependencyError = require("./ModuleDependencyError");
44
44
  const ModuleDependencyWarning = require("./ModuleDependencyWarning");
45
45
  const ModuleGraph = require("./ModuleGraph");
46
+ const ModuleHashingError = require("./ModuleHashingError");
46
47
  const ModuleNotFoundError = require("./ModuleNotFoundError");
47
48
  const ModuleProfile = require("./ModuleProfile");
48
49
  const ModuleRestoreError = require("./ModuleRestoreError");
@@ -3883,6 +3884,7 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
3883
3884
  let statModulesFromCache = 0;
3884
3885
  const { chunkGraph, runtimeTemplate, moduleMemCaches2 } = this;
3885
3886
  const { hashFunction, hashDigest, hashDigestLength } = this.outputOptions;
3887
+ const errors = [];
3886
3888
  for (const module of this.modules) {
3887
3889
  const memCache = moduleMemCaches2 && moduleMemCaches2.get(module);
3888
3890
  for (const runtime of chunkGraph.getModuleRuntimes(module)) {
@@ -3907,13 +3909,20 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
3907
3909
  hashFunction,
3908
3910
  runtimeTemplate,
3909
3911
  hashDigest,
3910
- hashDigestLength
3912
+ hashDigestLength,
3913
+ errors
3911
3914
  );
3912
3915
  if (memCache) {
3913
3916
  memCache.set(`moduleHash-${getRuntimeKey(runtime)}`, digest);
3914
3917
  }
3915
3918
  }
3916
3919
  }
3920
+ if (errors.length > 0) {
3921
+ errors.sort(compareSelect(err => err.module, compareModulesByIdentifier));
3922
+ for (const error of errors) {
3923
+ this.errors.push(error);
3924
+ }
3925
+ }
3917
3926
  this.logger.log(
3918
3927
  `${statModulesHashed} modules hashed, ${statModulesFromCache} from cache (${
3919
3928
  Math.round(
@@ -3930,17 +3939,22 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
3930
3939
  hashFunction,
3931
3940
  runtimeTemplate,
3932
3941
  hashDigest,
3933
- hashDigestLength
3942
+ hashDigestLength,
3943
+ errors
3934
3944
  ) {
3935
- const moduleHash = createHash(hashFunction);
3936
- module.updateHash(moduleHash, {
3937
- chunkGraph,
3938
- runtime,
3939
- runtimeTemplate
3940
- });
3941
- const moduleHashDigest = /** @type {string} */ (
3942
- moduleHash.digest(hashDigest)
3943
- );
3945
+ let moduleHashDigest;
3946
+ try {
3947
+ const moduleHash = createHash(hashFunction);
3948
+ module.updateHash(moduleHash, {
3949
+ chunkGraph,
3950
+ runtime,
3951
+ runtimeTemplate
3952
+ });
3953
+ moduleHashDigest = /** @type {string} */ (moduleHash.digest(hashDigest));
3954
+ } catch (err) {
3955
+ errors.push(new ModuleHashingError(module, err));
3956
+ moduleHashDigest = "XXXXXX";
3957
+ }
3944
3958
  chunkGraph.setModuleHashes(
3945
3959
  module,
3946
3960
  runtime,
@@ -4091,6 +4105,7 @@ This prevents using hashes of each other and should be avoided.`);
4091
4105
  const codeGenerationJobs = [];
4092
4106
  /** @type {Map<string, Map<Module, {module: Module, hash: string, runtime: RuntimeSpec, runtimes: RuntimeSpec[]}>>} */
4093
4107
  const codeGenerationJobsMap = new Map();
4108
+ const errors = [];
4094
4109
 
4095
4110
  const processChunk = chunk => {
4096
4111
  // Last minute module hash generation for modules that depend on chunk hashes
@@ -4105,7 +4120,8 @@ This prevents using hashes of each other and should be avoided.`);
4105
4120
  hashFunction,
4106
4121
  runtimeTemplate,
4107
4122
  hashDigest,
4108
- hashDigestLength
4123
+ hashDigestLength,
4124
+ errors
4109
4125
  );
4110
4126
  let hashMap = codeGenerationJobsMap.get(hash);
4111
4127
  if (hashMap) {
@@ -4129,9 +4145,9 @@ This prevents using hashes of each other and should be avoided.`);
4129
4145
  }
4130
4146
  }
4131
4147
  this.logger.timeAggregate("hashing: hash runtime modules");
4132
- this.logger.time("hashing: hash chunks");
4133
- const chunkHash = createHash(hashFunction);
4134
4148
  try {
4149
+ this.logger.time("hashing: hash chunks");
4150
+ const chunkHash = createHash(hashFunction);
4135
4151
  if (outputOptions.hashSalt) {
4136
4152
  chunkHash.update(outputOptions.hashSalt);
4137
4153
  }
@@ -4162,6 +4178,12 @@ This prevents using hashes of each other and should be avoided.`);
4162
4178
  };
4163
4179
  otherChunks.forEach(processChunk);
4164
4180
  for (const chunk of runtimeChunks) processChunk(chunk);
4181
+ if (errors.length > 0) {
4182
+ errors.sort(compareSelect(err => err.module, compareModulesByIdentifier));
4183
+ for (const error of errors) {
4184
+ this.errors.push(error);
4185
+ }
4186
+ }
4165
4187
 
4166
4188
  this.logger.timeAggregateEnd("hashing: hash runtime modules");
4167
4189
  this.logger.timeAggregateEnd("hashing: hash chunks");
@@ -4801,6 +4823,9 @@ This prevents using hashes of each other and should be avoided.`);
4801
4823
  chunkGraph.connectChunkAndModule(chunk, module);
4802
4824
  }
4803
4825
 
4826
+ /** @type {WebpackError[]} */
4827
+ const errors = [];
4828
+
4804
4829
  // Hash modules
4805
4830
  for (const module of modules) {
4806
4831
  this._createModuleHash(
@@ -4810,15 +4835,14 @@ This prevents using hashes of each other and should be avoided.`);
4810
4835
  hashFunction,
4811
4836
  runtimeTemplate,
4812
4837
  hashDigest,
4813
- hashDigestLength
4838
+ hashDigestLength,
4839
+ errors
4814
4840
  );
4815
4841
  }
4816
4842
 
4817
4843
  const codeGenerationResults = new CodeGenerationResults(
4818
4844
  this.outputOptions.hashFunction
4819
4845
  );
4820
- /** @type {WebpackError[]} */
4821
- const errors = [];
4822
4846
  /**
4823
4847
  * @param {Module} module the module
4824
4848
  * @param {Callback} callback callback
@@ -61,7 +61,7 @@ const makeSerializable = require("./util/makeSerializable");
61
61
 
62
62
  /**
63
63
  * @typedef {Object} ContextModuleOptionsExtras
64
- * @property {string} resource
64
+ * @property {false|string|string[]} resource
65
65
  * @property {string=} resourceQuery
66
66
  * @property {string=} resourceFragment
67
67
  * @property {TODO} resolveOptions
@@ -92,23 +92,36 @@ class ContextModule extends Module {
92
92
  * @param {ContextModuleOptions} options options object
93
93
  */
94
94
  constructor(resolveDependencies, options) {
95
- const parsed = parseResource(options ? options.resource : "");
96
- const resource = parsed.path;
97
- const resourceQuery = (options && options.resourceQuery) || parsed.query;
98
- const resourceFragment =
99
- (options && options.resourceFragment) || parsed.fragment;
100
-
101
- super("javascript/dynamic", resource);
95
+ if (!options || typeof options.resource === "string") {
96
+ const parsed = parseResource(
97
+ options ? /** @type {string} */ (options.resource) : ""
98
+ );
99
+ const resource = parsed.path;
100
+ const resourceQuery = (options && options.resourceQuery) || parsed.query;
101
+ const resourceFragment =
102
+ (options && options.resourceFragment) || parsed.fragment;
103
+
104
+ super("javascript/dynamic", resource);
105
+ /** @type {ContextModuleOptions} */
106
+ this.options = {
107
+ ...options,
108
+ resource,
109
+ resourceQuery,
110
+ resourceFragment
111
+ };
112
+ } else {
113
+ super("javascript/dynamic");
114
+ /** @type {ContextModuleOptions} */
115
+ this.options = {
116
+ ...options,
117
+ resource: options.resource,
118
+ resourceQuery: options.resourceQuery || "",
119
+ resourceFragment: options.resourceFragment || ""
120
+ };
121
+ }
102
122
 
103
123
  // Info from Factory
104
124
  this.resolveDependencies = resolveDependencies;
105
- /** @type {ContextModuleOptions} */
106
- this.options = {
107
- ...options,
108
- resource,
109
- resourceQuery,
110
- resourceFragment
111
- };
112
125
  if (options && options.resolveOptions !== undefined) {
113
126
  this.resolveOptions = options.resolveOptions;
114
127
  }
@@ -155,7 +168,12 @@ class ContextModule extends Module {
155
168
  }
156
169
 
157
170
  _createIdentifier() {
158
- let identifier = this.context;
171
+ let identifier =
172
+ this.context ||
173
+ (typeof this.options.resource === "string" ||
174
+ this.options.resource === false
175
+ ? `${this.options.resource}`
176
+ : this.options.resource.join("|"));
159
177
  if (this.options.resourceQuery) {
160
178
  identifier += `|${this.options.resourceQuery}`;
161
179
  }
@@ -220,7 +238,19 @@ class ContextModule extends Module {
220
238
  * @returns {string} a user readable identifier of the module
221
239
  */
222
240
  readableIdentifier(requestShortener) {
223
- let identifier = requestShortener.shorten(this.context) + "/";
241
+ let identifier;
242
+ if (this.context) {
243
+ identifier = requestShortener.shorten(this.context) + "/";
244
+ } else if (
245
+ typeof this.options.resource === "string" ||
246
+ this.options.resource === false
247
+ ) {
248
+ identifier = requestShortener.shorten(`${this.options.resource}`) + "/";
249
+ } else {
250
+ identifier = this.options.resource
251
+ .map(r => requestShortener.shorten(r) + "/")
252
+ .join(" ");
253
+ }
224
254
  if (this.options.resourceQuery) {
225
255
  identifier += ` ${this.options.resourceQuery}`;
226
256
  }
@@ -270,11 +300,30 @@ class ContextModule extends Module {
270
300
  * @returns {string | null} an identifier for library inclusion
271
301
  */
272
302
  libIdent(options) {
273
- let identifier = contextify(
274
- options.context,
275
- this.context,
276
- options.associatedObjectForCache
277
- );
303
+ let identifier;
304
+
305
+ if (this.context) {
306
+ identifier = contextify(
307
+ options.context,
308
+ this.context,
309
+ options.associatedObjectForCache
310
+ );
311
+ } else if (typeof this.options.resource === "string") {
312
+ identifier = contextify(
313
+ options.context,
314
+ this.options.resource,
315
+ options.associatedObjectForCache
316
+ );
317
+ } else if (this.options.resource === false) {
318
+ identifier = "false";
319
+ } else {
320
+ identifier = this.options.resource
321
+ .map(res =>
322
+ contextify(options.context, res, options.associatedObjectForCache)
323
+ )
324
+ .join(" ");
325
+ }
326
+
278
327
  if (this.layer) identifier = `(${this.layer})/${identifier}`;
279
328
  if (this.options.mode) {
280
329
  identifier += ` ${this.options.mode}`;
@@ -323,8 +372,9 @@ class ContextModule extends Module {
323
372
  // build if enforced
324
373
  if (this._forceBuild) return callback(null, true);
325
374
 
326
- // always build when we have no snapshot
327
- if (!this.buildInfo.snapshot) return callback(null, true);
375
+ // always build when we have no snapshot and context
376
+ if (!this.buildInfo.snapshot)
377
+ return callback(null, Boolean(this.context || this.options.resource));
328
378
 
329
379
  fileSystemInfo.checkSnapshotValid(this.buildInfo.snapshot, (err, valid) => {
330
380
  callback(err, !valid);
@@ -439,10 +489,16 @@ class ContextModule extends Module {
439
489
  );
440
490
  return;
441
491
  }
492
+ if (!this.context && !this.options.resource) return callback();
493
+
442
494
  compilation.fileSystemInfo.createSnapshot(
443
495
  startTime,
444
496
  null,
445
- [this.context],
497
+ this.context
498
+ ? [this.context]
499
+ : typeof this.options.resource === "string"
500
+ ? [this.options.resource]
501
+ : /** @type {string[]} */ (this.options.resource),
446
502
  null,
447
503
  SNAPSHOT_OPTIONS,
448
504
  (err, snapshot) => {
@@ -466,7 +522,15 @@ class ContextModule extends Module {
466
522
  missingDependencies,
467
523
  buildDependencies
468
524
  ) {
469
- contextDependencies.add(this.context);
525
+ if (this.context) {
526
+ contextDependencies.add(this.context);
527
+ } else if (typeof this.options.resource === "string") {
528
+ contextDependencies.add(this.options.resource);
529
+ } else if (this.options.resource === false) {
530
+ return;
531
+ } else {
532
+ for (const res of this.options.resource) contextDependencies.add(res);
533
+ }
470
534
  }
471
535
 
472
536
  /**
@@ -167,6 +167,9 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
167
167
  asyncLib.parallel(
168
168
  [
169
169
  callback => {
170
+ const results = [];
171
+ const yield_ = obj => results.push(obj);
172
+
170
173
  contextResolver.resolve(
171
174
  {},
172
175
  context,
@@ -174,11 +177,12 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
174
177
  {
175
178
  fileDependencies,
176
179
  missingDependencies,
177
- contextDependencies
180
+ contextDependencies,
181
+ yield: yield_
178
182
  },
179
- (err, result) => {
183
+ err => {
180
184
  if (err) return callback(err);
181
- callback(null, result);
185
+ callback(null, results);
182
186
  }
183
187
  );
184
188
  },
@@ -213,15 +217,25 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
213
217
  contextDependencies
214
218
  });
215
219
  }
216
-
220
+ let [contextResult, loaderResult] = result;
221
+ if (contextResult.length > 1) {
222
+ const first = contextResult[0];
223
+ contextResult = contextResult.filter(r => r.path);
224
+ if (contextResult.length === 0) contextResult.push(first);
225
+ }
217
226
  this.hooks.afterResolve.callAsync(
218
227
  {
219
228
  addon:
220
229
  loadersPrefix +
221
- result[1].join("!") +
222
- (result[1].length > 0 ? "!" : ""),
223
- resource: result[0],
230
+ loaderResult.join("!") +
231
+ (loaderResult.length > 0 ? "!" : ""),
232
+ resource:
233
+ contextResult.length > 1
234
+ ? contextResult.map(r => r.path)
235
+ : contextResult[0].path,
224
236
  resolveDependencies: this.resolveDependencies.bind(this),
237
+ resourceQuery: contextResult[0].query,
238
+ resourceFragment: contextResult[0].fragment,
225
239
  ...beforeResolveResult
226
240
  },
227
241
  (err, result) => {
@@ -278,26 +292,28 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
278
292
  } = options;
279
293
  if (!regExp || !resource) return callback(null, []);
280
294
 
281
- const addDirectoryChecked = (directory, visited, callback) => {
295
+ let severalContexts = false;
296
+ const addDirectoryChecked = (ctx, directory, visited, callback) => {
282
297
  fs.realpath(directory, (err, realPath) => {
283
298
  if (err) return callback(err);
284
299
  if (visited.has(realPath)) return callback(null, []);
285
300
  let recursionStack;
286
301
  addDirectory(
302
+ ctx,
287
303
  directory,
288
- (dir, callback) => {
304
+ (_, dir, callback) => {
289
305
  if (recursionStack === undefined) {
290
306
  recursionStack = new Set(visited);
291
307
  recursionStack.add(realPath);
292
308
  }
293
- addDirectoryChecked(dir, recursionStack, callback);
309
+ addDirectoryChecked(ctx, dir, recursionStack, callback);
294
310
  },
295
311
  callback
296
312
  );
297
313
  });
298
314
  };
299
315
 
300
- const addDirectory = (directory, addSubDirectory, callback) => {
316
+ const addDirectory = (ctx, directory, addSubDirectory, callback) => {
301
317
  fs.readdir(directory, (err, files) => {
302
318
  if (err) return callback(err);
303
319
  const processedFiles = cmf.hooks.contextModuleFiles.call(
@@ -324,16 +340,15 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
324
340
 
325
341
  if (stat.isDirectory()) {
326
342
  if (!recursive) return callback();
327
- addSubDirectory(subResource, callback);
343
+ addSubDirectory(ctx, subResource, callback);
328
344
  } else if (
329
345
  stat.isFile() &&
330
346
  (!include || subResource.match(include))
331
347
  ) {
332
348
  const obj = {
333
- context: resource,
349
+ context: ctx,
334
350
  request:
335
- "." +
336
- subResource.substr(resource.length).replace(/\\/g, "/")
351
+ "." + subResource.substr(ctx.length).replace(/\\/g, "/")
337
352
  };
338
353
 
339
354
  this.hooks.alternativeRequests.callAsync(
@@ -344,8 +359,11 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
344
359
  alternatives = alternatives
345
360
  .filter(obj => regExp.test(obj.request))
346
361
  .map(obj => {
362
+ const request = severalContexts
363
+ ? join(fs, obj.context, obj.request)
364
+ : obj.request;
347
365
  const dep = new ContextElementDependency(
348
- obj.request + resourceQuery + resourceFragment,
366
+ request + resourceQuery + resourceFragment,
349
367
  obj.request,
350
368
  typePrefix,
351
369
  category,
@@ -382,12 +400,38 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
382
400
  });
383
401
  };
384
402
 
385
- if (typeof fs.realpath === "function") {
386
- addDirectoryChecked(resource, new Set(), callback);
403
+ const addSubDirectory = (ctx, dir, callback) =>
404
+ addDirectory(ctx, dir, addSubDirectory, callback);
405
+
406
+ const visitResource = (resource, callback) => {
407
+ if (typeof fs.realpath === "function") {
408
+ addDirectoryChecked(resource, resource, new Set(), callback);
409
+ } else {
410
+ addDirectory(resource, resource, addSubDirectory, callback);
411
+ }
412
+ };
413
+
414
+ if (typeof resource === "string") {
415
+ visitResource(resource, callback);
387
416
  } else {
388
- const addSubDirectory = (dir, callback) =>
389
- addDirectory(dir, addSubDirectory, callback);
390
- addDirectory(resource, addSubDirectory, callback);
417
+ severalContexts = true;
418
+ asyncLib.map(resource, visitResource, (err, result) => {
419
+ if (err) return callback(err);
420
+
421
+ // result dependencies should have unique userRequest
422
+ // ordered by resolve result
423
+ const temp = new Set();
424
+ const res = [];
425
+ for (let i = 0; i < result.length; i++) {
426
+ const inner = result[i];
427
+ for (const el of inner) {
428
+ if (temp.has(el.userRequest)) continue;
429
+ res.push(el);
430
+ temp.add(el.userRequest);
431
+ }
432
+ }
433
+ callback(null, res);
434
+ });
391
435
  }
392
436
  }
393
437
  };
@@ -62,6 +62,7 @@ class EntryOptionPlugin {
62
62
  runtime: desc.runtime,
63
63
  layer: desc.layer,
64
64
  dependOn: desc.dependOn,
65
+ baseUri: desc.baseUri,
65
66
  publicPath: desc.publicPath,
66
67
  chunkLoading: desc.chunkLoading,
67
68
  asyncChunks: desc.asyncChunks,