webpack 5.107.0 → 5.107.2
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.
- package/lib/BannerPlugin.js +3 -4
- package/lib/Chunk.js +21 -25
- package/lib/ChunkGroup.js +57 -15
- package/lib/Compilation.js +33 -11
- package/lib/Compiler.js +27 -3
- package/lib/ContextModuleFactory.js +45 -38
- package/lib/EvalSourceMapDevToolPlugin.js +0 -1
- package/lib/ExportsInfo.js +30 -34
- package/lib/ExternalModule.js +15 -11
- package/lib/ExternalModuleFactoryPlugin.js +2 -1
- package/lib/Module.js +1 -1
- package/lib/ModuleNotFoundError.js +10 -0
- package/lib/ModuleSourceTypeConstants.js +24 -22
- package/lib/MultiCompiler.js +14 -0
- package/lib/NormalModule.js +531 -53
- package/lib/NormalModuleFactory.js +38 -26
- package/lib/ProgressPlugin.js +1 -1
- package/lib/RuntimePlugin.js +1 -1
- package/lib/SourceMapDevToolPlugin.js +335 -57
- package/lib/Template.js +1 -1
- package/lib/TemplatedPathPlugin.js +22 -4
- package/lib/asset/AssetBytesGenerator.js +6 -6
- package/lib/asset/AssetGenerator.js +14 -14
- package/lib/asset/AssetModulesPlugin.js +3 -7
- package/lib/asset/AssetSourceGenerator.js +6 -6
- package/lib/buildChunkGraph.js +24 -2
- package/lib/cache/getLazyHashedEtag.js +9 -2
- package/lib/css/CssModulesPlugin.js +2 -2
- package/lib/dependencies/CommonJsImportsParserPlugin.js +108 -1
- package/lib/dependencies/CssUrlDependency.js +3 -2
- package/lib/dependencies/HarmonyDetectionParserPlugin.js +21 -1
- package/lib/dependencies/HarmonyExportInitFragment.js +8 -9
- package/lib/dependencies/HtmlInlineScriptDependency.js +3 -14
- package/lib/dependencies/HtmlInlineStyleDependency.js +17 -0
- package/lib/dependencies/HtmlScriptSrcDependency.js +265 -65
- package/lib/dependencies/HtmlSourceDependency.js +21 -2
- package/lib/dependencies/WorkerPlugin.js +18 -4
- package/lib/hmr/LazyCompilationPlugin.js +104 -0
- package/lib/html/HtmlGenerator.js +81 -33
- package/lib/html/HtmlModulesPlugin.js +87 -28
- package/lib/html/walkHtmlTokens.js +641 -125
- package/lib/index.js +2 -0
- package/lib/javascript/JavascriptModulesPlugin.js +2 -2
- package/lib/javascript/JavascriptParser.js +1 -1
- package/lib/library/ModuleLibraryPlugin.js +30 -24
- package/lib/node/NodeWatchFileSystem.js +37 -22
- package/lib/optimize/ConcatenatedModule.js +3 -2
- package/lib/optimize/SideEffectsFlagPlugin.js +1 -2
- package/lib/optimize/SplitChunksPlugin.js +4 -4
- package/lib/runtime/AutoPublicPathRuntimeModule.js +3 -3
- package/lib/runtime/GetChunkFilenameRuntimeModule.js +5 -5
- package/lib/sharing/ConsumeSharedPlugin.js +2 -8
- package/lib/sharing/ProvideSharedPlugin.js +4 -4
- package/lib/util/fs.js +6 -1
- package/lib/wasm-async/AsyncWebAssemblyModulesPlugin.js +1 -2
- package/package.json +5 -5
- package/schemas/WebpackOptions.check.js +1 -1
- package/schemas/WebpackOptions.json +11 -9
- package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
- package/schemas/plugins/container/ContainerReferencePlugin.json +1 -0
- package/schemas/plugins/container/ExternalsType.check.js +1 -1
- package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
- package/schemas/plugins/container/ModuleFederationPlugin.json +1 -0
- package/types.d.ts +472 -149
|
@@ -30,18 +30,18 @@ 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}
|
|
33
|
+
/** @typedef {import("./TemplatedPathPlugin").TemplatePath} SourceMappingURLComment */
|
|
34
34
|
/** @typedef {import("./util/fs").OutputFileSystem} OutputFileSystem */
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Defines the source map task type used by this module.
|
|
38
38
|
* @typedef {object} SourceMapTask
|
|
39
|
-
* @property {Source} asset
|
|
40
39
|
* @property {AssetInfo} assetInfo
|
|
41
40
|
* @property {(string | Module)[]} modules
|
|
42
41
|
* @property {string} source
|
|
43
42
|
* @property {string} file
|
|
44
43
|
* @property {RawSourceMap} sourceMap
|
|
44
|
+
* @property {Source} mapSource the Source instance whose `sourceAndMap` we called (the current asset or, when its map was already stripped, the pinned original from `originalSources`) — what `clearCache` should target
|
|
45
45
|
* @property {ItemCacheFacade} cacheItem cache item
|
|
46
46
|
*/
|
|
47
47
|
|
|
@@ -70,6 +70,93 @@ 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. `mapSource` identifies which Source instance was
|
|
115
|
+
* actually queried for the map (the current asset, or the pinned original);
|
|
116
|
+
* that's the one whose internal caches the caller should release.
|
|
117
|
+
* @param {string} file file name
|
|
118
|
+
* @param {Source} asset source object as currently held by the compilation
|
|
119
|
+
* @param {MapOptions} options map extraction options
|
|
120
|
+
* @param {Map<string, Source>} registry compilation-scoped original-source registry
|
|
121
|
+
* @returns {{ source: string, sourceMap: RawSourceMap, mapSource: Source } | undefined} extracted pair or `undefined` when no map is recoverable
|
|
122
|
+
*/
|
|
123
|
+
const extractSourceAndMap = (file, asset, options, registry) => {
|
|
124
|
+
/** @type {string | Buffer} */
|
|
125
|
+
let source;
|
|
126
|
+
/** @type {null | RawSourceMap} */
|
|
127
|
+
let sourceMap;
|
|
128
|
+
if (asset.sourceAndMap) {
|
|
129
|
+
const sourceAndMap = asset.sourceAndMap(options);
|
|
130
|
+
source = sourceAndMap.source;
|
|
131
|
+
sourceMap = sourceAndMap.map;
|
|
132
|
+
} else {
|
|
133
|
+
source = asset.source();
|
|
134
|
+
sourceMap = asset.map(options);
|
|
135
|
+
}
|
|
136
|
+
// Bail before touching the registry if we can't return a usable string
|
|
137
|
+
// source — pinning a non-string-producing asset would only waste the slot.
|
|
138
|
+
if (typeof source !== "string") return;
|
|
139
|
+
if (sourceMap) {
|
|
140
|
+
// The current asset still owns the original map — pin a reference so
|
|
141
|
+
// that a later plugin instance (which will see a rewrapped asset
|
|
142
|
+
// without a map) can recover it on demand.
|
|
143
|
+
if (!registry.has(file)) registry.set(file, asset);
|
|
144
|
+
return { source, sourceMap, mapSource: asset };
|
|
145
|
+
}
|
|
146
|
+
// The current asset (typically a `RawSource` left by an earlier
|
|
147
|
+
// SourceMapDevToolPlugin instance) has no internal map. Re-extract
|
|
148
|
+
// the map from the original Source we pinned earlier. We keep using
|
|
149
|
+
// `source` from the current asset so that any prior wrappers (e.g.
|
|
150
|
+
// appended sourceMappingURL comments) are preserved.
|
|
151
|
+
const original = registry.get(file);
|
|
152
|
+
if (!original) return;
|
|
153
|
+
sourceMap = original.sourceAndMap
|
|
154
|
+
? original.sourceAndMap(options).map
|
|
155
|
+
: original.map(options);
|
|
156
|
+
if (!sourceMap) return;
|
|
157
|
+
return { source, sourceMap, mapSource: original };
|
|
158
|
+
};
|
|
159
|
+
|
|
73
160
|
/**
|
|
74
161
|
* Creating {@link SourceMapTask} for given file
|
|
75
162
|
* @param {string} file current compiled file
|
|
@@ -78,6 +165,7 @@ const quoteMeta = (str) => str.replace(METACHARACTERS_REGEXP, "\\$&");
|
|
|
78
165
|
* @param {MapOptions} options source map options
|
|
79
166
|
* @param {Compilation} compilation compilation instance
|
|
80
167
|
* @param {ItemCacheFacade} cacheItem cache item
|
|
168
|
+
* @param {Map<string, Source>} registry compilation-scoped original-source registry
|
|
81
169
|
* @returns {SourceMapTask | undefined} created task instance or `undefined`
|
|
82
170
|
*/
|
|
83
171
|
const getTaskForFile = (
|
|
@@ -86,24 +174,12 @@ const getTaskForFile = (
|
|
|
86
174
|
assetInfo,
|
|
87
175
|
options,
|
|
88
176
|
compilation,
|
|
89
|
-
cacheItem
|
|
177
|
+
cacheItem,
|
|
178
|
+
registry
|
|
90
179
|
) => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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;
|
|
180
|
+
const extracted = extractSourceAndMap(file, asset, options, registry);
|
|
181
|
+
if (!extracted) return;
|
|
182
|
+
const { source, sourceMap, mapSource } = extracted;
|
|
107
183
|
const context = compilation.options.context;
|
|
108
184
|
const root = compilation.compiler.root;
|
|
109
185
|
const cachedAbsolutify = makePathsAbsolute.bindContextCache(context, root);
|
|
@@ -116,10 +192,10 @@ const getTaskForFile = (
|
|
|
116
192
|
|
|
117
193
|
return {
|
|
118
194
|
file,
|
|
119
|
-
|
|
120
|
-
source,
|
|
195
|
+
source: /** @type {string} */ (source),
|
|
121
196
|
assetInfo,
|
|
122
197
|
sourceMap,
|
|
198
|
+
mapSource,
|
|
123
199
|
modules,
|
|
124
200
|
cacheItem
|
|
125
201
|
};
|
|
@@ -127,6 +203,29 @@ const getTaskForFile = (
|
|
|
127
203
|
|
|
128
204
|
const PLUGIN_NAME = "SourceMapDevToolPlugin";
|
|
129
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Maps a configuration value (string, RegExp, function, nullish, or array of
|
|
208
|
+
* such) into a JSON-serializable form. Functions and RegExps are turned into
|
|
209
|
+
* their `.toString()` representation so that changes to inline callbacks
|
|
210
|
+
* invalidate caches; everything else is returned as-is so that the surrounding
|
|
211
|
+
* `JSON.stringify` does the escaping.
|
|
212
|
+
*
|
|
213
|
+
* The result is used through `JSON.stringify` to build cache identifiers, so
|
|
214
|
+
* we deliberately avoid any homemade `|` / `,` separators that could collide
|
|
215
|
+
* with characters appearing inside user-provided values such as `publicPath`,
|
|
216
|
+
* template strings, or `sourceRoot`.
|
|
217
|
+
* @param {EXPECTED_ANY} value option value
|
|
218
|
+
* @returns {EXPECTED_ANY} JSON-serializable representation
|
|
219
|
+
*/
|
|
220
|
+
const toCacheKeyValue = (value) => {
|
|
221
|
+
if (value === undefined || value === null) return value;
|
|
222
|
+
if (Array.isArray(value)) return value.map(toCacheKeyValue);
|
|
223
|
+
if (value instanceof RegExp || typeof value === "function") {
|
|
224
|
+
return value.toString();
|
|
225
|
+
}
|
|
226
|
+
return value;
|
|
227
|
+
};
|
|
228
|
+
|
|
130
229
|
class SourceMapDevToolPlugin {
|
|
131
230
|
/**
|
|
132
231
|
* Creates an instance of SourceMapDevToolPlugin.
|
|
@@ -136,7 +235,7 @@ class SourceMapDevToolPlugin {
|
|
|
136
235
|
constructor(options = {}) {
|
|
137
236
|
/** @type {undefined | null | false | string} */
|
|
138
237
|
this.sourceMapFilename = options.filename;
|
|
139
|
-
/** @type {false |
|
|
238
|
+
/** @type {false | SourceMappingURLComment} */
|
|
140
239
|
this.sourceMappingURLComment =
|
|
141
240
|
options.append === false
|
|
142
241
|
? false
|
|
@@ -153,6 +252,28 @@ class SourceMapDevToolPlugin {
|
|
|
153
252
|
this.namespace = options.namespace || "";
|
|
154
253
|
/** @type {SourceMapDevToolPluginOptions} */
|
|
155
254
|
this.options = options;
|
|
255
|
+
// Cache salt derived from output-affecting options, so that two
|
|
256
|
+
// SourceMapDevToolPlugin instances (or `devtool` + a plugin) operating
|
|
257
|
+
// on the same asset don't share a cache entry. We serialize via
|
|
258
|
+
// `JSON.stringify` rather than a homemade separator so that any
|
|
259
|
+
// special characters (e.g. `|` inside a publicPath or sourceRoot)
|
|
260
|
+
// can't accidentally make two different option sets collide.
|
|
261
|
+
/** @type {string} */
|
|
262
|
+
this._cacheSalt = JSON.stringify([
|
|
263
|
+
toCacheKeyValue(options.filename),
|
|
264
|
+
toCacheKeyValue(options.append),
|
|
265
|
+
toCacheKeyValue(this.moduleFilenameTemplate),
|
|
266
|
+
toCacheKeyValue(this.fallbackModuleFilenameTemplate),
|
|
267
|
+
toCacheKeyValue(this.namespace),
|
|
268
|
+
options.module !== false,
|
|
269
|
+
options.columns !== false,
|
|
270
|
+
Boolean(options.noSources),
|
|
271
|
+
Boolean(options.debugIds),
|
|
272
|
+
options.sourceRoot || "",
|
|
273
|
+
toCacheKeyValue(options.ignoreList),
|
|
274
|
+
options.publicPath || "",
|
|
275
|
+
options.fileContext || ""
|
|
276
|
+
]);
|
|
156
277
|
}
|
|
157
278
|
|
|
158
279
|
/**
|
|
@@ -195,6 +316,14 @@ class SourceMapDevToolPlugin {
|
|
|
195
316
|
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
196
317
|
new SourceMapDevToolModuleOptionsPlugin(options).apply(compilation);
|
|
197
318
|
|
|
319
|
+
// All SourceMapDevToolPlugin instances on the same compilation share
|
|
320
|
+
// a registry of pristine asset sources, so the second instance to
|
|
321
|
+
// run can still recover the original map after the first instance
|
|
322
|
+
// has replaced the asset with a `RawSource`. The registry lives on a
|
|
323
|
+
// module-scoped `WeakMap` keyed by compilation so it is released
|
|
324
|
+
// automatically and never pollutes the compilation object.
|
|
325
|
+
const originalSources = getOriginalSourceRegistry(compilation);
|
|
326
|
+
|
|
198
327
|
compilation.hooks.processAssets.tapAsync(
|
|
199
328
|
{
|
|
200
329
|
name: PLUGIN_NAME,
|
|
@@ -233,24 +362,47 @@ class SourceMapDevToolPlugin {
|
|
|
233
362
|
const tasks = [];
|
|
234
363
|
let fileIndex = 0;
|
|
235
364
|
|
|
365
|
+
// Shared deduplication set for `Source#clearCache` calls below.
|
|
366
|
+
// Webpack chunks routinely share module-level `CachedSource`
|
|
367
|
+
// instances. A per-call WeakSet would re-walk those shared
|
|
368
|
+
// subtrees once per chunk — 50 chunks × thousands of shared
|
|
369
|
+
// modules in dev/non-minified setups — and worse, every
|
|
370
|
+
// chunk's `sourceAndMap` would have to recompute the cleared
|
|
371
|
+
// caches, churning allocations (measured: +700 MB peak RSS,
|
|
372
|
+
// +6 s wall time on a 50×1000 synthetic build).
|
|
373
|
+
//
|
|
374
|
+
// Sharing one set lets each shared subtree be walked exactly
|
|
375
|
+
// once. The trade-off is that subsequent chunks' `sourceAndMap`
|
|
376
|
+
// calls can repopulate a shared module's `_cachedMaps` after
|
|
377
|
+
// its own clear was skipped (because the module is already in
|
|
378
|
+
// the visited set), leaving at most one populated cache entry
|
|
379
|
+
// per shared module at the end of the run — bounded to a few
|
|
380
|
+
// MB even at the scale of #20961. That's strictly preferable
|
|
381
|
+
// to the alternative's hundreds of MB of transient peak RSS.
|
|
382
|
+
const clearCacheVisited = new WeakSet();
|
|
383
|
+
|
|
236
384
|
asyncLib.each(
|
|
237
385
|
files,
|
|
238
386
|
(file, callback) => {
|
|
239
387
|
const asset =
|
|
240
388
|
/** @type {Readonly<Asset>} */
|
|
241
389
|
(compilation.getAsset(file));
|
|
242
|
-
if (asset.info.related && asset.info.related.sourceMap) {
|
|
243
|
-
fileIndex++;
|
|
244
|
-
return callback();
|
|
245
|
-
}
|
|
246
390
|
|
|
247
391
|
const chunk = fileToChunk.get(file);
|
|
248
392
|
const sourceMapNamespace = compilation.getPath(this.namespace, {
|
|
249
393
|
chunk
|
|
250
394
|
});
|
|
251
395
|
|
|
396
|
+
// The cache item identifier must include the per-instance
|
|
397
|
+
// salt so two SourceMapDevToolPlugin instances that target
|
|
398
|
+
// the same `file` don't collide in the persistent cache —
|
|
399
|
+
// they'd otherwise write different content to the same key
|
|
400
|
+
// and invalidate every pack on each build. We encode via
|
|
401
|
+
// `JSON.stringify` so that special characters (e.g. `|`)
|
|
402
|
+
// in an asset filename can't be spoofed to collide with the
|
|
403
|
+
// salt portion of the identifier.
|
|
252
404
|
const cacheItem = cache.getItemCache(
|
|
253
|
-
file,
|
|
405
|
+
JSON.stringify([file, this._cacheSalt]),
|
|
254
406
|
cache.mergeEtags(
|
|
255
407
|
cache.getLazyHashedEtag(asset.source),
|
|
256
408
|
sourceMapNamespace
|
|
@@ -265,6 +417,16 @@ class SourceMapDevToolPlugin {
|
|
|
265
417
|
* If presented in cache, reassigns assets. Cache assets already have source maps.
|
|
266
418
|
*/
|
|
267
419
|
if (cacheEntry) {
|
|
420
|
+
// Pin the still-unwrapped asset source in the registry
|
|
421
|
+
// before `compilation.updateAsset` replaces it. This is a
|
|
422
|
+
// pointer assignment — no source-map extraction work — and
|
|
423
|
+
// it lets a subsequent SourceMapDevToolPlugin instance
|
|
424
|
+
// extract the original map on demand even though the
|
|
425
|
+
// persistent cache hit lets us skip processing here.
|
|
426
|
+
if (!originalSources.has(file)) {
|
|
427
|
+
originalSources.set(file, asset.source);
|
|
428
|
+
}
|
|
429
|
+
|
|
268
430
|
const { assets, assetsInfo } = cacheEntry;
|
|
269
431
|
for (const cachedFile of Object.keys(assets)) {
|
|
270
432
|
if (cachedFile === file) {
|
|
@@ -313,9 +475,48 @@ class SourceMapDevToolPlugin {
|
|
|
313
475
|
columns: options.columns
|
|
314
476
|
},
|
|
315
477
|
compilation,
|
|
316
|
-
cacheItem
|
|
478
|
+
cacheItem,
|
|
479
|
+
originalSources
|
|
317
480
|
);
|
|
318
481
|
|
|
482
|
+
// Release the per-instance caches that `sourceAndMap`
|
|
483
|
+
// just populated. The composed map (and, for
|
|
484
|
+
// `SourceMapSource`, the parsed `_sourceMapAsObject` /
|
|
485
|
+
// `_innerSourceMapAsObject`) otherwise sit on the
|
|
486
|
+
// CachedSource — and every shared child — until phase
|
|
487
|
+
// 2 replaces the asset, which is what causes the OOM
|
|
488
|
+
// spike on builds with thousands of chunks
|
|
489
|
+
// (webpack#20961). Keep `source` since downstream
|
|
490
|
+
// consumers reading the original asset still need it;
|
|
491
|
+
// `hash`/`size` default to retained because they're
|
|
492
|
+
// cheap to keep but expensive to rebuild.
|
|
493
|
+
// `clearCacheVisited` is shared across every call (see
|
|
494
|
+
// its declaration above for the rationale).
|
|
495
|
+
//
|
|
496
|
+
// Target `task.mapSource` (not `asset.source`): when
|
|
497
|
+
// `extractSourceAndMap` falls back to the pinned
|
|
498
|
+
// original (the current asset is a `RawSource` left
|
|
499
|
+
// by an earlier plugin instance), the `sourceAndMap`
|
|
500
|
+
// call populated the original's caches, not the
|
|
501
|
+
// current asset's.
|
|
502
|
+
//
|
|
503
|
+
// Feature-detected: `clearCache` landed in
|
|
504
|
+
// `webpack-sources` 3.5, but `compilation.assets` may
|
|
505
|
+
// hold `Source`-like instances from a third-party
|
|
506
|
+
// plugin built against an older copy of
|
|
507
|
+
// `webpack-sources` (or a hand-rolled implementation).
|
|
508
|
+
// Calling it unconditionally would throw on those.
|
|
509
|
+
if (task && typeof task.mapSource.clearCache === "function") {
|
|
510
|
+
task.mapSource.clearCache(
|
|
511
|
+
{
|
|
512
|
+
maps: true,
|
|
513
|
+
source: false,
|
|
514
|
+
parsedMap: true
|
|
515
|
+
},
|
|
516
|
+
clearCacheVisited
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
|
|
319
520
|
if (task) {
|
|
320
521
|
const modules = task.modules;
|
|
321
522
|
|
|
@@ -446,12 +647,21 @@ class SourceMapDevToolPlugin {
|
|
|
446
647
|
"attach SourceMap"
|
|
447
648
|
);
|
|
448
649
|
|
|
449
|
-
const moduleFilenames =
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
650
|
+
const moduleFilenames =
|
|
651
|
+
/** @type {string[]} */
|
|
652
|
+
(modules.map((m) => moduleToSourceNameMapping.get(m)));
|
|
653
|
+
// We deliberately do NOT mutate `sourceMap` in place: the
|
|
654
|
+
// task's `sourceMap` reference may be shared with a
|
|
655
|
+
// `SourceMapSource` whose internal map cache is the same
|
|
656
|
+
// object (webpack-sources keeps it cached). A second
|
|
657
|
+
// `SourceMapDevToolPlugin` instance that reads the original
|
|
658
|
+
// source through the registry would otherwise see our
|
|
659
|
+
// rewrites. Instead we build a fresh `outputSourceMap` for
|
|
660
|
+
// the .map file and leave the original alone.
|
|
661
|
+
/** @type {number[] | undefined} */
|
|
662
|
+
let ignoreList;
|
|
453
663
|
if (options.ignoreList) {
|
|
454
|
-
const
|
|
664
|
+
const list = moduleFilenames.reduce(
|
|
455
665
|
/** @type {(acc: number[], sourceName: string, idx: number) => number[]} */ (
|
|
456
666
|
(acc, sourceName, idx) => {
|
|
457
667
|
const rule = /** @type {Rules} */ (
|
|
@@ -467,35 +677,29 @@ class SourceMapDevToolPlugin {
|
|
|
467
677
|
),
|
|
468
678
|
[]
|
|
469
679
|
);
|
|
470
|
-
if (
|
|
471
|
-
sourceMap.ignoreList = ignoreList;
|
|
472
|
-
}
|
|
680
|
+
if (list.length > 0) ignoreList = list;
|
|
473
681
|
}
|
|
474
682
|
|
|
475
|
-
if (options.noSources) {
|
|
476
|
-
sourceMap.sourcesContent = undefined;
|
|
477
|
-
}
|
|
478
|
-
sourceMap.sourceRoot = options.sourceRoot || "";
|
|
479
|
-
sourceMap.file = file;
|
|
480
683
|
const usesContentHash =
|
|
481
684
|
sourceMapFilename &&
|
|
482
685
|
CONTENT_HASH_DETECT_REGEXP.test(sourceMapFilename);
|
|
483
686
|
|
|
484
687
|
resetRegexpState(CONTENT_HASH_DETECT_REGEXP);
|
|
485
688
|
|
|
689
|
+
let outputFile = file;
|
|
486
690
|
// If SourceMap and asset uses contenthash, avoid a circular dependency by hiding hash in `file`
|
|
487
691
|
if (usesContentHash && task.assetInfo.contenthash) {
|
|
488
692
|
const contenthash = task.assetInfo.contenthash;
|
|
489
693
|
const pattern = Array.isArray(contenthash)
|
|
490
694
|
? contenthash.map(quoteMeta).join("|")
|
|
491
695
|
: quoteMeta(contenthash);
|
|
492
|
-
|
|
696
|
+
outputFile = outputFile.replace(
|
|
493
697
|
new RegExp(pattern, "g"),
|
|
494
698
|
(m) => "x".repeat(m.length)
|
|
495
699
|
);
|
|
496
700
|
}
|
|
497
701
|
|
|
498
|
-
/** @type {false |
|
|
702
|
+
/** @type {false | SourceMappingURLComment} */
|
|
499
703
|
let currentSourceMappingURLComment = sourceMappingURLComment;
|
|
500
704
|
const cssExtensionDetected =
|
|
501
705
|
CSS_EXTENSION_DETECT_REGEXP.test(file);
|
|
@@ -512,23 +716,64 @@ class SourceMapDevToolPlugin {
|
|
|
512
716
|
);
|
|
513
717
|
}
|
|
514
718
|
|
|
719
|
+
/** @type {string | undefined} */
|
|
720
|
+
let debugIdValue;
|
|
515
721
|
if (options.debugIds) {
|
|
516
|
-
const debugId = generateDebugId(source,
|
|
517
|
-
|
|
722
|
+
const debugId = generateDebugId(source, outputFile);
|
|
723
|
+
debugIdValue = debugId;
|
|
518
724
|
|
|
519
725
|
const debugIdComment = `\n//# debugId=${debugId}`;
|
|
520
|
-
currentSourceMappingURLComment
|
|
521
|
-
currentSourceMappingURLComment
|
|
522
|
-
|
|
523
|
-
|
|
726
|
+
if (currentSourceMappingURLComment === false) {
|
|
727
|
+
currentSourceMappingURLComment = debugIdComment;
|
|
728
|
+
} else if (
|
|
729
|
+
typeof currentSourceMappingURLComment === "function"
|
|
730
|
+
) {
|
|
731
|
+
// Wrap the user's append function so the debug-id
|
|
732
|
+
// comment is prepended at call time. Template-string
|
|
733
|
+
// concatenation would coerce the function to a string
|
|
734
|
+
// and lose its dynamic behavior.
|
|
735
|
+
const wrappedFn = currentSourceMappingURLComment;
|
|
736
|
+
currentSourceMappingURLComment = (pathData, assetInfo) =>
|
|
737
|
+
`${debugIdComment}${wrappedFn(pathData, assetInfo)}`;
|
|
738
|
+
} else {
|
|
739
|
+
currentSourceMappingURLComment = `${debugIdComment}${currentSourceMappingURLComment}`;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/** @type {RawSourceMap} */
|
|
744
|
+
const outputSourceMap = {
|
|
745
|
+
...sourceMap,
|
|
746
|
+
sources: moduleFilenames,
|
|
747
|
+
sourceRoot: options.sourceRoot || "",
|
|
748
|
+
file: outputFile
|
|
749
|
+
};
|
|
750
|
+
if (ignoreList !== undefined) {
|
|
751
|
+
outputSourceMap.ignoreList = ignoreList;
|
|
752
|
+
}
|
|
753
|
+
if (options.noSources) {
|
|
754
|
+
outputSourceMap.sourcesContent = undefined;
|
|
755
|
+
}
|
|
756
|
+
if (debugIdValue !== undefined) {
|
|
757
|
+
outputSourceMap.debugId = debugIdValue;
|
|
524
758
|
}
|
|
525
759
|
|
|
526
|
-
const sourceMapString = JSON.stringify(sourceMap);
|
|
527
760
|
if (sourceMapFilename) {
|
|
761
|
+
// External `.map` file: hold the serialized map as a
|
|
762
|
+
// `Buffer` instead of a V8 string. `RawSource` accepts
|
|
763
|
+
// a buffer directly, and the emitted asset stays in
|
|
764
|
+
// `compilation.assets` until the build finishes — so
|
|
765
|
+
// storing the bytes off the V8 heap (where Buffers
|
|
766
|
+
// live, accounted as `external` memory) avoids keeping
|
|
767
|
+
// a large V8 string alive for the rest of the build
|
|
768
|
+
// and reduces heap pressure on `--max-old-space-size`.
|
|
769
|
+
const sourceMapBuffer = Buffer.from(
|
|
770
|
+
JSON.stringify(outputSourceMap),
|
|
771
|
+
"utf8"
|
|
772
|
+
);
|
|
528
773
|
const filename = file;
|
|
529
774
|
const sourceMapContentHash = usesContentHash
|
|
530
775
|
? createHash(compilation.outputOptions.hashFunction)
|
|
531
|
-
.update(
|
|
776
|
+
.update(sourceMapBuffer)
|
|
532
777
|
.digest("hex")
|
|
533
778
|
: undefined;
|
|
534
779
|
|
|
@@ -567,14 +812,40 @@ class SourceMapDevToolPlugin {
|
|
|
567
812
|
})
|
|
568
813
|
);
|
|
569
814
|
}
|
|
815
|
+
// Preserve any existing related.sourceMap entries from
|
|
816
|
+
// earlier SourceMapDevToolPlugin runs on the same asset so
|
|
817
|
+
// that all generated maps remain discoverable via asset
|
|
818
|
+
// info (the schema allows string or string[]).
|
|
819
|
+
const existingSourceMap =
|
|
820
|
+
task.assetInfo.related &&
|
|
821
|
+
task.assetInfo.related.sourceMap;
|
|
822
|
+
/** @type {string | string[]} */
|
|
823
|
+
let relatedSourceMap;
|
|
824
|
+
if (
|
|
825
|
+
existingSourceMap === undefined ||
|
|
826
|
+
existingSourceMap === null
|
|
827
|
+
) {
|
|
828
|
+
relatedSourceMap = sourceMapFile;
|
|
829
|
+
} else if (Array.isArray(existingSourceMap)) {
|
|
830
|
+
relatedSourceMap = existingSourceMap.includes(
|
|
831
|
+
sourceMapFile
|
|
832
|
+
)
|
|
833
|
+
? existingSourceMap
|
|
834
|
+
: [...existingSourceMap, sourceMapFile];
|
|
835
|
+
} else {
|
|
836
|
+
relatedSourceMap =
|
|
837
|
+
existingSourceMap === sourceMapFile
|
|
838
|
+
? existingSourceMap
|
|
839
|
+
: [existingSourceMap, sourceMapFile];
|
|
840
|
+
}
|
|
570
841
|
const assetInfo = {
|
|
571
|
-
related: { sourceMap:
|
|
842
|
+
related: { sourceMap: relatedSourceMap }
|
|
572
843
|
};
|
|
573
844
|
assets[file] = asset;
|
|
574
845
|
assetsInfo[file] = assetInfo;
|
|
575
846
|
compilation.updateAsset(file, asset, assetInfo);
|
|
576
847
|
// Add source map file to compilation assets and chunk files
|
|
577
|
-
const sourceMapAsset = new RawSource(
|
|
848
|
+
const sourceMapAsset = new RawSource(sourceMapBuffer);
|
|
578
849
|
const sourceMapAssetInfo = {
|
|
579
850
|
...sourceMapInfo,
|
|
580
851
|
development: true
|
|
@@ -600,6 +871,16 @@ class SourceMapDevToolPlugin {
|
|
|
600
871
|
`${PLUGIN_NAME}: append can't be a function when no filename is provided`
|
|
601
872
|
);
|
|
602
873
|
}
|
|
874
|
+
// Inline data-URL form: `[map]` gets the raw JSON, `[url]`
|
|
875
|
+
// gets the same JSON base64-encoded. `URL_COMMENT_REGEXP`
|
|
876
|
+
// is a `/g` regex, so a user `append` template with more
|
|
877
|
+
// than one `[url]` placeholder would otherwise re-encode
|
|
878
|
+
// the same JSON per match. Pre-compute both once.
|
|
879
|
+
const sourceMapString = JSON.stringify(outputSourceMap);
|
|
880
|
+
const sourceMapBase64 = Buffer.from(
|
|
881
|
+
sourceMapString,
|
|
882
|
+
"utf8"
|
|
883
|
+
).toString("base64");
|
|
603
884
|
/**
|
|
604
885
|
* Add source map as data url to asset
|
|
605
886
|
*/
|
|
@@ -610,10 +891,7 @@ class SourceMapDevToolPlugin {
|
|
|
610
891
|
.replace(
|
|
611
892
|
URL_COMMENT_REGEXP,
|
|
612
893
|
() =>
|
|
613
|
-
`data:application/json;charset=utf-8;base64,${
|
|
614
|
-
sourceMapString,
|
|
615
|
-
"utf8"
|
|
616
|
-
).toString("base64")}`
|
|
894
|
+
`data:application/json;charset=utf-8;base64,${sourceMapBase64}`
|
|
617
895
|
)
|
|
618
896
|
);
|
|
619
897
|
assets[file] = asset;
|
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 {
|
|
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
|
-
/**
|
|
142
|
-
|
|
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
|
-
* @
|
|
147
|
-
* @param {
|
|
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
|
*/
|
|
@@ -9,11 +9,11 @@ const { RawSource } = require("webpack-sources");
|
|
|
9
9
|
const ConcatenationScope = require("../ConcatenationScope");
|
|
10
10
|
const Generator = require("../Generator");
|
|
11
11
|
const {
|
|
12
|
+
ASSET_URL_TYPE,
|
|
13
|
+
ASSET_URL_TYPES,
|
|
12
14
|
CSS_TYPE,
|
|
13
|
-
CSS_URL_TYPE,
|
|
14
|
-
CSS_URL_TYPES,
|
|
15
15
|
HTML_TYPE,
|
|
16
|
-
|
|
16
|
+
JAVASCRIPT_AND_ASSET_URL_TYPES,
|
|
17
17
|
JAVASCRIPT_TYPE,
|
|
18
18
|
JAVASCRIPT_TYPES,
|
|
19
19
|
NO_TYPES
|
|
@@ -80,7 +80,7 @@ class AssetSourceGenerator extends Generator {
|
|
|
80
80
|
}
|
|
81
81
|
return new RawSource(sourceContent);
|
|
82
82
|
}
|
|
83
|
-
case
|
|
83
|
+
case ASSET_URL_TYPE: {
|
|
84
84
|
if (!originalSource) {
|
|
85
85
|
return null;
|
|
86
86
|
}
|
|
@@ -151,9 +151,9 @@ class AssetSourceGenerator extends Generator {
|
|
|
151
151
|
sourceTypes.has(JAVASCRIPT_TYPE) &&
|
|
152
152
|
(sourceTypes.has(CSS_TYPE) || sourceTypes.has(HTML_TYPE))
|
|
153
153
|
) {
|
|
154
|
-
return
|
|
154
|
+
return JAVASCRIPT_AND_ASSET_URL_TYPES;
|
|
155
155
|
} else if (sourceTypes.has(CSS_TYPE) || sourceTypes.has(HTML_TYPE)) {
|
|
156
|
-
return
|
|
156
|
+
return ASSET_URL_TYPES;
|
|
157
157
|
}
|
|
158
158
|
return JAVASCRIPT_TYPES;
|
|
159
159
|
}
|