| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";const path = require("path");const { ConcatSource, RawSource } = require("webpack-sources");const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");const SourceMapDevToolModuleOptionsPlugin = require("./SourceMapDevToolModuleOptionsPlugin");const createHash = require("./util/createHash");const { absolutify } = require("./util/identifier");const validateOptions = require("schema-utils");const schema = require("../schemas/plugins/SourceMapDevToolPlugin.json");/** @typedef {import("../declarations/plugins/SourceMapDevToolPlugin").SourceMapDevToolPluginOptions} SourceMapDevToolPluginOptions *//** @typedef {import("./Chunk")} Chunk *//** @typedef {import("webpack-sources").Source} Source *//** @typedef {import("source-map").RawSourceMap} SourceMap *//** @typedef {import("./Module")} Module *//** @typedef {import("./Compilation")} Compilation *//** @typedef {import("./Compiler")} Compiler *//** @typedef {import("./Compilation")} SourceMapDefinition *//** * @typedef {object} SourceMapTask * @property {Source} asset * @property {Array<string | Module>} [modules] * @property {string} source * @property {string} file * @property {SourceMap} sourceMap * @property {Chunk} chunk *//** * @param {string} name file path * @returns {string} file name */const basename = name => {	if (!name.includes("/")) return name;	return name.substr(name.lastIndexOf("/") + 1);};/** * @type {WeakMap<Source, {file: string, assets: {[k: string]: ConcatSource | RawSource}}>} */const assetsCache = new WeakMap();/** * Creating {@link SourceMapTask} for given file * @param {string} file current compiled file * @param {Source} asset the asset * @param {Chunk} chunk related chunk * @param {SourceMapDevToolPluginOptions} options source map options * @param {Compilation} compilation compilation instance * @returns {SourceMapTask | undefined} created task instance or `undefined` */const getTaskForFile = (file, asset, chunk, options, compilation) => {	let source, sourceMap;	/**	 * Check if asset can build source map	 */	if (asset.sourceAndMap) {		const sourceAndMap = asset.sourceAndMap(options);		sourceMap = sourceAndMap.map;		source = sourceAndMap.source;	} else {		sourceMap = asset.map(options);		source = asset.source();	}	if (!sourceMap || typeof source !== "string") return;	const context = compilation.options.context;	const modules = sourceMap.sources.map(source => {		if (source.startsWith("webpack://")) {			source = absolutify(context, source.slice(10));		}		const module = compilation.findModule(source);		return module || source;	});	return {		chunk,		file,		asset,		source,		sourceMap,		modules	};};class SourceMapDevToolPlugin {	/**	 * @param {SourceMapDevToolPluginOptions} [options] options object	 * @throws {Error} throws error, if got more than 1 arguments	 */	constructor(options) {		if (arguments.length > 1) {			throw new Error(				"SourceMapDevToolPlugin only takes one argument (pass an options object)"			);		}		if (!options) options = {};		validateOptions(schema, options, "SourceMap DevTool Plugin");		/** @type {string | false} */		this.sourceMapFilename = options.filename;		/** @type {string | false} */		this.sourceMappingURLComment =			options.append === false				? false				: options.append || "\n//# sourceMappingURL=[url]";		/** @type {string | Function} */		this.moduleFilenameTemplate =			options.moduleFilenameTemplate || "webpack://[namespace]/[resourcePath]";		/** @type {string | Function} */		this.fallbackModuleFilenameTemplate =			options.fallbackModuleFilenameTemplate ||			"webpack://[namespace]/[resourcePath]?[hash]";		/** @type {string} */		this.namespace = options.namespace || "";		/** @type {SourceMapDevToolPluginOptions} */		this.options = options;	}	/**	 * Apply compiler	 * @param {Compiler} compiler compiler instance	 * @returns {void}	 */	apply(compiler) {		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 || /\.(m?js|css)($|\?)/i;		const matchObject = ModuleFilenameHelpers.matchObject.bind(			undefined,			options		);		compiler.hooks.compilation.tap("SourceMapDevToolPlugin", compilation => {			new SourceMapDevToolModuleOptionsPlugin(options).apply(compilation);			compilation.hooks.afterOptimizeChunkAssets.tap(				/** @type {TODO} */				({ name: "SourceMapDevToolPlugin", context: true }),				/**				 * @param {object} context hook context				 * @param {Array<Chunk>} chunks resulted chunks				 * @throws {Error} throws error, if `sourceMapFilename === false && sourceMappingURLComment === false`				 * @returns {void}				 */				(context, chunks) => {					/** @type {Map<string | Module, string>} */					const moduleToSourceNameMapping = new Map();					/**					 * @type {Function}					 * @returns {void}					 */					const reportProgress =						context && context.reportProgress							? context.reportProgress							: () => {};					const files = [];					for (const chunk of chunks) {						for (const file of chunk.files) {							if (matchObject(file)) {								files.push({									file,									chunk								});							}						}					}					reportProgress(0.0);					const tasks = [];					files.forEach(({ file, chunk }, idx) => {						const asset = compilation.getAsset(file).source;						const cache = assetsCache.get(asset);						/**						 * If presented in cache, reassigns assets. Cache assets already have source maps.						 */						if (cache && cache.file === file) {							for (const cachedFile in cache.assets) {								if (cachedFile === file) {									compilation.updateAsset(cachedFile, cache.assets[cachedFile]);								} else {									compilation.emitAsset(cachedFile, cache.assets[cachedFile], {										development: true									});								}								/**								 * Add file to chunk, if not presented there								 */								if (cachedFile !== file) chunk.files.push(cachedFile);							}							return;						}						reportProgress(							(0.5 * idx) / files.length,							file,							"generate SourceMap"						);						/** @type {SourceMapTask | undefined} */						const task = getTaskForFile(							file,							asset,							chunk,							options,							compilation						);						if (task) {							const modules = task.modules;							for (let idx = 0; idx < modules.length; idx++) {								const module = modules[idx];								if (!moduleToSourceNameMapping.get(module)) {									moduleToSourceNameMapping.set(										module,										ModuleFilenameHelpers.createFilename(											module,											{												moduleFilenameTemplate: moduleFilenameTemplate,												namespace: namespace											},											requestShortener										)									);								}							}							tasks.push(task);						}					});					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 {Array<string | Module>}					 */					const allModules = Array.from(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 = 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: namespace							},							requestShortener						);						hasName = usedNamesSet.has(sourceName);						if (!hasName) {							moduleToSourceNameMapping.set(module, sourceName);							usedNamesSet.add(sourceName);							continue;						}						// elsewise just append stars until we have a valid name						while (hasName) {							sourceName += "*";							hasName = usedNamesSet.has(sourceName);						}						moduleToSourceNameMapping.set(module, sourceName);						usedNamesSet.add(sourceName);					}					tasks.forEach((task, index) => {						reportProgress(							0.5 + (0.5 * index) / tasks.length,							task.file,							"attach SourceMap"						);						const assets = Object.create(null);						const chunk = task.chunk;						const file = task.file;						const asset = task.asset;						const sourceMap = task.sourceMap;						const source = task.source;						const modules = task.modules;						const moduleFilenames = modules.map(m =>							moduleToSourceNameMapping.get(m)						);						sourceMap.sources = moduleFilenames;						if (options.noSources) {							sourceMap.sourcesContent = undefined;						}						sourceMap.sourceRoot = options.sourceRoot || "";						sourceMap.file = file;						assetsCache.set(asset, { file, assets });						/** @type {string | false} */						let currentSourceMappingURLComment = sourceMappingURLComment;						if (							currentSourceMappingURLComment !== false &&							/\.css($|\?)/i.test(file)						) {							currentSourceMappingURLComment = currentSourceMappingURLComment.replace(								/^\n\/\/(.*)$/,								"\n/*$1*/"							);						}						const sourceMapString = JSON.stringify(sourceMap);						if (sourceMapFilename) {							let filename = file;							let query = "";							const idx = filename.indexOf("?");							if (idx >= 0) {								query = filename.substr(idx);								filename = filename.substr(0, idx);							}							const pathParams = {								chunk,								filename: options.fileContext									? path.relative(options.fileContext, filename)									: filename,								query,								basename: basename(filename),								contentHash: createHash("md4")									.update(sourceMapString)									.digest("hex")							};							let sourceMapFile = compilation.getPath(								sourceMapFilename,								pathParams							);							const sourceMapUrl = options.publicPath								? options.publicPath + sourceMapFile.replace(/\\/g, "/")								: path										.relative(path.dirname(file), sourceMapFile)										.replace(/\\/g, "/");							/**							 * Add source map url to compilation asset, if {@link currentSourceMappingURLComment} presented							 */							if (currentSourceMappingURLComment !== false) {								const asset = new ConcatSource(									new RawSource(source),									compilation.getPath(										currentSourceMappingURLComment,										Object.assign({ url: sourceMapUrl }, pathParams)									)								);								assets[file] = asset;								compilation.updateAsset(file, asset);							}							/**							 * Add source map file to compilation assets and chunk files							 */							const asset = new RawSource(sourceMapString);							assets[sourceMapFile] = asset;							compilation.emitAsset(sourceMapFile, asset, {								development: true							});							chunk.files.push(sourceMapFile);						} else {							if (currentSourceMappingURLComment === false) {								throw new Error(									"SourceMapDevToolPlugin: append can't be false when no filename is provided"								);							}							/**							 * Add source map as data url to asset							 */							const asset = new ConcatSource(								new RawSource(source),								currentSourceMappingURLComment									.replace(/\[map\]/g, () => sourceMapString)									.replace(										/\[url\]/g,										() =>											`data:application/json;charset=utf-8;base64,${Buffer.from(												sourceMapString,												"utf-8"											).toString("base64")}`									)							);							assets[file] = asset;							compilation.updateAsset(file, asset);						}					});					reportProgress(1.0);				}			);		});	}}module.exports = SourceMapDevToolPlugin;
 |