webpack 5.105.2 → 5.105.3

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.
Files changed (36) hide show
  1. package/lib/CleanPlugin.js +1 -0
  2. package/lib/Compilation.js +8 -6
  3. package/lib/ContextModule.js +14 -8
  4. package/lib/Dependency.js +1 -1
  5. package/lib/EnvironmentNotSupportAsyncWarning.js +1 -0
  6. package/lib/EvalDevToolModulePlugin.js +3 -0
  7. package/lib/EvalSourceMapDevToolPlugin.js +8 -1
  8. package/lib/ExportsInfo.js +0 -30
  9. package/lib/ExternalModule.js +2 -2
  10. package/lib/Module.js +30 -5
  11. package/lib/ModuleGraphConnection.js +0 -9
  12. package/lib/SourceMapDevToolModuleOptionsPlugin.js +1 -0
  13. package/lib/SourceMapDevToolPlugin.js +10 -2
  14. package/lib/WebpackOptionsApply.js +13 -3
  15. package/lib/asset/AssetModulesPlugin.js +16 -1
  16. package/lib/asset/RawDataUrlModule.js +5 -1
  17. package/lib/css/CssGenerator.js +3 -6
  18. package/lib/css/CssModulesPlugin.js +7 -0
  19. package/lib/dependencies/CommonJsExportRequireDependency.js +4 -0
  20. package/lib/dependencies/CommonJsImportsParserPlugin.js +314 -508
  21. package/lib/dependencies/CreateRequireParserPlugin.js +345 -0
  22. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +4 -8
  23. package/lib/dependencies/HarmonyImportDependency.js +30 -0
  24. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +8 -20
  25. package/lib/dependencies/HarmonyModulesPlugin.js +4 -0
  26. package/lib/dependencies/ImportParserPlugin.js +1 -11
  27. package/lib/dependencies/ImportPhase.js +4 -0
  28. package/lib/javascript/JavascriptModulesPlugin.js +75 -22
  29. package/lib/javascript/JavascriptParser.js +2 -2
  30. package/lib/performance/AssetsOverSizeLimitWarning.js +1 -0
  31. package/lib/performance/EntrypointsOverSizeLimitWarning.js +1 -0
  32. package/lib/performance/SizeLimitsPlugin.js +1 -0
  33. package/lib/runtime/ToBinaryRuntimeModule.js +14 -6
  34. package/lib/util/findGraphRoots.js +79 -109
  35. package/package.json +12 -9
  36. package/types.d.ts +147 -62
@@ -0,0 +1,345 @@
1
+ /*
2
+ MIT License http://www.opensource.org/licenses/mit-license.php
3
+ Author Tobias Koppers @sokra
4
+ */
5
+
6
+ "use strict";
7
+
8
+ const { fileURLToPath } = require("url");
9
+ const WebpackError = require("../WebpackError");
10
+ const BasicEvaluatedExpression = require("../javascript/BasicEvaluatedExpression");
11
+ const { VariableInfo } = require("../javascript/JavascriptParser");
12
+ const {
13
+ evaluateToString,
14
+ expressionIsUnsupported,
15
+ toConstantDependency
16
+ } = require("../javascript/JavascriptParserHelpers");
17
+ const CommonJsImportsParserPlugin = require("./CommonJsImportsParserPlugin");
18
+ const ConstDependency = require("./ConstDependency");
19
+
20
+ /** @typedef {import("estree").CallExpression} CallExpression */
21
+ /** @typedef {import("estree").Expression} Expression */
22
+ /** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
23
+ /** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
24
+ /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
25
+ /** @typedef {import("../javascript/JavascriptParser").ImportSource} ImportSource */
26
+ /** @typedef {import("../javascript/JavascriptParser").Range} Range */
27
+
28
+ /**
29
+ * @typedef {object} CommonJsImportSettings
30
+ * @property {string=} name
31
+ * @property {string} context
32
+ */
33
+
34
+ const createRequireSpecifierTag = Symbol("createRequire");
35
+ const createdRequireIdentifierTag = Symbol("createRequire()");
36
+
37
+ const PLUGIN_NAME = "CreateRequireParserPlugin";
38
+
39
+ const {
40
+ createProcessResolveHandler,
41
+ createRequireAsExpressionHandler,
42
+ createRequireCacheDependency,
43
+ createRequireHandler
44
+ } = CommonJsImportsParserPlugin;
45
+
46
+ class CreateRequireParserPlugin {
47
+ /**
48
+ * @param {JavascriptParserOptions} options parser options
49
+ */
50
+ constructor(options) {
51
+ this.options = options;
52
+ }
53
+
54
+ /**
55
+ * @param {JavascriptParser} parser the parser
56
+ * @returns {void}
57
+ */
58
+ apply(parser) {
59
+ const options = this.options;
60
+ if (!options.createRequire) return;
61
+
62
+ const getContext = () => {
63
+ if (parser.currentTagData) {
64
+ const { context } =
65
+ /** @type {CommonJsImportSettings} */
66
+ (parser.currentTagData);
67
+ return context;
68
+ }
69
+ };
70
+
71
+ /**
72
+ * @param {string | symbol} tag tag
73
+ */
74
+ const tapRequireExpressionTag = (tag) => {
75
+ parser.hooks.typeof
76
+ .for(tag)
77
+ .tap(
78
+ PLUGIN_NAME,
79
+ toConstantDependency(parser, JSON.stringify("function"))
80
+ );
81
+ parser.hooks.evaluateTypeof
82
+ .for(tag)
83
+ .tap(PLUGIN_NAME, evaluateToString("function"));
84
+ };
85
+
86
+ /**
87
+ * @param {Expression} expr expression
88
+ * @returns {boolean} true when set undefined
89
+ */
90
+ const defineUndefined = (expr) => {
91
+ const dep = new ConstDependency(
92
+ "undefined",
93
+ /** @type {Range} */ (expr.range)
94
+ );
95
+ dep.loc = /** @type {DependencyLocation} */ (expr.loc);
96
+ parser.state.module.addPresentationalDependency(dep);
97
+ return false;
98
+ };
99
+
100
+ const requireCache = createRequireCacheDependency(parser);
101
+ const requireAsExpressionHandler = createRequireAsExpressionHandler(
102
+ parser,
103
+ options,
104
+ getContext
105
+ );
106
+ const createRequireCallHandler = createRequireHandler(
107
+ parser,
108
+ options,
109
+ getContext
110
+ );
111
+ const processResolve = createProcessResolveHandler(
112
+ parser,
113
+ options,
114
+ getContext
115
+ );
116
+
117
+ /** @type {ImportSource[]} */
118
+ let moduleNames = [];
119
+ /** @type {string | undefined} */
120
+ let specifierName;
121
+
122
+ if (options.createRequire === true) {
123
+ moduleNames = ["module", "node:module"];
124
+ specifierName = "createRequire";
125
+ } else if (typeof options.createRequire === "string") {
126
+ /** @type {undefined | string} */
127
+ let parsedModuleName;
128
+ const match = /^(.*) from (.*)$/.exec(options.createRequire);
129
+ if (match) {
130
+ [, specifierName, parsedModuleName] = match;
131
+ }
132
+ if (!specifierName || !parsedModuleName) {
133
+ const err = new WebpackError(
134
+ `Parsing javascript parser option "createRequire" failed, got ${JSON.stringify(
135
+ options.createRequire
136
+ )}`
137
+ );
138
+ err.details =
139
+ 'Expected string in format "createRequire from module", where "createRequire" is specifier name and "module" name of the module';
140
+ throw err;
141
+ }
142
+ moduleNames = [parsedModuleName];
143
+ } else {
144
+ return;
145
+ }
146
+
147
+ /**
148
+ * @param {CallExpression} expr call expression
149
+ * @returns {string | void} context
150
+ */
151
+ const parseCreateRequireArguments = (expr) => {
152
+ const args = expr.arguments;
153
+ if (args.length !== 1) {
154
+ const err = new WebpackError(
155
+ "module.createRequire supports only one argument."
156
+ );
157
+ err.loc = /** @type {DependencyLocation} */ (expr.loc);
158
+ parser.state.module.addWarning(err);
159
+ return;
160
+ }
161
+ const arg = args[0];
162
+ const evaluated = parser.evaluateExpression(arg);
163
+ if (!evaluated.isString()) {
164
+ const err = new WebpackError(
165
+ "module.createRequire failed parsing argument."
166
+ );
167
+ err.loc = /** @type {DependencyLocation} */ (arg.loc);
168
+ parser.state.module.addWarning(err);
169
+ return;
170
+ }
171
+ const ctx = /** @type {string} */ (evaluated.string).startsWith("file://")
172
+ ? fileURLToPath(/** @type {string} */ (evaluated.string))
173
+ : /** @type {string} */ (evaluated.string);
174
+ // argument always should be a filename
175
+ return ctx.slice(0, ctx.lastIndexOf(ctx.startsWith("/") ? "/" : "\\"));
176
+ };
177
+
178
+ tapRequireExpressionTag(createdRequireIdentifierTag);
179
+ tapRequireExpressionTag(createRequireSpecifierTag);
180
+
181
+ parser.hooks.evaluateCallExpression
182
+ .for(createRequireSpecifierTag)
183
+ .tap(PLUGIN_NAME, (expr) => {
184
+ const context = parseCreateRequireArguments(expr);
185
+ if (context === undefined) return;
186
+ const ident = parser.evaluatedVariable({
187
+ tag: createdRequireIdentifierTag,
188
+ data: { context },
189
+ next: undefined
190
+ });
191
+
192
+ return new BasicEvaluatedExpression()
193
+ .setIdentifier(ident, ident, () => [])
194
+ .setSideEffects(false)
195
+ .setRange(/** @type {Range} */ (expr.range));
196
+ });
197
+
198
+ parser.hooks.unhandledExpressionMemberChain
199
+ .for(createdRequireIdentifierTag)
200
+ .tap(PLUGIN_NAME, (expr, members) =>
201
+ expressionIsUnsupported(
202
+ parser,
203
+ `createRequire().${members.join(".")} is not supported by webpack.`
204
+ )(expr)
205
+ );
206
+ parser.hooks.canRename
207
+ .for(createdRequireIdentifierTag)
208
+ .tap(PLUGIN_NAME, () => true);
209
+ parser.hooks.canRename
210
+ .for(createRequireSpecifierTag)
211
+ .tap(PLUGIN_NAME, () => true);
212
+ parser.hooks.rename
213
+ .for(createRequireSpecifierTag)
214
+ .tap(PLUGIN_NAME, defineUndefined);
215
+ parser.hooks.expression
216
+ .for(createdRequireIdentifierTag)
217
+ .tap(PLUGIN_NAME, requireAsExpressionHandler);
218
+ parser.hooks.call
219
+ .for(createdRequireIdentifierTag)
220
+ .tap(PLUGIN_NAME, createRequireCallHandler(false));
221
+
222
+ parser.hooks.import.tap(
223
+ {
224
+ name: PLUGIN_NAME,
225
+ stage: -10
226
+ },
227
+ (statement, source) => {
228
+ if (
229
+ !moduleNames.includes(source) ||
230
+ statement.specifiers.length !== 1 ||
231
+ statement.specifiers[0].type !== "ImportSpecifier" ||
232
+ statement.specifiers[0].imported.type !== "Identifier" ||
233
+ statement.specifiers[0].imported.name !== specifierName
234
+ ) {
235
+ return;
236
+ }
237
+ // clear for 'import { createRequire as x } from "module"'
238
+ // if any other specifier was used import module
239
+ const clearDep = new ConstDependency(
240
+ parser.isAsiPosition(/** @type {Range} */ (statement.range)[0])
241
+ ? ";"
242
+ : "",
243
+ /** @type {Range} */ (statement.range)
244
+ );
245
+ clearDep.loc = /** @type {DependencyLocation} */ (statement.loc);
246
+ parser.state.module.addPresentationalDependency(clearDep);
247
+ parser.unsetAsiPosition(/** @type {Range} */ (statement.range)[1]);
248
+ return true;
249
+ }
250
+ );
251
+ parser.hooks.importSpecifier.tap(
252
+ {
253
+ name: PLUGIN_NAME,
254
+ stage: -10
255
+ },
256
+ (statement, source, id, name) => {
257
+ if (!moduleNames.includes(source) || id !== specifierName) return;
258
+ parser.tagVariable(name, createRequireSpecifierTag);
259
+ return true;
260
+ }
261
+ );
262
+ parser.hooks.preDeclarator.tap(PLUGIN_NAME, (declarator) => {
263
+ if (
264
+ declarator.id.type !== "Identifier" ||
265
+ !declarator.init ||
266
+ declarator.init.type !== "CallExpression" ||
267
+ declarator.init.callee.type !== "Identifier"
268
+ ) {
269
+ return;
270
+ }
271
+ const variableInfo = parser.getVariableInfo(declarator.init.callee.name);
272
+ if (
273
+ variableInfo instanceof VariableInfo &&
274
+ variableInfo.tagInfo &&
275
+ variableInfo.tagInfo.tag === createRequireSpecifierTag
276
+ ) {
277
+ const context = parseCreateRequireArguments(declarator.init);
278
+ if (context === undefined) return;
279
+ parser.tagVariable(declarator.id.name, createdRequireIdentifierTag, {
280
+ name: declarator.id.name,
281
+ context
282
+ });
283
+ return true;
284
+ }
285
+ });
286
+
287
+ parser.hooks.memberChainOfCallMemberChain
288
+ .for(createRequireSpecifierTag)
289
+ .tap(PLUGIN_NAME, (expr, calleeMembers, callExpr, members) => {
290
+ if (
291
+ calleeMembers.length !== 0 ||
292
+ members.length !== 1 ||
293
+ members[0] !== "cache"
294
+ ) {
295
+ return;
296
+ }
297
+ // createRequire().cache
298
+ const context = parseCreateRequireArguments(callExpr);
299
+ if (context === undefined) return;
300
+ return requireCache(expr);
301
+ });
302
+ parser.hooks.callMemberChainOfCallMemberChain
303
+ .for(createRequireSpecifierTag)
304
+ .tap(PLUGIN_NAME, (expr, calleeMembers, innerCallExpression, members) => {
305
+ if (
306
+ calleeMembers.length !== 0 ||
307
+ members.length !== 1 ||
308
+ members[0] !== "resolve"
309
+ ) {
310
+ return;
311
+ }
312
+ // createRequire().resolve()
313
+ return processResolve(expr, false);
314
+ });
315
+ parser.hooks.expressionMemberChain
316
+ .for(createdRequireIdentifierTag)
317
+ .tap(PLUGIN_NAME, (expr, members) => {
318
+ // require.cache
319
+ if (members.length === 1 && members[0] === "cache") {
320
+ return requireCache(expr);
321
+ }
322
+ });
323
+ parser.hooks.callMemberChain
324
+ .for(createdRequireIdentifierTag)
325
+ .tap(PLUGIN_NAME, (expr, members) => {
326
+ // require.resolve()
327
+ if (members.length === 1 && members[0] === "resolve") {
328
+ return processResolve(expr, false);
329
+ }
330
+ });
331
+ parser.hooks.call
332
+ .for(createRequireSpecifierTag)
333
+ .tap(PLUGIN_NAME, (expr) => {
334
+ const clearDep = new ConstDependency(
335
+ "/* createRequire() */ undefined",
336
+ /** @type {Range} */ (expr.range)
337
+ );
338
+ clearDep.loc = /** @type {DependencyLocation} */ (expr.loc);
339
+ parser.state.module.addPresentationalDependency(clearDep);
340
+ return true;
341
+ });
342
+ }
343
+ }
344
+
345
+ module.exports = CreateRequireParserPlugin;
@@ -39,14 +39,10 @@ module.exports = class HarmonyExportDependencyParserPlugin {
39
39
  */
40
40
  constructor(options) {
41
41
  this.options = options;
42
- this.exportPresenceMode =
43
- options.reexportExportsPresence !== undefined
44
- ? ExportPresenceModes.fromUserOption(options.reexportExportsPresence)
45
- : options.exportsPresence !== undefined
46
- ? ExportPresenceModes.fromUserOption(options.exportsPresence)
47
- : options.strictExportPresence
48
- ? ExportPresenceModes.ERROR
49
- : ExportPresenceModes.AUTO;
42
+ this.exportPresenceMode = ExportPresenceModes.resolveFromOptions(
43
+ options.reexportExportsPresence,
44
+ options
45
+ );
50
46
  }
51
47
 
52
48
  /**
@@ -53,9 +53,38 @@ const ExportPresenceModes = {
53
53
  default:
54
54
  throw new Error(`Invalid export presence value ${str}`);
55
55
  }
56
+ },
57
+ /**
58
+ * Resolve export presence mode from parser options with a specific key and shared fallbacks.
59
+ * @param {string | false | undefined} specificValue the type-specific option value (e.g. importExportsPresence or reexportExportsPresence)
60
+ * @param {import("../../declarations/WebpackOptions").JavascriptParserOptions} options parser options
61
+ * @returns {ExportPresenceMode} resolved mode
62
+ */
63
+ resolveFromOptions(specificValue, options) {
64
+ if (specificValue !== undefined) {
65
+ return ExportPresenceModes.fromUserOption(specificValue);
66
+ }
67
+ if (options.exportsPresence !== undefined) {
68
+ return ExportPresenceModes.fromUserOption(options.exportsPresence);
69
+ }
70
+ return options.strictExportPresence
71
+ ? ExportPresenceModes.ERROR
72
+ : ExportPresenceModes.AUTO;
56
73
  }
57
74
  };
58
75
 
76
+ /**
77
+ * Get the non-optional leading part of a member chain.
78
+ * @param {string[]} members members
79
+ * @param {boolean[]} membersOptionals optionality for each member
80
+ * @returns {string[]} the non-optional prefix
81
+ */
82
+ const getNonOptionalPart = (members, membersOptionals) => {
83
+ let i = 0;
84
+ while (i < members.length && membersOptionals[i] === false) i++;
85
+ return i !== members.length ? members.slice(0, i) : members;
86
+ };
87
+
59
88
  /** @typedef {string[]} Ids */
60
89
 
61
90
  class HarmonyImportDependency extends ModuleDependency {
@@ -427,3 +456,4 @@ HarmonyImportDependency.Template = class HarmonyImportDependencyTemplate extends
427
456
  };
428
457
 
429
458
  module.exports.ExportPresenceModes = ExportPresenceModes;
459
+ module.exports.getNonOptionalPart = getNonOptionalPart;
@@ -18,7 +18,10 @@ const HarmonyAcceptDependency = require("./HarmonyAcceptDependency");
18
18
  const HarmonyAcceptImportDependency = require("./HarmonyAcceptImportDependency");
19
19
  const HarmonyEvaluatedImportSpecifierDependency = require("./HarmonyEvaluatedImportSpecifierDependency");
20
20
  const HarmonyExports = require("./HarmonyExports");
21
- const { ExportPresenceModes } = require("./HarmonyImportDependency");
21
+ const {
22
+ ExportPresenceModes,
23
+ getNonOptionalPart
24
+ } = require("./HarmonyImportDependency");
22
25
  const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");
23
26
  const HarmonyImportSpecifierDependency = require("./HarmonyImportSpecifierDependency");
24
27
  const { ImportPhaseUtils, createGetImportPhase } = require("./ImportPhase");
@@ -82,14 +85,10 @@ module.exports = class HarmonyImportDependencyParserPlugin {
82
85
  constructor(options) {
83
86
  this.options = options;
84
87
  /** @type {ExportPresenceMode} */
85
- this.exportPresenceMode =
86
- options.importExportsPresence !== undefined
87
- ? ExportPresenceModes.fromUserOption(options.importExportsPresence)
88
- : options.exportsPresence !== undefined
89
- ? ExportPresenceModes.fromUserOption(options.exportsPresence)
90
- : options.strictExportPresence
91
- ? ExportPresenceModes.ERROR
92
- : ExportPresenceModes.AUTO;
88
+ this.exportPresenceMode = ExportPresenceModes.resolveFromOptions(
89
+ options.importExportsPresence,
90
+ options
91
+ );
93
92
  this.strictThisContextOnImports = options.strictThisContextOnImports;
94
93
  }
95
94
 
@@ -119,17 +118,6 @@ module.exports = class HarmonyImportDependencyParserPlugin {
119
118
  apply(parser) {
120
119
  const getImportPhase = createGetImportPhase(this.options.deferImport);
121
120
 
122
- /**
123
- * @param {Members} members members
124
- * @param {MembersOptionals} membersOptionals members Optionals
125
- * @returns {Ids} a non optional part
126
- */
127
- function getNonOptionalPart(members, membersOptionals) {
128
- let i = 0;
129
- while (i < members.length && membersOptionals[i] === false) i++;
130
- return i !== members.length ? members.slice(0, i) : members;
131
- }
132
-
133
121
  /**
134
122
  * @param {MemberExpression} node member expression
135
123
  * @param {number} count count
@@ -9,6 +9,7 @@ const {
9
9
  JAVASCRIPT_MODULE_TYPE_AUTO,
10
10
  JAVASCRIPT_MODULE_TYPE_ESM
11
11
  } = require("../ModuleTypeConstants");
12
+ const CreateRequireParserPlugin = require("./CreateRequireParserPlugin");
12
13
  const HarmonyAcceptDependency = require("./HarmonyAcceptDependency");
13
14
  const HarmonyAcceptImportDependency = require("./HarmonyAcceptImportDependency");
14
15
  const HarmonyCompatibilityDependency = require("./HarmonyCompatibilityDependency");
@@ -138,6 +139,9 @@ class HarmonyModulesPlugin {
138
139
  new HarmonyImportDependencyParserPlugin(parserOptions).apply(parser);
139
140
  new HarmonyExportDependencyParserPlugin(parserOptions).apply(parser);
140
141
  new HarmonyTopLevelThisParserPlugin().apply(parser);
142
+ if (parserOptions.createRequire) {
143
+ new CreateRequireParserPlugin(parserOptions).apply(parser);
144
+ }
141
145
  };
142
146
 
143
147
  normalModuleFactory.hooks.parser
@@ -14,6 +14,7 @@ const {
14
14
  } = require("../javascript/JavascriptParser");
15
15
  const traverseDestructuringAssignmentProperties = require("../util/traverseDestructuringAssignmentProperties");
16
16
  const ContextDependencyHelpers = require("./ContextDependencyHelpers");
17
+ const { getNonOptionalPart } = require("./HarmonyImportDependency");
17
18
  const ImportContextDependency = require("./ImportContextDependency");
18
19
  const ImportDependency = require("./ImportDependency");
19
20
  const ImportEagerDependency = require("./ImportEagerDependency");
@@ -178,17 +179,6 @@ class ImportParserPlugin {
178
179
  * @returns {void}
179
180
  */
180
181
  apply(parser) {
181
- /**
182
- * @param {Members} members members
183
- * @param {MembersOptionals} membersOptionals members Optionals
184
- * @returns {string[]} a non optional part
185
- */
186
- function getNonOptionalPart(members, membersOptionals) {
187
- let i = 0;
188
- while (i < members.length && membersOptionals[i] === false) i++;
189
- return i !== members.length ? members.slice(0, i) : members;
190
- }
191
-
192
182
  parser.hooks.collectDestructuringAssignmentProperties.tap(
193
183
  PLUGIN_NAME,
194
184
  (expr) => {
@@ -27,12 +27,16 @@ const ImportPhase = Object.freeze({
27
27
 
28
28
  /**
29
29
  * @typedef {object} ImportPhaseUtils
30
+ * @property {(phase: ImportPhaseType) => boolean} isEvaluation true if phase is evaluation
30
31
  * @property {(phase: ImportPhaseType) => boolean} isDefer true if phase is defer
31
32
  * @property {(phase: ImportPhaseType) => boolean} isSource true if phase is source
32
33
  */
33
34
 
34
35
  /** @type {ImportPhaseUtils} */
35
36
  const ImportPhaseUtils = {
37
+ isEvaluation(phase) {
38
+ return phase === ImportPhase.Evaluation;
39
+ },
36
40
  isDefer(phase) {
37
41
  return phase === ImportPhase.Defer;
38
42
  },
@@ -56,6 +56,7 @@ const JavascriptParser = require("./JavascriptParser");
56
56
  /** @typedef {import("../config/defaults").OutputNormalizedWithDefaults} OutputOptions */
57
57
  /** @typedef {import("../Chunk")} Chunk */
58
58
  /** @typedef {import("../ChunkGraph")} ChunkGraph */
59
+ /** @typedef {import("../ChunkGraph").EntryModuleWithChunkGroup} EntryModuleWithChunkGroup */
59
60
  /** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
60
61
  /** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
61
62
  /** @typedef {import("../Compilation").ExecuteModuleObject} ExecuteModuleObject */
@@ -75,37 +76,75 @@ const JavascriptParser = require("./JavascriptParser");
75
76
  /** @typedef {import("../util/concatenate").ScopeSet} ScopeSet */
76
77
  /** @typedef {import("../util/concatenate").UsedNamesInScopeInfo} UsedNamesInScopeInfo */
77
78
 
79
+ /** @type {WeakMap<ChunkGraph, WeakMap<Chunk, boolean>>} */
80
+ const chunkHasJsCache = new WeakMap();
81
+
78
82
  /**
79
83
  * @param {Chunk} chunk a chunk
80
84
  * @param {ChunkGraph} chunkGraph the chunk graph
81
85
  * @returns {boolean} true, when a JS file is needed for this chunk
82
86
  */
83
- const chunkHasJs = (chunk, chunkGraph) => {
84
- if (chunkGraph.getNumberOfEntryModules(chunk) > 0) return true;
87
+ const _chunkHasJs = (chunk, chunkGraph) => {
88
+ if (chunkGraph.getNumberOfEntryModules(chunk) > 0) {
89
+ for (const module of chunkGraph.getChunkEntryModulesIterable(chunk)) {
90
+ if (chunkGraph.getModuleSourceTypes(module).has(JAVASCRIPT_TYPE)) {
91
+ return true;
92
+ }
93
+ }
94
+ }
85
95
 
86
96
  return Boolean(
87
97
  chunkGraph.getChunkModulesIterableBySourceType(chunk, JAVASCRIPT_TYPE)
88
98
  );
89
99
  };
90
100
 
101
+ /**
102
+ * @param {Chunk} chunk a chunk
103
+ * @param {ChunkGraph} chunkGraph the chunk graph
104
+ * @returns {boolean} true, when a JS file is needed for this chunk
105
+ */
106
+ const chunkHasJs = (chunk, chunkGraph) => {
107
+ let innerCache = chunkHasJsCache.get(chunkGraph);
108
+ if (innerCache === undefined) {
109
+ innerCache = new WeakMap();
110
+ chunkHasJsCache.set(chunkGraph, innerCache);
111
+ }
112
+
113
+ const cachedResult = innerCache.get(chunk);
114
+ if (cachedResult !== undefined) {
115
+ return cachedResult;
116
+ }
117
+
118
+ const result = _chunkHasJs(chunk, chunkGraph);
119
+ innerCache.set(chunk, result);
120
+ return result;
121
+ };
122
+
91
123
  /**
92
124
  * @param {Chunk} chunk a chunk
93
125
  * @param {ChunkGraph} chunkGraph the chunk graph
94
126
  * @returns {boolean} true, when a JS file is needed for this chunk
95
127
  */
96
128
  const chunkHasRuntimeOrJs = (chunk, chunkGraph) => {
129
+ if (chunkHasJs(chunk, chunkGraph)) {
130
+ return true;
131
+ }
132
+
97
133
  if (
98
134
  chunkGraph.getChunkModulesIterableBySourceType(
99
135
  chunk,
100
136
  WEBPACK_MODULE_TYPE_RUNTIME
101
137
  )
102
138
  ) {
103
- return true;
139
+ for (const chunkGroup of chunk.groupsIterable) {
140
+ for (const c of chunkGroup.chunks) {
141
+ if (chunkHasJs(c, chunkGraph)) return true;
142
+ }
143
+ }
144
+ return false;
104
145
  }
105
146
 
106
- return Boolean(
107
- chunkGraph.getChunkModulesIterableBySourceType(chunk, JAVASCRIPT_TYPE)
108
- );
147
+ return false;
109
148
  };
110
149
 
111
150
  /**
@@ -1312,17 +1351,22 @@ class JavascriptModulesPlugin {
1312
1351
  const runtimeRequirements =
1313
1352
  chunkGraph.getTreeRuntimeRequirements(chunk);
1314
1353
  buf2.push("// Load entry module and return exports");
1315
- let i = chunkGraph.getNumberOfEntryModules(chunk);
1354
+
1355
+ /** @type {EntryModuleWithChunkGroup[]} */
1356
+ const jsEntries = [];
1316
1357
  for (const [
1317
1358
  entryModule,
1318
1359
  entrypoint
1319
1360
  ] of chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)) {
1320
1361
  if (
1321
- !chunkGraph.getModuleSourceTypes(entryModule).has(JAVASCRIPT_TYPE)
1362
+ chunkGraph.getModuleSourceTypes(entryModule).has(JAVASCRIPT_TYPE)
1322
1363
  ) {
1323
- i--;
1364
+ jsEntries.push([entryModule, entrypoint]);
1324
1365
  continue;
1325
1366
  }
1367
+ }
1368
+ let i = jsEntries.length;
1369
+ for (const [entryModule, entrypoint] of jsEntries) {
1326
1370
  const chunks =
1327
1371
  /** @type {Entrypoint} */
1328
1372
  (entrypoint).chunks.filter((c) => c !== chunk);
@@ -1546,20 +1590,42 @@ class JavascriptModulesPlugin {
1546
1590
  runtimeTemplate: { outputOptions }
1547
1591
  } = renderContext;
1548
1592
  const runtimeRequirements = chunkGraph.getTreeRuntimeRequirements(chunk);
1593
+
1594
+ /**
1595
+ * @param {string} condition guard expression
1596
+ * @returns {string[]} source
1597
+ */
1598
+ const renderMissingModuleError = (condition) =>
1599
+ outputOptions.pathinfo
1600
+ ? [
1601
+ `if (${condition}) {`,
1602
+ Template.indent([
1603
+ "delete __webpack_module_cache__[moduleId];",
1604
+ 'var e = new Error("Cannot find module \'" + moduleId + "\'");',
1605
+ "e.code = 'MODULE_NOT_FOUND';",
1606
+ "throw e;"
1607
+ ]),
1608
+ "}"
1609
+ ]
1610
+ : [];
1611
+
1549
1612
  const moduleExecution = runtimeRequirements.has(
1550
1613
  RuntimeGlobals.interceptModuleExecution
1551
1614
  )
1552
1615
  ? Template.asString([
1553
1616
  `var execOptions = { id: moduleId, module: module, factory: __webpack_modules__[moduleId], require: ${RuntimeGlobals.require} };`,
1554
1617
  `${RuntimeGlobals.interceptModuleExecution}.forEach(function(handler) { handler(execOptions); });`,
1618
+ ...renderMissingModuleError("!execOptions.factory"),
1555
1619
  "module = execOptions.module;",
1556
1620
  "execOptions.factory.call(module.exports, module, module.exports, execOptions.require);"
1557
1621
  ])
1558
1622
  : runtimeRequirements.has(RuntimeGlobals.thisAsExports)
1559
1623
  ? Template.asString([
1624
+ ...renderMissingModuleError("!(moduleId in __webpack_modules__)"),
1560
1625
  `__webpack_modules__[moduleId].call(module.exports, module, module.exports, ${RuntimeGlobals.require});`
1561
1626
  ])
1562
1627
  : Template.asString([
1628
+ ...renderMissingModuleError("!(moduleId in __webpack_modules__)"),
1563
1629
  `__webpack_modules__[moduleId](module, module.exports, ${RuntimeGlobals.require});`
1564
1630
  ]);
1565
1631
  const needModuleId = runtimeRequirements.has(RuntimeGlobals.moduleId);
@@ -1580,19 +1646,6 @@ class JavascriptModulesPlugin {
1580
1646
  ])
1581
1647
  : Template.indent("return cachedModule.exports;"),
1582
1648
  "}",
1583
- // Add helpful error message in development mode when module is not found
1584
- ...(outputOptions.pathinfo
1585
- ? [
1586
- "// Check if module exists (development only)",
1587
- "if (__webpack_modules__[moduleId] === undefined) {",
1588
- Template.indent([
1589
- 'var e = new Error("Cannot find module \'" + moduleId + "\'");',
1590
- "e.code = 'MODULE_NOT_FOUND';",
1591
- "throw e;"
1592
- ]),
1593
- "}"
1594
- ]
1595
- : []),
1596
1649
  "// Create a new module (and put it into the cache)",
1597
1650
  "var module = __webpack_module_cache__[moduleId] = {",
1598
1651
  Template.indent([