\x89PNG\r\n\x1a\n\x00\x00\x00\x0DIHDR\x00\x00\x00\x01\x00 \x00\x00\x01\x08\x06\x00\x00\x00\x1F\x15\xC4\x89\x00\x00\x00 \x0AIDATx\x9Ccb\x00\x00\x00\x06\x00\x03\x1A\x05\x9D\x00\x00 \x00\x00IEND\xAE\x42\x60\x82
| Path : /var/www/html/infraai.ai/frontend/node_modules/webpack/lib/ |
|
B-Con CMD Config cPanel C-Rdp D-Log Info Jump Mass Ransom Symlink vHost Zone-H |
| Current File : /var/www/html/infraai.ai/frontend/node_modules/webpack/lib/SourceMapDevToolPlugin.js |
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const asyncLib = require("neo-async");
const { ConcatSource, RawSource } = require("webpack-sources");
const Compilation = require("./Compilation");
const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
const ProgressPlugin = require("./ProgressPlugin");
const SourceMapDevToolModuleOptionsPlugin = require("./SourceMapDevToolModuleOptionsPlugin");
const createHash = require("./util/createHash");
const { dirname, relative } = require("./util/fs");
const generateDebugId = require("./util/generateDebugId");
const { makePathsAbsolute } = require("./util/identifier");
/** @typedef {import("webpack-sources").MapOptions} MapOptions */
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../declarations/WebpackOptions").DevtoolNamespace} DevtoolNamespace */
/** @typedef {import("../declarations/WebpackOptions").DevtoolModuleFilenameTemplate} DevtoolModuleFilenameTemplate */
/** @typedef {import("../declarations/WebpackOptions").DevtoolFallbackModuleFilenameTemplate} DevtoolFallbackModuleFilenameTemplate */
/** @typedef {import("../declarations/plugins/SourceMapDevToolPlugin").SourceMapDevToolPluginOptions} SourceMapDevToolPluginOptions */
/** @typedef {import("../declarations/plugins/SourceMapDevToolPlugin").Rules} Rules */
/** @typedef {import("./CacheFacade").ItemCacheFacade} ItemCacheFacade */
/** @typedef {import("./Chunk")} Chunk */
/** @typedef {import("./Compilation").Asset} Asset */
/** @typedef {import("./Compilation").AssetInfo} AssetInfo */
/** @typedef {import("./Compiler")} Compiler */
/** @typedef {import("./Module")} Module */
/** @typedef {import("./NormalModule").RawSourceMap} RawSourceMap */
/** @typedef {import("./TemplatedPathPlugin").TemplatePath} SourceMappingURLComment */
/** @typedef {import("./util/fs").OutputFileSystem} OutputFileSystem */
/**
* Defines the source map task type used by this module.
* @typedef {object} SourceMapTask
* @property {AssetInfo} assetInfo
* @property {(string | Module)[]} modules
* @property {string} source
* @property {string} file
* @property {RawSourceMap} sourceMap
* @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
* @property {ItemCacheFacade} cacheItem cache item
*/
const METACHARACTERS_REGEXP = /[-[\]\\/{}()*+?.^$|]/g;
const CONTENT_HASH_DETECT_REGEXP = /\[contenthash(?::\w+)?\]/;
const CSS_AND_JS_MODULE_EXTENSIONS_REGEXP = /\.((c|m)?js|css)($|\?)/i;
const CSS_EXTENSION_DETECT_REGEXP = /\.css(?:$|\?)/i;
const MAP_URL_COMMENT_REGEXP = /\[map\]/g;
const URL_COMMENT_REGEXP = /\[url\]/g;
const URL_FORMATTING_REGEXP = /^\n\/\/(.*)$/;
/**
* Reset's .lastIndex of stateful Regular Expressions
* For when `test` or `exec` is called on them
* @param {RegExp} regexp Stateful Regular Expression to be reset
* @returns {void}
*/
const resetRegexpState = (regexp) => {
regexp.lastIndex = -1;
};
/**
* Escapes regular expression metacharacters
* @param {string} str String to quote
* @returns {string} Escaped string
*/
const quoteMeta = (str) => str.replace(METACHARACTERS_REGEXP, "\\$&");
/**
* Compilation-scoped registry of original asset sources for multi-plugin
* cooperation. The first SourceMapDevToolPlugin instance to see a file pins a
* reference to the asset's still-unwrapped {@link Source} object; later
* instances whose `asset.source.sourceAndMap()` would now return `null` (the
* earlier instance replaced the asset with a `RawSource`) can re-extract the
* map from this pinned reference. We keep the registry on a module-scoped
* `WeakMap` so the entries are reclaimed automatically when the compilation
* itself becomes unreachable; we never store anything on the compilation
* object directly.
*
* Stashing the `Source` object itself rather than an extracted map keeps the
* fast path free of cloning and source-map serialization work — the
* extraction only happens if a subsequent plugin actually needs the map.
* @type {WeakMap<Compilation, Map<string, Source>>}
*/
const originalSourceRegistry = new WeakMap();
/**
* Returns (creating if necessary) the per-compilation registry of original
* asset {@link Source} objects.
* @param {Compilation} compilation compilation
* @returns {Map<string, Source>} registry
*/
const getOriginalSourceRegistry = (compilation) => {
let registry = originalSourceRegistry.get(compilation);
if (registry === undefined) {
registry = new Map();
originalSourceRegistry.set(compilation, registry);
}
return registry;
};
/**
* Extracts source and source map from a Source object, falling back to a
* registered original source for assets that another SourceMapDevToolPlugin
* instance has already wrapped (whose internal map is now `null`).
*
* The returned source is read from the asset as it currently stands — that way
* any `sourceMappingURL` comments appended by earlier plugin instances survive
* — while the map is taken from the pinned original Source when the current
* one no longer carries it. `mapSource` identifies which Source instance was
* actually queried for the map (the current asset, or the pinned original);
* that's the one whose internal caches the caller should release.
* @param {string} file file name
* @param {Source} asset source object as currently held by the compilation
* @param {MapOptions} options map extraction options
* @param {Map<string, Source>} registry compilation-scoped original-source registry
* @returns {{ source: string, sourceMap: RawSourceMap, mapSource: Source } | undefined} extracted pair or `undefined` when no map is recoverable
*/
const extractSourceAndMap = (file, asset, options, registry) => {
/** @type {string | Buffer} */
let source;
/** @type {null | RawSourceMap} */
let sourceMap;
if (asset.sourceAndMap) {
const sourceAndMap = asset.sourceAndMap(options);
source = sourceAndMap.source;
sourceMap = sourceAndMap.map;
} else {
source = asset.source();
sourceMap = asset.map(options);
}
// Bail before touching the registry if we can't return a usable string
// source — pinning a non-string-producing asset would only waste the slot.
if (typeof source !== "string") return;
if (sourceMap) {
// The current asset still owns the original map — pin a reference so
// that a later plugin instance (which will see a rewrapped asset
// without a map) can recover it on demand.
if (!registry.has(file)) registry.set(file, asset);
return { source, sourceMap, mapSource: asset };
}
// The current asset (typically a `RawSource` left by an earlier
// SourceMapDevToolPlugin instance) has no internal map. Re-extract
// the map from the original Source we pinned earlier. We keep using
// `source` from the current asset so that any prior wrappers (e.g.
// appended sourceMappingURL comments) are preserved.
const original = registry.get(file);
if (!original) return;
sourceMap = original.sourceAndMap
? original.sourceAndMap(options).map
: original.map(options);
if (!sourceMap) return;
return { source, sourceMap, mapSource: original };
};
/**
* Creating {@link SourceMapTask} for given file
* @param {string} file current compiled file
* @param {Source} asset the asset
* @param {AssetInfo} assetInfo the asset info
* @param {MapOptions} options source map options
* @param {Compilation} compilation compilation instance
* @param {ItemCacheFacade} cacheItem cache item
* @param {Map<string, Source>} registry compilation-scoped original-source registry
* @returns {SourceMapTask | undefined} created task instance or `undefined`
*/
const getTaskForFile = (
file,
asset,
assetInfo,
options,
compilation,
cacheItem,
registry
) => {
const extracted = extractSourceAndMap(file, asset, options, registry);
if (!extracted) return;
const { source, sourceMap, mapSource } = extracted;
const context = compilation.options.context;
const root = compilation.compiler.root;
const cachedAbsolutify = makePathsAbsolute.bindContextCache(context, root);
const modules = sourceMap.sources.map((source) => {
if (!source.startsWith("webpack://")) return source;
source = cachedAbsolutify(source.slice(10));
const module = compilation.findModule(source);
return module || source;
});
return {
file,
source: /** @type {string} */ (source),
assetInfo,
sourceMap,
mapSource,
modules,
cacheItem
};
};
const PLUGIN_NAME = "SourceMapDevToolPlugin";
/**
* Maps a configuration value (string, RegExp, function, nullish, or array of
* such) into a JSON-serializable form. Functions and RegExps are turned into
* their `.toString()` representation so that changes to inline callbacks
* invalidate caches; everything else is returned as-is so that the surrounding
* `JSON.stringify` does the escaping.
*
* The result is used through `JSON.stringify` to build cache identifiers, so
* we deliberately avoid any homemade `|` / `,` separators that could collide
* with characters appearing inside user-provided values such as `publicPath`,
* template strings, or `sourceRoot`.
* @param {EXPECTED_ANY} value option value
* @returns {EXPECTED_ANY} JSON-serializable representation
*/
const toCacheKeyValue = (value) => {
if (value === undefined || value === null) return value;
if (Array.isArray(value)) return value.map(toCacheKeyValue);
if (value instanceof RegExp || typeof value === "function") {
return value.toString();
}
return value;
};
class SourceMapDevToolPlugin {
/**
* Creates an instance of SourceMapDevToolPlugin.
* @param {SourceMapDevToolPluginOptions=} options options object
* @throws {Error} throws error, if got more than 1 arguments
*/
constructor(options = {}) {
/** @type {undefined | null | false | string} */
this.sourceMapFilename = options.filename;
/** @type {false | SourceMappingURLComment} */
this.sourceMappingURLComment =
options.append === false
? false
: // eslint-disable-next-line no-useless-concat
options.append || "\n//# source" + "MappingURL=[url]";
/** @type {DevtoolModuleFilenameTemplate} */
this.moduleFilenameTemplate =
options.moduleFilenameTemplate || "webpack://[namespace]/[resourcePath]";
/** @type {DevtoolFallbackModuleFilenameTemplate} */
this.fallbackModuleFilenameTemplate =
options.fallbackModuleFilenameTemplate ||
"webpack://[namespace]/[resourcePath]?[hash]";
/** @type {DevtoolNamespace} */
this.namespace = options.namespace || "";
/** @type {SourceMapDevToolPluginOptions} */
this.options = options;
// Cache salt derived from output-affecting options, so that two
// SourceMapDevToolPlugin instances (or `devtool` + a plugin) operating
// on the same asset don't share a cache entry. We serialize via
// `JSON.stringify` rather than a homemade separator so that any
// special characters (e.g. `|` inside a publicPath or sourceRoot)
// can't accidentally make two different option sets collide.
/** @type {string} */
this._cacheSalt = JSON.stringify([
toCacheKeyValue(options.filename),
toCacheKeyValue(options.append),
toCacheKeyValue(this.moduleFilenameTemplate),
toCacheKeyValue(this.fallbackModuleFilenameTemplate),
toCacheKeyValue(this.namespace),
options.module !== false,
options.columns !== false,
Boolean(options.noSources),
Boolean(options.debugIds),
options.sourceRoot || "",
toCacheKeyValue(options.ignoreList),
options.publicPath || "",
options.fileContext || ""
]);
}
/**
* Applies the plugin by registering its hooks on the compiler.
* @param {Compiler} compiler compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.hooks.validate.tap(PLUGIN_NAME, () => {
compiler.validate(
() => require("../schemas/plugins/SourceMapDevToolPlugin.json"),
this.options,
{
name: "SourceMap DevTool Plugin",
baseDataPath: "options"
},
(options) =>
require("../schemas/plugins/SourceMapDevToolPlugin.check")(options)
);
});
const outputFs =
/** @type {OutputFileSystem} */
(compiler.outputFileSystem);
const sourceMapFilename = this.sourceMapFilename;
const sourceMappingURLComment = this.sourceMappingURLComment;
const moduleFilenameTemplate = this.moduleFilenameTemplate;
const namespace = this.namespace;
const fallbackModuleFilenameTemplate = this.fallbackModuleFilenameTemplate;
const requestShortener = compiler.requestShortener;
const options = this.options;
options.test = options.test || CSS_AND_JS_MODULE_EXTENSIONS_REGEXP;
/** @type {(filename: string) => boolean} */
const matchObject = ModuleFilenameHelpers.matchObject.bind(
undefined,
options
);
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
new SourceMapDevToolModuleOptionsPlugin(options).apply(compilation);
// All SourceMapDevToolPlugin instances on the same compilation share
// a registry of pristine asset sources, so the second instance to
// run can still recover the original map after the first instance
// has replaced the asset with a `RawSource`. The registry lives on a
// module-scoped `WeakMap` keyed by compilation so it is released
// automatically and never pollutes the compilation object.
const originalSources = getOriginalSourceRegistry(compilation);
compilation.hooks.processAssets.tapAsync(
{
name: PLUGIN_NAME,
stage: Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING,
additionalAssets: true
},
(assets, callback) => {
const chunkGraph = compilation.chunkGraph;
const cache = compilation.getCache(PLUGIN_NAME);
/** @type {Map<string | Module, string>} */
const moduleToSourceNameMapping = new Map();
const reportProgress =
ProgressPlugin.getReporter(compilation.compiler) || (() => {});
/** @type {Map<string, Chunk>} */
const fileToChunk = new Map();
for (const chunk of compilation.chunks) {
for (const file of chunk.files) {
fileToChunk.set(file, chunk);
}
for (const file of chunk.auxiliaryFiles) {
fileToChunk.set(file, chunk);
}
}
/** @type {string[]} */
const files = [];
for (const file of Object.keys(assets)) {
if (matchObject(file)) {
files.push(file);
}
}
reportProgress(0);
/** @type {SourceMapTask[]} */
const tasks = [];
let fileIndex = 0;
// Shared deduplication set for `Source#clearCache` calls below.
// Webpack chunks routinely share module-level `CachedSource`
// instances. A per-call WeakSet would re-walk those shared
// subtrees once per chunk — 50 chunks × thousands of shared
// modules in dev/non-minified setups — and worse, every
// chunk's `sourceAndMap` would have to recompute the cleared
// caches, churning allocations (measured: +700 MB peak RSS,
// +6 s wall time on a 50×1000 synthetic build).
//
// Sharing one set lets each shared subtree be walked exactly
// once. The trade-off is that subsequent chunks' `sourceAndMap`
// calls can repopulate a shared module's `_cachedMaps` after
// its own clear was skipped (because the module is already in
// the visited set), leaving at most one populated cache entry
// per shared module at the end of the run — bounded to a few
// MB even at the scale of #20961. That's strictly preferable
// to the alternative's hundreds of MB of transient peak RSS.
const clearCacheVisited = new WeakSet();
asyncLib.each(
files,
(file, callback) => {
const asset =
/** @type {Readonly<Asset>} */
(compilation.getAsset(file));
const chunk = fileToChunk.get(file);
const sourceMapNamespace = compilation.getPath(this.namespace, {
chunk
});
// The cache item identifier must include the per-instance
// salt so two SourceMapDevToolPlugin instances that target
// the same `file` don't collide in the persistent cache —
// they'd otherwise write different content to the same key
// and invalidate every pack on each build. We encode via
// `JSON.stringify` so that special characters (e.g. `|`)
// in an asset filename can't be spoofed to collide with the
// salt portion of the identifier.
const cacheItem = cache.getItemCache(
JSON.stringify([file, this._cacheSalt]),
cache.mergeEtags(
cache.getLazyHashedEtag(asset.source),
sourceMapNamespace
)
);
cacheItem.get((err, cacheEntry) => {
if (err) {
return callback(err);
}
/**
* If presented in cache, reassigns assets. Cache assets already have source maps.
*/
if (cacheEntry) {
// Pin the still-unwrapped asset source in the registry
// before `compilation.updateAsset` replaces it. This is a
// pointer assignment — no source-map extraction work — and
// it lets a subsequent SourceMapDevToolPlugin instance
// extract the original map on demand even though the
// persistent cache hit lets us skip processing here.
if (!originalSources.has(file)) {
originalSources.set(file, asset.source);
}
const { assets, assetsInfo } = cacheEntry;
for (const cachedFile of Object.keys(assets)) {
if (cachedFile === file) {
compilation.updateAsset(
cachedFile,
assets[cachedFile],
assetsInfo[cachedFile]
);
} else {
compilation.emitAsset(
cachedFile,
assets[cachedFile],
assetsInfo[cachedFile]
);
}
/**
* Add file to chunk, if not presented there
*/
if (cachedFile !== file && chunk !== undefined) {
chunk.auxiliaryFiles.add(cachedFile);
}
}
reportProgress(
(0.5 * ++fileIndex) / files.length,
file,
"restored cached SourceMap"
);
return callback();
}
reportProgress(
(0.5 * fileIndex) / files.length,
file,
"generate SourceMap"
);
/** @type {SourceMapTask | undefined} */
const task = getTaskForFile(
file,
asset.source,
asset.info,
{
module: options.module,
columns: options.columns
},
compilation,
cacheItem,
originalSources
);
// Release the per-instance caches that `sourceAndMap`
// just populated. The composed map (and, for
// `SourceMapSource`, the parsed `_sourceMapAsObject` /
// `_innerSourceMapAsObject`) otherwise sit on the
// CachedSource — and every shared child — until phase
// 2 replaces the asset, which is what causes the OOM
// spike on builds with thousands of chunks
// (webpack#20961). Keep `source` since downstream
// consumers reading the original asset still need it;
// `hash`/`size` default to retained because they're
// cheap to keep but expensive to rebuild.
// `clearCacheVisited` is shared across every call (see
// its declaration above for the rationale).
//
// Target `task.mapSource` (not `asset.source`): when
// `extractSourceAndMap` falls back to the pinned
// original (the current asset is a `RawSource` left
// by an earlier plugin instance), the `sourceAndMap`
// call populated the original's caches, not the
// current asset's.
//
// Feature-detected: `clearCache` landed in
// `webpack-sources` 3.5, but `compilation.assets` may
// hold `Source`-like instances from a third-party
// plugin built against an older copy of
// `webpack-sources` (or a hand-rolled implementation).
// Calling it unconditionally would throw on those.
if (task && typeof task.mapSource.clearCache === "function") {
task.mapSource.clearCache(
{
maps: true,
source: false,
parsedMap: true
},
clearCacheVisited
);
}
if (task) {
const modules = task.modules;
for (let idx = 0; idx < modules.length; idx++) {
const module = modules[idx];
if (
typeof module === "string" &&
/^(?:data|https?):/.test(module)
) {
moduleToSourceNameMapping.set(module, module);
continue;
}
if (!moduleToSourceNameMapping.get(module)) {
moduleToSourceNameMapping.set(
module,
ModuleFilenameHelpers.createFilename(
module,
{
moduleFilenameTemplate,
namespace: sourceMapNamespace
},
{
requestShortener,
chunkGraph,
hashFunction: compilation.outputOptions.hashFunction
}
)
);
}
}
tasks.push(task);
}
reportProgress(
(0.5 * ++fileIndex) / files.length,
file,
"generated SourceMap"
);
callback();
});
},
(err) => {
if (err) {
return callback(err);
}
reportProgress(0.5, "resolve sources");
/** @type {Set<string>} */
const usedNamesSet = new Set(moduleToSourceNameMapping.values());
/** @type {Set<string>} */
const conflictDetectionSet = new Set();
/**
* all modules in defined order (longest identifier first)
* @type {(string | Module)[]}
*/
const allModules = [...moduleToSourceNameMapping.keys()].sort(
(a, b) => {
const ai = typeof a === "string" ? a : a.identifier();
const bi = typeof b === "string" ? b : b.identifier();
return ai.length - bi.length;
}
);
// find modules with conflicting source names
for (let idx = 0; idx < allModules.length; idx++) {
const module = allModules[idx];
let sourceName =
/** @type {string} */
(moduleToSourceNameMapping.get(module));
let hasName = conflictDetectionSet.has(sourceName);
if (!hasName) {
conflictDetectionSet.add(sourceName);
continue;
}
// try the fallback name first
sourceName = ModuleFilenameHelpers.createFilename(
module,
{
moduleFilenameTemplate: fallbackModuleFilenameTemplate,
namespace
},
{
requestShortener,
chunkGraph,
hashFunction: compilation.outputOptions.hashFunction
}
);
hasName = usedNamesSet.has(sourceName);
if (!hasName) {
moduleToSourceNameMapping.set(module, sourceName);
usedNamesSet.add(sourceName);
continue;
}
// otherwise just append stars until we have a valid name
while (hasName) {
sourceName += "*";
hasName = usedNamesSet.has(sourceName);
}
moduleToSourceNameMapping.set(module, sourceName);
usedNamesSet.add(sourceName);
}
let taskIndex = 0;
asyncLib.each(
tasks,
(task, callback) => {
/** @type {Record<string, Source>} */
const assets = Object.create(null);
/** @type {Record<string, AssetInfo | undefined>} */
const assetsInfo = Object.create(null);
const file = task.file;
const chunk = fileToChunk.get(file);
const sourceMap = task.sourceMap;
const source = task.source;
const modules = task.modules;
reportProgress(
0.5 + (0.5 * taskIndex) / tasks.length,
file,
"attach SourceMap"
);
const moduleFilenames =
/** @type {string[]} */
(modules.map((m) => moduleToSourceNameMapping.get(m)));
// We deliberately do NOT mutate `sourceMap` in place: the
// task's `sourceMap` reference may be shared with a
// `SourceMapSource` whose internal map cache is the same
// object (webpack-sources keeps it cached). A second
// `SourceMapDevToolPlugin` instance that reads the original
// source through the registry would otherwise see our
// rewrites. Instead we build a fresh `outputSourceMap` for
// the .map file and leave the original alone.
/** @type {number[] | undefined} */
let ignoreList;
if (options.ignoreList) {
const list = moduleFilenames.reduce(
/** @type {(acc: number[], sourceName: string, idx: number) => number[]} */ (
(acc, sourceName, idx) => {
const rule = /** @type {Rules} */ (
options.ignoreList
);
if (
ModuleFilenameHelpers.matchPart(sourceName, rule)
) {
acc.push(idx);
}
return acc;
}
),
[]
);
if (list.length > 0) ignoreList = list;
}
const usesContentHash =
sourceMapFilename &&
CONTENT_HASH_DETECT_REGEXP.test(sourceMapFilename);
resetRegexpState(CONTENT_HASH_DETECT_REGEXP);
let outputFile = file;
// If SourceMap and asset uses contenthash, avoid a circular dependency by hiding hash in `file`
if (usesContentHash && task.assetInfo.contenthash) {
const contenthash = task.assetInfo.contenthash;
const pattern = Array.isArray(contenthash)
? contenthash.map(quoteMeta).join("|")
: quoteMeta(contenthash);
outputFile = outputFile.replace(
new RegExp(pattern, "g"),
(m) => "x".repeat(m.length)
);
}
/** @type {false | SourceMappingURLComment} */
let currentSourceMappingURLComment = sourceMappingURLComment;
const cssExtensionDetected =
CSS_EXTENSION_DETECT_REGEXP.test(file);
resetRegexpState(CSS_EXTENSION_DETECT_REGEXP);
if (
currentSourceMappingURLComment !== false &&
typeof currentSourceMappingURLComment !== "function" &&
cssExtensionDetected
) {
currentSourceMappingURLComment =
currentSourceMappingURLComment.replace(
URL_FORMATTING_REGEXP,
"\n/*$1*/"
);
}
/** @type {string | undefined} */
let debugIdValue;
if (options.debugIds) {
const debugId = generateDebugId(source, outputFile);
debugIdValue = debugId;
const debugIdComment = `\n//# debugId=${debugId}`;
if (currentSourceMappingURLComment === false) {
currentSourceMappingURLComment = debugIdComment;
} else if (
typeof currentSourceMappingURLComment === "function"
) {
// Wrap the user's append function so the debug-id
// comment is prepended at call time. Template-string
// concatenation would coerce the function to a string
// and lose its dynamic behavior.
const wrappedFn = currentSourceMappingURLComment;
currentSourceMappingURLComment = (pathData, assetInfo) =>
`${debugIdComment}${wrappedFn(pathData, assetInfo)}`;
} else {
currentSourceMappingURLComment = `${debugIdComment}${currentSourceMappingURLComment}`;
}
}
/** @type {RawSourceMap} */
const outputSourceMap = {
...sourceMap,
sources: moduleFilenames,
sourceRoot: options.sourceRoot || "",
file: outputFile
};
if (ignoreList !== undefined) {
outputSourceMap.ignoreList = ignoreList;
}
if (options.noSources) {
outputSourceMap.sourcesContent = undefined;
}
if (debugIdValue !== undefined) {
outputSourceMap.debugId = debugIdValue;
}
if (sourceMapFilename) {
// External `.map` file: hold the serialized map as a
// `Buffer` instead of a V8 string. `RawSource` accepts
// a buffer directly, and the emitted asset stays in
// `compilation.assets` until the build finishes — so
// storing the bytes off the V8 heap (where Buffers
// live, accounted as `external` memory) avoids keeping
// a large V8 string alive for the rest of the build
// and reduces heap pressure on `--max-old-space-size`.
const sourceMapBuffer = Buffer.from(
JSON.stringify(outputSourceMap),
"utf8"
);
const filename = file;
const sourceMapContentHash = usesContentHash
? createHash(compilation.outputOptions.hashFunction)
.update(sourceMapBuffer)
.digest("hex")
: undefined;
const pathParams = {
chunk,
filename: options.fileContext
? relative(
outputFs,
`/${options.fileContext}`,
`/${filename}`
)
: filename,
contentHash: sourceMapContentHash
};
const { path: sourceMapFile, info: sourceMapInfo } =
compilation.getPathWithInfo(
sourceMapFilename,
pathParams
);
const sourceMapUrl = options.publicPath
? options.publicPath + sourceMapFile
: relative(
outputFs,
dirname(outputFs, `/${file}`),
`/${sourceMapFile}`
);
/** @type {Source} */
let asset = new RawSource(source);
if (currentSourceMappingURLComment !== false) {
// Add source map url to compilation asset, if currentSourceMappingURLComment is set
asset = new ConcatSource(
asset,
compilation.getPath(currentSourceMappingURLComment, {
url: sourceMapUrl,
...pathParams
})
);
}
// Preserve any existing related.sourceMap entries from
// earlier SourceMapDevToolPlugin runs on the same asset so
// that all generated maps remain discoverable via asset
// info (the schema allows string or string[]).
const existingSourceMap =
task.assetInfo.related &&
task.assetInfo.related.sourceMap;
/** @type {string | string[]} */
let relatedSourceMap;
if (
existingSourceMap === undefined ||
existingSourceMap === null
) {
relatedSourceMap = sourceMapFile;
} else if (Array.isArray(existingSourceMap)) {
relatedSourceMap = existingSourceMap.includes(
sourceMapFile
)
? existingSourceMap
: [...existingSourceMap, sourceMapFile];
} else {
relatedSourceMap =
existingSourceMap === sourceMapFile
? existingSourceMap
: [existingSourceMap, sourceMapFile];
}
const assetInfo = {
related: { sourceMap: relatedSourceMap }
};
assets[file] = asset;
assetsInfo[file] = assetInfo;
compilation.updateAsset(file, asset, assetInfo);
// Add source map file to compilation assets and chunk files
const sourceMapAsset = new RawSource(sourceMapBuffer);
const sourceMapAssetInfo = {
...sourceMapInfo,
development: true
};
assets[sourceMapFile] = sourceMapAsset;
assetsInfo[sourceMapFile] = sourceMapAssetInfo;
compilation.emitAsset(
sourceMapFile,
sourceMapAsset,
sourceMapAssetInfo
);
if (chunk !== undefined) {
chunk.auxiliaryFiles.add(sourceMapFile);
}
} else {
if (currentSourceMappingURLComment === false) {
throw new Error(
`${PLUGIN_NAME}: append can't be false when no filename is provided`
);
}
if (typeof currentSourceMappingURLComment === "function") {
throw new Error(
`${PLUGIN_NAME}: append can't be a function when no filename is provided`
);
}
// Inline data-URL form: `[map]` gets the raw JSON, `[url]`
// gets the same JSON base64-encoded. `URL_COMMENT_REGEXP`
// is a `/g` regex, so a user `append` template with more
// than one `[url]` placeholder would otherwise re-encode
// the same JSON per match. Pre-compute both once.
const sourceMapString = JSON.stringify(outputSourceMap);
const sourceMapBase64 = Buffer.from(
sourceMapString,
"utf8"
).toString("base64");
/**
* Add source map as data url to asset
*/
const asset = new ConcatSource(
new RawSource(source),
currentSourceMappingURLComment
.replace(MAP_URL_COMMENT_REGEXP, () => sourceMapString)
.replace(
URL_COMMENT_REGEXP,
() =>
`data:application/json;charset=utf-8;base64,${sourceMapBase64}`
)
);
assets[file] = asset;
assetsInfo[file] = undefined;
compilation.updateAsset(file, asset);
}
task.cacheItem.store({ assets, assetsInfo }, (err) => {
reportProgress(
0.5 + (0.5 * ++taskIndex) / tasks.length,
task.file,
"attached SourceMap"
);
if (err) {
return callback(err);
}
callback();
});
},
(err) => {
reportProgress(1);
callback(err);
}
);
}
);
}
);
});
}
}
module.exports = SourceMapDevToolPlugin;