webpack 5.78.0 → 5.80.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 (35) hide show
  1. package/README.md +3 -0
  2. package/lib/CompatibilityPlugin.js +27 -10
  3. package/lib/Compiler.js +7 -4
  4. package/lib/DefinePlugin.js +18 -6
  5. package/lib/LibManifestPlugin.js +2 -1
  6. package/lib/NormalModuleFactory.js +11 -1
  7. package/lib/NormalModuleReplacementPlugin.js +1 -1
  8. package/lib/WarnCaseSensitiveModulesPlugin.js +12 -0
  9. package/lib/asset/AssetGenerator.js +11 -3
  10. package/lib/css/CssLoadingRuntimeModule.js +1 -1
  11. package/lib/css/CssParser.js +113 -18
  12. package/lib/css/walkCssTokens.js +134 -74
  13. package/lib/dependencies/CssUrlDependency.js +30 -18
  14. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +4 -0
  15. package/lib/dependencies/HarmonyImportSpecifierDependency.js +28 -3
  16. package/lib/dependencies/ImportMetaPlugin.js +56 -26
  17. package/lib/dependencies/ImportParserPlugin.js +17 -1
  18. package/lib/ids/OccurrenceModuleIdsPlugin.js +1 -1
  19. package/lib/index.js +5 -0
  20. package/lib/javascript/JavascriptParser.js +99 -1
  21. package/lib/schemes/DataUriPlugin.js +12 -3
  22. package/lib/stats/DefaultStatsFactoryPlugin.js +98 -25
  23. package/lib/stats/DefaultStatsPresetPlugin.js +9 -0
  24. package/lib/stats/DefaultStatsPrinterPlugin.js +18 -0
  25. package/lib/util/hash/md4.js +2 -2
  26. package/lib/util/hash/xxhash64.js +1 -1
  27. package/lib/webpack.js +1 -1
  28. package/package.json +54 -51
  29. package/schemas/WebpackOptions.check.js +1 -1
  30. package/schemas/WebpackOptions.json +8 -0
  31. package/schemas/plugins/ProgressPlugin.check.js +1 -1
  32. package/schemas/plugins/SourceMapDevToolPlugin.check.js +1 -1
  33. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  34. package/schemas/plugins/sharing/SharePlugin.check.js +1 -1
  35. package/types.d.ts +195 -139
package/README.md CHANGED
@@ -35,6 +35,9 @@
35
35
  <a href="https://gitter.im/webpack/webpack">
36
36
  <img src="https://badges.gitter.im/webpack/webpack.svg">
37
37
  </a>
38
+ <a href="https://twitter.com/Webpack">
39
+ <img src="https://img.shields.io/twitter/follow/Webpack?style=social">
40
+ </a>
38
41
  <h1>webpack</h1>
39
42
  <p>
40
43
  Webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.
@@ -15,7 +15,7 @@ const ConstDependency = require("./dependencies/ConstDependency");
15
15
  /** @typedef {import("./Compiler")} Compiler */
16
16
  /** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */
17
17
 
18
- const nestedWebpackRequireTag = Symbol("nested __webpack_require__");
18
+ const nestedWebpackIdentifierTag = Symbol("nested webpack identifier");
19
19
  const PLUGIN_NAME = "CompatibilityPlugin";
20
20
 
21
21
  class CompatibilityPlugin {
@@ -82,14 +82,18 @@ class CompatibilityPlugin {
82
82
  statement.id.name === "__webpack_require__"
83
83
  ) {
84
84
  const newName = `__nested_webpack_require_${statement.range[0]}__`;
85
- parser.tagVariable(statement.id.name, nestedWebpackRequireTag, {
86
- name: newName,
87
- declaration: {
88
- updated: false,
89
- loc: statement.id.loc,
90
- range: statement.id.range
85
+ parser.tagVariable(
86
+ statement.id.name,
87
+ nestedWebpackIdentifierTag,
88
+ {
89
+ name: newName,
90
+ declaration: {
91
+ updated: false,
92
+ loc: statement.id.loc,
93
+ range: statement.id.range
94
+ }
91
95
  }
92
- });
96
+ );
93
97
  return true;
94
98
  }
95
99
  });
@@ -97,7 +101,7 @@ class CompatibilityPlugin {
97
101
  .for("__webpack_require__")
98
102
  .tap(PLUGIN_NAME, pattern => {
99
103
  const newName = `__nested_webpack_require_${pattern.range[0]}__`;
100
- parser.tagVariable(pattern.name, nestedWebpackRequireTag, {
104
+ parser.tagVariable(pattern.name, nestedWebpackIdentifierTag, {
101
105
  name: newName,
102
106
  declaration: {
103
107
  updated: false,
@@ -107,8 +111,21 @@ class CompatibilityPlugin {
107
111
  });
108
112
  return true;
109
113
  });
114
+ parser.hooks.pattern
115
+ .for("__webpack_exports__")
116
+ .tap(PLUGIN_NAME, pattern => {
117
+ parser.tagVariable(pattern.name, nestedWebpackIdentifierTag, {
118
+ name: "__nested_webpack_exports__",
119
+ declaration: {
120
+ updated: false,
121
+ loc: pattern.loc,
122
+ range: pattern.range
123
+ }
124
+ });
125
+ return true;
126
+ });
110
127
  parser.hooks.expression
111
- .for(nestedWebpackRequireTag)
128
+ .for(nestedWebpackIdentifierTag)
112
129
  .tap(PLUGIN_NAME, expr => {
113
130
  const { name, declaration } = parser.currentTagData;
114
131
  if (!declaration.updated) {
package/lib/Compiler.js CHANGED
@@ -970,10 +970,13 @@ ${other}`);
970
970
  readRecords(callback) {
971
971
  if (this.hooks.readRecords.isUsed()) {
972
972
  if (this.recordsInputPath) {
973
- asyncLib.parallel([
974
- cb => this.hooks.readRecords.callAsync(cb),
975
- this._readRecords.bind(this)
976
- ]);
973
+ asyncLib.parallel(
974
+ [
975
+ cb => this.hooks.readRecords.callAsync(cb),
976
+ this._readRecords.bind(this)
977
+ ],
978
+ err => callback(err)
979
+ );
977
980
  } else {
978
981
  this.records = {};
979
982
  this.hooks.readRecords.callAsync(callback);
@@ -118,6 +118,7 @@ class RuntimeValue {
118
118
  * @param {string} key the defined key
119
119
  * @param {RuntimeTemplate} runtimeTemplate the runtime template
120
120
  * @param {boolean|undefined|null=} asiSafe asi safe (undefined: unknown, null: unneeded)
121
+ * @param {Set<string>|undefined=} objKeys used keys
121
122
  * @returns {string} code converted to string that evaluates
122
123
  */
123
124
  const stringifyObj = (
@@ -126,7 +127,8 @@ const stringifyObj = (
126
127
  valueCacheVersions,
127
128
  key,
128
129
  runtimeTemplate,
129
- asiSafe
130
+ asiSafe,
131
+ objKeys
130
132
  ) => {
131
133
  let code;
132
134
  let arr = Array.isArray(obj);
@@ -137,7 +139,12 @@ const stringifyObj = (
137
139
  )
138
140
  .join(",")}]`;
139
141
  } else {
140
- code = `{${Object.keys(obj)
142
+ let keys = Object.keys(obj);
143
+ if (objKeys) {
144
+ if (objKeys.size === 0) keys = [];
145
+ else keys = keys.filter(k => objKeys.has(k));
146
+ }
147
+ code = `{${keys
141
148
  .map(key => {
142
149
  const code = obj[key];
143
150
  return (
@@ -169,6 +176,7 @@ const stringifyObj = (
169
176
  * @param {string} key the defined key
170
177
  * @param {RuntimeTemplate} runtimeTemplate the runtime template
171
178
  * @param {boolean|undefined|null=} asiSafe asi safe (undefined: unknown, null: unneeded)
179
+ * @param {Set<string>|undefined=} objKeys used keys
172
180
  * @returns {string} code converted to string that evaluates
173
181
  */
174
182
  const toCode = (
@@ -177,7 +185,8 @@ const toCode = (
177
185
  valueCacheVersions,
178
186
  key,
179
187
  runtimeTemplate,
180
- asiSafe
188
+ asiSafe,
189
+ objKeys
181
190
  ) => {
182
191
  if (code === null) {
183
192
  return "null";
@@ -211,7 +220,8 @@ const toCode = (
211
220
  valueCacheVersions,
212
221
  key,
213
222
  runtimeTemplate,
214
- asiSafe
223
+ asiSafe,
224
+ objKeys
215
225
  );
216
226
  }
217
227
  if (typeof code === "bigint") {
@@ -426,7 +436,8 @@ class DefinePlugin {
426
436
  compilation.valueCacheVersions,
427
437
  originalKey,
428
438
  runtimeTemplate,
429
- !parser.isAsiPosition(expr.range[0])
439
+ !parser.isAsiPosition(expr.range[0]),
440
+ parser.destructuringAssignmentPropertiesFor(expr)
430
441
  );
431
442
  if (WEBPACK_REQUIRE_FUNCTION_REGEXP.test(strCode)) {
432
443
  return toConstantDependency(parser, strCode, [
@@ -523,7 +534,8 @@ class DefinePlugin {
523
534
  compilation.valueCacheVersions,
524
535
  key,
525
536
  runtimeTemplate,
526
- !parser.isAsiPosition(expr.range[0])
537
+ !parser.isAsiPosition(expr.range[0]),
538
+ parser.destructuringAssignmentPropertiesFor(expr)
527
539
  );
528
540
 
529
541
  if (WEBPACK_REQUIRE_FUNCTION_REGEXP.test(strCode)) {
@@ -49,7 +49,8 @@ class LibManifestPlugin {
49
49
  const name =
50
50
  this.options.name &&
51
51
  compilation.getPath(this.options.name, {
52
- chunk
52
+ chunk,
53
+ contentHashType: "javascript"
53
54
  });
54
55
  const content = Object.create(null);
55
56
  for (const module of chunkGraph.getOrderedChunkModulesIterable(
@@ -1020,7 +1020,7 @@ If changing the source code is not an option there is also a resolve options cal
1020
1020
  context,
1021
1021
  item.loader,
1022
1022
  resolveContext,
1023
- (err, result) => {
1023
+ (err, result, resolveRequest) => {
1024
1024
  if (
1025
1025
  err &&
1026
1026
  /^[^/]*$/.test(item.loader) &&
@@ -1047,8 +1047,18 @@ If changing the source code is not an option there is also a resolve options cal
1047
1047
  if (err) return callback(err);
1048
1048
 
1049
1049
  const parsedResult = this._parseResourceWithoutFragment(result);
1050
+
1051
+ const type = /\.mjs$/i.test(parsedResult.path)
1052
+ ? "module"
1053
+ : /\.cjs$/i.test(parsedResult.path)
1054
+ ? "commonjs"
1055
+ : resolveRequest.descriptionFileData === undefined
1056
+ ? undefined
1057
+ : resolveRequest.descriptionFileData.type;
1058
+
1050
1059
  const resolved = {
1051
1060
  loader: parsedResult.path,
1061
+ type,
1052
1062
  options:
1053
1063
  item.options === undefined
1054
1064
  ? parsedResult.query
@@ -8,7 +8,7 @@
8
8
  const { join, dirname } = require("./util/fs");
9
9
 
10
10
  /** @typedef {import("./Compiler")} Compiler */
11
- /** @typedef {function(TODO): void} ModuleReplacer */
11
+ /** @typedef {function(import("./NormalModuleFactory").ResolveData): void} ModuleReplacer */
12
12
 
13
13
  class NormalModuleReplacementPlugin {
14
14
  /**
@@ -9,6 +9,7 @@ const CaseSensitiveModulesWarning = require("./CaseSensitiveModulesWarning");
9
9
 
10
10
  /** @typedef {import("./Compiler")} Compiler */
11
11
  /** @typedef {import("./Module")} Module */
12
+ /** @typedef {import("./NormalModule")} NormalModule */
12
13
 
13
14
  class WarnCaseSensitiveModulesPlugin {
14
15
  /**
@@ -25,6 +26,17 @@ class WarnCaseSensitiveModulesPlugin {
25
26
  const moduleWithoutCase = new Map();
26
27
  for (const module of compilation.modules) {
27
28
  const identifier = module.identifier();
29
+
30
+ // Ignore `data:` URLs, because it's not a real path
31
+ if (
32
+ /** @type {NormalModule} */
33
+ (module).resourceResolveData !== undefined &&
34
+ /** @type {NormalModule} */
35
+ (module).resourceResolveData.encodedContent !== undefined
36
+ ) {
37
+ continue;
38
+ }
39
+
28
40
  const lowerIdentifier = identifier.toLowerCase();
29
41
  let map = moduleWithoutCase.get(lowerIdentifier);
30
42
  if (map === undefined) {
@@ -108,9 +108,17 @@ const encodeDataUri = (encoding, source) => {
108
108
 
109
109
  const decodeDataUriContent = (encoding, content) => {
110
110
  const isBase64 = encoding === "base64";
111
- return isBase64
112
- ? Buffer.from(content, "base64")
113
- : Buffer.from(decodeURIComponent(content), "ascii");
111
+
112
+ if (isBase64) {
113
+ return Buffer.from(content, "base64");
114
+ }
115
+
116
+ // If we can't decode return the original body
117
+ try {
118
+ return Buffer.from(decodeURIComponent(content), "ascii");
119
+ } catch (_) {
120
+ return Buffer.from(content, "ascii");
121
+ }
114
122
  };
115
123
 
116
124
  const JS_TYPES = new Set(["javascript"]);
@@ -111,7 +111,7 @@ class CssLoadingRuntimeModule extends RuntimeModule {
111
111
  ? crossOriginLoading === "use-credentials"
112
112
  ? 'link.crossOrigin = "use-credentials";'
113
113
  : Template.asString([
114
- "if (link.src.indexOf(window.location.origin + '/') !== 0) {",
114
+ "if (link.href.indexOf(window.location.origin + '/') !== 0) {",
115
115
  Template.indent(
116
116
  `link.crossOrigin = ${JSON.stringify(crossOriginLoading)};`
117
117
  ),
@@ -17,21 +17,54 @@ const walkCssTokens = require("./walkCssTokens");
17
17
 
18
18
  /** @typedef {import("../Parser").ParserState} ParserState */
19
19
  /** @typedef {import("../Parser").PreparsedAst} PreparsedAst */
20
-
21
20
  const CC_LEFT_CURLY = "{".charCodeAt(0);
22
21
  const CC_RIGHT_CURLY = "}".charCodeAt(0);
23
22
  const CC_COLON = ":".charCodeAt(0);
24
23
  const CC_SLASH = "/".charCodeAt(0);
25
24
  const CC_SEMICOLON = ";".charCodeAt(0);
26
25
 
27
- const cssUnescape = str => {
28
- return str.replace(/\\([0-9a-fA-F]{1,6}[ \t\n\r\f]?|[\s\S])/g, match => {
29
- if (match.length > 2) {
30
- return String.fromCharCode(parseInt(match.slice(1).trim(), 16));
31
- } else {
32
- return match[1];
26
+ // https://www.w3.org/TR/css-syntax-3/#newline
27
+ // We don't have `preprocessing` stage, so we need specify all of them
28
+ const STRING_MULTILINE = /\\[\n\r\f]/g;
29
+ // https://www.w3.org/TR/css-syntax-3/#whitespace
30
+ const TRIM_WHITE_SPACES = /(^[ \t\n\r\f]*|[ \t\n\r\f]*$)/g;
31
+ const UNESCAPE = /\\([0-9a-fA-F]{1,6}[ \t\n\r\f]?|[\s\S])/g;
32
+ const IMAGE_SET_FUNCTION = /^(-\w+-)?image-set$/i;
33
+
34
+ const normalizeUrl = (str, isString) => {
35
+ // Remove extra spaces and newlines:
36
+ // `url("im\
37
+ // g.png")`
38
+ if (isString) {
39
+ str = str.replace(STRING_MULTILINE, "");
40
+ }
41
+
42
+ str = str
43
+ // Remove unnecessary spaces from `url(" img.png ")`
44
+ .replace(TRIM_WHITE_SPACES, "")
45
+ // Unescape
46
+ .replace(UNESCAPE, match => {
47
+ if (match.length > 2) {
48
+ return String.fromCharCode(parseInt(match.slice(1).trim(), 16));
49
+ } else {
50
+ return match[1];
51
+ }
52
+ });
53
+
54
+ if (/^data:/i.test(str)) {
55
+ return str;
56
+ }
57
+
58
+ if (str.includes("%")) {
59
+ // Convert `url('%2E/img.png')` -> `url('./img.png')`
60
+ try {
61
+ str = decodeURIComponent(str);
62
+ } catch (error) {
63
+ // Ignore
33
64
  }
34
- });
65
+ }
66
+
67
+ return str;
35
68
  };
36
69
 
37
70
  class LocConverter {
@@ -137,8 +170,11 @@ class CssParser extends Parser {
137
170
  let modeData = undefined;
138
171
  let singleClassSelector = undefined;
139
172
  let lastIdentifier = undefined;
140
- const modeStack = [];
141
173
  let awaitRightParenthesis = false;
174
+ /** @type [string, number, number][] */
175
+ const functionStack = [];
176
+ const modeStack = [];
177
+
142
178
  const isTopLevelLocal = () =>
143
179
  modeData === "local" ||
144
180
  (this.defaultMode === "local" && modeData === undefined);
@@ -279,8 +315,8 @@ class CssParser extends Parser {
279
315
  module.addDependency(dep);
280
316
  declaredCssVariables.add(name);
281
317
  } else if (
282
- propertyName === "animation-name" ||
283
- propertyName === "animation"
318
+ propertyName.toLowerCase() === "animation-name" ||
319
+ propertyName.toLowerCase() === "animation"
284
320
  ) {
285
321
  modeData = "animation";
286
322
  lastIdentifier = undefined;
@@ -304,8 +340,11 @@ class CssParser extends Parser {
304
340
  isSelector: () => {
305
341
  return mode !== CSS_MODE_IN_RULE && mode !== CSS_MODE_IN_LOCAL_RULE;
306
342
  },
307
- url: (input, start, end, contentStart, contentEnd) => {
308
- const value = cssUnescape(input.slice(contentStart, contentEnd));
343
+ url: (input, start, end, contentStart, contentEnd, isString) => {
344
+ let value = normalizeUrl(
345
+ input.slice(contentStart, contentEnd),
346
+ isString
347
+ );
309
348
  switch (mode) {
310
349
  case CSS_MODE_AT_IMPORT_EXPECT_URL: {
311
350
  modeData.url = value;
@@ -321,6 +360,15 @@ class CssParser extends Parser {
321
360
  )} at ${start} during ${explainMode(mode)}`
322
361
  );
323
362
  default: {
363
+ if (
364
+ // Ignore `url(#highlight)` URLs
365
+ /^#/.test(value) ||
366
+ // Ignore `url()`, `url('')` and `url("")`, they are valid by spec
367
+ value.length === 0
368
+ ) {
369
+ break;
370
+ }
371
+
324
372
  const dep = new CssUrlDependency(value, [start, end], "url");
325
373
  const { line: sl, column: sc } = locConverter.get(start);
326
374
  const { line: el, column: ec } = locConverter.get(end);
@@ -335,15 +383,49 @@ class CssParser extends Parser {
335
383
  string: (input, start, end) => {
336
384
  switch (mode) {
337
385
  case CSS_MODE_AT_IMPORT_EXPECT_URL: {
338
- modeData.url = cssUnescape(input.slice(start + 1, end - 1));
386
+ modeData.url = normalizeUrl(input.slice(start + 1, end - 1), true);
339
387
  mode = CSS_MODE_AT_IMPORT_EXPECT_SUPPORTS;
340
388
  break;
341
389
  }
390
+ default: {
391
+ // TODO move escaped parsing to tokenizer
392
+ const lastFunction = functionStack[functionStack.length - 1];
393
+
394
+ if (
395
+ lastFunction &&
396
+ (lastFunction[0].replace(/\\/g, "").toLowerCase() === "url" ||
397
+ IMAGE_SET_FUNCTION.test(lastFunction[0].replace(/\\/g, "")))
398
+ ) {
399
+ let value = normalizeUrl(input.slice(start + 1, end - 1), true);
400
+
401
+ if (
402
+ // Ignore `url(#highlight)` URLs
403
+ /^#/.test(value) ||
404
+ // Ignore `url()`, `url('')` and `url("")`, they are valid by spec
405
+ value.length === 0
406
+ ) {
407
+ break;
408
+ }
409
+
410
+ const isUrl =
411
+ lastFunction[0].replace(/\\/g, "").toLowerCase() === "url";
412
+ const dep = new CssUrlDependency(
413
+ value,
414
+ [start, end],
415
+ isUrl ? "string" : "url"
416
+ );
417
+ const { line: sl, column: sc } = locConverter.get(start);
418
+ const { line: el, column: ec } = locConverter.get(end);
419
+ dep.setLoc(sl, sc, el, ec);
420
+ module.addDependency(dep);
421
+ module.addCodeGenerationDependency(dep);
422
+ }
423
+ }
342
424
  }
343
425
  return end;
344
426
  },
345
427
  atKeyword: (input, start, end) => {
346
- const name = input.slice(start, end);
428
+ const name = input.slice(start, end).toLowerCase();
347
429
  if (name === "@namespace") {
348
430
  throw new Error("@namespace is not supported in bundled CSS");
349
431
  }
@@ -523,6 +605,8 @@ class CssParser extends Parser {
523
605
  return end;
524
606
  },
525
607
  rightParenthesis: (input, start, end) => {
608
+ functionStack.pop();
609
+
526
610
  switch (mode) {
527
611
  case CSS_MODE_TOP_LEVEL: {
528
612
  if (awaitRightParenthesis) {
@@ -537,13 +621,14 @@ class CssParser extends Parser {
537
621
  break;
538
622
  }
539
623
  }
624
+
540
625
  return end;
541
626
  },
542
627
  pseudoClass: (input, start, end) => {
543
628
  singleClassSelector = false;
544
629
  switch (mode) {
545
630
  case CSS_MODE_TOP_LEVEL: {
546
- const name = input.slice(start, end);
631
+ const name = input.slice(start, end).toLowerCase();
547
632
  if (this.allowModeSwitch && name === ":global") {
548
633
  modeData = "global";
549
634
  const dep = new ConstDependency("", [start, end]);
@@ -564,9 +649,14 @@ class CssParser extends Parser {
564
649
  return end;
565
650
  },
566
651
  pseudoFunction: (input, start, end) => {
652
+ let name = input.slice(start, end - 1);
653
+
654
+ functionStack.push([name, start, end]);
655
+
567
656
  switch (mode) {
568
657
  case CSS_MODE_TOP_LEVEL: {
569
- const name = input.slice(start, end - 1);
658
+ name = name.toLowerCase();
659
+
570
660
  if (this.allowModeSwitch && name === ":global") {
571
661
  modeStack.push(modeData);
572
662
  modeData = "global";
@@ -587,9 +677,14 @@ class CssParser extends Parser {
587
677
  return end;
588
678
  },
589
679
  function: (input, start, end) => {
680
+ let name = input.slice(start, end - 1);
681
+
682
+ functionStack.push([name, start, end]);
683
+
590
684
  switch (mode) {
591
685
  case CSS_MODE_IN_LOCAL_RULE: {
592
- const name = input.slice(start, end - 1);
686
+ name = name.toLowerCase();
687
+
593
688
  if (name === "var") {
594
689
  let pos = walkCssTokens.eatWhitespaceAndComments(input, end);
595
690
  if (pos === input.length) return pos;