webpack 5.108.4 → 5.109.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.
Files changed (186) hide show
  1. package/lib/APIPlugin.js +48 -29
  2. package/lib/CacheFacade.js +8 -0
  3. package/lib/ChunkGroup.js +1 -0
  4. package/lib/CompatibilityPlugin.js +31 -29
  5. package/lib/Compilation.js +49 -4
  6. package/lib/ConstPlugin.js +11 -11
  7. package/lib/ContextModule.js +239 -6
  8. package/lib/ContextModuleFactory.js +139 -14
  9. package/lib/DefinePlugin.js +396 -101
  10. package/lib/Entrypoint.js +1 -1
  11. package/lib/ExportsInfoApiPlugin.js +2 -2
  12. package/lib/ExternalModule.js +107 -5
  13. package/lib/ExternalModuleFactoryPlugin.js +20 -1
  14. package/lib/FileSystemInfo.js +130 -74
  15. package/lib/HotModuleReplacementPlugin.js +18 -15
  16. package/lib/Module.js +3 -0
  17. package/lib/ModuleFilenameHelpers.js +2 -2
  18. package/lib/ModuleTypeConstants.js +8 -1
  19. package/lib/MultiCompiler.js +13 -2
  20. package/lib/MultiWatching.js +1 -0
  21. package/lib/NodeStuffPlugin.js +51 -33
  22. package/lib/NormalModule.js +169 -401
  23. package/lib/NormalModuleFactory.js +76 -2
  24. package/lib/ProgressPlugin.js +211 -119
  25. package/lib/ProvidePlugin.js +2 -2
  26. package/lib/RuntimeGlobals.js +47 -0
  27. package/lib/RuntimePlugin.js +33 -0
  28. package/lib/RuntimeTemplate.js +304 -16
  29. package/lib/UseStrictPlugin.js +1 -1
  30. package/lib/WebpackIsIncludedPlugin.js +1 -1
  31. package/lib/WebpackOptionsApply.js +24 -19
  32. package/lib/asset/AssetGenerator.js +110 -28
  33. package/lib/asset/AssetModulesPlugin.js +92 -2
  34. package/lib/asset/WebManifestGenerator.js +164 -0
  35. package/lib/asset/WebManifestParser.js +130 -0
  36. package/lib/async-modules/AwaitDependenciesInitFragment.js +14 -4
  37. package/lib/async-modules/isGeneratorLowered.js +26 -0
  38. package/lib/cache/PackFileCacheStrategy.js +9 -3
  39. package/lib/config/browserslistTargetHandler.js +40 -0
  40. package/lib/config/defaults.js +219 -29
  41. package/lib/config/normalization.js +26 -0
  42. package/lib/config/target.js +9 -1
  43. package/lib/css/CssGenerator.js +40 -31
  44. package/lib/css/CssLoadingRuntimeModule.js +5 -1
  45. package/lib/css/CssModulesPlugin.js +13 -0
  46. package/lib/css/CssParser.js +594 -263
  47. package/lib/css/syntax.js +875 -462
  48. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -6
  49. package/lib/dependencies/AMDPlugin.js +2 -2
  50. package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +13 -13
  51. package/lib/dependencies/CommonJsDependencyHelpers.js +39 -0
  52. package/lib/dependencies/CommonJsExportsDependency.js +47 -2
  53. package/lib/dependencies/CommonJsExportsParserPlugin.js +10 -10
  54. package/lib/dependencies/CommonJsImportsParserPlugin.js +18 -18
  55. package/lib/dependencies/CommonJsPlugin.js +2 -2
  56. package/lib/dependencies/CommonJsRequireDependency.js +26 -1
  57. package/lib/dependencies/CommonJsSelfReferenceDependency.js +32 -1
  58. package/lib/dependencies/ContextDependencyHelpers.js +3 -3
  59. package/lib/dependencies/CreateRequireParserPlugin.js +6 -6
  60. package/lib/dependencies/CssIcssExportDependency.js +162 -71
  61. package/lib/dependencies/CssUrlDependency.js +35 -4
  62. package/lib/dependencies/ExternalModuleInitFragmentDependency.js +2 -2
  63. package/lib/dependencies/HarmonyCompatibilityDependency.js +32 -14
  64. package/lib/dependencies/HarmonyDetectionParserPlugin.js +43 -13
  65. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +7 -19
  66. package/lib/dependencies/HarmonyImportDependency.js +12 -1
  67. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +11 -11
  68. package/lib/dependencies/HarmonyModulesPlugin.js +6 -0
  69. package/lib/dependencies/HarmonyTopLevelThisParserPlugin.js +1 -1
  70. package/lib/dependencies/HtmlEntryDependency.js +797 -172
  71. package/lib/dependencies/HtmlSourceDependency.js +26 -0
  72. package/lib/dependencies/ImportDependency.js +43 -1
  73. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +31 -18
  74. package/lib/dependencies/ImportMetaContextPlugin.js +38 -7
  75. package/lib/dependencies/ImportMetaGlobDependency.js +81 -0
  76. package/lib/dependencies/ImportMetaGlobDependencyParserPlugin.js +78 -0
  77. package/lib/dependencies/ImportMetaGlobHelpers.js +660 -0
  78. package/lib/dependencies/ImportMetaPlugin.js +532 -180
  79. package/lib/dependencies/ImportMetaResolveDependency.js +90 -0
  80. package/lib/dependencies/ImportParserPlugin.js +80 -17
  81. package/lib/dependencies/ImportPhase.js +10 -8
  82. package/lib/dependencies/LoaderPlugin.js +5 -1
  83. package/lib/dependencies/RequireContextDependencyParserPlugin.js +1 -1
  84. package/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +3 -6
  85. package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +4 -11
  86. package/lib/dependencies/SystemPlugin.js +2 -4
  87. package/lib/dependencies/TopLevelAwaitDependency.js +93 -0
  88. package/lib/dependencies/URLDependency.js +141 -21
  89. package/lib/dependencies/{WorkerPlugin.js → WorkerAndWorkletPlugin.js} +324 -112
  90. package/lib/dependencies/WorkerDependency.js +86 -18
  91. package/lib/dependencies/WorkletDependency.js +278 -0
  92. package/lib/dll/DelegatedModuleFactoryPlugin.js +19 -0
  93. package/lib/errors/EnvironmentNotSupportAsyncWarning.js +1 -1
  94. package/lib/errors/LoaderLoadingError.js +20 -0
  95. package/lib/errors/ModuleParseError.js +39 -22
  96. package/lib/errors/NodeStuffInWebError.js +1 -1
  97. package/lib/esm/ModuleChunkFormatPlugin.js +47 -14
  98. package/lib/esm/ModuleChunkLoadingPlugin.js +8 -1
  99. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +29 -1
  100. package/lib/hmr/lazyCompilationBackend.js +32 -8
  101. package/lib/html/HtmlGenerator.js +887 -15
  102. package/lib/html/HtmlModule.js +2 -2
  103. package/lib/html/HtmlModulesPlugin.js +610 -166
  104. package/lib/html/HtmlParser.js +603 -247
  105. package/lib/html/favicon.svg +1 -0
  106. package/lib/html/syntax.js +1264 -661
  107. package/lib/ids/IdHelpers.js +1 -1
  108. package/lib/index.js +6 -0
  109. package/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js +4 -5
  110. package/lib/javascript/BasicEvaluatedExpression.js +366 -48
  111. package/lib/javascript/JavascriptGenerator.js +197 -49
  112. package/lib/javascript/JavascriptModulesPlugin.js +178 -24
  113. package/lib/javascript/JavascriptParser.js +701 -355
  114. package/lib/javascript/JavascriptParserHelpers.js +3 -6
  115. package/lib/javascript/syntax.js +4380 -0
  116. package/lib/library/ModuleLibraryPlugin.js +38 -1
  117. package/lib/loaders/LoaderRunner.js +714 -0
  118. package/lib/loaders/loadLoader.js +100 -0
  119. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +19 -0
  120. package/lib/node/ReadFileCompileWasmPlugin.js +14 -0
  121. package/lib/optimize/ConcatenatedModule.js +120 -16
  122. package/lib/optimize/FlagIncludedChunksPlugin.js +13 -3
  123. package/lib/optimize/InnerGraphPlugin.js +50 -30
  124. package/lib/optimize/ModuleConcatenationPlugin.js +69 -3
  125. package/lib/optimize/SideEffectsFlagPlugin.js +86 -43
  126. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +15 -0
  127. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +24 -13
  128. package/lib/prefetch/ResourceHintPlugin.js +663 -0
  129. package/lib/prefetch/ResourceHintRuntimeModule.js +149 -0
  130. package/lib/prefetch/StartupAssetHintRuntimeModule.js +210 -0
  131. package/lib/prefetch/parseResourceHintOptions.js +103 -0
  132. package/lib/rules/RuleSetCompiler.js +114 -0
  133. package/lib/runtime/AsyncModuleGeneratorRuntimeModule.js +56 -0
  134. package/lib/runtime/AsyncModuleRuntimeModule.js +24 -8
  135. package/lib/runtime/AutoPublicPathRuntimeModule.js +7 -1
  136. package/lib/runtime/CommonJsWrapRuntimeModule.js +37 -0
  137. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +59 -0
  138. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +204 -8
  139. package/lib/runtime/SetAnonymousDefaultNameRuntimeModule.js +4 -1
  140. package/lib/schemes/HttpUriPlugin.js +40 -1
  141. package/lib/schemes/VirtualUrlPlugin.js +1 -2
  142. package/lib/serialization/FileMiddleware.js +29 -3
  143. package/lib/stats/DefaultStatsFactoryPlugin.js +32 -3
  144. package/lib/stats/DefaultStatsPresetPlugin.js +2 -0
  145. package/lib/stats/DefaultStatsPrinterPlugin.js +12 -2
  146. package/lib/stats/StatsFactory.js +15 -8
  147. package/lib/url/URLParserPlugin.js +66 -13
  148. package/lib/util/AsyncQueue.js +122 -72
  149. package/lib/util/SetHelpers.js +1 -1
  150. package/lib/util/StackedMap.js +122 -48
  151. package/lib/util/binarySearchBounds.js +2 -2
  152. package/lib/util/concatenate.js +40 -14
  153. package/lib/util/createHooksRegistry.js +13 -5
  154. package/lib/util/dataURL.js +2 -2
  155. package/lib/util/deterministicGrouping.js +7 -5
  156. package/lib/util/formatSize.js +6 -2
  157. package/lib/util/globUtils.js +710 -0
  158. package/lib/util/identifier.js +23 -6
  159. package/lib/util/implicitTypeLoaderFallback.js +62 -0
  160. package/lib/util/internalSerializables.js +88 -71
  161. package/lib/util/magicComment.js +121 -25
  162. package/lib/util/numberHash.js +2 -2
  163. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +24 -18
  164. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +33 -22
  165. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +79 -16
  166. package/lib/web/FetchCompileAsyncWasmPlugin.js +19 -0
  167. package/lib/web/FetchCompileWasmPlugin.js +14 -0
  168. package/lib/web/JsonpChunkLoadingRuntimeModule.js +2 -0
  169. package/lib/webpack.js +65 -0
  170. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +3 -0
  171. package/module.d.ts +21 -0
  172. package/package.json +13 -12
  173. package/schemas/WebpackOptions.check.js +1 -1
  174. package/schemas/WebpackOptions.json +759 -36
  175. package/schemas/plugins/HtmlParserOptions.check.js +1 -1
  176. package/schemas/plugins/ProgressPlugin.check.js +1 -1
  177. package/schemas/plugins/ProgressPlugin.json +17 -1
  178. package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
  179. package/schemas/plugins/container/ContainerReferencePlugin.json +1 -0
  180. package/schemas/plugins/container/ExternalsType.check.js +1 -1
  181. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  182. package/schemas/plugins/container/ModuleFederationPlugin.json +1 -0
  183. package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +1 -1
  184. package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
  185. package/schemas/plugins/css/CssParserOptions.check.js +1 -1
  186. package/types.d.ts +1213 -96
@@ -57,14 +57,14 @@ const SystemPlugin = require("./dependencies/SystemPlugin");
57
57
 
58
58
  const URLPlugin = require("./dependencies/URLPlugin");
59
59
 
60
- const WorkerPlugin = require("./dependencies/WorkerPlugin");
60
+ const WorkerAndWorkletPlugin = require("./dependencies/WorkerAndWorkletPlugin");
61
61
 
62
62
  const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
63
- const JavascriptParser = require("./javascript/JavascriptParser");
64
63
 
65
64
  const JsonModulesPlugin = require("./json/JsonModulesPlugin");
66
65
 
67
66
  const ChunkPrefetchPreloadPlugin = require("./prefetch/ChunkPrefetchPreloadPlugin");
67
+ const ResourceHintPlugin = require("./prefetch/ResourceHintPlugin");
68
68
 
69
69
  const DataUriPlugin = require("./schemes/DataUriPlugin");
70
70
  const FileUriPlugin = require("./schemes/FileUriPlugin");
@@ -241,6 +241,7 @@ class WebpackOptionsApply extends OptionsApply {
241
241
  }
242
242
 
243
243
  new ChunkPrefetchPreloadPlugin().apply(compiler);
244
+ new ResourceHintPlugin(options.output.resourceHints).apply(compiler);
244
245
 
245
246
  if (typeof options.output.chunkFormat === "string") {
246
247
  switch (options.output.chunkFormat) {
@@ -529,20 +530,6 @@ class WebpackOptionsApply extends OptionsApply {
529
530
  new HttpUriPlugin(httpOptions).apply(compiler);
530
531
  }
531
532
 
532
- if (
533
- !(
534
- /** @type {typeof JavascriptParser & { __importPhasesExtended?: true }} */
535
- (JavascriptParser).__importPhasesExtended
536
- ) &&
537
- (options.experiments.deferImport || options.experiments.sourceImport)
538
- ) {
539
- const importPhases = require("acorn-import-phases");
540
-
541
- JavascriptParser.extend(importPhases({ source: true, defer: true }));
542
- /** @type {typeof JavascriptParser & { __importPhasesExtended?: true }} */
543
- (JavascriptParser).__importPhasesExtended = true;
544
- }
545
-
546
533
  new EntryOptionPlugin().apply(compiler);
547
534
  compiler.hooks.entryOption.call(options.context, options.entry);
548
535
 
@@ -584,11 +571,12 @@ class WebpackOptionsApply extends OptionsApply {
584
571
  new SystemPlugin().apply(compiler);
585
572
  new ImportMetaPlugin().apply(compiler);
586
573
  new URLPlugin().apply(compiler);
587
- new WorkerPlugin(
574
+ new WorkerAndWorkletPlugin(
588
575
  options.output.workerChunkLoading,
589
576
  options.output.workerWasmLoading,
590
577
  options.output.module,
591
- options.output.workerPublicPath
578
+ options.output.workerPublicPath,
579
+ options.experiments.futureDefaults
592
580
  ).apply(compiler);
593
581
 
594
582
  new DefaultStatsFactoryPlugin().apply(compiler);
@@ -805,10 +793,27 @@ class WebpackOptionsApply extends OptionsApply {
805
793
  const DefinePlugin = require("./DefinePlugin");
806
794
 
807
795
  const defValue = JSON.stringify(options.optimization.nodeEnv);
796
+ const development = options.mode === "development";
797
+ // SSR = server-only build (node, not browser); a universal (node+browser) target is null here, so not SSR.
798
+ const ssr =
799
+ compiler.platform.node === true && compiler.platform.browser !== true;
800
+ const { publicPath } = options.output;
801
+ // Vite-compatible base URL; fall back to "/" for runtime ("auto") or empty publicPath.
802
+ const baseUrl =
803
+ typeof publicPath === "string" &&
804
+ publicPath !== "auto" &&
805
+ publicPath !== ""
806
+ ? publicPath
807
+ : "/";
808
808
 
809
809
  new DefinePlugin({
810
810
  "process.env.NODE_ENV": defValue,
811
- "import.meta.env.NODE_ENV": defValue
811
+ "import.meta.env.NODE_ENV": defValue,
812
+ "import.meta.env.MODE": defValue,
813
+ "import.meta.env.DEV": JSON.stringify(development),
814
+ "import.meta.env.PROD": JSON.stringify(!development),
815
+ "import.meta.env.SSR": JSON.stringify(ssr),
816
+ "import.meta.env.BASE_URL": JSON.stringify(baseUrl)
812
817
  }).apply(compiler);
813
818
  }
814
819
  if (options.optimization.minimize) {
@@ -23,7 +23,10 @@ const {
23
23
  JAVASCRIPT_TYPES,
24
24
  NO_TYPES
25
25
  } = require("../ModuleSourceTypeConstants");
26
- const { ASSET_MODULE_TYPE } = require("../ModuleTypeConstants");
26
+ const {
27
+ ASSET_MODULE_TYPE,
28
+ ASSET_MODULE_TYPE_WEBMANIFEST
29
+ } = require("../ModuleTypeConstants");
27
30
  const RuntimeGlobals = require("../RuntimeGlobals");
28
31
  const createHash = require("../util/createHash");
29
32
  const { makePathsRelative } = require("../util/identifier");
@@ -45,6 +48,7 @@ const getMimeTypes = memoize(() => require("../util/mimeTypes"));
45
48
  /** @typedef {import("../../declarations/WebpackOptions").AssetResourceGeneratorOptions} AssetResourceGeneratorOptions */
46
49
  /** @typedef {import("../../declarations/WebpackOptions").RawPublicPath} RawPublicPath */
47
50
  /** @typedef {import("../ChunkGraph")} ChunkGraph */
51
+ /** @typedef {import("../Compilation")} Compilation */
48
52
  /** @typedef {import("../Compilation").AssetInfo} AssetInfo */
49
53
  /** @typedef {import("../Generator").GenerateContext} GenerateContext */
50
54
  /** @typedef {import("../Generator").UpdateHashContext} UpdateHashContext */
@@ -56,12 +60,28 @@ const getMimeTypes = memoize(() => require("../util/mimeTypes"));
56
60
  /** @typedef {import("../Module").SourceTypes} SourceTypes */
57
61
  /** @typedef {import("../ModuleGraph")} ModuleGraph */
58
62
  /** @typedef {import("../NormalModule")} NormalModule */
63
+ /** @typedef {import("../dependencies/URLDependency")} URLDependency */
59
64
  /** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
60
65
  /** @typedef {import("../util/Hash")} Hash */
61
66
  /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
62
67
 
63
68
  /** @typedef {(source: string | Buffer, context: { filename: string, module: Module }) => string} DataUrlFunction */
64
69
 
70
+ // slash separating a module type from its subtype (`css/auto` -> `css`)
71
+ const TYPE_SEPARATOR_CHAR_CODE = 47;
72
+
73
+ /**
74
+ * Whether a module type's prefix (the part before `/`) equals `prefix`, without
75
+ * allocating the substring `type.split("/")[0]` would produce.
76
+ * @param {string} type module type (e.g. `javascript/auto`)
77
+ * @param {string} prefix expected prefix (e.g. `javascript`)
78
+ * @returns {boolean} whether the type's prefix equals `prefix`
79
+ */
80
+ const typePrefixEquals = (type, prefix) =>
81
+ type.startsWith(prefix) &&
82
+ (type.length === prefix.length ||
83
+ type.charCodeAt(prefix.length) === TYPE_SEPARATOR_CHAR_CODE);
84
+
65
85
  /**
66
86
  * Merges maybe arrays.
67
87
  * @template T
@@ -156,14 +176,10 @@ const encodeDataUri = (encoding, source) => {
156
176
  case false: {
157
177
  const content = source.source();
158
178
 
159
- if (typeof content !== "string") {
160
- encodedContent = content.toString("utf8");
161
- }
179
+ encodedContent =
180
+ typeof content === "string" ? content : content.toString("utf8");
162
181
 
163
- encodedContent = encodeURIComponent(
164
- /** @type {string} */
165
- (encodedContent)
166
- ).replace(
182
+ encodedContent = encodeURIComponent(encodedContent).replace(
167
183
  /[!'()*]/g,
168
184
  (character) =>
169
185
  `%${/** @type {number} */ (character.codePointAt(0)).toString(16)}`
@@ -192,9 +208,9 @@ const decodeDataUriContent = (encoding, content) => {
192
208
 
193
209
  // If we can't decode return the original body
194
210
  try {
195
- return Buffer.from(decodeURIComponent(content), "ascii");
211
+ return Buffer.from(decodeURIComponent(content), "utf8");
196
212
  } catch (_) {
197
- return Buffer.from(content, "ascii");
213
+ return Buffer.from(content, "utf8");
198
214
  }
199
215
  };
200
216
 
@@ -209,6 +225,7 @@ class AssetGenerator extends Generator {
209
225
  * @param {RawPublicPath=} publicPath override for output.assetModulePublicPath
210
226
  * @param {AssetModuleOutputPath=} outputPath the output path for the emitted file which is not included in the runtime import
211
227
  * @param {boolean=} emit generate output asset
228
+ * @param {Compilation=} compilation the compilation (`getTypes` has no generate context, so the analyzable-output decision reads `output.module`/`output.publicPath` from here)
212
229
  */
213
230
  constructor(
214
231
  moduleGraph,
@@ -216,7 +233,8 @@ class AssetGenerator extends Generator {
216
233
  filename,
217
234
  publicPath,
218
235
  outputPath,
219
- emit
236
+ emit,
237
+ compilation
220
238
  ) {
221
239
  super();
222
240
  /** @type {AssetGeneratorOptions["dataUrl"] | undefined} */
@@ -229,6 +247,8 @@ class AssetGenerator extends Generator {
229
247
  this.outputPath = outputPath;
230
248
  /** @type {boolean | undefined} */
231
249
  this.emit = emit;
250
+ /** @type {Compilation | undefined} */
251
+ this._compilation = compilation;
232
252
  /** @type {ModuleGraph} */
233
253
  this._moduleGraph = moduleGraph;
234
254
  }
@@ -697,36 +717,101 @@ class AssetGenerator extends Generator {
697
717
  }
698
718
  }
699
719
 
720
+ /**
721
+ * Returns whether the effective publicPath yields a chunk-independent absolute
722
+ * url (scheme-relative, protocol-absolute, or root-absolute) with no hash tokens.
723
+ * Only then is a `new URL` literal correct for every chunk the asset is shared by,
724
+ * so the `module.exports` wrapper can be dropped in favor of the `asset-url` type.
725
+ * @returns {boolean} true when the publicPath is a token-free absolute string
726
+ */
727
+ _hasAbsolutePublicPath() {
728
+ const publicPath =
729
+ this.publicPath !== undefined
730
+ ? this.publicPath
731
+ : this._compilation && this._compilation.outputOptions.publicPath;
732
+ return (
733
+ typeof publicPath === "string" &&
734
+ !publicPath.includes("[") &&
735
+ (publicPath.startsWith("/") ||
736
+ /^[a-z][a-z\d+\-.]*:\/\//i.test(publicPath))
737
+ );
738
+ }
739
+
700
740
  /**
701
741
  * Returns the source types available for this module.
702
742
  * @param {NormalModule} module fresh module
703
743
  * @returns {SourceTypes} available types (do not mutate)
704
744
  */
705
745
  getTypes(module) {
706
- /** @type {Set<string>} */
707
- const sourceTypes = new Set();
708
746
  const connections = this._moduleGraph.getIncomingConnections(module);
709
747
 
748
+ // Only ESM output can drop a bare `new URL()` to an analyzable asset-url; without
749
+ // it every JS consumer needs the wrapper, so skip the per-connection dependency
750
+ // probe entirely and keep the pre-existing cheap path.
751
+ const isModule = Boolean(
752
+ this._compilation && this._compilation.outputOptions.module
753
+ );
754
+
755
+ // Collapse the incoming origin types into flags instead of a Set of prefixes:
756
+ // on assets shared by many modules this loop runs millions of times, so avoid
757
+ // the per-connection `split("/")` allocation and stop early when the result is fixed.
758
+ let hasOrigin = false;
759
+ // JS origin that needs the `module.exports = …` wrapper (not a `new URL` ref).
760
+ let hasOtherJs = false;
761
+ // JS origin via `new URL(…, import.meta.url)` — droppable to `asset-url` in ESM.
762
+ let hasUrlJs = false;
763
+ // A manifest embeds a bare URL string (like css/html), so its icons must
764
+ // resolve to `ASSET_URL_TYPE` rather than the JS runtime form.
765
+ let hasUrl = false;
710
766
  for (const connection of connections) {
711
- if (!connection.originModule) {
767
+ const originModule = connection.originModule;
768
+ if (!originModule) {
712
769
  continue;
713
770
  }
714
-
715
- sourceTypes.add(connection.originModule.type.split("/")[0]);
771
+ hasOrigin = true;
772
+ const originType = originModule.type;
773
+ if (originType === ASSET_MODULE_TYPE_WEBMANIFEST) {
774
+ hasUrl = true;
775
+ } else if (typePrefixEquals(originType, JAVASCRIPT_TYPE)) {
776
+ // Only a bare `new URL()` becomes the analyzable literal; relative and
777
+ // prefetch/preload refs keep the runtime form and still need the wrapper.
778
+ const dependency = isModule && connection.dependency;
779
+ if (
780
+ dependency &&
781
+ dependency.type === "new URL()" &&
782
+ !(/** @type {URLDependency} */ (dependency).relative) &&
783
+ !(/** @type {URLDependency} */ (dependency).prefetch) &&
784
+ !(/** @type {URLDependency} */ (dependency).preload)
785
+ ) {
786
+ hasUrlJs = true;
787
+ } else {
788
+ hasOtherJs = true;
789
+ }
790
+ } else if (
791
+ typePrefixEquals(originType, CSS_TYPE) ||
792
+ typePrefixEquals(originType, HTML_TYPE)
793
+ ) {
794
+ hasUrl = true;
795
+ }
796
+ // Once a non-URL JS consumer and a url consumer coexist the result is fixed.
797
+ if (hasOtherJs && hasUrl) break;
716
798
  }
717
799
 
800
+ const jsAsAssetUrl =
801
+ isModule && hasUrlJs && !hasOtherJs && this._hasAbsolutePublicPath();
802
+ // JS wrapper is needed for non-URL JS consumers, or URL consumers not dropped to asset-url.
803
+ const wantJs = hasOtherJs || (hasUrlJs && !jsAsAssetUrl);
804
+ const wantUrl = hasUrl || jsAsAssetUrl;
805
+
718
806
  if (
719
807
  (module.buildInfo &&
720
808
  /** @type {AssetModuleBuildInfo} */ (module.buildInfo).dataUrl) ||
721
809
  this.emit === false
722
810
  ) {
723
- if (sourceTypes.size > 0) {
724
- if (
725
- sourceTypes.has(JAVASCRIPT_TYPE) &&
726
- (sourceTypes.has(CSS_TYPE) || sourceTypes.has(HTML_TYPE))
727
- ) {
811
+ if (hasOrigin) {
812
+ if (wantJs && wantUrl) {
728
813
  return JAVASCRIPT_AND_ASSET_URL_TYPES;
729
- } else if (sourceTypes.has(CSS_TYPE) || sourceTypes.has(HTML_TYPE)) {
814
+ } else if (wantUrl) {
730
815
  return ASSET_URL_TYPES;
731
816
  }
732
817
  return JAVASCRIPT_TYPES;
@@ -735,13 +820,10 @@ class AssetGenerator extends Generator {
735
820
  return NO_TYPES;
736
821
  }
737
822
 
738
- if (sourceTypes.size > 0) {
739
- if (
740
- sourceTypes.has(JAVASCRIPT_TYPE) &&
741
- (sourceTypes.has(CSS_TYPE) || sourceTypes.has(HTML_TYPE))
742
- ) {
823
+ if (hasOrigin) {
824
+ if (wantJs && wantUrl) {
743
825
  return ASSET_AND_JAVASCRIPT_AND_ASSET_URL_TYPES;
744
- } else if (sourceTypes.has(CSS_TYPE) || sourceTypes.has(HTML_TYPE)) {
826
+ } else if (wantUrl) {
745
827
  return ASSET_AND_ASSET_URL_TYPES;
746
828
  }
747
829
  return ASSET_AND_JAVASCRIPT_TYPES;
@@ -5,15 +5,21 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ const { RawSource } = require("webpack-sources");
9
+ const HotUpdateChunk = require("../HotUpdateChunk");
8
10
  const {
9
11
  ASSET_MODULE_TYPE,
10
12
  ASSET_MODULE_TYPE_BYTES,
11
13
  ASSET_MODULE_TYPE_INLINE,
12
14
  ASSET_MODULE_TYPE_RESOURCE,
13
- ASSET_MODULE_TYPE_SOURCE
15
+ ASSET_MODULE_TYPE_SOURCE,
16
+ ASSET_MODULE_TYPE_WEBMANIFEST
14
17
  } = require("../ModuleTypeConstants");
15
18
  const { compareModulesByFullName } = require("../util/comparators");
19
+ const createHash = require("../util/createHash");
20
+ const { getUndoPath } = require("../util/identifier");
16
21
  const memoize = require("../util/memoize");
22
+ const { PUBLIC_PATH_AUTO } = require("../util/publicPathPlaceholder");
17
23
 
18
24
  /** @typedef {import("webpack-sources").Source} Source */
19
25
  /** @typedef {import("schema-utils").Schema} Schema */
@@ -57,8 +63,14 @@ const getAssetSourceGenerator = memoize(() =>
57
63
  );
58
64
  const getAssetBytesGenerator = memoize(() => require("./AssetBytesGenerator"));
59
65
  const getAssetModule = memoize(() => require("./AssetModule"));
66
+ const getWebManifestParser = memoize(() => require("./WebManifestParser"));
67
+ const getWebManifestGenerator = memoize(() =>
68
+ require("./WebManifestGenerator")
69
+ );
60
70
 
61
71
  const type = ASSET_MODULE_TYPE;
72
+ // Source type produced by `WebManifestGenerator` (its emitted, rewritten JSON).
73
+ const WEBMANIFEST_SOURCE_TYPE = "webmanifest";
62
74
  const PLUGIN_NAME = "AssetModulesPlugin";
63
75
 
64
76
  /**
@@ -240,7 +252,8 @@ class AssetModulesPlugin {
240
252
  filename,
241
253
  publicPath,
242
254
  outputPath,
243
- generatorOptions.emit !== false
255
+ generatorOptions.emit !== false,
256
+ compilation
244
257
  );
245
258
  });
246
259
  }
@@ -353,6 +366,83 @@ class AssetModulesPlugin {
353
366
  return result;
354
367
  });
355
368
 
369
+ // A Web App Manifest (`asset/webmanifest`, gated on `experiments.html`)
370
+ // parses/emits like an asset, but its icon URLs are rewritten in place
371
+ // and the emitted file needs its own per-file public-path resolution.
372
+ if (compilation.options.experiments.html) {
373
+ normalModuleFactory.hooks.createParser
374
+ .for(ASSET_MODULE_TYPE_WEBMANIFEST)
375
+ .tap(PLUGIN_NAME, () => {
376
+ const WebManifestParser = getWebManifestParser();
377
+
378
+ return new WebManifestParser();
379
+ });
380
+ normalModuleFactory.hooks.createGenerator
381
+ .for(ASSET_MODULE_TYPE_WEBMANIFEST)
382
+ .tap(PLUGIN_NAME, () => {
383
+ const WebManifestGenerator = getWebManifestGenerator();
384
+
385
+ return new WebManifestGenerator(compilation.moduleGraph);
386
+ });
387
+
388
+ compilation.hooks.renderManifest.tap(
389
+ PLUGIN_NAME,
390
+ (result, options) => {
391
+ const { chunk, codeGenerationResults } = options;
392
+ if (chunk instanceof HotUpdateChunk) return result;
393
+ const { chunkGraph, outputOptions } = compilation;
394
+ const modules =
395
+ chunkGraph.getOrderedChunkModulesIterableBySourceType(
396
+ chunk,
397
+ WEBMANIFEST_SOURCE_TYPE,
398
+ compareModulesByFullName(compilation.compiler)
399
+ );
400
+ if (!modules) return result;
401
+ for (const module of modules) {
402
+ const codeGenResult = codeGenerationResults.get(
403
+ module,
404
+ chunk.runtime
405
+ );
406
+ const source = codeGenResult.sources.get(
407
+ WEBMANIFEST_SOURCE_TYPE
408
+ );
409
+ if (!source) continue;
410
+ const filename = /** @type {string} */ (
411
+ codeGenResult.data && codeGenResult.data.get("filename")
412
+ );
413
+ if (!filename) continue;
414
+ // Icon URLs resolve relative to the manifest's own location.
415
+ const undoPath = getUndoPath(
416
+ filename,
417
+ /** @type {string} */ (outputOptions.path),
418
+ false
419
+ );
420
+ const content = source
421
+ .source()
422
+ .toString()
423
+ .split(PUBLIC_PATH_AUTO)
424
+ .join(undoPath);
425
+ const hash = createHash(outputOptions.hashFunction || "md4");
426
+ hash.update(content);
427
+ const contentHash = /** @type {string} */ (
428
+ hash.digest(outputOptions.hashDigest || "hex")
429
+ ).slice(0, outputOptions.hashDigestLength || 20);
430
+ result.push({
431
+ render: () => new RawSource(content),
432
+ filename,
433
+ info: { immutable: true },
434
+ auxiliary: true,
435
+ identifier: `webManifestModule${chunkGraph.getModuleId(
436
+ module
437
+ )}|${filename}`,
438
+ hash: contentHash
439
+ });
440
+ }
441
+ return result;
442
+ }
443
+ );
444
+ }
445
+
356
446
  compilation.hooks.prepareModuleExecution.tap(
357
447
  PLUGIN_NAME,
358
448
  (options, context) => {
@@ -0,0 +1,164 @@
1
+ /*
2
+ MIT License http://www.opensource.org/licenses/mit-license.php
3
+ Author Alexander Akait @alexander-akait
4
+ */
5
+
6
+ "use strict";
7
+
8
+ const { RawSource, ReplaceSource } = require("webpack-sources");
9
+ const { ASSET_URL_TYPE } = require("../ModuleSourceTypeConstants");
10
+ const createHash = require("../util/createHash");
11
+ const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
12
+ const { PUBLIC_PATH_AUTO } = require("../util/publicPathPlaceholder");
13
+ const AssetGenerator = require("./AssetGenerator");
14
+
15
+ /** @typedef {import("webpack-sources").Source} Source */
16
+ /** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
17
+ /** @typedef {import("../Compilation").DependencyConstructor} DependencyConstructor */
18
+ /** @typedef {import("../Dependency")} Dependency */
19
+ /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
20
+ /** @typedef {import("../Generator").GenerateContext} GenerateContext */
21
+ /** @typedef {import("../Module").SourceType} SourceType */
22
+ /** @typedef {import("../Module").SourceTypes} SourceTypes */
23
+ /** @typedef {import("../ModuleGraph")} ModuleGraph */
24
+ /** @typedef {import("../NormalModule")} NormalModule */
25
+
26
+ const WEBMANIFEST_TYPE = "webmanifest";
27
+ const WEBMANIFEST_TYPES = new Set([WEBMANIFEST_TYPE]);
28
+ const WEBMANIFEST_AND_ASSET_URL_TYPES = new Set([
29
+ WEBMANIFEST_TYPE,
30
+ ASSET_URL_TYPE
31
+ ]);
32
+
33
+ /**
34
+ * A Web App Manifest is a "typed asset": a variant of `AssetGenerator` (reusing
35
+ * its filename/hash and `ASSET_URL_TYPE` machinery) whose emitted content is the
36
+ * raw JSON with each icon `src` rewritten to its bundled asset URL. The manifest
37
+ * itself is emitted — with its own URL exposed to the referencing
38
+ * `<link rel="manifest">` — by `AssetModulesPlugin`'s `renderManifest`, which
39
+ * resolves the per-file public path (like the CSS/HTML plugins).
40
+ */
41
+ class WebManifestGenerator extends AssetGenerator {
42
+ /**
43
+ * @param {ModuleGraph} moduleGraph the module graph
44
+ */
45
+ constructor(moduleGraph) {
46
+ super(moduleGraph, undefined, undefined, undefined, undefined, true);
47
+ }
48
+
49
+ /**
50
+ * Returns the source types available for this module.
51
+ * @param {NormalModule} module fresh module
52
+ * @returns {SourceTypes} available types (do not mutate)
53
+ */
54
+ getTypes(module) {
55
+ // Expose the manifest's own URL only when it is referenced (from HTML).
56
+ const connections = this._moduleGraph.getIncomingConnections(module);
57
+ const referenced = !connections[Symbol.iterator]().next().done;
58
+ return referenced ? WEBMANIFEST_AND_ASSET_URL_TYPES : WEBMANIFEST_TYPES;
59
+ }
60
+
61
+ /**
62
+ * Returns the estimated size for the requested source type.
63
+ * @param {NormalModule} module the module
64
+ * @param {SourceType=} type source type
65
+ * @returns {number} estimate size of the module
66
+ */
67
+ getSize(module, type) {
68
+ const originalSource = module.originalSource();
69
+ return originalSource ? originalSource.size() : 0;
70
+ }
71
+
72
+ /**
73
+ * Rewrites each icon `src` in the raw manifest to its emitted asset URL.
74
+ * @param {NormalModule} module the manifest module
75
+ * @param {GenerateContext} generateContext context for generate
76
+ * @returns {string} the rewritten manifest content
77
+ */
78
+ _rewrite(module, generateContext) {
79
+ const originalSource = /** @type {Source} */ (module.originalSource());
80
+ const source = new ReplaceSource(originalSource);
81
+ // `HtmlSourceDependency.Template` only reads `moduleGraph`/`runtimeTemplate`/
82
+ // `codeGenerationResults`; the rest is filled to satisfy the context shape.
83
+ /** @type {DependencyTemplateContext} */
84
+ const templateContext = {
85
+ runtimeTemplate: generateContext.runtimeTemplate,
86
+ dependencyTemplates: generateContext.dependencyTemplates,
87
+ moduleGraph: generateContext.moduleGraph,
88
+ chunkGraph: generateContext.chunkGraph,
89
+ module,
90
+ runtime: generateContext.runtime,
91
+ runtimeRequirements: generateContext.runtimeRequirements,
92
+ concatenationScope: generateContext.concatenationScope,
93
+ codeGenerationResults:
94
+ /** @type {CodeGenerationResults} */
95
+ (generateContext.codeGenerationResults),
96
+ initFragments: [],
97
+ chunkInitFragments: []
98
+ };
99
+ for (const dependency of module.dependencies) {
100
+ const constructor = /** @type {DependencyConstructor} */ (
101
+ dependency.constructor
102
+ );
103
+ const template = templateContext.dependencyTemplates.get(constructor);
104
+ if (!template) {
105
+ throw new Error(
106
+ `No template for dependency: ${dependency.constructor.name}`
107
+ );
108
+ }
109
+ template.apply(dependency, source, templateContext);
110
+ }
111
+ return /** @type {string} */ (source.source());
112
+ }
113
+
114
+ /**
115
+ * Generates generated code for this runtime module.
116
+ * @param {NormalModule} module module for which the code should be generated
117
+ * @param {GenerateContext} generateContext context for generate
118
+ * @returns {Source | null} generated code
119
+ */
120
+ generate(module, generateContext) {
121
+ if (!module.originalSource()) return new RawSource("");
122
+
123
+ const { type, getData } = generateContext;
124
+ const content = this._rewrite(module, generateContext);
125
+
126
+ // Hash the *rewritten* content (so the filename tracks the icons) and reuse
127
+ // the asset filename machinery, so a plain (icon-less) manifest emits like a
128
+ // plain asset (`output.assetModuleFilename`, `[name]`, `[ext]`).
129
+ const outputOptions = generateContext.runtimeTemplate.outputOptions;
130
+ const hash = createHash(outputOptions.hashFunction);
131
+ if (outputOptions.hashSalt) hash.update(outputOptions.hashSalt);
132
+ hash.update(content);
133
+ const fullContentHash = /** @type {string} */ (
134
+ hash.digest(outputOptions.hashDigest)
135
+ );
136
+ const contentHash = nonNumericOnlyHash(
137
+ fullContentHash,
138
+ /** @type {number} */ (outputOptions.hashDigestLength)
139
+ );
140
+ const { filename, assetInfo } = AssetGenerator.getFilenameWithInfo(
141
+ module,
142
+ { filename: undefined, outputPath: undefined },
143
+ generateContext,
144
+ contentHash,
145
+ fullContentHash
146
+ );
147
+
148
+ const data = getData ? getData() : undefined;
149
+ if (data) {
150
+ data.set("filename", filename);
151
+ data.set("assetInfo", assetInfo);
152
+ if (type === ASSET_URL_TYPE) {
153
+ data.set("url", {
154
+ ...data.get("url"),
155
+ [ASSET_URL_TYPE]: PUBLIC_PATH_AUTO + filename
156
+ });
157
+ }
158
+ }
159
+
160
+ return type === ASSET_URL_TYPE ? null : new RawSource(content);
161
+ }
162
+ }
163
+
164
+ module.exports = WebManifestGenerator;