| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";const parseJson = require("json-parse-even-better-errors");const asyncLib = require("neo-async");const {	SyncHook,	SyncBailHook,	AsyncParallelHook,	AsyncSeriesHook} = require("tapable");const { SizeOnlySource } = require("webpack-sources");const webpack = require("./");const Cache = require("./Cache");const CacheFacade = require("./CacheFacade");const ChunkGraph = require("./ChunkGraph");const Compilation = require("./Compilation");const ConcurrentCompilationError = require("./ConcurrentCompilationError");const ContextModuleFactory = require("./ContextModuleFactory");const ModuleGraph = require("./ModuleGraph");const NormalModuleFactory = require("./NormalModuleFactory");const RequestShortener = require("./RequestShortener");const ResolverFactory = require("./ResolverFactory");const Stats = require("./Stats");const Watching = require("./Watching");const WebpackError = require("./WebpackError");const { Logger } = require("./logging/Logger");const { join, dirname, mkdirp } = require("./util/fs");const { makePathsRelative } = require("./util/identifier");const { isSourceEqual } = require("./util/source");/** @typedef {import("webpack-sources").Source} Source *//** @typedef {import("../declarations/WebpackOptions").EntryNormalized} Entry *//** @typedef {import("../declarations/WebpackOptions").OutputNormalized} OutputOptions *//** @typedef {import("../declarations/WebpackOptions").WatchOptions} WatchOptions *//** @typedef {import("../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions *//** @typedef {import("../declarations/WebpackOptions").WebpackPluginInstance} WebpackPluginInstance *//** @typedef {import("./Chunk")} Chunk *//** @typedef {import("./Dependency")} Dependency *//** @typedef {import("./FileSystemInfo").FileSystemInfoEntry} FileSystemInfoEntry *//** @typedef {import("./Module")} Module *//** @typedef {import("./util/WeakTupleMap")} WeakTupleMap *//** @typedef {import("./util/fs").InputFileSystem} InputFileSystem *//** @typedef {import("./util/fs").IntermediateFileSystem} IntermediateFileSystem *//** @typedef {import("./util/fs").OutputFileSystem} OutputFileSystem *//** @typedef {import("./util/fs").WatchFileSystem} WatchFileSystem *//** * @typedef {Object} CompilationParams * @property {NormalModuleFactory} normalModuleFactory * @property {ContextModuleFactory} contextModuleFactory *//** * @template T * @callback Callback * @param {(Error | null)=} err * @param {T=} result *//** * @callback RunAsChildCallback * @param {(Error | null)=} err * @param {Chunk[]=} entries * @param {Compilation=} compilation *//** * @typedef {Object} AssetEmittedInfo * @property {Buffer} content * @property {Source} source * @property {Compilation} compilation * @property {string} outputPath * @property {string} targetPath *//** * @param {string[]} array an array * @returns {boolean} true, if the array is sorted */const isSorted = array => {	for (let i = 1; i < array.length; i++) {		if (array[i - 1] > array[i]) return false;	}	return true;};/** * @param {Object} obj an object * @param {string[]} keys the keys of the object * @returns {Object} the object with properties sorted by property name */const sortObject = (obj, keys) => {	const o = {};	for (const k of keys.sort()) {		o[k] = obj[k];	}	return o;};/** * @param {string} filename filename * @param {string | string[] | undefined} hashes list of hashes * @returns {boolean} true, if the filename contains any hash */const includesHash = (filename, hashes) => {	if (!hashes) return false;	if (Array.isArray(hashes)) {		return hashes.some(hash => filename.includes(hash));	} else {		return filename.includes(hashes);	}};class Compiler {	/**	 * @param {string} context the compilation path	 * @param {WebpackOptions} options options	 */	constructor(context, options = /** @type {WebpackOptions} */ ({})) {		this.hooks = Object.freeze({			/** @type {SyncHook<[]>} */			initialize: new SyncHook([]),			/** @type {SyncBailHook<[Compilation], boolean>} */			shouldEmit: new SyncBailHook(["compilation"]),			/** @type {AsyncSeriesHook<[Stats]>} */			done: new AsyncSeriesHook(["stats"]),			/** @type {SyncHook<[Stats]>} */			afterDone: new SyncHook(["stats"]),			/** @type {AsyncSeriesHook<[]>} */			additionalPass: new AsyncSeriesHook([]),			/** @type {AsyncSeriesHook<[Compiler]>} */			beforeRun: new AsyncSeriesHook(["compiler"]),			/** @type {AsyncSeriesHook<[Compiler]>} */			run: new AsyncSeriesHook(["compiler"]),			/** @type {AsyncSeriesHook<[Compilation]>} */			emit: new AsyncSeriesHook(["compilation"]),			/** @type {AsyncSeriesHook<[string, AssetEmittedInfo]>} */			assetEmitted: new AsyncSeriesHook(["file", "info"]),			/** @type {AsyncSeriesHook<[Compilation]>} */			afterEmit: new AsyncSeriesHook(["compilation"]),			/** @type {SyncHook<[Compilation, CompilationParams]>} */			thisCompilation: new SyncHook(["compilation", "params"]),			/** @type {SyncHook<[Compilation, CompilationParams]>} */			compilation: new SyncHook(["compilation", "params"]),			/** @type {SyncHook<[NormalModuleFactory]>} */			normalModuleFactory: new SyncHook(["normalModuleFactory"]),			/** @type {SyncHook<[ContextModuleFactory]>}  */			contextModuleFactory: new SyncHook(["contextModuleFactory"]),			/** @type {AsyncSeriesHook<[CompilationParams]>} */			beforeCompile: new AsyncSeriesHook(["params"]),			/** @type {SyncHook<[CompilationParams]>} */			compile: new SyncHook(["params"]),			/** @type {AsyncParallelHook<[Compilation]>} */			make: new AsyncParallelHook(["compilation"]),			/** @type {AsyncParallelHook<[Compilation]>} */			finishMake: new AsyncSeriesHook(["compilation"]),			/** @type {AsyncSeriesHook<[Compilation]>} */			afterCompile: new AsyncSeriesHook(["compilation"]),			/** @type {AsyncSeriesHook<[]>} */			readRecords: new AsyncSeriesHook([]),			/** @type {AsyncSeriesHook<[]>} */			emitRecords: new AsyncSeriesHook([]),			/** @type {AsyncSeriesHook<[Compiler]>} */			watchRun: new AsyncSeriesHook(["compiler"]),			/** @type {SyncHook<[Error]>} */			failed: new SyncHook(["error"]),			/** @type {SyncHook<[string | null, number]>} */			invalid: new SyncHook(["filename", "changeTime"]),			/** @type {SyncHook<[]>} */			watchClose: new SyncHook([]),			/** @type {AsyncSeriesHook<[]>} */			shutdown: new AsyncSeriesHook([]),			/** @type {SyncBailHook<[string, string, any[]], true>} */			infrastructureLog: new SyncBailHook(["origin", "type", "args"]),			// TODO the following hooks are weirdly located here			// TODO move them for webpack 5			/** @type {SyncHook<[]>} */			environment: new SyncHook([]),			/** @type {SyncHook<[]>} */			afterEnvironment: new SyncHook([]),			/** @type {SyncHook<[Compiler]>} */			afterPlugins: new SyncHook(["compiler"]),			/** @type {SyncHook<[Compiler]>} */			afterResolvers: new SyncHook(["compiler"]),			/** @type {SyncBailHook<[string, Entry], boolean>} */			entryOption: new SyncBailHook(["context", "entry"])		});		this.webpack = webpack;		/** @type {string=} */		this.name = undefined;		/** @type {Compilation=} */		this.parentCompilation = undefined;		/** @type {Compiler} */		this.root = this;		/** @type {string} */		this.outputPath = "";		/** @type {Watching} */		this.watching = undefined;		/** @type {OutputFileSystem} */		this.outputFileSystem = null;		/** @type {IntermediateFileSystem} */		this.intermediateFileSystem = null;		/** @type {InputFileSystem} */		this.inputFileSystem = null;		/** @type {WatchFileSystem} */		this.watchFileSystem = null;		/** @type {string|null} */		this.recordsInputPath = null;		/** @type {string|null} */		this.recordsOutputPath = null;		this.records = {};		/** @type {Set<string | RegExp>} */		this.managedPaths = new Set();		/** @type {Set<string | RegExp>} */		this.immutablePaths = new Set();		/** @type {ReadonlySet<string>} */		this.modifiedFiles = undefined;		/** @type {ReadonlySet<string>} */		this.removedFiles = undefined;		/** @type {ReadonlyMap<string, FileSystemInfoEntry | "ignore" | null>} */		this.fileTimestamps = undefined;		/** @type {ReadonlyMap<string, FileSystemInfoEntry | "ignore" | null>} */		this.contextTimestamps = undefined;		/** @type {number} */		this.fsStartTime = undefined;		/** @type {ResolverFactory} */		this.resolverFactory = new ResolverFactory();		this.infrastructureLogger = undefined;		this.options = options;		this.context = context;		this.requestShortener = new RequestShortener(context, this.root);		this.cache = new Cache();		/** @type {Map<Module, { buildInfo: object, references: WeakMap<Dependency, Module>, memCache: WeakTupleMap }> | undefined} */		this.moduleMemCaches = undefined;		this.compilerPath = "";		/** @type {boolean} */		this.running = false;		/** @type {boolean} */		this.idle = false;		/** @type {boolean} */		this.watchMode = false;		this._backCompat = this.options.experiments.backCompat !== false;		/** @type {Compilation} */		this._lastCompilation = undefined;		/** @type {NormalModuleFactory} */		this._lastNormalModuleFactory = undefined;		/** @private @type {WeakMap<Source, { sizeOnlySource: SizeOnlySource, writtenTo: Map<string, number> }>} */		this._assetEmittingSourceCache = new WeakMap();		/** @private @type {Map<string, number>} */		this._assetEmittingWrittenFiles = new Map();		/** @private @type {Set<string>} */		this._assetEmittingPreviousFiles = new Set();	}	/**	 * @param {string} name cache name	 * @returns {CacheFacade} the cache facade instance	 */	getCache(name) {		return new CacheFacade(			this.cache,			`${this.compilerPath}${name}`,			this.options.output.hashFunction		);	}	/**	 * @param {string | (function(): string)} name name of the logger, or function called once to get the logger name	 * @returns {Logger} a logger with that name	 */	getInfrastructureLogger(name) {		if (!name) {			throw new TypeError(				"Compiler.getInfrastructureLogger(name) called without a name"			);		}		return new Logger(			(type, args) => {				if (typeof name === "function") {					name = name();					if (!name) {						throw new TypeError(							"Compiler.getInfrastructureLogger(name) called with a function not returning a name"						);					}				}				if (this.hooks.infrastructureLog.call(name, type, args) === undefined) {					if (this.infrastructureLogger !== undefined) {						this.infrastructureLogger(name, type, args);					}				}			},			childName => {				if (typeof name === "function") {					if (typeof childName === "function") {						return this.getInfrastructureLogger(() => {							if (typeof name === "function") {								name = name();								if (!name) {									throw new TypeError(										"Compiler.getInfrastructureLogger(name) called with a function not returning a name"									);								}							}							if (typeof childName === "function") {								childName = childName();								if (!childName) {									throw new TypeError(										"Logger.getChildLogger(name) called with a function not returning a name"									);								}							}							return `${name}/${childName}`;						});					} else {						return this.getInfrastructureLogger(() => {							if (typeof name === "function") {								name = name();								if (!name) {									throw new TypeError(										"Compiler.getInfrastructureLogger(name) called with a function not returning a name"									);								}							}							return `${name}/${childName}`;						});					}				} else {					if (typeof childName === "function") {						return this.getInfrastructureLogger(() => {							if (typeof childName === "function") {								childName = childName();								if (!childName) {									throw new TypeError(										"Logger.getChildLogger(name) called with a function not returning a name"									);								}							}							return `${name}/${childName}`;						});					} else {						return this.getInfrastructureLogger(`${name}/${childName}`);					}				}			}		);	}	// TODO webpack 6: solve this in a better way	// e.g. move compilation specific info from Modules into ModuleGraph	_cleanupLastCompilation() {		if (this._lastCompilation !== undefined) {			for (const module of this._lastCompilation.modules) {				ChunkGraph.clearChunkGraphForModule(module);				ModuleGraph.clearModuleGraphForModule(module);				module.cleanupForCache();			}			for (const chunk of this._lastCompilation.chunks) {				ChunkGraph.clearChunkGraphForChunk(chunk);			}			this._lastCompilation = undefined;		}	}	// TODO webpack 6: solve this in a better way	_cleanupLastNormalModuleFactory() {		if (this._lastNormalModuleFactory !== undefined) {			this._lastNormalModuleFactory.cleanupForCache();			this._lastNormalModuleFactory = undefined;		}	}	/**	 * @param {WatchOptions} watchOptions the watcher's options	 * @param {Callback<Stats>} handler signals when the call finishes	 * @returns {Watching} a compiler watcher	 */	watch(watchOptions, handler) {		if (this.running) {			return handler(new ConcurrentCompilationError());		}		this.running = true;		this.watchMode = true;		this.watching = new Watching(this, watchOptions, handler);		return this.watching;	}	/**	 * @param {Callback<Stats>} callback signals when the call finishes	 * @returns {void}	 */	run(callback) {		if (this.running) {			return callback(new ConcurrentCompilationError());		}		let logger;		const finalCallback = (err, stats) => {			if (logger) logger.time("beginIdle");			this.idle = true;			this.cache.beginIdle();			this.idle = true;			if (logger) logger.timeEnd("beginIdle");			this.running = false;			if (err) {				this.hooks.failed.call(err);			}			if (callback !== undefined) callback(err, stats);			this.hooks.afterDone.call(stats);		};		const startTime = Date.now();		this.running = true;		const onCompiled = (err, compilation) => {			if (err) return finalCallback(err);			if (this.hooks.shouldEmit.call(compilation) === false) {				compilation.startTime = startTime;				compilation.endTime = Date.now();				const stats = new Stats(compilation);				this.hooks.done.callAsync(stats, err => {					if (err) return finalCallback(err);					return finalCallback(null, stats);				});				return;			}			process.nextTick(() => {				logger = compilation.getLogger("webpack.Compiler");				logger.time("emitAssets");				this.emitAssets(compilation, err => {					logger.timeEnd("emitAssets");					if (err) return finalCallback(err);					if (compilation.hooks.needAdditionalPass.call()) {						compilation.needAdditionalPass = true;						compilation.startTime = startTime;						compilation.endTime = Date.now();						logger.time("done hook");						const stats = new Stats(compilation);						this.hooks.done.callAsync(stats, err => {							logger.timeEnd("done hook");							if (err) return finalCallback(err);							this.hooks.additionalPass.callAsync(err => {								if (err) return finalCallback(err);								this.compile(onCompiled);							});						});						return;					}					logger.time("emitRecords");					this.emitRecords(err => {						logger.timeEnd("emitRecords");						if (err) return finalCallback(err);						compilation.startTime = startTime;						compilation.endTime = Date.now();						logger.time("done hook");						const stats = new Stats(compilation);						this.hooks.done.callAsync(stats, err => {							logger.timeEnd("done hook");							if (err) return finalCallback(err);							this.cache.storeBuildDependencies(								compilation.buildDependencies,								err => {									if (err) return finalCallback(err);									return finalCallback(null, stats);								}							);						});					});				});			});		};		const run = () => {			this.hooks.beforeRun.callAsync(this, err => {				if (err) return finalCallback(err);				this.hooks.run.callAsync(this, err => {					if (err) return finalCallback(err);					this.readRecords(err => {						if (err) return finalCallback(err);						this.compile(onCompiled);					});				});			});		};		if (this.idle) {			this.cache.endIdle(err => {				if (err) return finalCallback(err);				this.idle = false;				run();			});		} else {			run();		}	}	/**	 * @param {RunAsChildCallback} callback signals when the call finishes	 * @returns {void}	 */	runAsChild(callback) {		const startTime = Date.now();		const finalCallback = (err, entries, compilation) => {			try {				callback(err, entries, compilation);			} catch (e) {				const err = new WebpackError(					`compiler.runAsChild callback error: ${e}`				);				err.details = e.stack;				this.parentCompilation.errors.push(err);			}		};		this.compile((err, compilation) => {			if (err) return finalCallback(err);			this.parentCompilation.children.push(compilation);			for (const { name, source, info } of compilation.getAssets()) {				this.parentCompilation.emitAsset(name, source, info);			}			const entries = [];			for (const ep of compilation.entrypoints.values()) {				entries.push(...ep.chunks);			}			compilation.startTime = startTime;			compilation.endTime = Date.now();			return finalCallback(null, entries, compilation);		});	}	purgeInputFileSystem() {		if (this.inputFileSystem && this.inputFileSystem.purge) {			this.inputFileSystem.purge();		}	}	/**	 * @param {Compilation} compilation the compilation	 * @param {Callback<void>} callback signals when the assets are emitted	 * @returns {void}	 */	emitAssets(compilation, callback) {		let outputPath;		const emitFiles = err => {			if (err) return callback(err);			const assets = compilation.getAssets();			compilation.assets = { ...compilation.assets };			/** @type {Map<string, { path: string, source: Source, size: number, waiting: { cacheEntry: any, file: string }[] }>} */			const caseInsensitiveMap = new Map();			/** @type {Set<string>} */			const allTargetPaths = new Set();			asyncLib.forEachLimit(				assets,				15,				({ name: file, source, info }, callback) => {					let targetFile = file;					let immutable = info.immutable;					const queryStringIdx = targetFile.indexOf("?");					if (queryStringIdx >= 0) {						targetFile = targetFile.slice(0, queryStringIdx);						// We may remove the hash, which is in the query string						// So we recheck if the file is immutable						// This doesn't cover all cases, but immutable is only a performance optimization anyway						immutable =							immutable &&							(includesHash(targetFile, info.contenthash) ||								includesHash(targetFile, info.chunkhash) ||								includesHash(targetFile, info.modulehash) ||								includesHash(targetFile, info.fullhash));					}					const writeOut = err => {						if (err) return callback(err);						const targetPath = join(							this.outputFileSystem,							outputPath,							targetFile						);						allTargetPaths.add(targetPath);						// check if the target file has already been written by this Compiler						const targetFileGeneration =							this._assetEmittingWrittenFiles.get(targetPath);						// create an cache entry for this Source if not already existing						let cacheEntry = this._assetEmittingSourceCache.get(source);						if (cacheEntry === undefined) {							cacheEntry = {								sizeOnlySource: undefined,								writtenTo: new Map()							};							this._assetEmittingSourceCache.set(source, cacheEntry);						}						let similarEntry;						const checkSimilarFile = () => {							const caseInsensitiveTargetPath = targetPath.toLowerCase();							similarEntry = caseInsensitiveMap.get(caseInsensitiveTargetPath);							if (similarEntry !== undefined) {								const { path: other, source: otherSource } = similarEntry;								if (isSourceEqual(otherSource, source)) {									// Size may or may not be available at this point.									// If it's not available add to "waiting" list and it will be updated once available									if (similarEntry.size !== undefined) {										updateWithReplacementSource(similarEntry.size);									} else {										if (!similarEntry.waiting) similarEntry.waiting = [];										similarEntry.waiting.push({ file, cacheEntry });									}									alreadyWritten();								} else {									const err =										new WebpackError(`Prevent writing to file that only differs in casing or query string from already written file.This will lead to a race-condition and corrupted files on case-insensitive file systems.${targetPath}${other}`);									err.file = file;									callback(err);								}								return true;							} else {								caseInsensitiveMap.set(									caseInsensitiveTargetPath,									(similarEntry = {										path: targetPath,										source,										size: undefined,										waiting: undefined									})								);								return false;							}						};						/**						 * get the binary (Buffer) content from the Source						 * @returns {Buffer} content for the source						 */						const getContent = () => {							if (typeof source.buffer === "function") {								return source.buffer();							} else {								const bufferOrString = source.source();								if (Buffer.isBuffer(bufferOrString)) {									return bufferOrString;								} else {									return Buffer.from(bufferOrString, "utf8");								}							}						};						const alreadyWritten = () => {							// cache the information that the Source has been already been written to that location							if (targetFileGeneration === undefined) {								const newGeneration = 1;								this._assetEmittingWrittenFiles.set(targetPath, newGeneration);								cacheEntry.writtenTo.set(targetPath, newGeneration);							} else {								cacheEntry.writtenTo.set(targetPath, targetFileGeneration);							}							callback();						};						/**						 * Write the file to output file system						 * @param {Buffer} content content to be written						 * @returns {void}						 */						const doWrite = content => {							this.outputFileSystem.writeFile(targetPath, content, err => {								if (err) return callback(err);								// information marker that the asset has been emitted								compilation.emittedAssets.add(file);								// cache the information that the Source has been written to that location								const newGeneration =									targetFileGeneration === undefined										? 1										: targetFileGeneration + 1;								cacheEntry.writtenTo.set(targetPath, newGeneration);								this._assetEmittingWrittenFiles.set(targetPath, newGeneration);								this.hooks.assetEmitted.callAsync(									file,									{										content,										source,										outputPath,										compilation,										targetPath									},									callback								);							});						};						const updateWithReplacementSource = size => {							updateFileWithReplacementSource(file, cacheEntry, size);							similarEntry.size = size;							if (similarEntry.waiting !== undefined) {								for (const { file, cacheEntry } of similarEntry.waiting) {									updateFileWithReplacementSource(file, cacheEntry, size);								}							}						};						const updateFileWithReplacementSource = (							file,							cacheEntry,							size						) => {							// Create a replacement resource which only allows to ask for size							// This allows to GC all memory allocated by the Source							// (expect when the Source is stored in any other cache)							if (!cacheEntry.sizeOnlySource) {								cacheEntry.sizeOnlySource = new SizeOnlySource(size);							}							compilation.updateAsset(file, cacheEntry.sizeOnlySource, {								size							});						};						const processExistingFile = stats => {							// skip emitting if it's already there and an immutable file							if (immutable) {								updateWithReplacementSource(stats.size);								return alreadyWritten();							}							const content = getContent();							updateWithReplacementSource(content.length);							// if it exists and content on disk matches content							// skip writing the same content again							// (to keep mtime and don't trigger watchers)							// for a fast negative match file size is compared first							if (content.length === stats.size) {								compilation.comparedForEmitAssets.add(file);								return this.outputFileSystem.readFile(									targetPath,									(err, existingContent) => {										if (											err ||											!content.equals(/** @type {Buffer} */ (existingContent))										) {											return doWrite(content);										} else {											return alreadyWritten();										}									}								);							}							return doWrite(content);						};						const processMissingFile = () => {							const content = getContent();							updateWithReplacementSource(content.length);							return doWrite(content);						};						// if the target file has already been written						if (targetFileGeneration !== undefined) {							// check if the Source has been written to this target file							const writtenGeneration = cacheEntry.writtenTo.get(targetPath);							if (writtenGeneration === targetFileGeneration) {								// if yes, we may skip writing the file								// if it's already there								// (we assume one doesn't modify files while the Compiler is running, other then removing them)								if (this._assetEmittingPreviousFiles.has(targetPath)) {									// We assume that assets from the last compilation say intact on disk (they are not removed)									compilation.updateAsset(file, cacheEntry.sizeOnlySource, {										size: cacheEntry.sizeOnlySource.size()									});									return callback();								} else {									// Settings immutable will make it accept file content without comparing when file exist									immutable = true;								}							} else if (!immutable) {								if (checkSimilarFile()) return;								// We wrote to this file before which has very likely a different content								// skip comparing and assume content is different for performance								// This case happens often during watch mode.								return processMissingFile();							}						}						if (checkSimilarFile()) return;						if (this.options.output.compareBeforeEmit) {							this.outputFileSystem.stat(targetPath, (err, stats) => {								const exists = !err && stats.isFile();								if (exists) {									processExistingFile(stats);								} else {									processMissingFile();								}							});						} else {							processMissingFile();						}					};					if (targetFile.match(/\/|\\/)) {						const fs = this.outputFileSystem;						const dir = dirname(fs, join(fs, outputPath, targetFile));						mkdirp(fs, dir, writeOut);					} else {						writeOut();					}				},				err => {					// Clear map to free up memory					caseInsensitiveMap.clear();					if (err) {						this._assetEmittingPreviousFiles.clear();						return callback(err);					}					this._assetEmittingPreviousFiles = allTargetPaths;					this.hooks.afterEmit.callAsync(compilation, err => {						if (err) return callback(err);						return callback();					});				}			);		};		this.hooks.emit.callAsync(compilation, err => {			if (err) return callback(err);			outputPath = compilation.getPath(this.outputPath, {});			mkdirp(this.outputFileSystem, outputPath, emitFiles);		});	}	/**	 * @param {Callback<void>} callback signals when the call finishes	 * @returns {void}	 */	emitRecords(callback) {		if (this.hooks.emitRecords.isUsed()) {			if (this.recordsOutputPath) {				asyncLib.parallel(					[						cb => this.hooks.emitRecords.callAsync(cb),						this._emitRecords.bind(this)					],					err => callback(err)				);			} else {				this.hooks.emitRecords.callAsync(callback);			}		} else {			if (this.recordsOutputPath) {				this._emitRecords(callback);			} else {				callback();			}		}	}	/**	 * @param {Callback<void>} callback signals when the call finishes	 * @returns {void}	 */	_emitRecords(callback) {		const writeFile = () => {			this.outputFileSystem.writeFile(				this.recordsOutputPath,				JSON.stringify(					this.records,					(n, value) => {						if (							typeof value === "object" &&							value !== null &&							!Array.isArray(value)						) {							const keys = Object.keys(value);							if (!isSorted(keys)) {								return sortObject(value, keys);							}						}						return value;					},					2				),				callback			);		};		const recordsOutputPathDirectory = dirname(			this.outputFileSystem,			this.recordsOutputPath		);		if (!recordsOutputPathDirectory) {			return writeFile();		}		mkdirp(this.outputFileSystem, recordsOutputPathDirectory, err => {			if (err) return callback(err);			writeFile();		});	}	/**	 * @param {Callback<void>} callback signals when the call finishes	 * @returns {void}	 */	readRecords(callback) {		if (this.hooks.readRecords.isUsed()) {			if (this.recordsInputPath) {				asyncLib.parallel([					cb => this.hooks.readRecords.callAsync(cb),					this._readRecords.bind(this)				]);			} else {				this.records = {};				this.hooks.readRecords.callAsync(callback);			}		} else {			if (this.recordsInputPath) {				this._readRecords(callback);			} else {				this.records = {};				callback();			}		}	}	/**	 * @param {Callback<void>} callback signals when the call finishes	 * @returns {void}	 */	_readRecords(callback) {		if (!this.recordsInputPath) {			this.records = {};			return callback();		}		this.inputFileSystem.stat(this.recordsInputPath, err => {			// It doesn't exist			// We can ignore this.			if (err) return callback();			this.inputFileSystem.readFile(this.recordsInputPath, (err, content) => {				if (err) return callback(err);				try {					this.records = parseJson(content.toString("utf-8"));				} catch (e) {					e.message = "Cannot parse records: " + e.message;					return callback(e);				}				return callback();			});		});	}	/**	 * @param {Compilation} compilation the compilation	 * @param {string} compilerName the compiler's name	 * @param {number} compilerIndex the compiler's index	 * @param {OutputOptions=} outputOptions the output options	 * @param {WebpackPluginInstance[]=} plugins the plugins to apply	 * @returns {Compiler} a child compiler	 */	createChildCompiler(		compilation,		compilerName,		compilerIndex,		outputOptions,		plugins	) {		const childCompiler = new Compiler(this.context, {			...this.options,			output: {				...this.options.output,				...outputOptions			}		});		childCompiler.name = compilerName;		childCompiler.outputPath = this.outputPath;		childCompiler.inputFileSystem = this.inputFileSystem;		childCompiler.outputFileSystem = null;		childCompiler.resolverFactory = this.resolverFactory;		childCompiler.modifiedFiles = this.modifiedFiles;		childCompiler.removedFiles = this.removedFiles;		childCompiler.fileTimestamps = this.fileTimestamps;		childCompiler.contextTimestamps = this.contextTimestamps;		childCompiler.fsStartTime = this.fsStartTime;		childCompiler.cache = this.cache;		childCompiler.compilerPath = `${this.compilerPath}${compilerName}|${compilerIndex}|`;		childCompiler._backCompat = this._backCompat;		const relativeCompilerName = makePathsRelative(			this.context,			compilerName,			this.root		);		if (!this.records[relativeCompilerName]) {			this.records[relativeCompilerName] = [];		}		if (this.records[relativeCompilerName][compilerIndex]) {			childCompiler.records = this.records[relativeCompilerName][compilerIndex];		} else {			this.records[relativeCompilerName].push((childCompiler.records = {}));		}		childCompiler.parentCompilation = compilation;		childCompiler.root = this.root;		if (Array.isArray(plugins)) {			for (const plugin of plugins) {				plugin.apply(childCompiler);			}		}		for (const name in this.hooks) {			if (				![					"make",					"compile",					"emit",					"afterEmit",					"invalid",					"done",					"thisCompilation"				].includes(name)			) {				if (childCompiler.hooks[name]) {					childCompiler.hooks[name].taps = this.hooks[name].taps.slice();				}			}		}		compilation.hooks.childCompiler.call(			childCompiler,			compilerName,			compilerIndex		);		return childCompiler;	}	isChild() {		return !!this.parentCompilation;	}	createCompilation(params) {		this._cleanupLastCompilation();		return (this._lastCompilation = new Compilation(this, params));	}	/**	 * @param {CompilationParams} params the compilation parameters	 * @returns {Compilation} the created compilation	 */	newCompilation(params) {		const compilation = this.createCompilation(params);		compilation.name = this.name;		compilation.records = this.records;		this.hooks.thisCompilation.call(compilation, params);		this.hooks.compilation.call(compilation, params);		return compilation;	}	createNormalModuleFactory() {		this._cleanupLastNormalModuleFactory();		const normalModuleFactory = new NormalModuleFactory({			context: this.options.context,			fs: this.inputFileSystem,			resolverFactory: this.resolverFactory,			options: this.options.module,			associatedObjectForCache: this.root,			layers: this.options.experiments.layers		});		this._lastNormalModuleFactory = normalModuleFactory;		this.hooks.normalModuleFactory.call(normalModuleFactory);		return normalModuleFactory;	}	createContextModuleFactory() {		const contextModuleFactory = new ContextModuleFactory(this.resolverFactory);		this.hooks.contextModuleFactory.call(contextModuleFactory);		return contextModuleFactory;	}	newCompilationParams() {		const params = {			normalModuleFactory: this.createNormalModuleFactory(),			contextModuleFactory: this.createContextModuleFactory()		};		return params;	}	/**	 * @param {Callback<Compilation>} callback signals when the compilation finishes	 * @returns {void}	 */	compile(callback) {		const params = this.newCompilationParams();		this.hooks.beforeCompile.callAsync(params, err => {			if (err) return callback(err);			this.hooks.compile.call(params);			const compilation = this.newCompilation(params);			const logger = compilation.getLogger("webpack.Compiler");			logger.time("make hook");			this.hooks.make.callAsync(compilation, err => {				logger.timeEnd("make hook");				if (err) return callback(err);				logger.time("finish make hook");				this.hooks.finishMake.callAsync(compilation, err => {					logger.timeEnd("finish make hook");					if (err) return callback(err);					process.nextTick(() => {						logger.time("finish compilation");						compilation.finish(err => {							logger.timeEnd("finish compilation");							if (err) return callback(err);							logger.time("seal compilation");							compilation.seal(err => {								logger.timeEnd("seal compilation");								if (err) return callback(err);								logger.time("afterCompile hook");								this.hooks.afterCompile.callAsync(compilation, err => {									logger.timeEnd("afterCompile hook");									if (err) return callback(err);									return callback(null, compilation);								});							});						});					});				});			});		});	}	/**	 * @param {Callback<void>} callback signals when the compiler closes	 * @returns {void}	 */	close(callback) {		if (this.watching) {			// When there is still an active watching, close this first			this.watching.close(err => {				this.close(callback);			});			return;		}		this.hooks.shutdown.callAsync(err => {			if (err) return callback(err);			// Get rid of reference to last compilation to avoid leaking memory			// We can't run this._cleanupLastCompilation() as the Stats to this compilation			// might be still in use. We try to get rid of the reference to the cache instead.			this._lastCompilation = undefined;			this._lastNormalModuleFactory = undefined;			this.cache.shutdown(callback);		});	}}module.exports = Compiler;
 |