UNPKG

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,480 lines (1,360 loc) 159 kB
/* 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, AsyncParallelHook } = 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 Dependency = require("./Dependency"); const DependencyTemplates = require("./DependencyTemplates"); const Entrypoint = require("./Entrypoint"); const ErrorHelpers = require("./ErrorHelpers"); const FileSystemInfo = require("./FileSystemInfo"); const { connectChunkGroupAndChunk, connectChunkGroupParentAndChild } = require("./GraphHelpers"); const { makeWebpackError, tryRunOrWebpackError } = 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 { provide } = require("./util/MapHelpers"); const WeakTupleMap = require("./util/WeakTupleMap"); 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 processAsyncTree = require("./util/processAsyncTree"); const { getRuntimeKey } = require("./util/runtime"); const { isSourceEqual } = require("./util/source"); /** @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("../declarations/WebpackOptions").StatsOptions} StatsOptions */ /** @typedef {import("../declarations/WebpackOptions").WebpackPluginFunction} WebpackPluginFunction */ /** @typedef {import("../declarations/WebpackOptions").WebpackPluginInstance} WebpackPluginInstance */ /** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */ /** @typedef {import("./Cache")} Cache */ /** @typedef {import("./CacheFacade")} CacheFacade */ /** @typedef {import("./ChunkGroup").ChunkGroupOptions} ChunkGroupOptions */ /** @typedef {import("./Compiler")} Compiler */ /** @typedef {import("./Compiler").CompilationParams} CompilationParams */ /** @typedef {import("./DependenciesBlock")} DependenciesBlock */ /** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */ /** @typedef {import("./Dependency").ReferencedExport} ReferencedExport */ /** @typedef {import("./DependencyTemplate")} DependencyTemplate */ /** @typedef {import("./Entrypoint").EntryOptions} EntryOptions */ /** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */ /** @typedef {import("./ModuleFactory")} ModuleFactory */ /** @typedef {import("./ModuleFactory").ModuleFactoryCreateDataContextInfo} ModuleFactoryCreateDataContextInfo */ /** @typedef {import("./ModuleFactory").ModuleFactoryResult} ModuleFactoryResult */ /** @typedef {import("./RequestShortener")} RequestShortener */ /** @typedef {import("./RuntimeModule")} RuntimeModule */ /** @typedef {import("./Template").RenderManifestEntry} RenderManifestEntry */ /** @typedef {import("./Template").RenderManifestOptions} RenderManifestOptions */ /** @typedef {import("./stats/DefaultStatsFactoryPlugin").StatsAsset} StatsAsset */ /** @typedef {import("./stats/DefaultStatsFactoryPlugin").StatsError} StatsError */ /** @typedef {import("./stats/DefaultStatsFactoryPlugin").StatsModule} StatsModule */ /** @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 ModuleFactoryResultCallback * @param {WebpackError=} err * @param {ModuleFactoryResult=} result * @returns {void} */ /** * @callback ModuleOrFactoryResultCallback * @param {WebpackError=} err * @param {Module | ModuleFactoryResult=} result * @returns {void} */ /** * @callback ExecuteModuleCallback * @param {WebpackError=} err * @param {ExecuteModuleResult=} result * @returns {void} */ /** * @callback DepBlockVarDependenciesCallback * @param {Dependency} dependency * @returns {any} */ /** @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} RuntimeRequirementsContext * @property {ChunkGraph} chunkGraph the chunk graph * @property {CodeGenerationResults} codeGenerationResults the code generation results */ /** * @typedef {Object} ExecuteModuleOptions * @property {EntryOptions=} entryOptions */ /** * @typedef {Object} ExecuteModuleResult * @property {any} exports * @property {boolean} cacheable * @property {Map<string, { source: Source, info: AssetInfo }>} assets * @property {LazySet<string>} fileDependencies * @property {LazySet<string>} contextDependencies * @property {LazySet<string>} missingDependencies * @property {LazySet<string>} buildDependencies */ /** * @typedef {Object} ExecuteModuleArgument * @property {Module} module * @property {{ id: string, exports: any, loaded: boolean }=} moduleObject * @property {any} preparedInfo * @property {CodeGenerationResult} codeGenerationResult */ /** * @typedef {Object} ExecuteModuleContext * @property {Map<string, { source: Source, info: AssetInfo }>} assets * @property {Chunk} chunk * @property {ChunkGraph} chunkGraph * @property {function(string): any=} __webpack_require__ */ /** * @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} KnownAssetInfo * @property {boolean=} immutable true, if the asset can be long term cached forever (contains a hash) * @property {boolean=} minimized whether the asset is minimized * @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 {string=} sourceFilename when asset was created from a source file (potentially transformed), the original filename relative to compilation context * @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 {boolean=} javascriptModule true, when asset is javascript and an ESM * @property {Record<string, string | string[]>=} related object of pointers to other assets, keyed by type of relation (only points from parent to child) */ /** @typedef {KnownAssetInfo & Record<string, any>} AssetInfo */ /** * @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 */ /** * @typedef {Object} KnownNormalizedStatsOptions * @property {string} context * @property {RequestShortener} requestShortener * @property {string} chunksSort * @property {string} modulesSort * @property {string} chunkModulesSort * @property {string} nestedModulesSort * @property {string} assetsSort * @property {boolean} ids * @property {boolean} cachedAssets * @property {boolean} groupAssetsByEmitStatus * @property {boolean} groupAssetsByPath * @property {boolean} groupAssetsByExtension * @property {number} assetsSpace * @property {((value: string, asset: StatsAsset) => boolean)[]} excludeAssets * @property {((name: string, module: StatsModule, type: "module" | "chunk" | "root-of-chunk" | "nested") => boolean)[]} excludeModules * @property {((warning: StatsError, textValue: string) => boolean)[]} warningsFilter * @property {boolean} cachedModules * @property {boolean} orphanModules * @property {boolean} dependentModules * @property {boolean} runtimeModules * @property {boolean} groupModulesByCacheStatus * @property {boolean} groupModulesByLayer * @property {boolean} groupModulesByAttributes * @property {boolean} groupModulesByPath * @property {boolean} groupModulesByExtension * @property {boolean} groupModulesByType * @property {boolean | "auto"} entrypoints * @property {boolean} chunkGroups * @property {boolean} chunkGroupAuxiliary * @property {boolean} chunkGroupChildren * @property {number} chunkGroupMaxAssets * @property {number} modulesSpace * @property {number} chunkModulesSpace * @property {number} nestedModulesSpace * @property {false|"none"|"error"|"warn"|"info"|"log"|"verbose"} logging * @property {((value: string) => boolean)[]} loggingDebug * @property {boolean} loggingTrace * @property {any} _env */ /** @typedef {KnownNormalizedStatsOptions & Omit<StatsOptions, keyof KnownNormalizedStatsOptions> & Record<string, any>} NormalizedStatsOptions */ /** * @typedef {Object} KnownCreateStatsOptionsContext * @property {boolean=} forToString */ /** @typedef {KnownCreateStatsOptionsContext & Record<string, any>} CreateStatsOptionsContext */ /** @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" ); // TODO webpack 6: remove const defineRemovedModuleTemplates = moduleTemplates => { Object.defineProperties(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" ); } } }); moduleTemplates = undefined; }; 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); /** @type {WeakMap<Dependency, Module & { restoreFromUnsafeCache: Function } | null>} */ const unsafeCacheDependencies = new WeakMap(); /** @type {WeakMap<Module & { restoreFromUnsafeCache: Function }, object>} */ const unsafeCacheData = new WeakMap(); class Compilation { /** * Creates an instance of Compilation. * @param {Compiler} compiler the compiler which created the compilation * @param {CompilationParams} params the compilation parameters */ constructor(compiler, params) { this._backCompat = compiler._backCompat; const getNormalModuleLoader = () => deprecatedNormalModuleLoaderHook(this); /** @typedef {{ additionalAssets?: true | Function }} ProcessAssetsAdditionalOptions */ /** @type {AsyncSeriesHook<[CompilationAssets], ProcessAssetsAdditionalOptions>} */ const processAssetsHook = new AsyncSeriesHook(["assets"]); let savedAssets = new Set(); const popNewAssets = assets => { let newAssets = undefined; for (const file of Object.keys(assets)) { if (savedAssets.has(file)) continue; if (newAssets === undefined) { newAssets = Object.create(null); } newAssets[file] = assets[file]; savedAssets.add(file); } return newAssets; }; processAssetsHook.intercept({ name: "Compilation", call: () => { savedAssets = new Set(Object.keys(this.assets)); }, register: tap => { const { type, name } = tap; const { fn, additionalAssets, ...remainingTap } = tap; const additionalAssetsFn = additionalAssets === true ? fn : additionalAssets; const processedAssets = additionalAssetsFn ? new WeakSet() : undefined; switch (type) { case "sync": if (additionalAssetsFn) { this.hooks.processAdditionalAssets.tap(name, assets => { if (processedAssets.has(this.assets)) additionalAssetsFn(assets); }); } return { ...remainingTap, type: "async", fn: (assets, callback) => { try { fn(assets); } catch (e) { return callback(e); } if (processedAssets !== undefined) processedAssets.add(this.assets); const newAssets = popNewAssets(assets); if (newAssets !== undefined) { this.hooks.processAdditionalAssets.callAsync( newAssets, callback ); return; } callback(); } }; case "async": if (additionalAssetsFn) { this.hooks.processAdditionalAssets.tapAsync( name, (assets, callback) => { if (processedAssets.has(this.assets)) return additionalAssetsFn(assets, callback); callback(); } ); } return { ...remainingTap, fn: (assets, callback) => { fn(assets, err => { if (err) return callback(err); if (processedAssets !== undefined) processedAssets.add(this.assets); const newAssets = popNewAssets(assets); if (newAssets !== undefined) { this.hooks.processAdditionalAssets.callAsync( newAssets, callback ); return; } callback(); }); } }; case "promise": if (additionalAssetsFn) { this.hooks.processAdditionalAssets.tapPromise(name, assets => { if (processedAssets.has(this.assets)) return additionalAssetsFn(assets); return Promise.resolve(); }); } return { ...remainingTap, fn: assets => { const p = fn(assets); if (!p || !p.then) return p; return p.then(() => { if (processedAssets !== undefined) processedAssets.add(this.assets); const newAssets = popNewAssets(assets); if (newAssets !== undefined) { return this.hooks.processAdditionalAssets.promise( newAssets ); } }); } }; } } }); /** @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) => { if (!this._backCompat && code) return undefined; 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.hooks.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 {SyncHook<[ExecuteModuleArgument, ExecuteModuleContext]>} */ executeModule: new SyncHook(["options", "context"]), /** @type {AsyncParallelHook<[ExecuteModuleArgument, ExecuteModuleContext]>} */ prepareModuleExecution: new AsyncParallelHook(["options", "context"]), /** @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>, RuntimeRequirementsContext]>} */ additionalChunkRuntimeRequirements: new SyncHook([ "chunk", "runtimeRequirements", "context" ]), /** @type {HookMap<SyncBailHook<[Chunk, Set<string>, RuntimeRequirementsContext]>>} */ runtimeRequirementInChunk: new HookMap( () => new SyncBailHook(["chunk", "runtimeRequirements", "context"]) ), /** @type {SyncHook<[Module, Set<string>, RuntimeRequirementsContext]>} */ additionalModuleRuntimeRequirements: new SyncHook([ "module", "runtimeRequirements", "context" ]), /** @type {HookMap<SyncBailHook<[Module, Set<string>, RuntimeRequirementsContext]>>} */ runtimeRequirementInModule: new HookMap( () => new SyncBailHook(["module", "runtimeRequirements", "context"]) ), /** @type {SyncHook<[Chunk, Set<string>, RuntimeRequirementsContext]>} */ additionalTreeRuntimeRequirements: new SyncHook([ "chunk", "runtimeRequirements", "context" ]), /** @type {HookMap<SyncBailHook<[Chunk, Set<string>, RuntimeRequirementsContext]>>} */ runtimeRequirementInTree: new HookMap( () => new SyncBailHook(["chunk", "runtimeRequirements", "context"]) ), /** @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 {AsyncSeriesHook<[CompilationAssets]>} */ processAdditionalAssets: new AsyncSeriesHook(["assets"]), /** @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<[Partial<NormalizedStatsOptions>, CreateStatsOptionsContext]>>} */ statsPreset: new HookMap(() => new SyncHook(["options", "context"])), /** @type {SyncHook<[Partial<NormalizedStatsOptions>, CreateStatsOptionsContext]>} */ statsNormalize: new SyncHook(["options", "context"]), /** @type {SyncHook<[StatsFactory, NormalizedStatsOptions]>} */ statsFactory: new SyncHook(["statsFactory", "options"]), /** @type {SyncHook<[StatsPrinter, NormalizedStatsOptions]>} */ 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"), hashFunction: compiler.options.output.hashFunction }); if (compiler.fileTimestamps) { this.fileSystemInfo.addFileTimestamps(compiler.fileTimestamps, true); } if (compiler.contextTimestamps) { this.fileSystemInfo.addContextTimestamps( compiler.contextTimestamps, true ); } /** @type {Map<string, string | Set<string>>} */ this.valueCacheVersions = new Map(); 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.params = params; this.mainTemplate = new MainTemplate(this.outputOptions, this); this.chunkTemplate = new ChunkTemplate(this.outputOptions, this); this.runtimeTemplate = new RuntimeTemplate( this, this.outputOptions, this.requestShortener ); /** @type {{javascript: ModuleTemplate}} */ this.moduleTemplates = { javascript: new ModuleTemplate(this.runtimeTemplate, this) }; defineRemovedModuleTemplates(this.moduleTemplates); /** @type {Map<Module, WeakTupleMap<any, any>> | undefined} */ this.moduleMemCaches = undefined; /** @type {Map<Module, WeakTupleMap<any, any>> | undefined} */ this.moduleMemCaches2 = undefined; this.moduleGraph = new ModuleGraph(); /** @type {ChunkGraph} */ this.chunkGraph = undefined; /** @type {CodeGenerationResults} */ this.codeGenerationResults = undefined; /** @type {AsyncQueue<Module, Module, Module>} */ this.processDependenciesQueue = new AsyncQueue({ name: "processDependencies", parallelism: options.parallelism || 100, processor: this._processModuleDependencies.bind(this) }); /** @type {AsyncQueue<Module, string, Module>} */ this.addModuleQueue = new AsyncQueue({ name: "addModule", parent: this.processDependenciesQueue, getKey: module => module.identifier(), processor: this._addModule.bind(this) }); /** @type {AsyncQueue<FactorizeModuleOptions, string, Module | ModuleFactoryResult>} */ this.factorizeQueue = new AsyncQueue({ name: "factorize", parent: this.addModuleQueue, processor: this._factorizeModule.bind(this) }); /** @type {AsyncQueue<Module, Module, Module>} */ this.buildQueue = new AsyncQueue({ name: "build", parent: this.factorizeQueue, 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) }); /** * 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(); /** @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(); if (this._backCompat) { arrayToSetDeprecation(this.chunks, "Compilation.chunks"); 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 {Set<Module & { restoreFromUnsafeCache: Function }>} */ this._restoredUnsafeCacheModuleEntries = new Set(); /** @type {Map<string, Module & { restoreFromUnsafeCache: Function }>} */ this._restoredUnsafeCacheEntries = new Map(); /** @type {WeakSet<Module>} */ this.builtModules = new WeakSet(); /** @type {WeakSet<Module>} */ this.codeGeneratedModules = new WeakSet(); /** @type {WeakSet<Module>} */ this.buildTimeExecutedModules = 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"); const unsafeCache = options.module.unsafeCache; this._unsafeCache = !!unsafeCache; this._unsafeCachePredicate = typeof unsafeCache === "function" ? unsafeCache : () => true; } getStats() { return new Stats(this); } /** * @param {StatsOptions | string} optionsOrPreset stats option value * @param {CreateStatsOptionsContext} context context * @returns {NormalizedStatsOptions} normalized options */ 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 /** @type {Partial<NormalizedStatsOptions>} */ 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 /** @type {NormalizedStatsOptions} */ (options); } else { /** @type {Partial<NormalizedStatsOptions>} */ const options = {}; this.hooks.statsNormalize.call(options, context); return /** @type {NormalizedStatsOptions} */ (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); if (this._backCompat) 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( { compilation: this, fileSystemInfo: this.fileSystemInfo, valueCacheVersions: this.valueCacheVersions }, (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) { let i = 0; for (const dep of block.dependencies) { this.moduleGraph.setParents(dep, block, module, i++); } } 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) { /** @type {Array<{factory: ModuleFactory, dependencies: Dependency[], originModule: Module|null}>} */ const sortedDependencies = []; /** @type {DependenciesBlock} */ let currentBlock; /** @type {Map<ModuleFactory, Map<string, Dependency[]>>} */ let dependencies; /** @type {DepConstructor} */ let factoryCacheKey; /** @type {ModuleFactory} */ let factoryCacheKey2; /** @type {Map<string, Dependency[]>} */ let factoryCacheValue; /** @type {string} */ let listCacheKey1; /** @type {string} */ let listCacheKey2; /** @type {Dependency[]} */ let listCacheValue; let inProgressSorting = 1; let inProgressTransitive = 1; const onDependenciesSorted = err => { if (err) return callback(err); // early exit without changing parallelism back and forth if (sortedDependencies.length === 0 && inProgressTransitive === 1) { return callback(); } // This is nested so we need to allow one additional task this.processDependenciesQueue.increaseParallelism(); for (const item of sortedDependencies) { inProgressTransitive++; 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) { if (inProgressTransitive <= 0) return; inProgressTransitive = -1; // eslint-disable-next-line no-self-assign err.stack = err.stac