webpack 4.41.6 → 4.44.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.
@@ -22,6 +22,23 @@ const createHash = require("../util/createHash");
22
22
  /** @typedef {import("../Dependency")} Dependency */
23
23
  /** @typedef {import("../Compilation")} Compilation */
24
24
  /** @typedef {import("../util/createHash").Hash} Hash */
25
+ /** @typedef {import("../RequestShortener")} RequestShortener */
26
+
27
+ const joinIterableWithComma = iterable => {
28
+ // This is more performant than Array.from().join(", ")
29
+ // as it doesn't create an array
30
+ let str = "";
31
+ let first = true;
32
+ for (const item of iterable) {
33
+ if (first) {
34
+ first = false;
35
+ } else {
36
+ str += ", ";
37
+ }
38
+ str += item;
39
+ }
40
+ return str;
41
+ };
25
42
 
26
43
  /**
27
44
  * @typedef {Object} ConcatenationEntry
@@ -287,6 +304,41 @@ const getPathInAst = (ast, node) => {
287
304
  }
288
305
  };
289
306
 
307
+ const getHarmonyExportImportedSpecifierDependencyExports = dep => {
308
+ const importModule = dep._module;
309
+ if (!importModule) return [];
310
+ if (dep._id) {
311
+ // export { named } from "module"
312
+ return [
313
+ {
314
+ name: dep.name,
315
+ id: dep._id,
316
+ module: importModule
317
+ }
318
+ ];
319
+ }
320
+ if (dep.name) {
321
+ // export * as abc from "module"
322
+ return [
323
+ {
324
+ name: dep.name,
325
+ id: true,
326
+ module: importModule
327
+ }
328
+ ];
329
+ }
330
+ // export * from "module"
331
+ return importModule.buildMeta.providedExports
332
+ .filter(exp => exp !== "default" && !dep.activeExports.has(exp))
333
+ .map(exp => {
334
+ return {
335
+ name: exp,
336
+ id: exp,
337
+ module: importModule
338
+ };
339
+ });
340
+ };
341
+
290
342
  class ConcatenatedModule extends Module {
291
343
  constructor(rootModule, modules, concatenationList) {
292
344
  super("javascript/esm", null);
@@ -626,10 +678,7 @@ class ConcatenatedModule extends Module {
626
678
  );
627
679
  innerDependencyTemplates.set(
628
680
  HarmonyExportSpecifierDependency,
629
- new HarmonyExportSpecifierDependencyConcatenatedTemplate(
630
- dependencyTemplates.get(HarmonyExportSpecifierDependency),
631
- this.rootModule
632
- )
681
+ new NullTemplate()
633
682
  );
634
683
  innerDependencyTemplates.set(
635
684
  HarmonyExportExpressionDependency,
@@ -640,19 +689,11 @@ class ConcatenatedModule extends Module {
640
689
  );
641
690
  innerDependencyTemplates.set(
642
691
  HarmonyExportImportedSpecifierDependency,
643
- new HarmonyExportImportedSpecifierDependencyConcatenatedTemplate(
644
- dependencyTemplates.get(HarmonyExportImportedSpecifierDependency),
645
- this.rootModule,
646
- moduleToInfoMap
647
- )
692
+ new NullTemplate()
648
693
  );
649
694
  innerDependencyTemplates.set(
650
695
  HarmonyCompatibilityDependency,
651
- new HarmonyCompatibilityDependencyConcatenatedTemplate(
652
- dependencyTemplates.get(HarmonyCompatibilityDependency),
653
- this.rootModule,
654
- moduleToInfoMap
655
- )
696
+ new NullTemplate()
656
697
  );
657
698
 
658
699
  // Must use full identifier in our cache here to ensure that the source
@@ -1105,11 +1146,62 @@ class ConcatenatedModule extends Module {
1105
1146
  }
1106
1147
  }
1107
1148
 
1149
+ // Map with all root exposed used exports
1150
+ /** @type {Map<string, function(RequestShortener): string>} */
1151
+ const exportsMap = new Map();
1152
+
1153
+ // Set with all root exposed unused exports
1154
+ /** @type {Set<string>} */
1155
+ const unusedExports = new Set();
1156
+
1157
+ for (const dep of this.rootModule.dependencies) {
1158
+ if (dep instanceof HarmonyExportSpecifierDependency) {
1159
+ const used = this.rootModule.isUsed(dep.name);
1160
+ if (used) {
1161
+ const info = moduleToInfoMap.get(this.rootModule);
1162
+ if (!exportsMap.has(used)) {
1163
+ exportsMap.set(
1164
+ used,
1165
+ () => `/* binding */ ${info.internalNames.get(dep.id)}`
1166
+ );
1167
+ }
1168
+ } else {
1169
+ unusedExports.add(dep.name || "namespace");
1170
+ }
1171
+ } else if (dep instanceof HarmonyExportImportedSpecifierDependency) {
1172
+ const exportDefs = getHarmonyExportImportedSpecifierDependencyExports(
1173
+ dep
1174
+ );
1175
+ for (const def of exportDefs) {
1176
+ const info = moduleToInfoMap.get(def.module);
1177
+ const used = dep.originModule.isUsed(def.name);
1178
+ if (used) {
1179
+ if (!exportsMap.has(used)) {
1180
+ exportsMap.set(used, requestShortener => {
1181
+ const finalName = getFinalName(
1182
+ info,
1183
+ def.id,
1184
+ moduleToInfoMap,
1185
+ requestShortener,
1186
+ false,
1187
+ this.rootModule.buildMeta.strictHarmonyModule
1188
+ );
1189
+ return `/* reexport */ ${finalName}`;
1190
+ });
1191
+ }
1192
+ } else {
1193
+ unusedExports.add(def.name);
1194
+ }
1195
+ }
1196
+ }
1197
+ }
1198
+
1108
1199
  const result = new ConcatSource();
1109
1200
 
1110
1201
  // add harmony compatibility flag (must be first because of possible circular dependencies)
1111
1202
  const usedExports = this.rootModule.usedExports;
1112
1203
  if (usedExports === true || usedExports === null) {
1204
+ result.add(`// ESM COMPAT FLAG\n`);
1113
1205
  result.add(
1114
1206
  runtimeTemplate.defineEsModuleFlagStatement({
1115
1207
  exportsArgument: this.exportsArgument
@@ -1117,9 +1209,33 @@ class ConcatenatedModule extends Module {
1117
1209
  );
1118
1210
  }
1119
1211
 
1212
+ // define exports
1213
+ if (exportsMap.size > 0) {
1214
+ result.add(`\n// EXPORTS\n`);
1215
+ for (const [key, value] of exportsMap) {
1216
+ result.add(
1217
+ `__webpack_require__.d(${this.exportsArgument}, ${JSON.stringify(
1218
+ key
1219
+ )}, function() { return ${value(requestShortener)}; });\n`
1220
+ );
1221
+ }
1222
+ }
1223
+
1224
+ // list unused exports
1225
+ if (unusedExports.size > 0) {
1226
+ result.add(
1227
+ `\n// UNUSED EXPORTS: ${joinIterableWithComma(unusedExports)}\n`
1228
+ );
1229
+ }
1230
+
1120
1231
  // define required namespace objects (must be before evaluation modules)
1121
1232
  for (const info of modulesWithInfo) {
1122
1233
  if (info.namespaceObjectSource) {
1234
+ result.add(
1235
+ `\n// NAMESPACE OBJECT: ${info.module.readableIdentifier(
1236
+ requestShortener
1237
+ )}\n`
1238
+ );
1123
1239
  result.add(info.namespaceObjectSource);
1124
1240
  }
1125
1241
  }
@@ -1321,38 +1437,6 @@ class HarmonyImportSideEffectDependencyConcatenatedTemplate {
1321
1437
  }
1322
1438
  }
1323
1439
 
1324
- class HarmonyExportSpecifierDependencyConcatenatedTemplate {
1325
- constructor(originalTemplate, rootModule) {
1326
- this.originalTemplate = originalTemplate;
1327
- this.rootModule = rootModule;
1328
- }
1329
-
1330
- getHarmonyInitOrder(dep) {
1331
- if (dep.originModule === this.rootModule) {
1332
- return this.originalTemplate.getHarmonyInitOrder(dep);
1333
- }
1334
- return NaN;
1335
- }
1336
-
1337
- harmonyInit(dep, source, runtime, dependencyTemplates) {
1338
- if (dep.originModule === this.rootModule) {
1339
- this.originalTemplate.harmonyInit(
1340
- dep,
1341
- source,
1342
- runtime,
1343
- dependencyTemplates
1344
- );
1345
- return;
1346
- }
1347
- }
1348
-
1349
- apply(dep, source, runtime, dependencyTemplates) {
1350
- if (dep.originModule === this.rootModule) {
1351
- this.originalTemplate.apply(dep, source, runtime, dependencyTemplates);
1352
- }
1353
- }
1354
- }
1355
-
1356
1440
  class HarmonyExportExpressionDependencyConcatenatedTemplate {
1357
1441
  constructor(originalTemplate, rootModule) {
1358
1442
  this.originalTemplate = originalTemplate;
@@ -1386,119 +1470,8 @@ class HarmonyExportExpressionDependencyConcatenatedTemplate {
1386
1470
  }
1387
1471
  }
1388
1472
 
1389
- class HarmonyExportImportedSpecifierDependencyConcatenatedTemplate {
1390
- constructor(originalTemplate, rootModule, modulesMap) {
1391
- this.originalTemplate = originalTemplate;
1392
- this.rootModule = rootModule;
1393
- this.modulesMap = modulesMap;
1394
- }
1395
-
1396
- getExports(dep) {
1397
- const importModule = dep._module;
1398
- if (dep._id) {
1399
- // export { named } from "module"
1400
- return [
1401
- {
1402
- name: dep.name,
1403
- id: dep._id,
1404
- module: importModule
1405
- }
1406
- ];
1407
- }
1408
- if (dep.name) {
1409
- // export * as abc from "module"
1410
- return [
1411
- {
1412
- name: dep.name,
1413
- id: true,
1414
- module: importModule
1415
- }
1416
- ];
1417
- }
1418
- // export * from "module"
1419
- return importModule.buildMeta.providedExports
1420
- .filter(exp => exp !== "default" && !dep.activeExports.has(exp))
1421
- .map(exp => {
1422
- return {
1423
- name: exp,
1424
- id: exp,
1425
- module: importModule
1426
- };
1427
- });
1428
- }
1429
-
1430
- getHarmonyInitOrder(dep) {
1431
- const module = dep._module;
1432
- const info = this.modulesMap.get(module);
1433
- if (!info) {
1434
- return this.originalTemplate.getHarmonyInitOrder(dep);
1435
- }
1436
- return NaN;
1437
- }
1438
-
1439
- harmonyInit(dep, source, runtime, dependencyTemplates) {
1440
- const module = dep._module;
1441
- const info = this.modulesMap.get(module);
1442
- if (!info) {
1443
- this.originalTemplate.harmonyInit(
1444
- dep,
1445
- source,
1446
- runtime,
1447
- dependencyTemplates
1448
- );
1449
- return;
1450
- }
1451
- }
1452
-
1453
- apply(dep, source, runtime, dependencyTemplates) {
1454
- if (dep.originModule === this.rootModule) {
1455
- if (this.modulesMap.get(dep._module)) {
1456
- const exportDefs = this.getExports(dep);
1457
- for (const def of exportDefs) {
1458
- const info = this.modulesMap.get(def.module);
1459
- const used = dep.originModule.isUsed(def.name);
1460
- if (!used) {
1461
- source.insert(
1462
- -1,
1463
- `/* unused concated harmony import ${def.name} */\n`
1464
- );
1465
- continue;
1466
- }
1467
- let finalName;
1468
- const strictFlag = dep.originModule.buildMeta.strictHarmonyModule
1469
- ? "_strict"
1470
- : "";
1471
- if (def.id === true) {
1472
- finalName = `__WEBPACK_MODULE_REFERENCE__${info.index}_ns${strictFlag}__`;
1473
- } else {
1474
- const exportData = Buffer.from(def.id, "utf-8").toString("hex");
1475
- finalName = `__WEBPACK_MODULE_REFERENCE__${info.index}_${exportData}${strictFlag}__`;
1476
- }
1477
- const exportsName = this.rootModule.exportsArgument;
1478
- const content =
1479
- `/* concated harmony reexport ${def.name} */` +
1480
- `__webpack_require__.d(${exportsName}, ` +
1481
- `${JSON.stringify(used)}, ` +
1482
- `function() { return ${finalName}; });\n`;
1483
- source.insert(-1, content);
1484
- }
1485
- } else {
1486
- this.originalTemplate.apply(dep, source, runtime, dependencyTemplates);
1487
- }
1488
- }
1489
- }
1490
- }
1491
-
1492
- class HarmonyCompatibilityDependencyConcatenatedTemplate {
1493
- constructor(originalTemplate, rootModule, modulesMap) {
1494
- this.originalTemplate = originalTemplate;
1495
- this.rootModule = rootModule;
1496
- this.modulesMap = modulesMap;
1497
- }
1498
-
1499
- apply(dep, source, runtime, dependencyTemplates) {
1500
- // do nothing
1501
- }
1473
+ class NullTemplate {
1474
+ apply() {}
1502
1475
  }
1503
1476
 
1504
1477
  module.exports = ConcatenatedModule;
@@ -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;