webpack 5.106.2 → 5.107.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 (235) hide show
  1. package/README.md +2 -2
  2. package/lib/APIPlugin.js +1 -1
  3. package/lib/BannerPlugin.js +3 -4
  4. package/lib/Cache.js +3 -6
  5. package/lib/Chunk.js +21 -25
  6. package/lib/ChunkGroup.js +57 -15
  7. package/lib/CompatibilityPlugin.js +8 -7
  8. package/lib/Compilation.js +67 -37
  9. package/lib/Compiler.js +4 -13
  10. package/lib/ContextModule.js +2 -2
  11. package/lib/DefinePlugin.js +2 -2
  12. package/lib/Dependency.js +22 -1
  13. package/lib/DependencyTemplate.js +2 -1
  14. package/lib/EnvironmentPlugin.js +1 -1
  15. package/lib/EvalSourceMapDevToolPlugin.js +8 -10
  16. package/lib/ExportsInfo.js +30 -34
  17. package/lib/ExternalModule.js +91 -26
  18. package/lib/ExternalModuleFactoryPlugin.js +7 -1
  19. package/lib/FileSystemInfo.js +187 -72
  20. package/lib/Generator.js +3 -3
  21. package/lib/HotModuleReplacementPlugin.js +26 -8
  22. package/lib/IgnorePlugin.js +2 -1
  23. package/lib/Module.js +20 -19
  24. package/lib/ModuleFactory.js +1 -1
  25. package/lib/ModuleNotFoundError.js +3 -84
  26. package/lib/ModuleSourceTypeConstants.js +51 -19
  27. package/lib/ModuleTypeConstants.js +12 -3
  28. package/lib/MultiCompiler.js +2 -2
  29. package/lib/NodeStuffPlugin.js +1 -1
  30. package/lib/NormalModule.js +119 -77
  31. package/lib/NormalModuleFactory.js +47 -27
  32. package/lib/Parser.js +1 -1
  33. package/lib/ProgressPlugin.js +129 -56
  34. package/lib/RuntimeGlobals.js +5 -5
  35. package/lib/RuntimeModule.js +9 -7
  36. package/lib/RuntimePlugin.js +12 -1
  37. package/lib/SourceMapDevToolPlugin.js +250 -49
  38. package/lib/Template.js +1 -1
  39. package/lib/TemplatedPathPlugin.js +22 -4
  40. package/lib/WarnCaseSensitiveModulesPlugin.js +70 -2
  41. package/lib/WarnDeprecatedOptionPlugin.js +1 -1
  42. package/lib/WarnNoModeSetPlugin.js +16 -1
  43. package/lib/Watching.js +2 -3
  44. package/lib/WebpackError.js +3 -77
  45. package/lib/WebpackIsIncludedPlugin.js +1 -1
  46. package/lib/WebpackOptionsApply.js +13 -1
  47. package/lib/asset/AssetBytesGenerator.js +12 -8
  48. package/lib/asset/AssetGenerator.js +36 -22
  49. package/lib/asset/AssetModulesPlugin.js +6 -8
  50. package/lib/asset/AssetSourceGenerator.js +12 -8
  51. package/lib/buildChunkGraph.js +4 -6
  52. package/lib/cache/PackFileCacheStrategy.js +4 -4
  53. package/lib/cli.js +3 -1
  54. package/lib/config/defaults.js +197 -10
  55. package/lib/config/normalization.js +3 -1
  56. package/lib/css/CssGenerator.js +320 -105
  57. package/lib/css/CssInjectStyleRuntimeModule.js +44 -42
  58. package/lib/css/CssLoadingRuntimeModule.js +22 -4
  59. package/lib/{CssModule.js → css/CssModule.js} +15 -15
  60. package/lib/css/CssModulesPlugin.js +168 -88
  61. package/lib/css/CssParser.js +566 -269
  62. package/lib/css/walkCssTokens.js +148 -2
  63. package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +1 -1
  64. package/lib/dependencies/CommonJsDependencyHelpers.js +63 -0
  65. package/lib/dependencies/CommonJsExportRequireDependency.js +54 -10
  66. package/lib/dependencies/CommonJsExportsParserPlugin.js +1 -1
  67. package/lib/dependencies/CommonJsFullRequireDependency.js +32 -9
  68. package/lib/dependencies/CommonJsImportsParserPlugin.js +112 -4
  69. package/lib/dependencies/CommonJsRequireDependency.js +67 -4
  70. package/lib/dependencies/ContextDependency.js +1 -1
  71. package/lib/dependencies/ContextDependencyHelpers.js +1 -1
  72. package/lib/dependencies/CreateRequireParserPlugin.js +1 -1
  73. package/lib/dependencies/CriticalDependencyWarning.js +1 -1
  74. package/lib/dependencies/CssIcssExportDependency.js +332 -67
  75. package/lib/dependencies/CssIcssImportDependency.js +49 -7
  76. package/lib/dependencies/CssIcssSymbolDependency.js +11 -3
  77. package/lib/dependencies/CssImportDependency.js +8 -0
  78. package/lib/dependencies/CssUrlDependency.js +28 -2
  79. package/lib/dependencies/HarmonyDetectionParserPlugin.js +22 -2
  80. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +8 -7
  81. package/lib/dependencies/HarmonyExportExpressionDependency.js +22 -14
  82. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +110 -3
  83. package/lib/dependencies/HarmonyImportDependency.js +10 -2
  84. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +22 -1
  85. package/lib/dependencies/HarmonyImportSpecifierDependency.js +1 -1
  86. package/lib/{HarmonyLinkingError.js → dependencies/HarmonyLinkingError.js} +5 -3
  87. package/lib/dependencies/HtmlInlineScriptDependency.js +133 -0
  88. package/lib/dependencies/HtmlInlineStyleDependency.js +101 -0
  89. package/lib/dependencies/HtmlScriptSrcDependency.js +557 -0
  90. package/lib/dependencies/HtmlSourceDependency.js +128 -0
  91. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +1 -1
  92. package/lib/dependencies/ImportParserPlugin.js +2 -2
  93. package/lib/dependencies/ImportPhase.js +1 -1
  94. package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +1 -1
  95. package/lib/{RequireJsStuffPlugin.js → dependencies/RequireJsStuffPlugin.js} +7 -7
  96. package/lib/dependencies/SystemPlugin.js +1 -1
  97. package/lib/dependencies/WebAssemblyImportDependency.js +1 -1
  98. package/lib/dependencies/WorkerPlugin.js +2 -2
  99. package/lib/{DelegatedModule.js → dll/DelegatedModule.js} +31 -31
  100. package/lib/{DelegatedModuleFactoryPlugin.js → dll/DelegatedModuleFactoryPlugin.js} +4 -4
  101. package/lib/{DelegatedPlugin.js → dll/DelegatedPlugin.js} +2 -2
  102. package/lib/{DllEntryPlugin.js → dll/DllEntryPlugin.js} +4 -4
  103. package/lib/{DllModule.js → dll/DllModule.js} +24 -24
  104. package/lib/{DllModuleFactory.js → dll/DllModuleFactory.js} +4 -4
  105. package/lib/{DllPlugin.js → dll/DllPlugin.js} +6 -5
  106. package/lib/{DllReferencePlugin.js → dll/DllReferencePlugin.js} +14 -14
  107. package/lib/{LibManifestPlugin.js → dll/LibManifestPlugin.js} +9 -9
  108. package/lib/{AsyncDependencyToInitialChunkError.js → errors/AsyncDependencyToInitialChunkError.js} +2 -2
  109. package/lib/errors/BuildCycleError.js +1 -1
  110. package/lib/{ChunkRenderError.js → errors/ChunkRenderError.js} +1 -1
  111. package/lib/{CodeGenerationError.js → errors/CodeGenerationError.js} +1 -1
  112. package/lib/{CommentCompilationWarning.js → errors/CommentCompilationWarning.js} +3 -3
  113. package/lib/{ConcurrentCompilationError.js → errors/ConcurrentCompilationError.js} +4 -2
  114. package/lib/{EnvironmentNotSupportAsyncWarning.js → errors/EnvironmentNotSupportAsyncWarning.js} +4 -4
  115. package/lib/{HookWebpackError.js → errors/HookWebpackError.js} +5 -5
  116. package/lib/{IgnoreErrorModuleFactory.js → errors/IgnoreErrorModuleFactory.js} +4 -4
  117. package/lib/{InvalidDependenciesModuleWarning.js → errors/InvalidDependenciesModuleWarning.js} +3 -3
  118. package/lib/errors/JSONParseError.js +114 -0
  119. package/lib/{ModuleBuildError.js → errors/ModuleBuildError.js} +5 -5
  120. package/lib/{ModuleDependencyError.js → errors/ModuleDependencyError.js} +2 -2
  121. package/lib/{ModuleDependencyWarning.js → errors/ModuleDependencyWarning.js} +4 -4
  122. package/lib/{ModuleError.js → errors/ModuleError.js} +5 -5
  123. package/lib/{ModuleHashingError.js → errors/ModuleHashingError.js} +1 -1
  124. package/lib/errors/ModuleNotFoundError.js +91 -0
  125. package/lib/{ModuleParseError.js → errors/ModuleParseError.js} +8 -6
  126. package/lib/{ModuleRestoreError.js → errors/ModuleRestoreError.js} +1 -1
  127. package/lib/{ModuleStoreError.js → errors/ModuleStoreError.js} +1 -1
  128. package/lib/{ModuleWarning.js → errors/ModuleWarning.js} +5 -5
  129. package/lib/{NodeStuffInWebError.js → errors/NodeStuffInWebError.js} +4 -4
  130. package/lib/errors/NonErrorEmittedError.js +28 -0
  131. package/lib/{UnhandledSchemeError.js → errors/UnhandledSchemeError.js} +2 -2
  132. package/lib/{UnsupportedFeatureWarning.js → errors/UnsupportedFeatureWarning.js} +3 -3
  133. package/lib/errors/WebpackError.js +84 -0
  134. package/lib/html/HtmlGenerator.js +379 -0
  135. package/lib/html/HtmlModulesPlugin.js +429 -0
  136. package/lib/html/HtmlParser.js +1489 -0
  137. package/lib/html/walkHtmlTokens.js +3249 -0
  138. package/lib/ids/IdHelpers.js +2 -1
  139. package/lib/index.js +36 -15
  140. package/lib/javascript/JavascriptModulesPlugin.js +91 -10
  141. package/lib/javascript/JavascriptParser.js +197 -16
  142. package/lib/javascript/JavascriptParserHelpers.js +1 -1
  143. package/lib/json/JsonParser.js +7 -16
  144. package/lib/library/AbstractLibraryPlugin.js +1 -1
  145. package/lib/library/EnableLibraryPlugin.js +1 -1
  146. package/lib/{FalseIIFEUmdWarning.js → library/FalseIIFEUmdWarning.js} +1 -1
  147. package/lib/library/ModuleLibraryPlugin.js +74 -0
  148. package/lib/node/NodeEnvironmentPlugin.js +4 -2
  149. package/lib/node/nodeConsole.js +113 -64
  150. package/lib/optimize/ConcatenatedModule.js +51 -6
  151. package/lib/optimize/InnerGraph.js +1 -1
  152. package/lib/optimize/InnerGraphPlugin.js +11 -1
  153. package/lib/optimize/MinMaxSizeWarning.js +4 -4
  154. package/lib/optimize/ModuleConcatenationPlugin.js +15 -7
  155. package/lib/optimize/RealContentHashPlugin.js +89 -26
  156. package/lib/optimize/SideEffectsFlagPlugin.js +112 -5
  157. package/lib/optimize/SplitChunksPlugin.js +5 -5
  158. package/lib/performance/AssetsOverSizeLimitWarning.js +2 -2
  159. package/lib/performance/EntrypointsOverSizeLimitWarning.js +2 -2
  160. package/lib/performance/NoAsyncChunksWarning.js +5 -3
  161. package/lib/performance/SizeLimitsPlugin.js +1 -1
  162. package/lib/prefetch/ChunkPrefetchTriggerRuntimeModule.js +4 -1
  163. package/lib/rules/UseEffectRulePlugin.js +4 -3
  164. package/lib/runtime/AutoPublicPathRuntimeModule.js +3 -3
  165. package/lib/runtime/GetChunkFilenameRuntimeModule.js +5 -5
  166. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +119 -13
  167. package/lib/runtime/SetAnonymousDefaultNameRuntimeModule.js +35 -0
  168. package/lib/schemes/DataUriPlugin.js +13 -1
  169. package/lib/schemes/VirtualUrlPlugin.js +1 -1
  170. package/lib/serialization/SerializerMiddleware.js +2 -2
  171. package/lib/sharing/ConsumeSharedPlugin.js +4 -10
  172. package/lib/sharing/ConsumeSharedRuntimeModule.js +8 -4
  173. package/lib/sharing/ProvideSharedModule.js +1 -1
  174. package/lib/sharing/ProvideSharedPlugin.js +5 -5
  175. package/lib/sharing/resolveMatchedConfigs.js +1 -1
  176. package/lib/stats/DefaultStatsFactoryPlugin.js +2 -2
  177. package/lib/stats/DefaultStatsPresetPlugin.js +1 -1
  178. package/lib/stats/DefaultStatsPrinterPlugin.js +1 -1
  179. package/lib/stats/StatsFactory.js +1 -1
  180. package/lib/typescript/TypeScriptPlugin.js +210 -0
  181. package/lib/url/URLParserPlugin.js +2 -2
  182. package/lib/util/AsyncQueue.js +2 -2
  183. package/lib/util/Hash.js +2 -2
  184. package/lib/util/LocConverter.js +53 -0
  185. package/lib/util/SortableSet.js +1 -1
  186. package/lib/util/cleverMerge.js +2 -2
  187. package/lib/util/comparators.js +3 -3
  188. package/lib/util/concatenate.js +3 -3
  189. package/lib/util/conventions.js +42 -1
  190. package/lib/util/createMappings.js +118 -0
  191. package/lib/{formatLocation.js → util/formatLocation.js} +2 -2
  192. package/lib/{SizeFormatHelpers.js → util/formatSize.js} +3 -1
  193. package/lib/util/fs.js +8 -8
  194. package/lib/util/hash/md4.js +1 -1
  195. package/lib/util/hash/xxhash64.js +1 -1
  196. package/lib/util/identifier.js +48 -0
  197. package/lib/util/internalSerializables.js +35 -19
  198. package/lib/util/magicComment.js +10 -7
  199. package/lib/util/parseJson.js +2 -73
  200. package/lib/util/source.js +21 -0
  201. package/lib/util/topologicalSort.js +69 -0
  202. package/lib/wasm-async/AsyncWebAssemblyModulesPlugin.js +3 -4
  203. package/lib/wasm-async/AsyncWebAssemblyParser.js +1 -1
  204. package/lib/wasm-sync/UnsupportedWebAssemblyFeatureError.js +5 -3
  205. package/lib/wasm-sync/WasmFinalizeExportsPlugin.js +1 -1
  206. package/lib/wasm-sync/WebAssemblyInInitialChunkError.js +5 -3
  207. package/lib/webpack.js +3 -1
  208. package/package.json +24 -22
  209. package/schemas/WebpackOptions.check.js +1 -1
  210. package/schemas/WebpackOptions.json +129 -12
  211. package/schemas/plugins/{DllPlugin.check.d.ts → HtmlGeneratorOptions.check.d.ts} +1 -1
  212. package/schemas/plugins/HtmlGeneratorOptions.check.js +6 -0
  213. package/schemas/plugins/HtmlGeneratorOptions.json +3 -0
  214. package/schemas/plugins/ProgressPlugin.check.js +1 -1
  215. package/schemas/plugins/ProgressPlugin.json +22 -0
  216. package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
  217. package/schemas/plugins/container/ContainerReferencePlugin.json +1 -0
  218. package/schemas/plugins/container/ExternalsType.check.js +1 -1
  219. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  220. package/schemas/plugins/container/ModuleFederationPlugin.json +1 -0
  221. package/schemas/plugins/{DllReferencePlugin.check.d.ts → css/CssAutoOrModuleParserOptions.check.d.ts} +1 -1
  222. package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +6 -0
  223. package/schemas/plugins/css/CssAutoOrModuleParserOptions.json +3 -0
  224. package/schemas/plugins/dll/DllPlugin.check.d.ts +7 -0
  225. package/schemas/plugins/dll/DllReferencePlugin.check.d.ts +7 -0
  226. package/types.d.ts +1153 -233
  227. package/lib/CaseSensitiveModulesWarning.js +0 -80
  228. package/lib/GraphHelpers.js +0 -49
  229. package/lib/NoModeWarning.js +0 -23
  230. package/lib/css/CssMergeStyleSheetsRuntimeModule.js +0 -57
  231. /package/lib/{AbstractMethodError.js → errors/AbstractMethodError.js} +0 -0
  232. /package/schemas/plugins/{DllPlugin.check.js → dll/DllPlugin.check.js} +0 -0
  233. /package/schemas/plugins/{DllPlugin.json → dll/DllPlugin.json} +0 -0
  234. /package/schemas/plugins/{DllReferencePlugin.check.js → dll/DllReferencePlugin.check.js} +0 -0
  235. /package/schemas/plugins/{DllReferencePlugin.json → dll/DllReferencePlugin.json} +0 -0
@@ -30,7 +30,7 @@ const { makePathsAbsolute } = require("./util/identifier");
30
30
  /** @typedef {import("./Compiler")} Compiler */
31
31
  /** @typedef {import("./Module")} Module */
32
32
  /** @typedef {import("./NormalModule").RawSourceMap} RawSourceMap */
33
- /** @typedef {import("./TemplatedPathPlugin").TemplatePath} TemplatePath */
33
+ /** @typedef {import("./TemplatedPathPlugin").TemplatePath} SourceMappingURLComment */
34
34
  /** @typedef {import("./util/fs").OutputFileSystem} OutputFileSystem */
35
35
 
36
36
  /**
@@ -70,6 +70,91 @@ const resetRegexpState = (regexp) => {
70
70
  */
71
71
  const quoteMeta = (str) => str.replace(METACHARACTERS_REGEXP, "\\$&");
72
72
 
73
+ /**
74
+ * Compilation-scoped registry of original asset sources for multi-plugin
75
+ * cooperation. The first SourceMapDevToolPlugin instance to see a file pins a
76
+ * reference to the asset's still-unwrapped {@link Source} object; later
77
+ * instances whose `asset.source.sourceAndMap()` would now return `null` (the
78
+ * earlier instance replaced the asset with a `RawSource`) can re-extract the
79
+ * map from this pinned reference. We keep the registry on a module-scoped
80
+ * `WeakMap` so the entries are reclaimed automatically when the compilation
81
+ * itself becomes unreachable; we never store anything on the compilation
82
+ * object directly.
83
+ *
84
+ * Stashing the `Source` object itself rather than an extracted map keeps the
85
+ * fast path free of cloning and source-map serialization work — the
86
+ * extraction only happens if a subsequent plugin actually needs the map.
87
+ * @type {WeakMap<Compilation, Map<string, Source>>}
88
+ */
89
+ const originalSourceRegistry = new WeakMap();
90
+
91
+ /**
92
+ * Returns (creating if necessary) the per-compilation registry of original
93
+ * asset {@link Source} objects.
94
+ * @param {Compilation} compilation compilation
95
+ * @returns {Map<string, Source>} registry
96
+ */
97
+ const getOriginalSourceRegistry = (compilation) => {
98
+ let registry = originalSourceRegistry.get(compilation);
99
+ if (registry === undefined) {
100
+ registry = new Map();
101
+ originalSourceRegistry.set(compilation, registry);
102
+ }
103
+ return registry;
104
+ };
105
+
106
+ /**
107
+ * Extracts source and source map from a Source object, falling back to a
108
+ * registered original source for assets that another SourceMapDevToolPlugin
109
+ * instance has already wrapped (whose internal map is now `null`).
110
+ *
111
+ * The returned source is read from the asset as it currently stands — that way
112
+ * any `sourceMappingURL` comments appended by earlier plugin instances survive
113
+ * — while the map is taken from the pinned original Source when the current
114
+ * one no longer carries it.
115
+ * @param {string} file file name
116
+ * @param {Source} asset source object as currently held by the compilation
117
+ * @param {MapOptions} options map extraction options
118
+ * @param {Map<string, Source>} registry compilation-scoped original-source registry
119
+ * @returns {{ source: string, sourceMap: RawSourceMap } | undefined} extracted pair or `undefined` when no map is recoverable
120
+ */
121
+ const extractSourceAndMap = (file, asset, options, registry) => {
122
+ /** @type {string | Buffer} */
123
+ let source;
124
+ /** @type {null | RawSourceMap} */
125
+ let sourceMap;
126
+ if (asset.sourceAndMap) {
127
+ const sourceAndMap = asset.sourceAndMap(options);
128
+ source = sourceAndMap.source;
129
+ sourceMap = sourceAndMap.map;
130
+ } else {
131
+ source = asset.source();
132
+ sourceMap = asset.map(options);
133
+ }
134
+ // Bail before touching the registry if we can't return a usable string
135
+ // source — pinning a non-string-producing asset would only waste the slot.
136
+ if (typeof source !== "string") return;
137
+ if (sourceMap) {
138
+ // The current asset still owns the original map — pin a reference so
139
+ // that a later plugin instance (which will see a rewrapped asset
140
+ // without a map) can recover it on demand.
141
+ if (!registry.has(file)) registry.set(file, asset);
142
+ } else {
143
+ // The current asset (typically a `RawSource` left by an earlier
144
+ // SourceMapDevToolPlugin instance) has no internal map. Re-extract
145
+ // the map from the original Source we pinned earlier. We keep using
146
+ // `source` from the current asset so that any prior wrappers (e.g.
147
+ // appended sourceMappingURL comments) are preserved.
148
+ const original = registry.get(file);
149
+ if (!original) return;
150
+ sourceMap = original.sourceAndMap
151
+ ? original.sourceAndMap(options).map
152
+ : original.map(options);
153
+ if (!sourceMap) return;
154
+ }
155
+ return { source, sourceMap };
156
+ };
157
+
73
158
  /**
74
159
  * Creating {@link SourceMapTask} for given file
75
160
  * @param {string} file current compiled file
@@ -78,6 +163,7 @@ const quoteMeta = (str) => str.replace(METACHARACTERS_REGEXP, "\\$&");
78
163
  * @param {MapOptions} options source map options
79
164
  * @param {Compilation} compilation compilation instance
80
165
  * @param {ItemCacheFacade} cacheItem cache item
166
+ * @param {Map<string, Source>} registry compilation-scoped original-source registry
81
167
  * @returns {SourceMapTask | undefined} created task instance or `undefined`
82
168
  */
83
169
  const getTaskForFile = (
@@ -86,24 +172,12 @@ const getTaskForFile = (
86
172
  assetInfo,
87
173
  options,
88
174
  compilation,
89
- cacheItem
175
+ cacheItem,
176
+ registry
90
177
  ) => {
91
- /** @type {string | Buffer} */
92
- let source;
93
- /** @type {null | RawSourceMap} */
94
- let sourceMap;
95
- /**
96
- * Check if asset can build source map
97
- */
98
- if (asset.sourceAndMap) {
99
- const sourceAndMap = asset.sourceAndMap(options);
100
- sourceMap = sourceAndMap.map;
101
- source = sourceAndMap.source;
102
- } else {
103
- sourceMap = asset.map(options);
104
- source = asset.source();
105
- }
106
- if (!sourceMap || typeof source !== "string") return;
178
+ const extracted = extractSourceAndMap(file, asset, options, registry);
179
+ if (!extracted) return;
180
+ const { source, sourceMap } = extracted;
107
181
  const context = compilation.options.context;
108
182
  const root = compilation.compiler.root;
109
183
  const cachedAbsolutify = makePathsAbsolute.bindContextCache(context, root);
@@ -117,7 +191,7 @@ const getTaskForFile = (
117
191
  return {
118
192
  file,
119
193
  asset,
120
- source,
194
+ source: /** @type {string} */ (source),
121
195
  assetInfo,
122
196
  sourceMap,
123
197
  modules,
@@ -127,6 +201,29 @@ const getTaskForFile = (
127
201
 
128
202
  const PLUGIN_NAME = "SourceMapDevToolPlugin";
129
203
 
204
+ /**
205
+ * Maps a configuration value (string, RegExp, function, nullish, or array of
206
+ * such) into a JSON-serializable form. Functions and RegExps are turned into
207
+ * their `.toString()` representation so that changes to inline callbacks
208
+ * invalidate caches; everything else is returned as-is so that the surrounding
209
+ * `JSON.stringify` does the escaping.
210
+ *
211
+ * The result is used through `JSON.stringify` to build cache identifiers, so
212
+ * we deliberately avoid any homemade `|` / `,` separators that could collide
213
+ * with characters appearing inside user-provided values such as `publicPath`,
214
+ * template strings, or `sourceRoot`.
215
+ * @param {EXPECTED_ANY} value option value
216
+ * @returns {EXPECTED_ANY} JSON-serializable representation
217
+ */
218
+ const toCacheKeyValue = (value) => {
219
+ if (value === undefined || value === null) return value;
220
+ if (Array.isArray(value)) return value.map(toCacheKeyValue);
221
+ if (value instanceof RegExp || typeof value === "function") {
222
+ return value.toString();
223
+ }
224
+ return value;
225
+ };
226
+
130
227
  class SourceMapDevToolPlugin {
131
228
  /**
132
229
  * Creates an instance of SourceMapDevToolPlugin.
@@ -136,7 +233,7 @@ class SourceMapDevToolPlugin {
136
233
  constructor(options = {}) {
137
234
  /** @type {undefined | null | false | string} */
138
235
  this.sourceMapFilename = options.filename;
139
- /** @type {false | TemplatePath} */
236
+ /** @type {false | SourceMappingURLComment} */
140
237
  this.sourceMappingURLComment =
141
238
  options.append === false
142
239
  ? false
@@ -153,6 +250,28 @@ class SourceMapDevToolPlugin {
153
250
  this.namespace = options.namespace || "";
154
251
  /** @type {SourceMapDevToolPluginOptions} */
155
252
  this.options = options;
253
+ // Cache salt derived from output-affecting options, so that two
254
+ // SourceMapDevToolPlugin instances (or `devtool` + a plugin) operating
255
+ // on the same asset don't share a cache entry. We serialize via
256
+ // `JSON.stringify` rather than a homemade separator so that any
257
+ // special characters (e.g. `|` inside a publicPath or sourceRoot)
258
+ // can't accidentally make two different option sets collide.
259
+ /** @type {string} */
260
+ this._cacheSalt = JSON.stringify([
261
+ toCacheKeyValue(options.filename),
262
+ toCacheKeyValue(options.append),
263
+ toCacheKeyValue(this.moduleFilenameTemplate),
264
+ toCacheKeyValue(this.fallbackModuleFilenameTemplate),
265
+ toCacheKeyValue(this.namespace),
266
+ options.module !== false,
267
+ options.columns !== false,
268
+ Boolean(options.noSources),
269
+ Boolean(options.debugIds),
270
+ options.sourceRoot || "",
271
+ toCacheKeyValue(options.ignoreList),
272
+ options.publicPath || "",
273
+ options.fileContext || ""
274
+ ]);
156
275
  }
157
276
 
158
277
  /**
@@ -195,6 +314,14 @@ class SourceMapDevToolPlugin {
195
314
  compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
196
315
  new SourceMapDevToolModuleOptionsPlugin(options).apply(compilation);
197
316
 
317
+ // All SourceMapDevToolPlugin instances on the same compilation share
318
+ // a registry of pristine asset sources, so the second instance to
319
+ // run can still recover the original map after the first instance
320
+ // has replaced the asset with a `RawSource`. The registry lives on a
321
+ // module-scoped `WeakMap` keyed by compilation so it is released
322
+ // automatically and never pollutes the compilation object.
323
+ const originalSources = getOriginalSourceRegistry(compilation);
324
+
198
325
  compilation.hooks.processAssets.tapAsync(
199
326
  {
200
327
  name: PLUGIN_NAME,
@@ -239,18 +366,22 @@ class SourceMapDevToolPlugin {
239
366
  const asset =
240
367
  /** @type {Readonly<Asset>} */
241
368
  (compilation.getAsset(file));
242
- if (asset.info.related && asset.info.related.sourceMap) {
243
- fileIndex++;
244
- return callback();
245
- }
246
369
 
247
370
  const chunk = fileToChunk.get(file);
248
371
  const sourceMapNamespace = compilation.getPath(this.namespace, {
249
372
  chunk
250
373
  });
251
374
 
375
+ // The cache item identifier must include the per-instance
376
+ // salt so two SourceMapDevToolPlugin instances that target
377
+ // the same `file` don't collide in the persistent cache —
378
+ // they'd otherwise write different content to the same key
379
+ // and invalidate every pack on each build. We encode via
380
+ // `JSON.stringify` so that special characters (e.g. `|`)
381
+ // in an asset filename can't be spoofed to collide with the
382
+ // salt portion of the identifier.
252
383
  const cacheItem = cache.getItemCache(
253
- file,
384
+ JSON.stringify([file, this._cacheSalt]),
254
385
  cache.mergeEtags(
255
386
  cache.getLazyHashedEtag(asset.source),
256
387
  sourceMapNamespace
@@ -265,6 +396,16 @@ class SourceMapDevToolPlugin {
265
396
  * If presented in cache, reassigns assets. Cache assets already have source maps.
266
397
  */
267
398
  if (cacheEntry) {
399
+ // Pin the still-unwrapped asset source in the registry
400
+ // before `compilation.updateAsset` replaces it. This is a
401
+ // pointer assignment — no source-map extraction work — and
402
+ // it lets a subsequent SourceMapDevToolPlugin instance
403
+ // extract the original map on demand even though the
404
+ // persistent cache hit lets us skip processing here.
405
+ if (!originalSources.has(file)) {
406
+ originalSources.set(file, asset.source);
407
+ }
408
+
268
409
  const { assets, assetsInfo } = cacheEntry;
269
410
  for (const cachedFile of Object.keys(assets)) {
270
411
  if (cachedFile === file) {
@@ -313,7 +454,8 @@ class SourceMapDevToolPlugin {
313
454
  columns: options.columns
314
455
  },
315
456
  compilation,
316
- cacheItem
457
+ cacheItem,
458
+ originalSources
317
459
  );
318
460
 
319
461
  if (task) {
@@ -446,12 +588,21 @@ class SourceMapDevToolPlugin {
446
588
  "attach SourceMap"
447
589
  );
448
590
 
449
- const moduleFilenames = modules.map((m) =>
450
- moduleToSourceNameMapping.get(m)
451
- );
452
- sourceMap.sources = /** @type {string[]} */ (moduleFilenames);
591
+ const moduleFilenames =
592
+ /** @type {string[]} */
593
+ (modules.map((m) => moduleToSourceNameMapping.get(m)));
594
+ // We deliberately do NOT mutate `sourceMap` in place: the
595
+ // task's `sourceMap` reference may be shared with a
596
+ // `SourceMapSource` whose internal map cache is the same
597
+ // object (webpack-sources keeps it cached). A second
598
+ // `SourceMapDevToolPlugin` instance that reads the original
599
+ // source through the registry would otherwise see our
600
+ // rewrites. Instead we build a fresh `outputSourceMap` for
601
+ // the .map file and leave the original alone.
602
+ /** @type {number[] | undefined} */
603
+ let ignoreList;
453
604
  if (options.ignoreList) {
454
- const ignoreList = sourceMap.sources.reduce(
605
+ const list = moduleFilenames.reduce(
455
606
  /** @type {(acc: number[], sourceName: string, idx: number) => number[]} */ (
456
607
  (acc, sourceName, idx) => {
457
608
  const rule = /** @type {Rules} */ (
@@ -467,35 +618,29 @@ class SourceMapDevToolPlugin {
467
618
  ),
468
619
  []
469
620
  );
470
- if (ignoreList.length > 0) {
471
- sourceMap.ignoreList = ignoreList;
472
- }
621
+ if (list.length > 0) ignoreList = list;
473
622
  }
474
623
 
475
- if (options.noSources) {
476
- sourceMap.sourcesContent = undefined;
477
- }
478
- sourceMap.sourceRoot = options.sourceRoot || "";
479
- sourceMap.file = file;
480
624
  const usesContentHash =
481
625
  sourceMapFilename &&
482
626
  CONTENT_HASH_DETECT_REGEXP.test(sourceMapFilename);
483
627
 
484
628
  resetRegexpState(CONTENT_HASH_DETECT_REGEXP);
485
629
 
630
+ let outputFile = file;
486
631
  // If SourceMap and asset uses contenthash, avoid a circular dependency by hiding hash in `file`
487
632
  if (usesContentHash && task.assetInfo.contenthash) {
488
633
  const contenthash = task.assetInfo.contenthash;
489
634
  const pattern = Array.isArray(contenthash)
490
635
  ? contenthash.map(quoteMeta).join("|")
491
636
  : quoteMeta(contenthash);
492
- sourceMap.file = sourceMap.file.replace(
637
+ outputFile = outputFile.replace(
493
638
  new RegExp(pattern, "g"),
494
639
  (m) => "x".repeat(m.length)
495
640
  );
496
641
  }
497
642
 
498
- /** @type {false | TemplatePath} */
643
+ /** @type {false | SourceMappingURLComment} */
499
644
  let currentSourceMappingURLComment = sourceMappingURLComment;
500
645
  const cssExtensionDetected =
501
646
  CSS_EXTENSION_DETECT_REGEXP.test(file);
@@ -512,18 +657,48 @@ class SourceMapDevToolPlugin {
512
657
  );
513
658
  }
514
659
 
660
+ /** @type {string | undefined} */
661
+ let debugIdValue;
515
662
  if (options.debugIds) {
516
- const debugId = generateDebugId(source, sourceMap.file);
517
- sourceMap.debugId = debugId;
663
+ const debugId = generateDebugId(source, outputFile);
664
+ debugIdValue = debugId;
518
665
 
519
666
  const debugIdComment = `\n//# debugId=${debugId}`;
520
- currentSourceMappingURLComment =
521
- currentSourceMappingURLComment
522
- ? `${debugIdComment}${currentSourceMappingURLComment}`
523
- : debugIdComment;
667
+ if (currentSourceMappingURLComment === false) {
668
+ currentSourceMappingURLComment = debugIdComment;
669
+ } else if (
670
+ typeof currentSourceMappingURLComment === "function"
671
+ ) {
672
+ // Wrap the user's append function so the debug-id
673
+ // comment is prepended at call time. Template-string
674
+ // concatenation would coerce the function to a string
675
+ // and lose its dynamic behavior.
676
+ const wrappedFn = currentSourceMappingURLComment;
677
+ currentSourceMappingURLComment = (pathData, assetInfo) =>
678
+ `${debugIdComment}${wrappedFn(pathData, assetInfo)}`;
679
+ } else {
680
+ currentSourceMappingURLComment = `${debugIdComment}${currentSourceMappingURLComment}`;
681
+ }
524
682
  }
525
683
 
526
- const sourceMapString = JSON.stringify(sourceMap);
684
+ /** @type {RawSourceMap} */
685
+ const outputSourceMap = {
686
+ ...sourceMap,
687
+ sources: moduleFilenames,
688
+ sourceRoot: options.sourceRoot || "",
689
+ file: outputFile
690
+ };
691
+ if (ignoreList !== undefined) {
692
+ outputSourceMap.ignoreList = ignoreList;
693
+ }
694
+ if (options.noSources) {
695
+ outputSourceMap.sourcesContent = undefined;
696
+ }
697
+ if (debugIdValue !== undefined) {
698
+ outputSourceMap.debugId = debugIdValue;
699
+ }
700
+
701
+ const sourceMapString = JSON.stringify(outputSourceMap);
527
702
  if (sourceMapFilename) {
528
703
  const filename = file;
529
704
  const sourceMapContentHash = usesContentHash
@@ -567,8 +742,34 @@ class SourceMapDevToolPlugin {
567
742
  })
568
743
  );
569
744
  }
745
+ // Preserve any existing related.sourceMap entries from
746
+ // earlier SourceMapDevToolPlugin runs on the same asset so
747
+ // that all generated maps remain discoverable via asset
748
+ // info (the schema allows string or string[]).
749
+ const existingSourceMap =
750
+ task.assetInfo.related &&
751
+ task.assetInfo.related.sourceMap;
752
+ /** @type {string | string[]} */
753
+ let relatedSourceMap;
754
+ if (
755
+ existingSourceMap === undefined ||
756
+ existingSourceMap === null
757
+ ) {
758
+ relatedSourceMap = sourceMapFile;
759
+ } else if (Array.isArray(existingSourceMap)) {
760
+ relatedSourceMap = existingSourceMap.includes(
761
+ sourceMapFile
762
+ )
763
+ ? existingSourceMap
764
+ : [...existingSourceMap, sourceMapFile];
765
+ } else {
766
+ relatedSourceMap =
767
+ existingSourceMap === sourceMapFile
768
+ ? existingSourceMap
769
+ : [existingSourceMap, sourceMapFile];
770
+ }
570
771
  const assetInfo = {
571
- related: { sourceMap: sourceMapFile }
772
+ related: { sourceMap: relatedSourceMap }
572
773
  };
573
774
  assets[file] = asset;
574
775
  assetsInfo[file] = assetInfo;
package/lib/Template.js CHANGED
@@ -63,7 +63,7 @@ const MATCH_PADDED_HYPHENS_REPLACE_REGEX = /^-|-$/g;
63
63
  * Defines the render manifest entry templated type used by this module.
64
64
  * @typedef {object} RenderManifestEntryTemplated
65
65
  * @property {() => Source} render
66
- * @property {TemplatePath} filenameTemplate
66
+ * @property {string | import("./TemplatedPathPlugin").TemplatePathFn<EXPECTED_ANY>} filenameTemplate
67
67
  * @property {PathData=} pathOptions
68
68
  * @property {AssetInfo=} info
69
69
  * @property {string} identifier
@@ -18,6 +18,8 @@ const getMimeTypes = memoize(() => require("./util/mimeTypes"));
18
18
  /** @typedef {import("./ChunkGraph").ModuleId} ModuleId */
19
19
  /** @typedef {import("./Compilation").AssetInfo} AssetInfo */
20
20
  /** @typedef {import("./Compilation").PathData} PathData */
21
+ /** @typedef {import("./Compilation").PathDataChunk} PathDataChunk */
22
+ /** @typedef {import("./Compilation").PathDataModule} PathDataModule */
21
23
  /** @typedef {import("./Compiler")} Compiler */
22
24
 
23
25
  const REGEXP = /\[\\*([\w:]+)\\*\]/g;
@@ -138,13 +140,29 @@ const deprecated = (fn, message, code) => {
138
140
  );
139
141
  };
140
142
 
141
- /** @typedef {(pathData: PathData, assetInfo?: AssetInfo) => string} TemplatePathFn */
142
- /** @typedef {string | TemplatePathFn} TemplatePath */
143
+ /**
144
+ * Callback used to compute a path from contextual data. The type parameter
145
+ * narrows the `pathData` shape when the caller knows it operates in a chunk
146
+ * (`PathDataChunk`) or module (`PathDataModule`) context — defaults to the
147
+ * fully-optional `PathData` for backward compatibility.
148
+ * @template {PathData} [T=PathData]
149
+ * @typedef {(pathData: T, assetInfo?: AssetInfo) => string} TemplatePathFn
150
+ */
151
+
152
+ /**
153
+ * Either a raw template string (e.g. `"[name].[contenthash].js"`) or a
154
+ * generic `TemplatePathFn`. Method signatures that need to thread a narrowed
155
+ * `PathData` shape spell the function side out as `TemplatePathFn<T>`
156
+ * directly — `TemplatePath` itself stays a plain alias so local JSDoc
157
+ * re-imports keep a single shared identity.
158
+ * @typedef {string | TemplatePathFn} TemplatePath
159
+ */
143
160
 
144
161
  /**
145
162
  * Returns the interpolated path.
146
- * @param {TemplatePath} path the raw path
147
- * @param {PathData} data context data
163
+ * @template {PathData} [T=PathData]
164
+ * @param {string | TemplatePathFn<T>} path the raw path
165
+ * @param {T} data context data
148
166
  * @param {AssetInfo=} assetInfo extra info about the asset (will be written to)
149
167
  * @returns {string} the interpolated path
150
168
  */
@@ -5,12 +5,80 @@
5
5
 
6
6
  "use strict";
7
7
 
8
- const CaseSensitiveModulesWarning = require("./CaseSensitiveModulesWarning");
9
-
10
8
  /** @typedef {import("./Compiler")} Compiler */
11
9
  /** @typedef {import("./Module")} Module */
10
+ /** @typedef {import("./ModuleGraph")} ModuleGraph */
12
11
  /** @typedef {import("./NormalModule")} NormalModule */
13
12
 
13
+ const WebpackError = require("./errors/WebpackError");
14
+
15
+ /**
16
+ * Sorts the conflicting modules by identifier to keep warning output stable.
17
+ * @param {Module[]} modules the modules to be sorted
18
+ * @returns {Module[]} sorted version of original modules
19
+ */
20
+ const sortModules = (modules) =>
21
+ modules.sort((a, b) => {
22
+ const aIdent = a.identifier();
23
+ const bIdent = b.identifier();
24
+ /* istanbul ignore next */
25
+ if (aIdent < bIdent) return -1;
26
+ /* istanbul ignore next */
27
+ if (aIdent > bIdent) return 1;
28
+ /* istanbul ignore next */
29
+ return 0;
30
+ });
31
+
32
+ /**
33
+ * Formats the conflicting modules and one representative incoming reason for
34
+ * each module into the warning body.
35
+ * @param {Module[]} modules each module from throw
36
+ * @param {ModuleGraph} moduleGraph the module graph
37
+ * @returns {string} each message from provided modules
38
+ */
39
+ const createModulesListMessage = (modules, moduleGraph) =>
40
+ modules
41
+ .map((m) => {
42
+ let message = `* ${m.identifier()}`;
43
+ const validReasons = [
44
+ ...moduleGraph.getIncomingConnectionsByOriginModule(m).keys()
45
+ ].filter(Boolean);
46
+
47
+ if (validReasons.length > 0) {
48
+ message += `\n Used by ${validReasons.length} module(s), i. e.`;
49
+ message += `\n ${
50
+ /** @type {Module[]} */ (validReasons)[0].identifier()
51
+ }`;
52
+ }
53
+ return message;
54
+ })
55
+ .join("\n");
56
+
57
+ /**
58
+ * Warning emitted when webpack finds modules whose identifiers differ only by
59
+ * letter casing, which can behave inconsistently across filesystems.
60
+ */
61
+ class CaseSensitiveModulesWarning extends WebpackError {
62
+ /**
63
+ * Builds a warning message that lists the case-conflicting modules and
64
+ * representative importers that caused them to be included.
65
+ * @param {Iterable<Module>} modules modules that were detected
66
+ * @param {ModuleGraph} moduleGraph the module graph
67
+ */
68
+ constructor(modules, moduleGraph) {
69
+ const sortedModules = sortModules([...modules]);
70
+ const modulesList = createModulesListMessage(sortedModules, moduleGraph);
71
+ super(`There are multiple modules with names that only differ in casing.
72
+ This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.
73
+ Use equal casing. Compare these module identifiers:
74
+ ${modulesList}`);
75
+
76
+ /** @type {string} */
77
+ this.name = "CaseSensitiveModulesWarning";
78
+ this.module = sortedModules[0];
79
+ }
80
+ }
81
+
14
82
  const PLUGIN_NAME = "WarnCaseSensitiveModulesPlugin";
15
83
 
16
84
  class WarnCaseSensitiveModulesPlugin {
@@ -5,7 +5,7 @@
5
5
 
6
6
  "use strict";
7
7
 
8
- const WebpackError = require("./WebpackError");
8
+ const WebpackError = require("./errors/WebpackError");
9
9
 
10
10
  /** @typedef {import("./Compiler")} Compiler */
11
11
 
@@ -5,7 +5,22 @@
5
5
 
6
6
  "use strict";
7
7
 
8
- const NoModeWarning = require("./NoModeWarning");
8
+ const WebpackError = require("./errors/WebpackError");
9
+
10
+ class NoModeWarning extends WebpackError {
11
+ constructor() {
12
+ super();
13
+
14
+ /** @type {string} */
15
+ this.name = "NoModeWarning";
16
+ this.message =
17
+ "configuration\n" +
18
+ "The 'mode' option has not been set, webpack will fallback to 'production' for this value.\n" +
19
+ "Set 'mode' option to 'development' or 'production' to enable defaults for each environment.\n" +
20
+ "You can also set it to 'none' to disable any default behavior. " +
21
+ "Learn more: https://webpack.js.org/configuration/mode/";
22
+ }
23
+ }
9
24
 
10
25
  /** @typedef {import("./Compiler")} Compiler */
11
26
 
package/lib/Watching.js CHANGED
@@ -11,7 +11,6 @@ const Stats = require("./Stats");
11
11
  /** @typedef {import("./Compilation")} Compilation */
12
12
  /** @typedef {import("./Compiler")} Compiler */
13
13
  /** @typedef {import("./Compiler").ErrorCallback} ErrorCallback */
14
- /** @typedef {import("./WebpackError")} WebpackError */
15
14
  /** @typedef {import("./logging/Logger").Logger} Logger */
16
15
  /** @typedef {import("./util/fs").TimeInfoEntries} TimeInfoEntries */
17
16
  /** @typedef {import("./util/fs").WatchFileSystem} WatchFileSystem */
@@ -480,7 +479,7 @@ class Watching {
480
479
  }
481
480
  /**
482
481
  * Processes the provided err.
483
- * @param {WebpackError | null} err error if any
482
+ * @param {Error | null} err error if any
484
483
  * @param {Compilation=} compilation compilation if any
485
484
  */
486
485
  const finalCallback = (err, compilation) => {
@@ -495,7 +494,7 @@ class Watching {
495
494
  this.compiler.fsStartTime = undefined;
496
495
  /**
497
496
  * Processes the provided err.
498
- * @param {WebpackError | null} err error if any
497
+ * @param {Error | null} err error if any
499
498
  */
500
499
  const shutdown = (err) => {
501
500
  this.compiler.hooks.watchClose.call();