webpack 5.108.4 → 5.109.1

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 (200) hide show
  1. package/bin/webpack.js +12 -0
  2. package/lib/APIPlugin.js +48 -29
  3. package/lib/CacheFacade.js +8 -0
  4. package/lib/ChunkGraph.js +17 -15
  5. package/lib/ChunkGroup.js +1 -0
  6. package/lib/CompatibilityPlugin.js +62 -29
  7. package/lib/Compilation.js +58 -6
  8. package/lib/ConstPlugin.js +11 -11
  9. package/lib/ContextModule.js +239 -6
  10. package/lib/ContextModuleFactory.js +139 -14
  11. package/lib/DefinePlugin.js +433 -112
  12. package/lib/Entrypoint.js +1 -1
  13. package/lib/ExportsInfo.js +22 -5
  14. package/lib/ExportsInfoApiPlugin.js +2 -2
  15. package/lib/ExternalModule.js +107 -5
  16. package/lib/ExternalModuleFactoryPlugin.js +20 -1
  17. package/lib/FileSystemInfo.js +256 -100
  18. package/lib/FlagDependencyUsagePlugin.js +33 -12
  19. package/lib/HotModuleReplacementPlugin.js +18 -15
  20. package/lib/InitFragment.js +20 -32
  21. package/lib/Module.js +3 -0
  22. package/lib/ModuleFilenameHelpers.js +2 -2
  23. package/lib/ModuleTypeConstants.js +8 -1
  24. package/lib/MultiCompiler.js +13 -2
  25. package/lib/MultiWatching.js +1 -0
  26. package/lib/NodeStuffPlugin.js +51 -33
  27. package/lib/NormalModule.js +175 -401
  28. package/lib/NormalModuleFactory.js +76 -2
  29. package/lib/ProgressPlugin.js +211 -119
  30. package/lib/ProvidePlugin.js +2 -2
  31. package/lib/RuntimeGlobals.js +47 -0
  32. package/lib/RuntimePlugin.js +67 -107
  33. package/lib/RuntimeTemplate.js +304 -16
  34. package/lib/Template.js +23 -3
  35. package/lib/UseStrictPlugin.js +1 -1
  36. package/lib/WebpackIsIncludedPlugin.js +1 -1
  37. package/lib/WebpackOptionsApply.js +24 -19
  38. package/lib/asset/AssetGenerator.js +110 -28
  39. package/lib/asset/AssetModulesPlugin.js +92 -2
  40. package/lib/asset/WebManifestGenerator.js +164 -0
  41. package/lib/asset/WebManifestParser.js +130 -0
  42. package/lib/async-modules/AwaitDependenciesInitFragment.js +14 -4
  43. package/lib/async-modules/isGeneratorLowered.js +26 -0
  44. package/lib/cache/PackFileCacheStrategy.js +9 -3
  45. package/lib/config/browserslistTargetHandler.js +40 -0
  46. package/lib/config/defaults.js +219 -29
  47. package/lib/config/normalization.js +26 -0
  48. package/lib/config/target.js +9 -1
  49. package/lib/container/HoistContainerReferencesPlugin.js +47 -55
  50. package/lib/css/CssGenerator.js +40 -31
  51. package/lib/css/CssLoadingRuntimeModule.js +5 -1
  52. package/lib/css/CssModulesPlugin.js +13 -0
  53. package/lib/css/CssParser.js +729 -313
  54. package/lib/css/syntax.js +1371 -861
  55. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -6
  56. package/lib/dependencies/AMDPlugin.js +2 -2
  57. package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +13 -13
  58. package/lib/dependencies/CommonJsDependencyHelpers.js +39 -0
  59. package/lib/dependencies/CommonJsExportRequireDependency.js +19 -0
  60. package/lib/dependencies/CommonJsExportsDependency.js +47 -2
  61. package/lib/dependencies/CommonJsExportsParserPlugin.js +10 -10
  62. package/lib/dependencies/CommonJsImportsParserPlugin.js +18 -18
  63. package/lib/dependencies/CommonJsPlugin.js +2 -2
  64. package/lib/dependencies/CommonJsRequireDependency.js +26 -1
  65. package/lib/dependencies/CommonJsSelfReferenceDependency.js +32 -1
  66. package/lib/dependencies/ContextDependencyHelpers.js +3 -3
  67. package/lib/dependencies/CreateRequireParserPlugin.js +6 -6
  68. package/lib/dependencies/CssIcssExportDependency.js +162 -71
  69. package/lib/dependencies/CssUrlDependency.js +35 -4
  70. package/lib/dependencies/ExternalModuleInitFragmentDependency.js +2 -2
  71. package/lib/dependencies/HarmonyCompatibilityDependency.js +32 -14
  72. package/lib/dependencies/HarmonyDetectionParserPlugin.js +43 -13
  73. package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +2 -1
  74. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +7 -19
  75. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +17 -9
  76. package/lib/dependencies/HarmonyImportDependency.js +13 -2
  77. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +11 -11
  78. package/lib/dependencies/HarmonyImportSpecifierDependency.js +10 -7
  79. package/lib/dependencies/HarmonyModulesPlugin.js +6 -0
  80. package/lib/dependencies/HarmonyTopLevelThisParserPlugin.js +1 -1
  81. package/lib/dependencies/HtmlEntryDependency.js +797 -172
  82. package/lib/dependencies/HtmlSourceDependency.js +26 -0
  83. package/lib/dependencies/ImportDependency.js +43 -1
  84. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +31 -18
  85. package/lib/dependencies/ImportMetaContextPlugin.js +38 -7
  86. package/lib/dependencies/ImportMetaGlobDependency.js +81 -0
  87. package/lib/dependencies/ImportMetaGlobDependencyParserPlugin.js +78 -0
  88. package/lib/dependencies/ImportMetaGlobHelpers.js +660 -0
  89. package/lib/dependencies/ImportMetaPlugin.js +532 -180
  90. package/lib/dependencies/ImportMetaResolveDependency.js +90 -0
  91. package/lib/dependencies/ImportParserPlugin.js +80 -17
  92. package/lib/dependencies/ImportPhase.js +10 -8
  93. package/lib/dependencies/LoaderPlugin.js +5 -1
  94. package/lib/dependencies/RequireContextDependencyParserPlugin.js +1 -1
  95. package/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +3 -6
  96. package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +4 -11
  97. package/lib/dependencies/SystemPlugin.js +2 -4
  98. package/lib/dependencies/TopLevelAwaitDependency.js +93 -0
  99. package/lib/dependencies/URLDependency.js +141 -21
  100. package/lib/dependencies/{WorkerPlugin.js → WorkerAndWorkletPlugin.js} +324 -112
  101. package/lib/dependencies/WorkerDependency.js +86 -18
  102. package/lib/dependencies/WorkletDependency.js +278 -0
  103. package/lib/dll/DelegatedModuleFactoryPlugin.js +19 -0
  104. package/lib/errors/EnvironmentNotSupportAsyncWarning.js +1 -1
  105. package/lib/errors/LoaderLoadingError.js +20 -0
  106. package/lib/errors/ModuleParseError.js +39 -22
  107. package/lib/errors/NodeStuffInWebError.js +1 -1
  108. package/lib/esm/ModuleChunkFormatPlugin.js +47 -14
  109. package/lib/esm/ModuleChunkLoadingPlugin.js +8 -1
  110. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +29 -1
  111. package/lib/hmr/lazyCompilationBackend.js +51 -12
  112. package/lib/html/HtmlGenerator.js +907 -15
  113. package/lib/html/HtmlModule.js +2 -2
  114. package/lib/html/HtmlModulesPlugin.js +610 -166
  115. package/lib/html/HtmlParser.js +603 -247
  116. package/lib/html/favicon.svg +1 -0
  117. package/lib/html/syntax.js +3451 -2762
  118. package/lib/ids/IdHelpers.js +25 -11
  119. package/lib/index.js +6 -0
  120. package/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js +4 -5
  121. package/lib/javascript/BasicEvaluatedExpression.js +366 -48
  122. package/lib/javascript/JavascriptGenerator.js +197 -49
  123. package/lib/javascript/JavascriptModulesPlugin.js +178 -24
  124. package/lib/javascript/JavascriptParser.js +704 -355
  125. package/lib/javascript/JavascriptParserHelpers.js +3 -6
  126. package/lib/javascript/syntax.js +5037 -0
  127. package/lib/library/ModuleLibraryPlugin.js +38 -1
  128. package/lib/loaders/LoaderRunner.js +714 -0
  129. package/lib/loaders/loadLoader.js +100 -0
  130. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +29 -2
  131. package/lib/node/ReadFileCompileWasmPlugin.js +24 -2
  132. package/lib/optimize/AggressiveMergingPlugin.js +19 -15
  133. package/lib/optimize/ConcatenatedModule.js +139 -31
  134. package/lib/optimize/FlagIncludedChunksPlugin.js +13 -3
  135. package/lib/optimize/InnerGraphPlugin.js +50 -30
  136. package/lib/optimize/ModuleConcatenationPlugin.js +139 -24
  137. package/lib/optimize/SideEffectsFlagPlugin.js +86 -43
  138. package/lib/optimize/SplitChunksPlugin.js +146 -44
  139. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +15 -0
  140. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +24 -13
  141. package/lib/prefetch/ResourceHintPlugin.js +687 -0
  142. package/lib/prefetch/ResourceHintRuntimeModule.js +149 -0
  143. package/lib/prefetch/StartupAssetHintRuntimeModule.js +210 -0
  144. package/lib/prefetch/parseResourceHintOptions.js +103 -0
  145. package/lib/rules/RuleSetCompiler.js +114 -0
  146. package/lib/runtime/AsyncModuleGeneratorRuntimeModule.js +56 -0
  147. package/lib/runtime/AsyncModuleRuntimeModule.js +24 -8
  148. package/lib/runtime/AutoPublicPathRuntimeModule.js +7 -1
  149. package/lib/runtime/CommonJsWrapRuntimeModule.js +37 -0
  150. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +59 -0
  151. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +204 -8
  152. package/lib/runtime/SetAnonymousDefaultNameRuntimeModule.js +4 -1
  153. package/lib/schemes/HttpUriPlugin.js +40 -1
  154. package/lib/schemes/VirtualUrlPlugin.js +1 -2
  155. package/lib/serialization/BinaryMiddleware.js +270 -938
  156. package/lib/serialization/FileMiddleware.js +29 -3
  157. package/lib/stats/DefaultStatsFactoryPlugin.js +229 -194
  158. package/lib/stats/DefaultStatsPresetPlugin.js +2 -0
  159. package/lib/stats/DefaultStatsPrinterPlugin.js +36 -168
  160. package/lib/stats/StatsFactory.js +46 -17
  161. package/lib/url/URLParserPlugin.js +66 -13
  162. package/lib/util/AsyncQueue.js +122 -72
  163. package/lib/util/SetHelpers.js +1 -1
  164. package/lib/util/StackedMap.js +122 -48
  165. package/lib/util/binarySearchBounds.js +2 -2
  166. package/lib/util/concatenate.js +40 -14
  167. package/lib/util/createHooksRegistry.js +13 -5
  168. package/lib/util/dataURL.js +2 -2
  169. package/lib/util/deterministicGrouping.js +7 -5
  170. package/lib/util/formatSize.js +6 -2
  171. package/lib/util/globUtils.js +710 -0
  172. package/lib/util/identifier.js +23 -6
  173. package/lib/util/implicitTypeLoaderFallback.js +62 -0
  174. package/lib/util/internalSerializables.js +88 -71
  175. package/lib/util/magicComment.js +121 -25
  176. package/lib/util/numberHash.js +2 -2
  177. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +24 -18
  178. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +33 -22
  179. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +79 -16
  180. package/lib/web/FetchCompileAsyncWasmPlugin.js +19 -0
  181. package/lib/web/FetchCompileWasmPlugin.js +14 -0
  182. package/lib/web/JsonpChunkLoadingRuntimeModule.js +2 -0
  183. package/lib/webpack.js +65 -0
  184. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +3 -0
  185. package/module.d.ts +21 -0
  186. package/package.json +12 -11
  187. package/schemas/WebpackOptions.check.js +1 -1
  188. package/schemas/WebpackOptions.json +759 -36
  189. package/schemas/plugins/HtmlParserOptions.check.js +1 -1
  190. package/schemas/plugins/ProgressPlugin.check.js +1 -1
  191. package/schemas/plugins/ProgressPlugin.json +17 -1
  192. package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
  193. package/schemas/plugins/container/ContainerReferencePlugin.json +1 -0
  194. package/schemas/plugins/container/ExternalsType.check.js +1 -1
  195. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  196. package/schemas/plugins/container/ModuleFederationPlugin.json +1 -0
  197. package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +1 -1
  198. package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
  199. package/schemas/plugins/css/CssParserOptions.check.js +1 -1
  200. package/types.d.ts +1212 -99
@@ -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;
@@ -0,0 +1,130 @@
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 { parse } = require("acorn");
9
+ const Parser = require("../Parser");
10
+ const HtmlSourceDependency = require("../dependencies/HtmlSourceDependency");
11
+
12
+ /** @typedef {import("estree").Expression} Expression */
13
+ /** @typedef {import("estree").ObjectExpression} ObjectExpression */
14
+ /** @typedef {import("estree").Program} Program */
15
+ /** @typedef {import("estree").Property} Property */
16
+ /** @typedef {import("../Module").BuildInfo} BuildInfo */
17
+ /** @typedef {import("../Parser").ParserState} ParserState */
18
+ /** @typedef {import("../Parser").PreparsedAst} PreparsedAst */
19
+
20
+ // acorn decorates every node with numeric source offsets (absent from the estree types).
21
+ /** @typedef {{ start: number, end: number }} Positioned */
22
+
23
+ // A URL carrying its own scheme (`https:`, `data:`, …) is external; it is only
24
+ // bundled when `experiments.buildHttp` is enabled (like an absolute URL in HTML).
25
+ const ABSOLUTE_URL_SCHEME_REGEXP = /^[a-zA-Z][a-zA-Z\d+\-.]*:/;
26
+ // Manifest members whose array items carry an asset `src` (`icons[].src`,
27
+ // `screenshots[].src`, and `shortcuts[].icons[].src` — the last nests under `icons`).
28
+ const ICON_CONTAINER_KEYS = new Set(["icons", "screenshots"]);
29
+
30
+ /**
31
+ * Parses a Web App Manifest (`.webmanifest`) so its icon/screenshot `src`
32
+ * URLs are bundled as assets. Reuses acorn (JSON is a valid JS expression
33
+ * literal) to recover the exact source offsets, then emits an
34
+ * `HtmlSourceDependency` per URL so the generator can rewrite it in place.
35
+ */
36
+ class WebManifestParser extends Parser {
37
+ /**
38
+ * Parses the provided source and updates the parser state.
39
+ * @param {string | Buffer | PreparsedAst} source the source to parse
40
+ * @param {ParserState} state the parser state
41
+ * @returns {ParserState} the parser state
42
+ */
43
+ parse(source, state) {
44
+ if (Buffer.isBuffer(source)) {
45
+ source = source.toString("utf8");
46
+ } else if (typeof source === "object") {
47
+ throw new Error("webpackAst is unexpected for the WebManifestParser");
48
+ }
49
+ if (source[0] === "") {
50
+ source = source.slice(1);
51
+ }
52
+
53
+ const module = state.module;
54
+ // Without `buildHttp` there is no handler to fetch remote icons, so an
55
+ // absolute URL is left as-is rather than rewritten to an ignored asset.
56
+ const buildHttp = Boolean(state.options.experiments.buildHttp);
57
+
58
+ /** @type {Program | undefined} */
59
+ let ast;
60
+ try {
61
+ // Wrap in parens so the top-level object is an expression, not a block.
62
+ ast = /** @type {Program} */ (
63
+ /** @type {unknown} */ (parse(`(${source})`, { ecmaVersion: "latest" }))
64
+ );
65
+ } catch (_err) {
66
+ // Not valid JSON — leave the file untouched (still emitted as-is).
67
+ ast = undefined;
68
+ }
69
+
70
+ const statement = ast && ast.body[0];
71
+ if (
72
+ statement &&
73
+ statement.type === "ExpressionStatement" &&
74
+ statement.expression.type === "ObjectExpression"
75
+ ) {
76
+ /**
77
+ * @param {ObjectExpression} obj object expression node
78
+ * @param {string | undefined} parentKey key of the array/object this node sits in
79
+ */
80
+ const walk = (obj, parentKey) => {
81
+ for (const prop of obj.properties) {
82
+ if (prop.type !== "Property") continue;
83
+ const key =
84
+ prop.key.type === "Literal" && typeof prop.key.value === "string"
85
+ ? prop.key.value
86
+ : prop.key.type === "Identifier"
87
+ ? prop.key.name
88
+ : undefined;
89
+ const value = /** @type {Expression} */ (prop.value);
90
+ if (value.type === "ObjectExpression") {
91
+ walk(value, key);
92
+ } else if (value.type === "ArrayExpression") {
93
+ for (const element of value.elements) {
94
+ if (element && element.type === "ObjectExpression") {
95
+ walk(element, key);
96
+ }
97
+ }
98
+ } else if (
99
+ key === "src" &&
100
+ value.type === "Literal" &&
101
+ typeof value.value === "string" &&
102
+ parentKey !== undefined &&
103
+ ICON_CONTAINER_KEYS.has(parentKey)
104
+ ) {
105
+ const url = value.value;
106
+ // Fragment-only refs aren't assets; absolute URLs need `buildHttp`.
107
+ if (!url || url.startsWith("#")) continue;
108
+ if (!buildHttp && ABSOLUTE_URL_SCHEME_REGEXP.test(url)) continue;
109
+ // acorn offsets are into `(${source})`; the leading `(` and the
110
+ // opening quote cancel, so the inner span is [start, end - 2].
111
+ const { start, end } = /** @type {Positioned} */ (
112
+ /** @type {unknown} */ (value)
113
+ );
114
+ const dep = new HtmlSourceDependency(url, [start, end - 2]);
115
+ module.addDependency(dep);
116
+ module.addCodeGenerationDependency(dep);
117
+ }
118
+ }
119
+ };
120
+ walk(statement.expression, undefined);
121
+ }
122
+
123
+ /** @type {BuildInfo} */
124
+ (state.module.buildInfo).strict = true;
125
+
126
+ return state;
127
+ }
128
+ }
129
+
130
+ module.exports = WebManifestParser;