webpack
Version:
Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.
1,589 lines (1,459 loc) • 103 kB
JavaScript
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const asyncLib = require("neo-async");
const {
HookMap,
SyncHook,
SyncBailHook,
SyncWaterfallHook,
AsyncSeriesHook,
AsyncSeriesBailHook
} = require("tapable");
const util = require("util");
const { CachedSource } = require("webpack-sources");
const { MultiItemCache } = require("./CacheFacade");
const Chunk = require("./Chunk");
const ChunkGraph = require("./ChunkGraph");
const ChunkGroup = require("./ChunkGroup");
const ChunkRenderError = require("./ChunkRenderError");
const ChunkTemplate = require("./ChunkTemplate");
const CodeGenerationError = require("./CodeGenerationError");
const CodeGenerationResults = require("./CodeGenerationResults");
const DependencyTemplates = require("./DependencyTemplates");
const Entrypoint = require("./Entrypoint");
const ErrorHelpers = require("./ErrorHelpers");
const FileSystemInfo = require("./FileSystemInfo");
const {
connectChunkGroupAndChunk,
connectChunkGroupParentAndChild
} = require("./GraphHelpers");
const { makeWebpackError } = require("./HookWebpackError");
const MainTemplate = require("./MainTemplate");
const Module = require("./Module");
const ModuleDependencyError = require("./ModuleDependencyError");
const ModuleDependencyWarning = require("./ModuleDependencyWarning");
const ModuleGraph = require("./ModuleGraph");
const ModuleNotFoundError = require("./ModuleNotFoundError");
const ModuleProfile = require("./ModuleProfile");
const ModuleRestoreError = require("./ModuleRestoreError");
const ModuleStoreError = require("./ModuleStoreError");
const ModuleTemplate = require("./ModuleTemplate");
const RuntimeGlobals = require("./RuntimeGlobals");
const RuntimeTemplate = require("./RuntimeTemplate");
const Stats = require("./Stats");
const WebpackError = require("./WebpackError");
const buildChunkGraph = require("./buildChunkGraph");
const BuildCycleError = require("./errors/BuildCycleError");
const { Logger, LogType } = require("./logging/Logger");
const StatsFactory = require("./stats/StatsFactory");
const StatsPrinter = require("./stats/StatsPrinter");
const { equals: arrayEquals } = require("./util/ArrayHelpers");
const AsyncQueue = require("./util/AsyncQueue");
const LazySet = require("./util/LazySet");
const { cachedCleverMerge } = require("./util/cleverMerge");
const {
compareLocations,
concatComparators,
compareSelect,
compareIds,
compareStringsNumeric,
compareModulesByIdentifier
} = require("./util/comparators");
const createHash = require("./util/createHash");
const {
arrayToSetDeprecation,
soonFrozenObjectDeprecation,
createFakeHook
} = require("./util/deprecation");
const { getRuntimeKey } = require("./util/runtime");
/** @template T @typedef {import("tapable").AsArray<T>} AsArray<T> */
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../declarations/WebpackOptions").EntryDescriptionNormalized} EntryDescription */
/** @typedef {import("../declarations/WebpackOptions").OutputNormalized} OutputOptions */
/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
/** @typedef {import("./Cache")} Cache */
/** @typedef {import("./CacheFacade")} CacheFacade */
/** @typedef {import("./ChunkGroup").ChunkGroupOptions} ChunkGroupOptions */
/** @typedef {import("./Compiler")} Compiler */
/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
/** @typedef {import("./Dependency")} Dependency */
/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
/** @typedef {import("./Dependency").ReferencedExport} ReferencedExport */
/** @typedef {import("./DependencyTemplate")} DependencyTemplate */
/** @typedef {import("./Entrypoint").EntryOptions} EntryOptions */
/** @typedef {import("./Module")} Module */
/** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */
/** @typedef {import("./ModuleFactory")} ModuleFactory */
/** @typedef {import("./RuntimeModule")} RuntimeModule */
/** @typedef {import("./Template").RenderManifestEntry} RenderManifestEntry */
/** @typedef {import("./Template").RenderManifestOptions} RenderManifestOptions */
/** @typedef {import("./WebpackError")} WebpackError */
/** @typedef {import("./stats/StatsFactory")} StatsFactory */
/** @typedef {import("./stats/StatsPrinter")} StatsPrinter */
/** @typedef {import("./util/Hash")} Hash */
/** @template T @typedef {import("./util/deprecation").FakeHook<T>} FakeHook<T> */
/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
/**
* @callback Callback
* @param {WebpackError=} err
* @returns {void}
*/
/**
* @callback ModuleCallback
* @param {WebpackError=} err
* @param {Module=} result
* @returns {void}
*/
/**
* @callback DepBlockVarDependenciesCallback
* @param {Dependency} dependency
* @returns {any}
*/
/**
* @typedef {Object} Plugin
* @property {() => void} apply
*/
/** @typedef {new (...args: any[]) => Dependency} DepConstructor */
/** @typedef {Record<string, Source>} CompilationAssets */
/**
* @typedef {Object} AvailableModulesChunkGroupMapping
* @property {ChunkGroup} chunkGroup
* @property {Set<Module>} availableModules
* @property {boolean} needCopy
*/
/**
* @typedef {Object} DependenciesBlockLike
* @property {Dependency[]} dependencies
* @property {AsyncDependenciesBlock[]} blocks
*/
/**
* @typedef {Object} ChunkPathData
* @property {string|number} id
* @property {string=} name
* @property {string} hash
* @property {function(number): string=} hashWithLength
* @property {(Record<string, string>)=} contentHash
* @property {(Record<string, (length: number) => string>)=} contentHashWithLength
*/
/**
* @typedef {Object} ChunkHashContext
* @property {RuntimeTemplate} runtimeTemplate the runtime template
* @property {ModuleGraph} moduleGraph the module graph
* @property {ChunkGraph} chunkGraph the chunk graph
*/
/**
* @typedef {Object} EntryData
* @property {Dependency[]} dependencies dependencies of the entrypoint that should be evaluated at startup
* @property {Dependency[]} includeDependencies dependencies of the entrypoint that should be included but not evaluated
* @property {EntryOptions} options options of the entrypoint
*/
/**
* @typedef {Object} LogEntry
* @property {string} type
* @property {any[]} args
* @property {number} time
* @property {string[]=} trace
*/
/**
* @typedef {Object} AssetInfo
* @property {boolean=} immutable true, if the asset can be long term cached forever (contains a hash)
* @property {string | string[]=} fullhash the value(s) of the full hash used for this asset
* @property {string | string[]=} chunkhash the value(s) of the chunk hash used for this asset
* @property {string | string[]=} modulehash the value(s) of the module hash used for this asset
* @property {string | string[]=} contenthash the value(s) of the content hash used for this asset
* @property {number=} size size in bytes, only set after asset has been emitted
* @property {boolean=} development true, when asset is only used for development and doesn't count towards user-facing assets
* @property {boolean=} hotModuleReplacement true, when asset ships data for updating an existing application (HMR)
* @property {Record<string, string | string[]>=} related object of pointers to other assets, keyed by type of relation (only points from parent to child)
*/
/**
* @typedef {Object} Asset
* @property {string} name the filename of the asset
* @property {Source} source source of the asset
* @property {AssetInfo} info info about the asset
*/
/**
* @typedef {Object} ModulePathData
* @property {string|number} id
* @property {string} hash
* @property {function(number): string=} hashWithLength
*/
/**
* @typedef {Object} PathData
* @property {ChunkGraph=} chunkGraph
* @property {string=} hash
* @property {function(number): string=} hashWithLength
* @property {(Chunk|ChunkPathData)=} chunk
* @property {(Module|ModulePathData)=} module
* @property {RuntimeSpec=} runtime
* @property {string=} filename
* @property {string=} basename
* @property {string=} query
* @property {string=} contentHashType
* @property {string=} contentHash
* @property {function(number): string=} contentHashWithLength
* @property {boolean=} noChunkHash
* @property {string=} url
*/
/** @type {AssetInfo} */
const EMPTY_ASSET_INFO = Object.freeze({});
const esmDependencyCategory = "esm";
// TODO webpack 6: remove
const deprecatedNormalModuleLoaderHook = util.deprecate(
compilation => {
return require("./NormalModule").getCompilationHooks(compilation).loader;
},
"Compilation.hooks.normalModuleLoader was moved to NormalModule.getCompilationHooks(compilation).loader",
"DEP_WEBPACK_COMPILATION_NORMAL_MODULE_LOADER_HOOK"
);
const byId = compareSelect(
/**
* @param {Chunk} c chunk
* @returns {number | string} id
*/ c => c.id,
compareIds
);
const byNameOrHash = concatComparators(
compareSelect(
/**
* @param {Compilation} c compilation
* @returns {string} name
*/
c => c.name,
compareIds
),
compareSelect(
/**
* @param {Compilation} c compilation
* @returns {string} hash
*/ c => c.fullHash,
compareIds
)
);
const byMessage = compareSelect(err => `${err.message}`, compareStringsNumeric);
const byModule = compareSelect(
err => (err.module && err.module.identifier()) || "",
compareStringsNumeric
);
const byLocation = compareSelect(err => err.loc, compareLocations);
const compareErrors = concatComparators(byModule, byLocation, byMessage);
/**
* @param {Source} a a source
* @param {Source} b another source
* @returns {boolean} true, when both sources are equal
*/
const isSourceEqual = (a, b) => {
if (a === b) return true;
// TODO webpack 5: check .buffer() instead, it's called anyway during emit
/** @type {Buffer|string} */
let aSource = a.source();
/** @type {Buffer|string} */
let bSource = b.source();
if (aSource === bSource) return true;
if (typeof aSource === "string" && typeof bSource === "string") return false;
if (!Buffer.isBuffer(aSource)) aSource = Buffer.from(aSource, "utf-8");
if (!Buffer.isBuffer(bSource)) bSource = Buffer.from(bSource, "utf-8");
return aSource.equals(bSource);
};
class Compilation {
/**
* Creates an instance of Compilation.
* @param {Compiler} compiler the compiler which created the compilation
*/
constructor(compiler) {
const getNormalModuleLoader = () => deprecatedNormalModuleLoaderHook(this);
/** @type {AsyncSeriesHook<[CompilationAssets]>} */
const processAssetsHook = new AsyncSeriesHook(["assets"]);
/** @type {SyncHook<[CompilationAssets]>} */
const afterProcessAssetsHook = new SyncHook(["assets"]);
/**
* @template T
* @param {string} name name of the hook
* @param {number} stage new stage
* @param {function(): AsArray<T>} getArgs get old hook function args
* @param {string=} code deprecation code (not deprecated when unset)
* @returns {FakeHook<Pick<AsyncSeriesHook<T>, "tap" | "tapAsync" | "tapPromise" | "name">>} fake hook which redirects
*/
const createProcessAssetsHook = (name, stage, getArgs, code) => {
const errorMessage = reason => `Can't automatically convert plugin using Compilation.hooks.${name} to Compilation.hooks.processAssets because ${reason}.
BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a single Compilation.hooks.processAssets hook.`;
const getOptions = options => {
if (typeof options === "string") options = { name: options };
if (options.stage) {
throw new Error(errorMessage("it's using the 'stage' option"));
}
return { ...options, stage: stage };
};
return createFakeHook(
{
name,
/** @type {AsyncSeriesHook<T>["intercept"]} */
intercept(interceptor) {
throw new Error(errorMessage("it's using 'intercept'"));
},
/** @type {AsyncSeriesHook<T>["tap"]} */
tap: (options, fn) => {
processAssetsHook.tap(getOptions(options), () => fn(...getArgs()));
},
/** @type {AsyncSeriesHook<T>["tapAsync"]} */
tapAsync: (options, fn) => {
processAssetsHook.tapAsync(
getOptions(options),
(assets, callback) =>
/** @type {any} */ (fn)(...getArgs(), callback)
);
},
/** @type {AsyncSeriesHook<T>["tapPromise"]} */
tapPromise: (options, fn) => {
processAssetsHook.tapPromise(getOptions(options), () =>
fn(...getArgs())
);
}
},
`${name} is deprecated (use Compilation.hook.processAssets instead and use one of Compilation.PROCESS_ASSETS_STAGE_* as stage option)`,
code
);
};
this.hooks = Object.freeze({
/** @type {SyncHook<[Module]>} */
buildModule: new SyncHook(["module"]),
/** @type {SyncHook<[Module]>} */
rebuildModule: new SyncHook(["module"]),
/** @type {SyncHook<[Module, WebpackError]>} */
failedModule: new SyncHook(["module", "error"]),
/** @type {SyncHook<[Module]>} */
succeedModule: new SyncHook(["module"]),
/** @type {SyncHook<[Module]>} */
stillValidModule: new SyncHook(["module"]),
/** @type {SyncHook<[Dependency, EntryOptions]>} */
addEntry: new SyncHook(["entry", "options"]),
/** @type {SyncHook<[Dependency, EntryOptions, Error]>} */
failedEntry: new SyncHook(["entry", "options", "error"]),
/** @type {SyncHook<[Dependency, EntryOptions, Module]>} */
succeedEntry: new SyncHook(["entry", "options", "module"]),
/** @type {SyncWaterfallHook<[(string[] | ReferencedExport)[], Dependency, RuntimeSpec]>} */
dependencyReferencedExports: new SyncWaterfallHook([
"referencedExports",
"dependency",
"runtime"
]),
/** @type {AsyncSeriesHook<[Iterable<Module>]>} */
finishModules: new AsyncSeriesHook(["modules"]),
/** @type {AsyncSeriesHook<[Module]>} */
finishRebuildingModule: new AsyncSeriesHook(["module"]),
/** @type {SyncHook<[]>} */
unseal: new SyncHook([]),
/** @type {SyncHook<[]>} */
seal: new SyncHook([]),
/** @type {SyncHook<[]>} */
beforeChunks: new SyncHook([]),
/** @type {SyncHook<[Iterable<Chunk>]>} */
afterChunks: new SyncHook(["chunks"]),
/** @type {SyncBailHook<[Iterable<Module>]>} */
optimizeDependencies: new SyncBailHook(["modules"]),
/** @type {SyncHook<[Iterable<Module>]>} */
afterOptimizeDependencies: new SyncHook(["modules"]),
/** @type {SyncHook<[]>} */
optimize: new SyncHook([]),
/** @type {SyncBailHook<[Iterable<Module>]>} */
optimizeModules: new SyncBailHook(["modules"]),
/** @type {SyncHook<[Iterable<Module>]>} */
afterOptimizeModules: new SyncHook(["modules"]),
/** @type {SyncBailHook<[Iterable<Chunk>, ChunkGroup[]]>} */
optimizeChunks: new SyncBailHook(["chunks", "chunkGroups"]),
/** @type {SyncHook<[Iterable<Chunk>, ChunkGroup[]]>} */
afterOptimizeChunks: new SyncHook(["chunks", "chunkGroups"]),
/** @type {AsyncSeriesHook<[Iterable<Chunk>, Iterable<Module>]>} */
optimizeTree: new AsyncSeriesHook(["chunks", "modules"]),
/** @type {SyncHook<[Iterable<Chunk>, Iterable<Module>]>} */
afterOptimizeTree: new SyncHook(["chunks", "modules"]),
/** @type {AsyncSeriesBailHook<[Iterable<Chunk>, Iterable<Module>]>} */
optimizeChunkModules: new AsyncSeriesBailHook(["chunks", "modules"]),
/** @type {SyncHook<[Iterable<Chunk>, Iterable<Module>]>} */
afterOptimizeChunkModules: new SyncHook(["chunks", "modules"]),
/** @type {SyncBailHook<[], boolean>} */
shouldRecord: new SyncBailHook([]),
/** @type {SyncHook<[Chunk, Set<string>]>} */
additionalChunkRuntimeRequirements: new SyncHook([
"chunk",
"runtimeRequirements"
]),
/** @type {HookMap<SyncBailHook<[Chunk, Set<string>]>>} */
runtimeRequirementInChunk: new HookMap(
() => new SyncBailHook(["chunk", "runtimeRequirements"])
),
/** @type {SyncHook<[Module, Set<string>]>} */
additionalModuleRuntimeRequirements: new SyncHook([
"module",
"runtimeRequirements"
]),
/** @type {HookMap<SyncBailHook<[Module, Set<string>]>>} */
runtimeRequirementInModule: new HookMap(
() => new SyncBailHook(["module", "runtimeRequirements"])
),
/** @type {SyncHook<[Chunk, Set<string>]>} */
additionalTreeRuntimeRequirements: new SyncHook([
"chunk",
"runtimeRequirements"
]),
/** @type {HookMap<SyncBailHook<[Chunk, Set<string>]>>} */
runtimeRequirementInTree: new HookMap(
() => new SyncBailHook(["chunk", "runtimeRequirements"])
),
/** @type {SyncHook<[RuntimeModule, Chunk]>} */
runtimeModule: new SyncHook(["module", "chunk"]),
/** @type {SyncHook<[Iterable<Module>, any]>} */
reviveModules: new SyncHook(["modules", "records"]),
/** @type {SyncHook<[Iterable<Module>]>} */
beforeModuleIds: new SyncHook(["modules"]),
/** @type {SyncHook<[Iterable<Module>]>} */
moduleIds: new SyncHook(["modules"]),
/** @type {SyncHook<[Iterable<Module>]>} */
optimizeModuleIds: new SyncHook(["modules"]),
/** @type {SyncHook<[Iterable<Module>]>} */
afterOptimizeModuleIds: new SyncHook(["modules"]),
/** @type {SyncHook<[Iterable<Chunk>, any]>} */
reviveChunks: new SyncHook(["chunks", "records"]),
/** @type {SyncHook<[Iterable<Chunk>]>} */
beforeChunkIds: new SyncHook(["chunks"]),
/** @type {SyncHook<[Iterable<Chunk>]>} */
chunkIds: new SyncHook(["chunks"]),
/** @type {SyncHook<[Iterable<Chunk>]>} */
optimizeChunkIds: new SyncHook(["chunks"]),
/** @type {SyncHook<[Iterable<Chunk>]>} */
afterOptimizeChunkIds: new SyncHook(["chunks"]),
/** @type {SyncHook<[Iterable<Module>, any]>} */
recordModules: new SyncHook(["modules", "records"]),
/** @type {SyncHook<[Iterable<Chunk>, any]>} */
recordChunks: new SyncHook(["chunks", "records"]),
/** @type {SyncHook<[Iterable<Module>]>} */
optimizeCodeGeneration: new SyncHook(["modules"]),
/** @type {SyncHook<[]>} */
beforeModuleHash: new SyncHook([]),
/** @type {SyncHook<[]>} */
afterModuleHash: new SyncHook([]),
/** @type {SyncHook<[]>} */
beforeCodeGeneration: new SyncHook([]),
/** @type {SyncHook<[]>} */
afterCodeGeneration: new SyncHook([]),
/** @type {SyncHook<[]>} */
beforeRuntimeRequirements: new SyncHook([]),
/** @type {SyncHook<[]>} */
afterRuntimeRequirements: new SyncHook([]),
/** @type {SyncHook<[]>} */
beforeHash: new SyncHook([]),
/** @type {SyncHook<[Chunk]>} */
contentHash: new SyncHook(["chunk"]),
/** @type {SyncHook<[]>} */
afterHash: new SyncHook([]),
/** @type {SyncHook<[any]>} */
recordHash: new SyncHook(["records"]),
/** @type {SyncHook<[Compilation, any]>} */
record: new SyncHook(["compilation", "records"]),
/** @type {SyncHook<[]>} */
beforeModuleAssets: new SyncHook([]),
/** @type {SyncBailHook<[], boolean>} */
shouldGenerateChunkAssets: new SyncBailHook([]),
/** @type {SyncHook<[]>} */
beforeChunkAssets: new SyncHook([]),
// TODO webpack 6 remove
/** @deprecated */
additionalChunkAssets: createProcessAssetsHook(
"additionalChunkAssets",
Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
() => [this.chunks],
"DEP_WEBPACK_COMPILATION_ADDITIONAL_CHUNK_ASSETS"
),
// TODO webpack 6 deprecate
/** @deprecated */
additionalAssets: createProcessAssetsHook(
"additionalAssets",
Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
() => []
),
// TODO webpack 6 remove
/** @deprecated */
optimizeChunkAssets: createProcessAssetsHook(
"optimizeChunkAssets",
Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE,
() => [this.chunks],
"DEP_WEBPACK_COMPILATION_OPTIMIZE_CHUNK_ASSETS"
),
// TODO webpack 6 remove
/** @deprecated */
afterOptimizeChunkAssets: createProcessAssetsHook(
"afterOptimizeChunkAssets",
Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE + 1,
() => [this.chunks],
"DEP_WEBPACK_COMPILATION_AFTER_OPTIMIZE_CHUNK_ASSETS"
),
// TODO webpack 6 deprecate
/** @deprecated */
optimizeAssets: processAssetsHook,
// TODO webpack 6 deprecate
/** @deprecated */
afterOptimizeAssets: afterProcessAssetsHook,
processAssets: processAssetsHook,
afterProcessAssets: afterProcessAssetsHook,
/** @type {SyncBailHook<[], boolean>} */
needAdditionalSeal: new SyncBailHook([]),
/** @type {AsyncSeriesHook<[]>} */
afterSeal: new AsyncSeriesHook([]),
/** @type {SyncWaterfallHook<[RenderManifestEntry[], RenderManifestOptions]>} */
renderManifest: new SyncWaterfallHook(["result", "options"]),
/** @type {SyncHook<[Hash]>} */
fullHash: new SyncHook(["hash"]),
/** @type {SyncHook<[Chunk, Hash, ChunkHashContext]>} */
chunkHash: new SyncHook(["chunk", "chunkHash", "ChunkHashContext"]),
/** @type {SyncHook<[Module, string]>} */
moduleAsset: new SyncHook(["module", "filename"]),
/** @type {SyncHook<[Chunk, string]>} */
chunkAsset: new SyncHook(["chunk", "filename"]),
/** @type {SyncWaterfallHook<[string, object, AssetInfo]>} */
assetPath: new SyncWaterfallHook(["path", "options", "assetInfo"]),
/** @type {SyncBailHook<[], boolean>} */
needAdditionalPass: new SyncBailHook([]),
/** @type {SyncHook<[Compiler, string, number]>} */
childCompiler: new SyncHook([
"childCompiler",
"compilerName",
"compilerIndex"
]),
/** @type {SyncBailHook<[string, LogEntry], true>} */
log: new SyncBailHook(["origin", "logEntry"]),
/** @type {SyncWaterfallHook<[WebpackError[]]>} */
processWarnings: new SyncWaterfallHook(["warnings"]),
/** @type {SyncWaterfallHook<[WebpackError[]]>} */
processErrors: new SyncWaterfallHook(["errors"]),
/** @type {HookMap<SyncHook<[Object, Object]>>} */
statsPreset: new HookMap(() => new SyncHook(["options", "context"])),
/** @type {SyncHook<[Object, Object]>} */
statsNormalize: new SyncHook(["options", "context"]),
/** @type {SyncHook<[StatsFactory, Object]>} */
statsFactory: new SyncHook(["statsFactory", "options"]),
/** @type {SyncHook<[StatsPrinter, Object]>} */
statsPrinter: new SyncHook(["statsPrinter", "options"]),
get normalModuleLoader() {
return getNormalModuleLoader();
}
});
/** @type {string=} */
this.name = undefined;
this.startTime = undefined;
this.endTime = undefined;
/** @type {Compiler} */
this.compiler = compiler;
this.resolverFactory = compiler.resolverFactory;
this.inputFileSystem = compiler.inputFileSystem;
this.fileSystemInfo = new FileSystemInfo(this.inputFileSystem, {
managedPaths: compiler.managedPaths,
immutablePaths: compiler.immutablePaths,
logger: this.getLogger("webpack.FileSystemInfo")
});
if (compiler.fileTimestamps) {
this.fileSystemInfo.addFileTimestamps(compiler.fileTimestamps);
}
if (compiler.contextTimestamps) {
this.fileSystemInfo.addContextTimestamps(compiler.contextTimestamps);
}
this.requestShortener = compiler.requestShortener;
this.compilerPath = compiler.compilerPath;
this.logger = this.getLogger("webpack.Compilation");
const options = compiler.options;
this.options = options;
this.outputOptions = options && options.output;
/** @type {boolean} */
this.bail = (options && options.bail) || false;
/** @type {boolean} */
this.profile = (options && options.profile) || false;
this.mainTemplate = new MainTemplate(this.outputOptions, this);
this.chunkTemplate = new ChunkTemplate(this.outputOptions, this);
this.runtimeTemplate = new RuntimeTemplate(
this.outputOptions,
this.requestShortener
);
/** @type {{javascript: ModuleTemplate}} */
this.moduleTemplates = {
javascript: new ModuleTemplate(this.runtimeTemplate, this)
};
Object.defineProperties(this.moduleTemplates, {
asset: {
enumerable: false,
configurable: false,
get() {
throw new WebpackError(
"Compilation.moduleTemplates.asset has been removed"
);
}
},
webassembly: {
enumerable: false,
configurable: false,
get() {
throw new WebpackError(
"Compilation.moduleTemplates.webassembly has been removed"
);
}
}
});
this.moduleGraph = new ModuleGraph();
this.chunkGraph = undefined;
/** @type {CodeGenerationResults} */
this.codeGenerationResults = undefined;
/** @type {AsyncQueue<FactorizeModuleOptions, string, Module>} */
this.factorizeQueue = new AsyncQueue({
name: "factorize",
parallelism: options.parallelism || 100,
processor: this._factorizeModule.bind(this)
});
/** @type {AsyncQueue<Module, string, Module>} */
this.addModuleQueue = new AsyncQueue({
name: "addModule",
parallelism: options.parallelism || 100,
getKey: module => module.identifier(),
processor: this._addModule.bind(this)
});
/** @type {AsyncQueue<Module, Module, Module>} */
this.buildQueue = new AsyncQueue({
name: "build",
parallelism: options.parallelism || 100,
processor: this._buildModule.bind(this)
});
/** @type {AsyncQueue<Module, Module, Module>} */
this.rebuildQueue = new AsyncQueue({
name: "rebuild",
parallelism: options.parallelism || 100,
processor: this._rebuildModule.bind(this)
});
/** @type {AsyncQueue<Module, Module, Module>} */
this.processDependenciesQueue = new AsyncQueue({
name: "processDependencies",
parallelism: options.parallelism || 100,
processor: this._processModuleDependencies.bind(this)
});
/**
* Modules in value are building during the build of Module in key.
* Means value blocking key from finishing.
* Needed to detect build cycles.
* @type {WeakMap<Module, Set<Module>>}
*/
this.creatingModuleDuringBuild = new WeakMap();
/** @type {Map<string, EntryData>} */
this.entries = new Map();
/** @type {EntryData} */
this.globalEntry = {
dependencies: [],
includeDependencies: [],
options: {
name: undefined
}
};
/** @type {Map<string, Entrypoint>} */
this.entrypoints = new Map();
/** @type {Entrypoint[]} */
this.asyncEntrypoints = [];
/** @type {Set<Chunk>} */
this.chunks = new Set();
arrayToSetDeprecation(this.chunks, "Compilation.chunks");
/** @type {ChunkGroup[]} */
this.chunkGroups = [];
/** @type {Map<string, ChunkGroup>} */
this.namedChunkGroups = new Map();
/** @type {Map<string, Chunk>} */
this.namedChunks = new Map();
/** @type {Set<Module>} */
this.modules = new Set();
arrayToSetDeprecation(this.modules, "Compilation.modules");
/** @private @type {Map<string, Module>} */
this._modules = new Map();
this.records = null;
/** @type {string[]} */
this.additionalChunkAssets = [];
/** @type {CompilationAssets} */
this.assets = {};
/** @type {Map<string, AssetInfo>} */
this.assetsInfo = new Map();
/** @type {Map<string, Map<string, Set<string>>>} */
this._assetsRelatedIn = new Map();
/** @type {WebpackError[]} */
this.errors = [];
/** @type {WebpackError[]} */
this.warnings = [];
/** @type {Compilation[]} */
this.children = [];
/** @type {Map<string, LogEntry[]>} */
this.logging = new Map();
/** @type {Map<DepConstructor, ModuleFactory>} */
this.dependencyFactories = new Map();
/** @type {DependencyTemplates} */
this.dependencyTemplates = new DependencyTemplates();
this.childrenCounters = {};
/** @type {Set<number|string>} */
this.usedChunkIds = null;
/** @type {Set<number>} */
this.usedModuleIds = null;
/** @type {boolean} */
this.needAdditionalPass = false;
/** @type {WeakSet<Module>} */
this.builtModules = new WeakSet();
/** @type {WeakSet<Module>} */
this.codeGeneratedModules = new WeakSet();
/** @private @type {Map<Module, Callback[]>} */
this._rebuildingModules = new Map();
/** @type {Set<string>} */
this.emittedAssets = new Set();
/** @type {Set<string>} */
this.comparedForEmitAssets = new Set();
/** @type {LazySet<string>} */
this.fileDependencies = new LazySet();
/** @type {LazySet<string>} */
this.contextDependencies = new LazySet();
/** @type {LazySet<string>} */
this.missingDependencies = new LazySet();
/** @type {LazySet<string>} */
this.buildDependencies = new LazySet();
// TODO webpack 6 remove
this.compilationDependencies = {
add: util.deprecate(
item => this.fileDependencies.add(item),
"Compilation.compilationDependencies is deprecated (used Compilation.fileDependencies instead)",
"DEP_WEBPACK_COMPILATION_COMPILATION_DEPENDENCIES"
)
};
this._modulesCache = this.getCache("Compilation/modules");
this._assetsCache = this.getCache("Compilation/assets");
this._codeGenerationCache = this.getCache("Compilation/codeGeneration");
}
getStats() {
return new Stats(this);
}
createStatsOptions(optionsOrPreset, context = {}) {
if (
typeof optionsOrPreset === "boolean" ||
typeof optionsOrPreset === "string"
) {
optionsOrPreset = { preset: optionsOrPreset };
}
if (typeof optionsOrPreset === "object" && optionsOrPreset !== null) {
// We use this method of shallow cloning this object to include
// properties in the prototype chain
const options = {};
for (const key in optionsOrPreset) {
options[key] = optionsOrPreset[key];
}
if (options.preset !== undefined) {
this.hooks.statsPreset.for(options.preset).call(options, context);
}
this.hooks.statsNormalize.call(options, context);
return options;
} else {
const options = {};
this.hooks.statsNormalize.call(options, context);
return options;
}
}
createStatsFactory(options) {
const statsFactory = new StatsFactory();
this.hooks.statsFactory.call(statsFactory, options);
return statsFactory;
}
createStatsPrinter(options) {
const statsPrinter = new StatsPrinter();
this.hooks.statsPrinter.call(statsPrinter, options);
return statsPrinter;
}
/**
* @param {string} name cache name
* @returns {CacheFacade} the cache facade instance
*/
getCache(name) {
return this.compiler.getCache(name);
}
/**
* @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
*/
getLogger(name) {
if (!name) {
throw new TypeError("Compilation.getLogger(name) called without a name");
}
/** @type {LogEntry[] | undefined} */
let logEntries;
return new Logger(
(type, args) => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compilation.getLogger(name) called with a function not returning a name"
);
}
}
let trace;
switch (type) {
case LogType.warn:
case LogType.error:
case LogType.trace:
trace = ErrorHelpers.cutOffLoaderExecution(new Error("Trace").stack)
.split("\n")
.slice(3);
break;
}
/** @type {LogEntry} */
const logEntry = {
time: Date.now(),
type,
args,
trace
};
if (this.hooks.log.call(name, logEntry) === undefined) {
if (logEntry.type === LogType.profileEnd) {
// eslint-disable-next-line node/no-unsupported-features/node-builtins
if (typeof console.profileEnd === "function") {
// eslint-disable-next-line node/no-unsupported-features/node-builtins
console.profileEnd(`[${name}] ${logEntry.args[0]}`);
}
}
if (logEntries === undefined) {
logEntries = this.logging.get(name);
if (logEntries === undefined) {
logEntries = [];
this.logging.set(name, logEntries);
}
}
logEntries.push(logEntry);
if (logEntry.type === LogType.profile) {
// eslint-disable-next-line node/no-unsupported-features/node-builtins
if (typeof console.profile === "function") {
// eslint-disable-next-line node/no-unsupported-features/node-builtins
console.profile(`[${name}] ${logEntry.args[0]}`);
}
}
}
},
childName => {
if (typeof name === "function") {
if (typeof childName === "function") {
return this.getLogger(() => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compilation.getLogger(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.getLogger(() => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compilation.getLogger(name) called with a function not returning a name"
);
}
}
return `${name}/${childName}`;
});
}
} else {
if (typeof childName === "function") {
return this.getLogger(() => {
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.getLogger(`${name}/${childName}`);
}
}
}
);
}
/**
* @param {Module} module module to be added that was created
* @param {ModuleCallback} callback returns the module in the compilation,
* it could be the passed one (if new), or an already existing in the compilation
* @returns {void}
*/
addModule(module, callback) {
this.addModuleQueue.add(module, callback);
}
/**
* @param {Module} module module to be added that was created
* @param {ModuleCallback} callback returns the module in the compilation,
* it could be the passed one (if new), or an already existing in the compilation
* @returns {void}
*/
_addModule(module, callback) {
const identifier = module.identifier();
const alreadyAddedModule = this._modules.get(identifier);
if (alreadyAddedModule) {
return callback(null, alreadyAddedModule);
}
const currentProfile = this.profile
? this.moduleGraph.getProfile(module)
: undefined;
if (currentProfile !== undefined) {
currentProfile.markRestoringStart();
}
this._modulesCache.get(identifier, null, (err, cacheModule) => {
if (err) return callback(new ModuleRestoreError(module, err));
if (currentProfile !== undefined) {
currentProfile.markRestoringEnd();
currentProfile.markIntegrationStart();
}
if (cacheModule) {
cacheModule.updateCacheModule(module);
module = cacheModule;
}
this._modules.set(identifier, module);
this.modules.add(module);
ModuleGraph.setModuleGraphForModule(module, this.moduleGraph);
if (currentProfile !== undefined) {
currentProfile.markIntegrationEnd();
}
callback(null, module);
});
}
/**
* Fetches a module from a compilation by its identifier
* @param {Module} module the module provided
* @returns {Module} the module requested
*/
getModule(module) {
const identifier = module.identifier();
return this._modules.get(identifier);
}
/**
* Attempts to search for a module by its identifier
* @param {string} identifier identifier (usually path) for module
* @returns {Module|undefined} attempt to search for module and return it, else undefined
*/
findModule(identifier) {
return this._modules.get(identifier);
}
/**
* Schedules a build of the module object
*
* @param {Module} module module to be built
* @param {ModuleCallback} callback the callback
* @returns {void}
*/
buildModule(module, callback) {
this.buildQueue.add(module, callback);
}
/**
* Builds the module object
*
* @param {Module} module module to be built
* @param {ModuleCallback} callback the callback
* @returns {void}
*/
_buildModule(module, callback) {
const currentProfile = this.profile
? this.moduleGraph.getProfile(module)
: undefined;
if (currentProfile !== undefined) {
currentProfile.markBuildingStart();
}
module.needBuild(
{
fileSystemInfo: this.fileSystemInfo
},
(err, needBuild) => {
if (err) return callback(err);
if (!needBuild) {
if (currentProfile !== undefined) {
currentProfile.markBuildingEnd();
}
this.hooks.stillValidModule.call(module);
return callback();
}
this.hooks.buildModule.call(module);
this.builtModules.add(module);
module.build(
this.options,
this,
this.resolverFactory.get("normal", module.resolveOptions),
this.inputFileSystem,
err => {
if (currentProfile !== undefined) {
currentProfile.markBuildingEnd();
}
if (err) {
this.hooks.failedModule.call(module, err);
return callback(err);
}
if (currentProfile !== undefined) {
currentProfile.markStoringStart();
}
this._modulesCache.store(module.identifier(), null, module, err => {
if (currentProfile !== undefined) {
currentProfile.markStoringEnd();
}
if (err) {
this.hooks.failedModule.call(module, err);
return callback(new ModuleStoreError(module, err));
}
this.hooks.succeedModule.call(module);
return callback();
});
}
);
}
);
}
/**
* @param {Module} module to be processed for deps
* @param {ModuleCallback} callback callback to be triggered
* @returns {void}
*/
processModuleDependencies(module, callback) {
this.processDependenciesQueue.add(module, callback);
}
/**
* @param {Module} module to be processed for deps
* @returns {void}
*/
processModuleDependenciesNonRecursive(module) {
const processDependenciesBlock = block => {
if (block.dependencies) {
for (const dep of block.dependencies) {
this.moduleGraph.setParents(dep, block, module);
}
}
if (block.blocks) {
for (const b of block.blocks) processDependenciesBlock(b);
}
};
processDependenciesBlock(module);
}
/**
* @param {Module} module to be processed for deps
* @param {ModuleCallback} callback callback to be triggered
* @returns {void}
*/
_processModuleDependencies(module, callback) {
const dependencies = new Map();
/**
* @type {Array<{factory: ModuleFactory, dependencies: Dependency[], originModule: Module|null}>}
*/
const sortedDependencies = [];
let currentBlock = module;
let factoryCacheKey;
let factoryCacheValue;
let factoryCacheValue2;
let listCacheKey;
let listCacheValue;
const processDependency = dep => {
this.moduleGraph.setParents(dep, currentBlock, module);
const resourceIdent = dep.getResourceIdentifier();
if (resourceIdent) {
// Here webpack is using heuristic that assumes
// mostly esm dependencies would be used
// so we don't allocate extra string for them
const category = dep.category;
const cacheKey =
category === esmDependencyCategory
? resourceIdent
: `${category}${resourceIdent}`;
const constructor = dep.constructor;
let innerMap;
let factory;
if (factoryCacheKey === constructor) {
innerMap = factoryCacheValue;
if (listCacheKey === cacheKey) {
listCacheValue.push(dep);
return;
}
} else {
factory = this.dependencyFactories.get(dep.constructor);
if (factory === undefined) {
throw new Error(
`No module factory available for dependency type: ${dep.constructor.name}`
);
}
innerMap = dependencies.get(factory);
if (innerMap === undefined) {
dependencies.set(factory, (innerMap = new Map()));
}
factoryCacheKey = constructor;
factoryCacheValue = innerMap;
factoryCacheValue2 = factory;
}
let list = innerMap.get(cacheKey);
if (list === undefined) {
innerMap.set(cacheKey, (list = []));
sortedDependencies.push({
factory: factoryCacheValue2,
dependencies: list,
originModule: module
});
}
list.push(dep);
listCacheKey = cacheKey;
listCacheValue = list;
}
};
const processDependenciesBlock = block => {
if (block.dependencies) {
currentBlock = block;
for (const dep of block.dependencies) processDependency(dep);
}
if (block.blocks) {
for (const b of block.blocks) processDependenciesBlock(b);
}
};
try {
processDependenciesBlock(module);
} catch (e) {
return callback(e);
}
if (sortedDependencies.length === 0) {
callback();
return;
}
// This is nested so we need to allow one additional task
this.processDependenciesQueue.increaseParallelism();
asyncLib.forEach(
sortedDependencies,
(item, callback) => {
this.handleModuleCreation(item, err => {
// In V8, the Error objects keep a reference to the functions on the stack. These warnings &
// errors are created inside closures that keep a reference to the Compilation, so errors are
// leaking the Compilation object.
if (err && this.bail) {
// eslint-disable-next-line no-self-assign
err.stack = err.stack;
return callback(err);
}
callback();
});
},
err => {
this.processDependenciesQueue.decreaseParallelism();
return callback(err);
}
);
}
/**
* @typedef {Object} HandleModuleCreationOptions
* @property {ModuleFactory} factory
* @property {Dependency[]} dependencies
* @property {Module | null} originModule
* @property {string=} context
* @property {boolean=} recursive recurse into dependencies of the created module
*/
/**
* @param {HandleModuleCreationOptions} options options object
* @param {ModuleCallback} callback callback
* @returns {void}
*/
handleModuleCreation(
{ factory, dependencies, originModule, context, recursive = true },
callback
) {
const moduleGraph = this.moduleGraph;
const currentProfile = this.profile ? new ModuleProfile() : undefined;
this.factorizeModule(
{ currentProfile, factory, dependencies, originModule, context },
(err, newModule) => {
if (err) {
if (dependencies.every(d => d.optional)) {
this.warnings.push(err);
} else {
this.errors.push(err);
}
return callback(err);
}
if (!newModule) {
return callback();
}
if (currentProfile !== undefined) {
moduleGraph.setProfile(newModule, currentProfile);
}
this.addModule(newModule, (err, module) => {
if (err) {
if (!err.module) {
err.module = module;
}
this.errors.push(err);
return callback(err);
}
for (let i = 0; i < dependencies.length; i++) {
const dependency = dependencies[i];
moduleGraph.setResolvedModule(originModule, dependency, module);
}
moduleGraph.setIssuerIfUnset(
module,
originModule !== undefined ? originModule : null
);
if (module !== newModule) {
if (currentProfile !== undefined) {
const otherProfile = moduleGraph.getProfile(module);
if (otherProfile !== undefined) {
currentProfile.mergeInto(otherProfile);
} else {
moduleGraph.setProfile(module, currentProfile);
}
}
}
// Check for cycles when build is trigger inside another build
let creatingModuleDuringBuildSet = undefined;
if (!recursive && this.buildQueue.isProcessing(originModule)) {
// Track build dependency
creatingModuleDuringBuildSet = this.creatingModuleDuringBuild.get(
originModule
);
if (creatingModuleDuringBuildSet === undefined) {
creatingModuleDuringBuildSet = new Set();
this.creatingModuleDuringBuild.set(
originModule,
creatingModuleDuringBuildSet
);
}
creatingModuleDuringBuildSet.add(originModule);
// When building is blocked by another module
// search for a cycle, cancel the cycle by throwing
// an error (otherwise this would deadlock)
const blockReasons = this.creatingModuleDuringBuild.get(module);
if (blockReasons !== undefined) {
const set = new Set(blockReasons);
for (const item of set) {
const blockReasons = this.creatingModuleDuringBuild.get(item);
if (blockReasons !== undefined) {
for (const m of blockReasons) {
if (m === module) {
return callback(new BuildCycleError(module));
}
set.add(m);
}
}
}
}
}
this.buildModule(module, err => {
if (creatingModuleDuringBuildSet !== undefined) {
creatingModuleDuringBuildSet.delete(module);
}
if (err) {
if (!err.module) {
err.module = module;
}
this.errors.push(err);
return callback(err);
}
if (!recursive) {
this.processModuleDependenciesNonRecursive(module);
callback(null, module);
return;
}
// This avoids deadlocks for circular dependencies
if (this.processDependenciesQueue.isProcessing(module)) {
return callback();
}
this.processModuleDependencies(module, err => {
if (err) {
return callback(err);
}
callback(null, module);
});
});
});
}
);
}
/**
* @typedef {Object} FactorizeModuleOptions
* @property {ModuleProfile} currentProfile
* @property {ModuleFactory} factory
* @property {Dependency[]} dependencies
* @property {Module | null} originModule
* @property {string=} context
*/
/**
* @param {FactorizeModuleOptions} options options object
* @param {ModuleCallback} callback callback
* @returns {void}
*/
factorizeModule(options, callback) {
this.factorizeQueue.add(options, callback);
}
/**
* @param {FactorizeModuleOptions} options options object
* @param {ModuleCallback} callback callback
* @returns {void}
*/
_factorizeModule(
{ currentProfile, factory, dependencies, originModule, context },
callback
) {
if (currentProfile !== undefined) {
currentProfile.markFactoryStart();
}
factory.create(
{
contextInfo: {
issuer: originModule ? originModule.nameForCondition() : "",
compiler: this.compiler.name
},
resolveOptions: originModule ? originModule.resolveOptions : undefined,
context: context
? context
: originModule
? originModule.context
: this.compiler.context,
dependencies: dependencies
},
(err, result) => {
if (result) {
// TODO webpack 6: remove
// For backward-compat
if (result.module === undefined && result instanceof Module) {
result = {
module: result
};
}
const {
fileDependencies,
contextDependencies,
missingDependencies
} = result;
if (fileDependencies) {
this.fileDependencies.addAll(fileDependencies);
}
if (contextDependencies) {
this.contextDependencies.addAll(contextDependencies);
}
if (missingDependencies) {
this.missingDependencies.addAll(missingDependencies);
}
}
if (err) {
const notFoundError = new ModuleNotFoundError(
originModule,
err,
dependencies.map(d => d.loc).filter(Boolean)[0]
);
return callback(notFoundError);
}
if (!result) {
return callback();
}
const newModule = result.module;
if (!newModule) {
return callback();
}
if (currentProfile !== undefined) {
currentProfile.markFactoryEnd();
}
callback(null, newModule);
}
);
}
/**
*
* @param {string} context context string path
* @param {Dependency} dependency dependency used to create Module chain
* @param {ModuleCallback} callback callback for when module chain is complete
* @returns {void} will throw if dependency instance is not a valid Dependency
*/
addModuleChain(context, dependency, callback) {
if (
typeof dependency !== "object" ||
dependency === null ||
!dependency.constructor
) {
return callback(
new WebpackError("Parameter 'dependency' must be a Dependency")
);
}
const Dep = /** @type {DepConstructor} */ (dependency.constructor);
const moduleFactory = this.dependencyFactories.get(Dep);
if (!moduleFactory) {
return callback(
new WebpackError(
`No dependency factory available for this dependency type: ${dependency.constructor.name}`
)
);
}
this.handleModuleCreation(
{
factory: moduleFactory,
dependencies: [dependency],
originModule: null,
context
},
err => {
if (err && this.bail) {
callback(err);
this.buildQueue.stop();
this.rebuildQueue.stop();
this.processDependenciesQueue.stop();
this.factorizeQueue.stop();
} else {
callback();
}
}
);
}
/**
* @param {string} context context path for entry
* @param {Dependency} entry entry dependency that should be followed
* @param {string | EntryOptions} optionsOrName options or deprecated name of entry
* @param {ModuleCallback} callback callback function
* @returns {void} returns
*/
addEntry(context, entry, optionsOrName, callback) {
// TODO webpack 6 remove
const options =
typeof optionsOrName === "object"
? optionsOrName
: { name: optionsOrName };
this._addEntryItem(context, entry, "dependencies", options, callback);
}
/**
* @param {string} context context path for entry
* @param {Dependency} dependency dependency that should be followed
* @param {EntryOptions} options options
* @param {ModuleCallback} callback callback function
* @returns {void} returns
*/
addInclude(context, dependency, options, callback) {
this._addEntryItem(
context,
dependency,
"includeDependencies",
options,
callback
);
}
/**
* @param {string} context context path for entry
* @param {Dependency} entry entry dependency that should be followed
* @param {"dependencies" | "includeDependencies"} target type of entry
* @param {EntryOptions} options options
* @param {ModuleCallback} callback callback function
* @returns {void} returns
*/
_addEntryItem(contex