diff options
author | 2020-11-16 00:10:28 +0100 | |
---|---|---|
committer | 2020-11-16 00:10:28 +0100 | |
commit | e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d (patch) | |
tree | 55713f725f77b44ebfec86e4eec3ce33e71458ca /node_modules/webpack/lib/Compilation.js | |
download | website_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.tar.gz website_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.tar.bz2 website_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.zip |
api, login, auth
Diffstat (limited to 'node_modules/webpack/lib/Compilation.js')
-rw-r--r-- | node_modules/webpack/lib/Compilation.js | 2327 |
1 files changed, 2327 insertions, 0 deletions
diff --git a/node_modules/webpack/lib/Compilation.js b/node_modules/webpack/lib/Compilation.js new file mode 100644 index 0000000..6329174 --- /dev/null +++ b/node_modules/webpack/lib/Compilation.js @@ -0,0 +1,2327 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra + */ +"use strict"; + +const asyncLib = require("neo-async"); +const util = require("util"); +const { CachedSource } = require("webpack-sources"); +const { + Tapable, + SyncHook, + SyncBailHook, + SyncWaterfallHook, + AsyncSeriesHook +} = require("tapable"); +const EntryModuleNotFoundError = require("./EntryModuleNotFoundError"); +const ModuleNotFoundError = require("./ModuleNotFoundError"); +const ModuleDependencyWarning = require("./ModuleDependencyWarning"); +const ModuleDependencyError = require("./ModuleDependencyError"); +const ChunkGroup = require("./ChunkGroup"); +const Chunk = require("./Chunk"); +const Entrypoint = require("./Entrypoint"); +const MainTemplate = require("./MainTemplate"); +const ChunkTemplate = require("./ChunkTemplate"); +const HotUpdateChunkTemplate = require("./HotUpdateChunkTemplate"); +const ModuleTemplate = require("./ModuleTemplate"); +const RuntimeTemplate = require("./RuntimeTemplate"); +const ChunkRenderError = require("./ChunkRenderError"); +const Stats = require("./Stats"); +const Semaphore = require("./util/Semaphore"); +const createHash = require("./util/createHash"); +const SortableSet = require("./util/SortableSet"); +const GraphHelpers = require("./GraphHelpers"); +const ModuleDependency = require("./dependencies/ModuleDependency"); +const compareLocations = require("./compareLocations"); +const { Logger, LogType } = require("./logging/Logger"); +const ErrorHelpers = require("./ErrorHelpers"); +const buildChunkGraph = require("./buildChunkGraph"); +const WebpackError = require("./WebpackError"); + +/** @typedef {import("./Module")} Module */ +/** @typedef {import("./Compiler")} Compiler */ +/** @typedef {import("webpack-sources").Source} Source */ +/** @typedef {import("./DependenciesBlockVariable")} DependenciesBlockVariable */ +/** @typedef {import("./dependencies/SingleEntryDependency")} SingleEntryDependency */ +/** @typedef {import("./dependencies/MultiEntryDependency")} MultiEntryDependency */ +/** @typedef {import("./dependencies/DllEntryDependency")} DllEntryDependency */ +/** @typedef {import("./dependencies/DependencyReference")} DependencyReference */ +/** @typedef {import("./DependenciesBlock")} DependenciesBlock */ +/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */ +/** @typedef {import("./Dependency")} Dependency */ +/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */ +/** @typedef {import("./Dependency").DependencyTemplate} DependencyTemplate */ +/** @typedef {import("./util/createHash").Hash} Hash */ + +// TODO use @callback +/** @typedef {{[assetName: string]: Source}} CompilationAssets */ +/** @typedef {(err: Error|null, result?: Module) => void } ModuleCallback */ +/** @typedef {(err?: Error|null, result?: Module) => void } ModuleChainCallback */ +/** @typedef {(module: Module) => void} OnModuleCallback */ +/** @typedef {(err?: Error|null) => void} Callback */ +/** @typedef {(d: Dependency) => any} DepBlockVarDependenciesCallback */ +/** @typedef {new (...args: any[]) => Dependency} DepConstructor */ +/** @typedef {{apply: () => void}} Plugin */ + +/** + * @typedef {Object} ModuleFactoryCreateDataContextInfo + * @property {string} issuer + * @property {string} compiler + */ + +/** + * @typedef {Object} ModuleFactoryCreateData + * @property {ModuleFactoryCreateDataContextInfo} contextInfo + * @property {any=} resolveOptions + * @property {string} context + * @property {Dependency[]} dependencies + */ + +/** + * @typedef {Object} ModuleFactory + * @property {(data: ModuleFactoryCreateData, callback: ModuleCallback) => any} create + */ + +/** + * @typedef {Object} SortedDependency + * @property {ModuleFactory} factory + * @property {Dependency[]} dependencies + */ + +/** + * @typedef {Object} DependenciesBlockLike + * @property {Dependency[]} dependencies + * @property {AsyncDependenciesBlock[]} blocks + * @property {DependenciesBlockVariable[]} variables + */ + +/** + * @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 {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) + */ + +/** + * @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 + */ + +/** + * @param {Chunk} a first chunk to sort by id + * @param {Chunk} b second chunk to sort by id + * @returns {-1|0|1} sort value + */ +const byId = (a, b) => { + if (typeof a.id !== typeof b.id) { + return typeof a.id < typeof b.id ? -1 : 1; + } + if (a.id < b.id) return -1; + if (a.id > b.id) return 1; + return 0; +}; + +/** + * @param {Module} a first module to sort by + * @param {Module} b second module to sort by + * @returns {-1|0|1} sort value + */ +const byIdOrIdentifier = (a, b) => { + if (typeof a.id !== typeof b.id) { + return typeof a.id < typeof b.id ? -1 : 1; + } + if (a.id < b.id) return -1; + if (a.id > b.id) return 1; + const identA = a.identifier(); + const identB = b.identifier(); + if (identA < identB) return -1; + if (identA > identB) return 1; + return 0; +}; + +/** + * @param {Module} a first module to sort by + * @param {Module} b second module to sort by + * @returns {-1|0|1} sort value + */ +const byIndexOrIdentifier = (a, b) => { + if (a.index < b.index) return -1; + if (a.index > b.index) return 1; + const identA = a.identifier(); + const identB = b.identifier(); + if (identA < identB) return -1; + if (identA > identB) return 1; + return 0; +}; + +/** + * @param {Compilation} a first compilation to sort by + * @param {Compilation} b second compilation to sort by + * @returns {-1|0|1} sort value + */ +const byNameOrHash = (a, b) => { + if (a.name < b.name) return -1; + if (a.name > b.name) return 1; + if (a.fullHash < b.fullHash) return -1; + if (a.fullHash > b.fullHash) return 1; + return 0; +}; + +/** + * @param {DependenciesBlockVariable[]} variables DepBlock Variables to iterate over + * @param {DepBlockVarDependenciesCallback} fn callback to apply on iterated elements + * @returns {void} + */ +const iterationBlockVariable = (variables, fn) => { + for ( + let indexVariable = 0; + indexVariable < variables.length; + indexVariable++ + ) { + const varDep = variables[indexVariable].dependencies; + for (let indexVDep = 0; indexVDep < varDep.length; indexVDep++) { + fn(varDep[indexVDep]); + } + } +}; + +/** + * @template T + * @param {T[]} arr array of elements to iterate over + * @param {function(T): void} fn callback applied to each element + * @returns {void} + */ +const iterationOfArrayCallback = (arr, fn) => { + for (let index = 0; index < arr.length; index++) { + fn(arr[index]); + } +}; + +/** + * @template T + * @param {Set<T>} set set to add items to + * @param {Set<T>} otherSet set to add items from + * @returns {void} + */ +const addAllToSet = (set, otherSet) => { + for (const item of otherSet) { + set.add(item); + } +}; + +/** + * @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 extends Tapable { + /** + * Creates an instance of Compilation. + * @param {Compiler} compiler the compiler which created the compilation + */ + constructor(compiler) { + super(); + this.hooks = { + /** @type {SyncHook<Module>} */ + buildModule: new SyncHook(["module"]), + /** @type {SyncHook<Module>} */ + rebuildModule: new SyncHook(["module"]), + /** @type {SyncHook<Module, Error>} */ + failedModule: new SyncHook(["module", "error"]), + /** @type {SyncHook<Module>} */ + succeedModule: new SyncHook(["module"]), + + /** @type {SyncHook<Dependency, string>} */ + addEntry: new SyncHook(["entry", "name"]), + /** @type {SyncHook<Dependency, string, Error>} */ + failedEntry: new SyncHook(["entry", "name", "error"]), + /** @type {SyncHook<Dependency, string, Module>} */ + succeedEntry: new SyncHook(["entry", "name", "module"]), + + /** @type {SyncWaterfallHook<DependencyReference, Dependency, Module>} */ + dependencyReference: new SyncWaterfallHook([ + "dependencyReference", + "dependency", + "module" + ]), + + /** @type {AsyncSeriesHook<Module[]>} */ + finishModules: new AsyncSeriesHook(["modules"]), + /** @type {SyncHook<Module>} */ + finishRebuildingModule: new SyncHook(["module"]), + /** @type {SyncHook} */ + unseal: new SyncHook([]), + /** @type {SyncHook} */ + seal: new SyncHook([]), + + /** @type {SyncHook} */ + beforeChunks: new SyncHook([]), + /** @type {SyncHook<Chunk[]>} */ + afterChunks: new SyncHook(["chunks"]), + + /** @type {SyncBailHook<Module[]>} */ + optimizeDependenciesBasic: new SyncBailHook(["modules"]), + /** @type {SyncBailHook<Module[]>} */ + optimizeDependencies: new SyncBailHook(["modules"]), + /** @type {SyncBailHook<Module[]>} */ + optimizeDependenciesAdvanced: new SyncBailHook(["modules"]), + /** @type {SyncBailHook<Module[]>} */ + afterOptimizeDependencies: new SyncHook(["modules"]), + + /** @type {SyncHook} */ + optimize: new SyncHook([]), + /** @type {SyncBailHook<Module[]>} */ + optimizeModulesBasic: new SyncBailHook(["modules"]), + /** @type {SyncBailHook<Module[]>} */ + optimizeModules: new SyncBailHook(["modules"]), + /** @type {SyncBailHook<Module[]>} */ + optimizeModulesAdvanced: new SyncBailHook(["modules"]), + /** @type {SyncHook<Module[]>} */ + afterOptimizeModules: new SyncHook(["modules"]), + + /** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */ + optimizeChunksBasic: new SyncBailHook(["chunks", "chunkGroups"]), + /** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */ + optimizeChunks: new SyncBailHook(["chunks", "chunkGroups"]), + /** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */ + optimizeChunksAdvanced: new SyncBailHook(["chunks", "chunkGroups"]), + /** @type {SyncHook<Chunk[], ChunkGroup[]>} */ + afterOptimizeChunks: new SyncHook(["chunks", "chunkGroups"]), + + /** @type {AsyncSeriesHook<Chunk[], Module[]>} */ + optimizeTree: new AsyncSeriesHook(["chunks", "modules"]), + /** @type {SyncHook<Chunk[], Module[]>} */ + afterOptimizeTree: new SyncHook(["chunks", "modules"]), + + /** @type {SyncBailHook<Chunk[], Module[]>} */ + optimizeChunkModulesBasic: new SyncBailHook(["chunks", "modules"]), + /** @type {SyncBailHook<Chunk[], Module[]>} */ + optimizeChunkModules: new SyncBailHook(["chunks", "modules"]), + /** @type {SyncBailHook<Chunk[], Module[]>} */ + optimizeChunkModulesAdvanced: new SyncBailHook(["chunks", "modules"]), + /** @type {SyncHook<Chunk[], Module[]>} */ + afterOptimizeChunkModules: new SyncHook(["chunks", "modules"]), + /** @type {SyncBailHook} */ + shouldRecord: new SyncBailHook([]), + + /** @type {SyncHook<Module[], any>} */ + reviveModules: new SyncHook(["modules", "records"]), + /** @type {SyncHook<Module[]>} */ + optimizeModuleOrder: new SyncHook(["modules"]), + /** @type {SyncHook<Module[]>} */ + advancedOptimizeModuleOrder: new SyncHook(["modules"]), + /** @type {SyncHook<Module[]>} */ + beforeModuleIds: new SyncHook(["modules"]), + /** @type {SyncHook<Module[]>} */ + moduleIds: new SyncHook(["modules"]), + /** @type {SyncHook<Module[]>} */ + optimizeModuleIds: new SyncHook(["modules"]), + /** @type {SyncHook<Module[]>} */ + afterOptimizeModuleIds: new SyncHook(["modules"]), + + /** @type {SyncHook<Chunk[], any>} */ + reviveChunks: new SyncHook(["chunks", "records"]), + /** @type {SyncHook<Chunk[]>} */ + optimizeChunkOrder: new SyncHook(["chunks"]), + /** @type {SyncHook<Chunk[]>} */ + beforeChunkIds: new SyncHook(["chunks"]), + /** @type {SyncHook<Chunk[]>} */ + optimizeChunkIds: new SyncHook(["chunks"]), + /** @type {SyncHook<Chunk[]>} */ + afterOptimizeChunkIds: new SyncHook(["chunks"]), + + /** @type {SyncHook<Module[], any>} */ + recordModules: new SyncHook(["modules", "records"]), + /** @type {SyncHook<Chunk[], any>} */ + recordChunks: new SyncHook(["chunks", "records"]), + + /** @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} */ + shouldGenerateChunkAssets: new SyncBailHook([]), + /** @type {SyncHook} */ + beforeChunkAssets: new SyncHook([]), + /** @type {SyncHook<Chunk[]>} */ + additionalChunkAssets: new SyncHook(["chunks"]), + + /** @type {AsyncSeriesHook} */ + additionalAssets: new AsyncSeriesHook([]), + /** @type {AsyncSeriesHook<Chunk[]>} */ + optimizeChunkAssets: new AsyncSeriesHook(["chunks"]), + /** @type {SyncHook<Chunk[]>} */ + afterOptimizeChunkAssets: new SyncHook(["chunks"]), + /** @type {AsyncSeriesHook<CompilationAssets>} */ + optimizeAssets: new AsyncSeriesHook(["assets"]), + /** @type {SyncHook<CompilationAssets>} */ + afterOptimizeAssets: new SyncHook(["assets"]), + + /** @type {SyncBailHook} */ + needAdditionalSeal: new SyncBailHook([]), + /** @type {AsyncSeriesHook} */ + afterSeal: new AsyncSeriesHook([]), + + /** @type {SyncHook<Chunk, Hash>} */ + chunkHash: new SyncHook(["chunk", "chunkHash"]), + /** @type {SyncHook<Module, string>} */ + moduleAsset: new SyncHook(["module", "filename"]), + /** @type {SyncHook<Chunk, string>} */ + chunkAsset: new SyncHook(["chunk", "filename"]), + + /** @type {SyncWaterfallHook<string, TODO>} */ + assetPath: new SyncWaterfallHook(["filename", "data"]), // TODO MainTemplate + + /** @type {SyncBailHook} */ + needAdditionalPass: new SyncBailHook([]), + + /** @type {SyncHook<Compiler, string, number>} */ + childCompiler: new SyncHook([ + "childCompiler", + "compilerName", + "compilerIndex" + ]), + + /** @type {SyncBailHook<string, LogEntry>} */ + log: new SyncBailHook(["origin", "logEntry"]), + + // TODO the following hooks are weirdly located here + // TODO move them for webpack 5 + /** @type {SyncHook<object, Module>} */ + normalModuleLoader: new SyncHook(["loaderContext", "module"]), + + /** @type {SyncBailHook<Chunk[]>} */ + optimizeExtractedChunksBasic: new SyncBailHook(["chunks"]), + /** @type {SyncBailHook<Chunk[]>} */ + optimizeExtractedChunks: new SyncBailHook(["chunks"]), + /** @type {SyncBailHook<Chunk[]>} */ + optimizeExtractedChunksAdvanced: new SyncBailHook(["chunks"]), + /** @type {SyncHook<Chunk[]>} */ + afterOptimizeExtractedChunks: new SyncHook(["chunks"]) + }; + this._pluginCompat.tap("Compilation", options => { + switch (options.name) { + case "optimize-tree": + case "additional-assets": + case "optimize-chunk-assets": + case "optimize-assets": + case "after-seal": + options.async = true; + break; + } + }); + /** @type {string=} */ + this.name = undefined; + /** @type {Compiler} */ + this.compiler = compiler; + this.resolverFactory = compiler.resolverFactory; + this.inputFileSystem = compiler.inputFileSystem; + this.requestShortener = compiler.requestShortener; + + const options = compiler.options; + this.options = options; + this.outputOptions = options && options.output; + /** @type {boolean=} */ + this.bail = options && options.bail; + this.profile = options && options.profile; + this.performance = options && options.performance; + + this.mainTemplate = new MainTemplate(this.outputOptions); + this.chunkTemplate = new ChunkTemplate(this.outputOptions); + this.hotUpdateChunkTemplate = new HotUpdateChunkTemplate( + this.outputOptions + ); + this.runtimeTemplate = new RuntimeTemplate( + this.outputOptions, + this.requestShortener + ); + this.moduleTemplates = { + javascript: new ModuleTemplate(this.runtimeTemplate, "javascript"), + webassembly: new ModuleTemplate(this.runtimeTemplate, "webassembly") + }; + + this.semaphore = new Semaphore(options.parallelism || 100); + + this.entries = []; + /** @private @type {{name: string, request: string, module: Module}[]} */ + this._preparedEntrypoints = []; + /** @type {Map<string, Entrypoint>} */ + this.entrypoints = new Map(); + /** @type {Chunk[]} */ + this.chunks = []; + /** @type {ChunkGroup[]} */ + this.chunkGroups = []; + /** @type {Map<string, ChunkGroup>} */ + this.namedChunkGroups = new Map(); + /** @type {Map<string, Chunk>} */ + this.namedChunks = new Map(); + /** @type {Module[]} */ + this.modules = []; + /** @private @type {Map<string, Module>} */ + this._modules = new Map(); + this.cache = null; + this.records = null; + /** @type {string[]} */ + this.additionalChunkAssets = []; + /** @type {CompilationAssets} */ + this.assets = {}; + /** @type {Map<string, AssetInfo>} */ + this.assetsInfo = 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 {Map<DepConstructor, DependencyTemplate>} */ + this.dependencyTemplates = new Map(); + // TODO refactor this in webpack 5 to a custom DependencyTemplates class with a hash property + // @ts-ignore + this.dependencyTemplates.set("hash", ""); + this.childrenCounters = {}; + /** @type {Set<number|string>} */ + this.usedChunkIds = null; + /** @type {Set<number>} */ + this.usedModuleIds = null; + /** @type {Map<string, number>=} */ + this.fileTimestamps = undefined; + /** @type {Map<string, number>=} */ + this.contextTimestamps = undefined; + /** @type {Set<string>=} */ + this.compilationDependencies = undefined; + /** @private @type {Map<Module, Callback[]>} */ + this._buildingModules = new Map(); + /** @private @type {Map<Module, Callback[]>} */ + this._rebuildingModules = new Map(); + /** @type {Set<string>} */ + this.emittedAssets = new Set(); + } + + getStats() { + return new Stats(this); + } + + /** + * @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]}`); + } + } + } + }); + } + + /** + * @typedef {Object} AddModuleResult + * @property {Module} module the added or existing module + * @property {boolean} issuer was this the first request for this module + * @property {boolean} build should the module be build + * @property {boolean} dependencies should dependencies be walked + */ + + /** + * @param {Module} module module to be added that was created + * @param {any=} cacheGroup cacheGroup it is apart of + * @returns {AddModuleResult} returns meta about whether or not the module had built + * had an issuer, or any dependnecies + */ + addModule(module, cacheGroup) { + const identifier = module.identifier(); + const alreadyAddedModule = this._modules.get(identifier); + if (alreadyAddedModule) { + return { + module: alreadyAddedModule, + issuer: false, + build: false, + dependencies: false + }; + } + const cacheName = (cacheGroup || "m") + identifier; + if (this.cache && this.cache[cacheName]) { + const cacheModule = this.cache[cacheName]; + + if (typeof cacheModule.updateCacheModule === "function") { + cacheModule.updateCacheModule(module); + } + + let rebuild = true; + if (this.fileTimestamps && this.contextTimestamps) { + rebuild = cacheModule.needRebuild( + this.fileTimestamps, + this.contextTimestamps + ); + } + + if (!rebuild) { + cacheModule.disconnect(); + this._modules.set(identifier, cacheModule); + this.modules.push(cacheModule); + for (const err of cacheModule.errors) { + this.errors.push(err); + } + for (const err of cacheModule.warnings) { + this.warnings.push(err); + } + return { + module: cacheModule, + issuer: true, + build: false, + dependencies: true + }; + } + cacheModule.unbuild(); + module = cacheModule; + } + this._modules.set(identifier, module); + if (this.cache) { + this.cache[cacheName] = module; + } + this.modules.push(module); + return { + module: module, + issuer: true, + build: true, + dependencies: true + }; + } + + /** + * 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); + } + + /** + * @param {Module} module module with its callback list + * @param {Callback} callback the callback function + * @returns {void} + */ + waitForBuildingFinished(module, callback) { + let callbackList = this._buildingModules.get(module); + if (callbackList) { + callbackList.push(() => callback()); + } else { + process.nextTick(callback); + } + } + + /** + * Builds the module object + * + * @param {Module} module module to be built + * @param {boolean} optional optional flag + * @param {Module=} origin origin module this module build was requested from + * @param {Dependency[]=} dependencies optional dependencies from the module to be built + * @param {TODO} thisCallback the callback + * @returns {TODO} returns the callback function with results + */ + buildModule(module, optional, origin, dependencies, thisCallback) { + let callbackList = this._buildingModules.get(module); + if (callbackList) { + callbackList.push(thisCallback); + return; + } + this._buildingModules.set(module, (callbackList = [thisCallback])); + + const callback = err => { + this._buildingModules.delete(module); + for (const cb of callbackList) { + cb(err); + } + }; + + this.hooks.buildModule.call(module); + module.build( + this.options, + this, + this.resolverFactory.get("normal", module.resolveOptions), + this.inputFileSystem, + error => { + const errors = module.errors; + for (let indexError = 0; indexError < errors.length; indexError++) { + const err = errors[indexError]; + err.origin = origin; + err.dependencies = dependencies; + if (optional) { + this.warnings.push(err); + } else { + this.errors.push(err); + } + } + + const warnings = module.warnings; + for ( + let indexWarning = 0; + indexWarning < warnings.length; + indexWarning++ + ) { + const war = warnings[indexWarning]; + war.origin = origin; + war.dependencies = dependencies; + this.warnings.push(war); + } + const originalMap = module.dependencies.reduce((map, v, i) => { + map.set(v, i); + return map; + }, new Map()); + module.dependencies.sort((a, b) => { + const cmp = compareLocations(a.loc, b.loc); + if (cmp) return cmp; + return originalMap.get(a) - originalMap.get(b); + }); + if (error) { + this.hooks.failedModule.call(module, error); + return callback(error); + } + 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) { + const dependencies = new Map(); + + const addDependency = dep => { + const resourceIdent = dep.getResourceIdentifier(); + if (resourceIdent) { + const factory = this.dependencyFactories.get(dep.constructor); + if (factory === undefined) { + throw new Error( + `No module factory available for dependency type: ${dep.constructor.name}` + ); + } + let innerMap = dependencies.get(factory); + if (innerMap === undefined) { + dependencies.set(factory, (innerMap = new Map())); + } + let list = innerMap.get(resourceIdent); + if (list === undefined) innerMap.set(resourceIdent, (list = [])); + list.push(dep); + } + }; + + const addDependenciesBlock = block => { + if (block.dependencies) { + iterationOfArrayCallback(block.dependencies, addDependency); + } + if (block.blocks) { + iterationOfArrayCallback(block.blocks, addDependenciesBlock); + } + if (block.variables) { + iterationBlockVariable(block.variables, addDependency); + } + }; + + try { + addDependenciesBlock(module); + } catch (e) { + callback(e); + } + + const sortedDependencies = []; + + for (const pair1 of dependencies) { + for (const pair2 of pair1[1]) { + sortedDependencies.push({ + factory: pair1[0], + dependencies: pair2[1] + }); + } + } + + this.addModuleDependencies( + module, + sortedDependencies, + this.bail, + null, + true, + callback + ); + } + + /** + * @param {Module} module module to add deps to + * @param {SortedDependency[]} dependencies set of sorted dependencies to iterate through + * @param {(boolean|null)=} bail whether to bail or not + * @param {TODO} cacheGroup optional cacheGroup + * @param {boolean} recursive whether it is recursive traversal + * @param {function} callback callback for when dependencies are finished being added + * @returns {void} + */ + addModuleDependencies( + module, + dependencies, + bail, + cacheGroup, + recursive, + callback + ) { + const start = this.profile && Date.now(); + const currentProfile = this.profile && {}; + + asyncLib.forEach( + dependencies, + (item, callback) => { + const dependencies = item.dependencies; + + const errorAndCallback = err => { + err.origin = module; + err.dependencies = dependencies; + this.errors.push(err); + if (bail) { + callback(err); + } else { + callback(); + } + }; + const warningAndCallback = err => { + err.origin = module; + this.warnings.push(err); + callback(); + }; + + const semaphore = this.semaphore; + semaphore.acquire(() => { + const factory = item.factory; + factory.create( + { + contextInfo: { + issuer: module.nameForCondition && module.nameForCondition(), + compiler: this.compiler.name + }, + resolveOptions: module.resolveOptions, + context: module.context, + dependencies: dependencies + }, + (err, dependentModule) => { + let afterFactory; + + const isOptional = () => { + return dependencies.every(d => d.optional); + }; + + const errorOrWarningAndCallback = err => { + if (isOptional()) { + return warningAndCallback(err); + } else { + return errorAndCallback(err); + } + }; + + if (err) { + semaphore.release(); + return errorOrWarningAndCallback( + new ModuleNotFoundError(module, err) + ); + } + if (!dependentModule) { + semaphore.release(); + return process.nextTick(callback); + } + if (currentProfile) { + afterFactory = Date.now(); + currentProfile.factory = afterFactory - start; + } + + const iterationDependencies = depend => { + for (let index = 0; index < depend.length; index++) { + const dep = depend[index]; + dep.module = dependentModule; + dependentModule.addReason(module, dep); + } + }; + + const addModuleResult = this.addModule( + dependentModule, + cacheGroup + ); + dependentModule = addModuleResult.module; + iterationDependencies(dependencies); + + const afterBuild = () => { + if (recursive && addModuleResult.dependencies) { + this.processModuleDependencies(dependentModule, callback); + } else { + return callback(); + } + }; + + if (addModuleResult.issuer) { + if (currentProfile) { + dependentModule.profile = currentProfile; + } + + dependentModule.issuer = module; + } else { + if (this.profile) { + if (module.profile) { + const time = Date.now() - start; + if ( + !module.profile.dependencies || + time > module.profile.dependencies + ) { + module.profile.dependencies = time; + } + } + } + } + + if (addModuleResult.build) { + this.buildModule( + dependentModule, + isOptional(), + module, + dependencies, + err => { + if (err) { + semaphore.release(); + return errorOrWarningAndCallback(err); + } + + if (currentProfile) { + const afterBuilding = Date.now(); + currentProfile.building = afterBuilding - afterFactory; + } + + semaphore.release(); + afterBuild(); + } + ); + } else { + semaphore.release(); + this.waitForBuildingFinished(dependentModule, afterBuild); + } + } + ); + }); + }, + 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) { + // eslint-disable-next-line no-self-assign + err.stack = err.stack; + return callback(err); + } + + return process.nextTick(callback); + } + ); + } + + /** + * + * @param {string} context context string path + * @param {Dependency} dependency dependency used to create Module chain + * @param {OnModuleCallback} onModule function invoked on modules creation + * @param {ModuleChainCallback} callback callback for when module chain is complete + * @returns {void} will throw if dependency instance is not a valid Dependency + */ + _addModuleChain(context, dependency, onModule, callback) { + const start = this.profile && Date.now(); + const currentProfile = this.profile && {}; + + const errorAndCallback = this.bail + ? err => { + callback(err); + } + : err => { + err.dependencies = [dependency]; + this.errors.push(err); + callback(); + }; + + if ( + typeof dependency !== "object" || + dependency === null || + !dependency.constructor + ) { + throw new Error("Parameter 'dependency' must be a Dependency"); + } + const Dep = /** @type {DepConstructor} */ (dependency.constructor); + const moduleFactory = this.dependencyFactories.get(Dep); + if (!moduleFactory) { + throw new Error( + `No dependency factory available for this dependency type: ${dependency.constructor.name}` + ); + } + + this.semaphore.acquire(() => { + moduleFactory.create( + { + contextInfo: { + issuer: "", + compiler: this.compiler.name + }, + context: context, + dependencies: [dependency] + }, + (err, module) => { + if (err) { + this.semaphore.release(); + return errorAndCallback(new EntryModuleNotFoundError(err)); + } + + let afterFactory; + + if (currentProfile) { + afterFactory = Date.now(); + currentProfile.factory = afterFactory - start; + } + + const addModuleResult = this.addModule(module); + module = addModuleResult.module; + + onModule(module); + + dependency.module = module; + module.addReason(null, dependency); + + const afterBuild = () => { + if (addModuleResult.dependencies) { + this.processModuleDependencies(module, err => { + if (err) return callback(err); + callback(null, module); + }); + } else { + return callback(null, module); + } + }; + + if (addModuleResult.issuer) { + if (currentProfile) { + module.profile = currentProfile; + } + } + + if (addModuleResult.build) { + this.buildModule(module, false, null, null, err => { + if (err) { + this.semaphore.release(); + return errorAndCallback(err); + } + + if (currentProfile) { + const afterBuilding = Date.now(); + currentProfile.building = afterBuilding - afterFactory; + } + + this.semaphore.release(); + afterBuild(); + }); + } else { + this.semaphore.release(); + this.waitForBuildingFinished(module, afterBuild); + } + } + ); + }); + } + + /** + * + * @param {string} context context path for entry + * @param {Dependency} entry entry dependency being created + * @param {string} name name of entry + * @param {ModuleCallback} callback callback function + * @returns {void} returns + */ + addEntry(context, entry, name, callback) { + this.hooks.addEntry.call(entry, name); + + const slot = { + name: name, + // TODO webpack 5 remove `request` + request: null, + module: null + }; + + if (entry instanceof ModuleDependency) { + slot.request = entry.request; + } + + // TODO webpack 5: merge modules instead when multiple entry modules are supported + const idx = this._preparedEntrypoints.findIndex(slot => slot.name === name); + if (idx >= 0) { + // Overwrite existing entrypoint + this._preparedEntrypoints[idx] = slot; + } else { + this._preparedEntrypoints.push(slot); + } + this._addModuleChain( + context, + entry, + module => { + this.entries.push(module); + }, + (err, module) => { + if (err) { + this.hooks.failedEntry.call(entry, name, err); + return callback(err); + } + + if (module) { + slot.module = module; + } else { + const idx = this._preparedEntrypoints.indexOf(slot); + if (idx >= 0) { + this._preparedEntrypoints.splice(idx, 1); + } + } + this.hooks.succeedEntry.call(entry, name, module); + return callback(null, module); + } + ); + } + + /** + * @param {string} context context path string + * @param {Dependency} dependency dep used to create module + * @param {ModuleCallback} callback module callback sending module up a level + * @returns {void} + */ + prefetch(context, dependency, callback) { + this._addModuleChain( + context, + dependency, + module => { + module.prefetched = true; + }, + callback + ); + } + + /** + * @param {Module} module module to be rebuilt + * @param {Callback} thisCallback callback when module finishes rebuilding + * @returns {void} + */ + rebuildModule(module, thisCallback) { + let callbackList = this._rebuildingModules.get(module); + if (callbackList) { + callbackList.push(thisCallback); + return; + } + this._rebuildingModules.set(module, (callbackList = [thisCallback])); + + const callback = err => { + this._rebuildingModules.delete(module); + for (const cb of callbackList) { + cb(err); + } + }; + + this.hooks.rebuildModule.call(module); + const oldDependencies = module.dependencies.slice(); + const oldVariables = module.variables.slice(); + const oldBlocks = module.blocks.slice(); + module.unbuild(); + this.buildModule(module, false, module, null, err => { + if (err) { + this.hooks.finishRebuildingModule.call(module); + return callback(err); + } + + this.processModuleDependencies(module, err => { + if (err) return callback(err); + this.removeReasonsOfDependencyBlock(module, { + dependencies: oldDependencies, + variables: oldVariables, + blocks: oldBlocks + }); + this.hooks.finishRebuildingModule.call(module); + callback(); + }); + }); + } + + finish(callback) { + const modules = this.modules; + this.hooks.finishModules.callAsync(modules, err => { + if (err) return callback(err); + + for (let index = 0; index < modules.length; index++) { + const module = modules[index]; + this.reportDependencyErrorsAndWarnings(module, [module]); + } + + callback(); + }); + } + + unseal() { + this.hooks.unseal.call(); + this.chunks.length = 0; + this.chunkGroups.length = 0; + this.namedChunks.clear(); + this.namedChunkGroups.clear(); + this.additionalChunkAssets.length = 0; + this.assets = {}; + this.assetsInfo.clear(); + for (const module of this.modules) { + module.unseal(); + } + } + + /** + * @param {Callback} callback signals when the seal method is finishes + * @returns {void} + */ + seal(callback) { + this.hooks.seal.call(); + + while ( + this.hooks.optimizeDependenciesBasic.call(this.modules) || + this.hooks.optimizeDependencies.call(this.modules) || + this.hooks.optimizeDependenciesAdvanced.call(this.modules) + ) { + /* empty */ + } + this.hooks.afterOptimizeDependencies.call(this.modules); + + this.hooks.beforeChunks.call(); + for (const preparedEntrypoint of this._preparedEntrypoints) { + const module = preparedEntrypoint.module; + const name = preparedEntrypoint.name; + const chunk = this.addChunk(name); + const entrypoint = new Entrypoint(name); + entrypoint.setRuntimeChunk(chunk); + entrypoint.addOrigin(null, name, preparedEntrypoint.request); + this.namedChunkGroups.set(name, entrypoint); + this.entrypoints.set(name, entrypoint); + this.chunkGroups.push(entrypoint); + + GraphHelpers.connectChunkGroupAndChunk(entrypoint, chunk); + GraphHelpers.connectChunkAndModule(chunk, module); + + chunk.entryModule = module; + chunk.name = name; + + this.assignDepth(module); + } + buildChunkGraph( + this, + /** @type {Entrypoint[]} */ (this.chunkGroups.slice()) + ); + this.sortModules(this.modules); + this.hooks.afterChunks.call(this.chunks); + + this.hooks.optimize.call(); + + while ( + this.hooks.optimizeModulesBasic.call(this.modules) || + this.hooks.optimizeModules.call(this.modules) || + this.hooks.optimizeModulesAdvanced.call(this.modules) + ) { + /* empty */ + } + this.hooks.afterOptimizeModules.call(this.modules); + + while ( + this.hooks.optimizeChunksBasic.call(this.chunks, this.chunkGroups) || + this.hooks.optimizeChunks.call(this.chunks, this.chunkGroups) || + this.hooks.optimizeChunksAdvanced.call(this.chunks, this.chunkGroups) + ) { + /* empty */ + } + this.hooks.afterOptimizeChunks.call(this.chunks, this.chunkGroups); + + this.hooks.optimizeTree.callAsync(this.chunks, this.modules, err => { + if (err) { + return callback(err); + } + + this.hooks.afterOptimizeTree.call(this.chunks, this.modules); + + while ( + this.hooks.optimizeChunkModulesBasic.call(this.chunks, this.modules) || + this.hooks.optimizeChunkModules.call(this.chunks, this.modules) || + this.hooks.optimizeChunkModulesAdvanced.call(this.chunks, this.modules) + ) { + /* empty */ + } + this.hooks.afterOptimizeChunkModules.call(this.chunks, this.modules); + + const shouldRecord = this.hooks.shouldRecord.call() !== false; + + this.hooks.reviveModules.call(this.modules, this.records); + this.hooks.optimizeModuleOrder.call(this.modules); + this.hooks.advancedOptimizeModuleOrder.call(this.modules); + this.hooks.beforeModuleIds.call(this.modules); + this.hooks.moduleIds.call(this.modules); + this.applyModuleIds(); + this.hooks.optimizeModuleIds.call(this.modules); + this.hooks.afterOptimizeModuleIds.call(this.modules); + + this.sortItemsWithModuleIds(); + + this.hooks.reviveChunks.call(this.chunks, this.records); + this.hooks.optimizeChunkOrder.call(this.chunks); + this.hooks.beforeChunkIds.call(this.chunks); + this.applyChunkIds(); + this.hooks.optimizeChunkIds.call(this.chunks); + this.hooks.afterOptimizeChunkIds.call(this.chunks); + + this.sortItemsWithChunkIds(); + + if (shouldRecord) { + this.hooks.recordModules.call(this.modules, this.records); + this.hooks.recordChunks.call(this.chunks, this.records); + } + + this.hooks.beforeHash.call(); + this.createHash(); + this.hooks.afterHash.call(); + + if (shouldRecord) { + this.hooks.recordHash.call(this.records); + } + + this.hooks.beforeModuleAssets.call(); + this.createModuleAssets(); + if (this.hooks.shouldGenerateChunkAssets.call() !== false) { + this.hooks.beforeChunkAssets.call(); + this.createChunkAssets(); + } + this.hooks.additionalChunkAssets.call(this.chunks); + this.summarizeDependencies(); + if (shouldRecord) { + this.hooks.record.call(this, this.records); + } + + this.hooks.additionalAssets.callAsync(err => { + if (err) { + return callback(err); + } + this.hooks.optimizeChunkAssets.callAsync(this.chunks, err => { + if (err) { + return callback(err); + } + this.hooks.afterOptimizeChunkAssets.call(this.chunks); + this.hooks.optimizeAssets.callAsync(this.assets, err => { + if (err) { + return callback(err); + } + this.hooks.afterOptimizeAssets.call(this.assets); + if (this.hooks.needAdditionalSeal.call()) { + this.unseal(); + return this.seal(callback); + } + return this.hooks.afterSeal.callAsync(callback); + }); + }); + }); + }); + } + + /** + * @param {Module[]} modules the modules array on compilation to perform the sort for + * @returns {void} + */ + sortModules(modules) { + // TODO webpack 5: this should only be enabled when `moduleIds: "natural"` + // TODO move it into a plugin (NaturalModuleIdsPlugin) and use this in WebpackOptionsApply + // TODO remove this method + modules.sort(byIndexOrIdentifier); + } + + /** + * @param {Module} module moulde to report from + * @param {DependenciesBlock[]} blocks blocks to report from + * @returns {void} + */ + reportDependencyErrorsAndWarnings(module, blocks) { + for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) { + const block = blocks[indexBlock]; + const dependencies = block.dependencies; + + for (let indexDep = 0; indexDep < dependencies.length; indexDep++) { + const d = dependencies[indexDep]; + + const warnings = d.getWarnings(); + if (warnings) { + for (let indexWar = 0; indexWar < warnings.length; indexWar++) { + const w = warnings[indexWar]; + + const warning = new ModuleDependencyWarning(module, w, d.loc); + this.warnings.push(warning); + } + } + const errors = d.getErrors(); + if (errors) { + for (let indexErr = 0; indexErr < errors.length; indexErr++) { + const e = errors[indexErr]; + + const error = new ModuleDependencyError(module, e, d.loc); + this.errors.push(error); + } + } + } + + this.reportDependencyErrorsAndWarnings(module, block.blocks); + } + } + + /** + * @param {TODO} groupOptions options for the chunk group + * @param {Module} module the module the references the chunk group + * @param {DependencyLocation} loc the location from with the chunk group is referenced (inside of module) + * @param {string} request the request from which the the chunk group is referenced + * @returns {ChunkGroup} the new or existing chunk group + */ + addChunkInGroup(groupOptions, module, loc, request) { + if (typeof groupOptions === "string") { + groupOptions = { name: groupOptions }; + } + const name = groupOptions.name; + if (name) { + const chunkGroup = this.namedChunkGroups.get(name); + if (chunkGroup !== undefined) { + chunkGroup.addOptions(groupOptions); + if (module) { + chunkGroup.addOrigin(module, loc, request); + } + return chunkGroup; + } + } + const chunkGroup = new ChunkGroup(groupOptions); + if (module) chunkGroup.addOrigin(module, loc, request); + const chunk = this.addChunk(name); + + GraphHelpers.connectChunkGroupAndChunk(chunkGroup, chunk); + + this.chunkGroups.push(chunkGroup); + if (name) { + this.namedChunkGroups.set(name, chunkGroup); + } + return chunkGroup; + } + + /** + * This method first looks to see if a name is provided for a new chunk, + * and first looks to see if any named chunks already exist and reuse that chunk instead. + * + * @param {string=} name optional chunk name to be provided + * @returns {Chunk} create a chunk (invoked during seal event) + */ + addChunk(name) { + if (name) { + const chunk = this.namedChunks.get(name); + if (chunk !== undefined) { + return chunk; + } + } + const chunk = new Chunk(name); + this.chunks.push(chunk); + if (name) { + this.namedChunks.set(name, chunk); + } + return chunk; + } + + /** + * @param {Module} module module to assign depth + * @returns {void} + */ + assignDepth(module) { + const queue = new Set([module]); + let depth; + + module.depth = 0; + + /** + * @param {Module} module module for processeing + * @returns {void} + */ + const enqueueJob = module => { + const d = module.depth; + if (typeof d === "number" && d <= depth) return; + queue.add(module); + module.depth = depth; + }; + + /** + * @param {Dependency} dependency dependency to assign depth to + * @returns {void} + */ + const assignDepthToDependency = dependency => { + if (dependency.module) { + enqueueJob(dependency.module); + } + }; + + /** + * @param {DependenciesBlock} block block to assign depth to + * @returns {void} + */ + const assignDepthToDependencyBlock = block => { + if (block.variables) { + iterationBlockVariable(block.variables, assignDepthToDependency); + } + + if (block.dependencies) { + iterationOfArrayCallback(block.dependencies, assignDepthToDependency); + } + + if (block.blocks) { + iterationOfArrayCallback(block.blocks, assignDepthToDependencyBlock); + } + }; + + for (module of queue) { + queue.delete(module); + depth = module.depth; + + depth++; + assignDepthToDependencyBlock(module); + } + } + + /** + * @param {Module} module the module containing the dependency + * @param {Dependency} dependency the dependency + * @returns {DependencyReference} a reference for the dependency + */ + getDependencyReference(module, dependency) { + // TODO remove dep.getReference existence check in webpack 5 + if (typeof dependency.getReference !== "function") return null; + const ref = dependency.getReference(); + if (!ref) return null; + return this.hooks.dependencyReference.call(ref, dependency, module); + } + + /** + * + * @param {Module} module module relationship for removal + * @param {DependenciesBlockLike} block //TODO: good description + * @returns {void} + */ + removeReasonsOfDependencyBlock(module, block) { + const iteratorDependency = d => { + if (!d.module) { + return; + } + if (d.module.removeReason(module, d)) { + for (const chunk of d.module.chunksIterable) { + this.patchChunksAfterReasonRemoval(d.module, chunk); + } + } + }; + + if (block.blocks) { + iterationOfArrayCallback(block.blocks, block => + this.removeReasonsOfDependencyBlock(module, block) + ); + } + + if (block.dependencies) { + iterationOfArrayCallback(block.dependencies, iteratorDependency); + } + + if (block.variables) { + iterationBlockVariable(block.variables, iteratorDependency); + } + } + + /** + * @param {Module} module module to patch tie + * @param {Chunk} chunk chunk to patch tie + * @returns {void} + */ + patchChunksAfterReasonRemoval(module, chunk) { + if (!module.hasReasons()) { + this.removeReasonsOfDependencyBlock(module, module); + } + if (!module.hasReasonForChunk(chunk)) { + if (module.removeChunk(chunk)) { + this.removeChunkFromDependencies(module, chunk); + } + } + } + + /** + * + * @param {DependenciesBlock} block block tie for Chunk + * @param {Chunk} chunk chunk to remove from dep + * @returns {void} + */ + removeChunkFromDependencies(block, chunk) { + const iteratorDependency = d => { + if (!d.module) { + return; + } + this.patchChunksAfterReasonRemoval(d.module, chunk); + }; + + const blocks = block.blocks; + for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) { + const asyncBlock = blocks[indexBlock]; + // Grab all chunks from the first Block's AsyncDepBlock + const chunks = asyncBlock.chunkGroup.chunks; + // For each chunk in chunkGroup + for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) { + const iteratedChunk = chunks[indexChunk]; + asyncBlock.chunkGroup.removeChunk(iteratedChunk); + asyncBlock.chunkGroup.removeParent(iteratedChunk); + // Recurse + this.removeChunkFromDependencies(block, iteratedChunk); + } + } + + if (block.dependencies) { + iterationOfArrayCallback(block.dependencies, iteratorDependency); + } + + if (block.variables) { + iterationBlockVariable(block.variables, iteratorDependency); + } + } + + applyModuleIds() { + const unusedIds = []; + let nextFreeModuleId = 0; + const usedIds = new Set(); + if (this.usedModuleIds) { + for (const id of this.usedModuleIds) { + usedIds.add(id); + } + } + + const modules1 = this.modules; + for (let indexModule1 = 0; indexModule1 < modules1.length; indexModule1++) { + const module1 = modules1[indexModule1]; + if (module1.id !== null) { + usedIds.add(module1.id); + } + } + + if (usedIds.size > 0) { + let usedIdMax = -1; + for (const usedIdKey of usedIds) { + if (typeof usedIdKey !== "number") { + continue; + } + + usedIdMax = Math.max(usedIdMax, usedIdKey); + } + + let lengthFreeModules = (nextFreeModuleId = usedIdMax + 1); + + while (lengthFreeModules--) { + if (!usedIds.has(lengthFreeModules)) { + unusedIds.push(lengthFreeModules); + } + } + } + + const modules2 = this.modules; + for (let indexModule2 = 0; indexModule2 < modules2.length; indexModule2++) { + const module2 = modules2[indexModule2]; + if (module2.id === null) { + if (unusedIds.length > 0) { + module2.id = unusedIds.pop(); + } else { + module2.id = nextFreeModuleId++; + } + } + } + } + + applyChunkIds() { + /** @type {Set<number>} */ + const usedIds = new Set(); + + // Get used ids from usedChunkIds property (i. e. from records) + if (this.usedChunkIds) { + for (const id of this.usedChunkIds) { + if (typeof id !== "number") { + continue; + } + + usedIds.add(id); + } + } + + // Get used ids from existing chunks + const chunks = this.chunks; + for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) { + const chunk = chunks[indexChunk]; + const usedIdValue = chunk.id; + + if (typeof usedIdValue !== "number") { + continue; + } + + usedIds.add(usedIdValue); + } + + // Calculate maximum assigned chunk id + let nextFreeChunkId = -1; + for (const id of usedIds) { + nextFreeChunkId = Math.max(nextFreeChunkId, id); + } + nextFreeChunkId++; + + // Determine free chunk ids from 0 to maximum + /** @type {number[]} */ + const unusedIds = []; + if (nextFreeChunkId > 0) { + let index = nextFreeChunkId; + while (index--) { + if (!usedIds.has(index)) { + unusedIds.push(index); + } + } + } + + // Assign ids to chunk which has no id + for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) { + const chunk = chunks[indexChunk]; + if (chunk.id === null) { + if (unusedIds.length > 0) { + chunk.id = unusedIds.pop(); + } else { + chunk.id = nextFreeChunkId++; + } + } + if (!chunk.ids) { + chunk.ids = [chunk.id]; + } + } + } + + sortItemsWithModuleIds() { + this.modules.sort(byIdOrIdentifier); + + const modules = this.modules; + for (let indexModule = 0; indexModule < modules.length; indexModule++) { + modules[indexModule].sortItems(false); + } + + const chunks = this.chunks; + for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) { + chunks[indexChunk].sortItems(); + } + + chunks.sort((a, b) => a.compareTo(b)); + } + + sortItemsWithChunkIds() { + for (const chunkGroup of this.chunkGroups) { + chunkGroup.sortItems(); + } + + this.chunks.sort(byId); + + for ( + let indexModule = 0; + indexModule < this.modules.length; + indexModule++ + ) { + this.modules[indexModule].sortItems(true); + } + + const chunks = this.chunks; + for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) { + chunks[indexChunk].sortItems(); + } + + /** + * Used to sort errors and warnings in compilation. this.warnings, and + * this.errors contribute to the compilation hash and therefore should be + * updated whenever other references (having a chunk id) are sorted. This preserves the hash + * integrity + * + * @param {WebpackError} a first WebpackError instance (including subclasses) + * @param {WebpackError} b second WebpackError instance (including subclasses) + * @returns {-1|0|1} sort order index + */ + const byMessage = (a, b) => { + const ma = `${a.message}`; + const mb = `${b.message}`; + if (ma < mb) return -1; + if (mb < ma) return 1; + return 0; + }; + + this.errors.sort(byMessage); + this.warnings.sort(byMessage); + this.children.sort(byNameOrHash); + } + + summarizeDependencies() { + this.fileDependencies = new SortableSet(this.compilationDependencies); + this.contextDependencies = new SortableSet(); + this.missingDependencies = new SortableSet(); + + for ( + let indexChildren = 0; + indexChildren < this.children.length; + indexChildren++ + ) { + const child = this.children[indexChildren]; + + addAllToSet(this.fileDependencies, child.fileDependencies); + addAllToSet(this.contextDependencies, child.contextDependencies); + addAllToSet(this.missingDependencies, child.missingDependencies); + } + + for ( + let indexModule = 0; + indexModule < this.modules.length; + indexModule++ + ) { + const module = this.modules[indexModule]; + + if (module.buildInfo.fileDependencies) { + addAllToSet(this.fileDependencies, module.buildInfo.fileDependencies); + } + if (module.buildInfo.contextDependencies) { + addAllToSet( + this.contextDependencies, + module.buildInfo.contextDependencies + ); + } + } + for (const error of this.errors) { + if ( + typeof error.missing === "object" && + error.missing && + error.missing[Symbol.iterator] + ) { + addAllToSet(this.missingDependencies, error.missing); + } + } + this.fileDependencies.sort(); + this.contextDependencies.sort(); + this.missingDependencies.sort(); + } + + createHash() { + const outputOptions = this.outputOptions; + const hashFunction = outputOptions.hashFunction; + const hashDigest = outputOptions.hashDigest; + const hashDigestLength = outputOptions.hashDigestLength; + const hash = createHash(hashFunction); + if (outputOptions.hashSalt) { + hash.update(outputOptions.hashSalt); + } + this.mainTemplate.updateHash(hash); + this.chunkTemplate.updateHash(hash); + for (const key of Object.keys(this.moduleTemplates).sort()) { + this.moduleTemplates[key].updateHash(hash); + } + for (const child of this.children) { + hash.update(child.hash); + } + for (const warning of this.warnings) { + hash.update(`${warning.message}`); + } + for (const error of this.errors) { + hash.update(`${error.message}`); + } + const modules = this.modules; + for (let i = 0; i < modules.length; i++) { + const module = modules[i]; + const moduleHash = createHash(hashFunction); + module.updateHash(moduleHash); + module.hash = /** @type {string} */ (moduleHash.digest(hashDigest)); + module.renderedHash = module.hash.substr(0, hashDigestLength); + } + // clone needed as sort below is inplace mutation + const chunks = this.chunks.slice(); + /** + * sort here will bring all "falsy" values to the beginning + * this is needed as the "hasRuntime()" chunks are dependent on the + * hashes of the non-runtime chunks. + */ + chunks.sort((a, b) => { + const aEntry = a.hasRuntime(); + const bEntry = b.hasRuntime(); + if (aEntry && !bEntry) return 1; + if (!aEntry && bEntry) return -1; + return byId(a, b); + }); + for (let i = 0; i < chunks.length; i++) { + const chunk = chunks[i]; + const chunkHash = createHash(hashFunction); + try { + if (outputOptions.hashSalt) { + chunkHash.update(outputOptions.hashSalt); + } + chunk.updateHash(chunkHash); + const template = chunk.hasRuntime() + ? this.mainTemplate + : this.chunkTemplate; + template.updateHashForChunk( + chunkHash, + chunk, + this.moduleTemplates.javascript, + this.dependencyTemplates + ); + this.hooks.chunkHash.call(chunk, chunkHash); + chunk.hash = /** @type {string} */ (chunkHash.digest(hashDigest)); + hash.update(chunk.hash); + chunk.renderedHash = chunk.hash.substr(0, hashDigestLength); + this.hooks.contentHash.call(chunk); + } catch (err) { + this.errors.push(new ChunkRenderError(chunk, "", err)); + } + } + this.fullHash = /** @type {string} */ (hash.digest(hashDigest)); + this.hash = this.fullHash.substr(0, hashDigestLength); + } + + /** + * @param {string} update extra information + * @returns {void} + */ + modifyHash(update) { + const outputOptions = this.outputOptions; + const hashFunction = outputOptions.hashFunction; + const hashDigest = outputOptions.hashDigest; + const hashDigestLength = outputOptions.hashDigestLength; + const hash = createHash(hashFunction); + hash.update(this.fullHash); + hash.update(update); + this.fullHash = /** @type {string} */ (hash.digest(hashDigest)); + this.hash = this.fullHash.substr(0, hashDigestLength); + } + + /** + * @param {string} file file name + * @param {Source} source asset source + * @param {AssetInfo} assetInfo extra asset information + * @returns {void} + */ + emitAsset(file, source, assetInfo = {}) { + if (this.assets[file]) { + if (!isSourceEqual(this.assets[file], source)) { + // TODO webpack 5: make this an error instead + this.warnings.push( + new WebpackError( + `Conflict: Multiple assets emit different content to the same filename ${file}` + ) + ); + this.assets[file] = source; + this.assetsInfo.set(file, assetInfo); + return; + } + const oldInfo = this.assetsInfo.get(file); + this.assetsInfo.set(file, Object.assign({}, oldInfo, assetInfo)); + return; + } + this.assets[file] = source; + this.assetsInfo.set(file, assetInfo); + } + + /** + * @param {string} file file name + * @param {Source | function(Source): Source} newSourceOrFunction new asset source or function converting old to new + * @param {AssetInfo | function(AssetInfo | undefined): AssetInfo} assetInfoUpdateOrFunction new asset info or function converting old to new + */ + updateAsset( + file, + newSourceOrFunction, + assetInfoUpdateOrFunction = undefined + ) { + if (!this.assets[file]) { + throw new Error( + `Called Compilation.updateAsset for not existing filename ${file}` + ); + } + if (typeof newSourceOrFunction === "function") { + this.assets[file] = newSourceOrFunction(this.assets[file]); + } else { + this.assets[file] = newSourceOrFunction; + } + if (assetInfoUpdateOrFunction !== undefined) { + const oldInfo = this.assetsInfo.get(file); + if (typeof assetInfoUpdateOrFunction === "function") { + this.assetsInfo.set(file, assetInfoUpdateOrFunction(oldInfo || {})); + } else { + this.assetsInfo.set( + file, + Object.assign({}, oldInfo, assetInfoUpdateOrFunction) + ); + } + } + } + + getAssets() { + /** @type {Asset[]} */ + const array = []; + for (const assetName of Object.keys(this.assets)) { + if (Object.prototype.hasOwnProperty.call(this.assets, assetName)) { + array.push({ + name: assetName, + source: this.assets[assetName], + info: this.assetsInfo.get(assetName) || {} + }); + } + } + return array; + } + + /** + * @param {string} name the name of the asset + * @returns {Asset | undefined} the asset or undefined when not found + */ + getAsset(name) { + if (!Object.prototype.hasOwnProperty.call(this.assets, name)) + return undefined; + return { + name, + source: this.assets[name], + info: this.assetsInfo.get(name) || {} + }; + } + + createModuleAssets() { + for (let i = 0; i < this.modules.length; i++) { + const module = this.modules[i]; + if (module.buildInfo.assets) { + const assetsInfo = module.buildInfo.assetsInfo; + for (const assetName of Object.keys(module.buildInfo.assets)) { + const fileName = this.getPath(assetName); + this.emitAsset( + fileName, + module.buildInfo.assets[assetName], + assetsInfo ? assetsInfo.get(assetName) : undefined + ); + this.hooks.moduleAsset.call(module, fileName); + } + } + } + } + + createChunkAssets() { + const outputOptions = this.outputOptions; + const cachedSourceMap = new Map(); + /** @type {Map<string, {hash: string, source: Source, chunk: Chunk}>} */ + const alreadyWrittenFiles = new Map(); + for (let i = 0; i < this.chunks.length; i++) { + const chunk = this.chunks[i]; + chunk.files = []; + let source; + let file; + let filenameTemplate; + try { + const template = chunk.hasRuntime() + ? this.mainTemplate + : this.chunkTemplate; + const manifest = template.getRenderManifest({ + chunk, + hash: this.hash, + fullHash: this.fullHash, + outputOptions, + moduleTemplates: this.moduleTemplates, + dependencyTemplates: this.dependencyTemplates + }); // [{ render(), filenameTemplate, pathOptions, identifier, hash }] + for (const fileManifest of manifest) { + const cacheName = fileManifest.identifier; + const usedHash = fileManifest.hash; + filenameTemplate = fileManifest.filenameTemplate; + const pathAndInfo = this.getPathWithInfo( + filenameTemplate, + fileManifest.pathOptions + ); + file = pathAndInfo.path; + const assetInfo = pathAndInfo.info; + + // check if the same filename was already written by another chunk + const alreadyWritten = alreadyWrittenFiles.get(file); + if (alreadyWritten !== undefined) { + if (alreadyWritten.hash === usedHash) { + if (this.cache) { + this.cache[cacheName] = { + hash: usedHash, + source: alreadyWritten.source + }; + } + chunk.files.push(file); + this.hooks.chunkAsset.call(chunk, file); + continue; + } else { + throw new Error( + `Conflict: Multiple chunks emit assets to the same filename ${file}` + + ` (chunks ${alreadyWritten.chunk.id} and ${chunk.id})` + ); + } + } + if ( + this.cache && + this.cache[cacheName] && + this.cache[cacheName].hash === usedHash + ) { + source = this.cache[cacheName].source; + } else { + source = fileManifest.render(); + // Ensure that source is a cached source to avoid additional cost because of repeated access + if (!(source instanceof CachedSource)) { + const cacheEntry = cachedSourceMap.get(source); + if (cacheEntry) { + source = cacheEntry; + } else { + const cachedSource = new CachedSource(source); + cachedSourceMap.set(source, cachedSource); + source = cachedSource; + } + } + if (this.cache) { + this.cache[cacheName] = { + hash: usedHash, + source + }; + } + } + this.emitAsset(file, source, assetInfo); + chunk.files.push(file); + this.hooks.chunkAsset.call(chunk, file); + alreadyWrittenFiles.set(file, { + hash: usedHash, + source, + chunk + }); + } + } catch (err) { + this.errors.push( + new ChunkRenderError(chunk, file || filenameTemplate, err) + ); + } + } + } + + /** + * @param {string} filename used to get asset path with hash + * @param {TODO=} data // TODO: figure out this param type + * @returns {string} interpolated path + */ + getPath(filename, data) { + data = data || {}; + data.hash = data.hash || this.hash; + return this.mainTemplate.getAssetPath(filename, data); + } + + /** + * @param {string} filename used to get asset path with hash + * @param {TODO=} data // TODO: figure out this param type + * @returns {{ path: string, info: AssetInfo }} interpolated path and asset info + */ + getPathWithInfo(filename, data) { + data = data || {}; + data.hash = data.hash || this.hash; + return this.mainTemplate.getAssetPathWithInfo(filename, data); + } + + /** + * This function allows you to run another instance of webpack inside of webpack however as + * a child with different settings and configurations (if desired) applied. It copies all hooks, plugins + * from parent (or top level compiler) and creates a child Compilation + * + * @param {string} name name of the child compiler + * @param {TODO} outputOptions // Need to convert config schema to types for this + * @param {Plugin[]} plugins webpack plugins that will be applied + * @returns {Compiler} creates a child Compiler instance + */ + createChildCompiler(name, outputOptions, plugins) { + const idx = this.childrenCounters[name] || 0; + this.childrenCounters[name] = idx + 1; + return this.compiler.createChildCompiler( + this, + name, + idx, + outputOptions, + plugins + ); + } + + checkConstraints() { + /** @type {Set<number|string>} */ + const usedIds = new Set(); + + const modules = this.modules; + for (let indexModule = 0; indexModule < modules.length; indexModule++) { + const moduleId = modules[indexModule].id; + if (moduleId === null) continue; + if (usedIds.has(moduleId)) { + throw new Error(`checkConstraints: duplicate module id ${moduleId}`); + } + usedIds.add(moduleId); + } + + const chunks = this.chunks; + for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) { + const chunk = chunks[indexChunk]; + if (chunks.indexOf(chunk) !== indexChunk) { + throw new Error( + `checkConstraints: duplicate chunk in compilation ${chunk.debugId}` + ); + } + } + + for (const chunkGroup of this.chunkGroups) { + chunkGroup.checkConstraints(); + } + } +} + +// TODO remove in webpack 5 +Compilation.prototype.applyPlugins = util.deprecate( + /** + * @deprecated + * @param {string} name Name + * @param {any[]} args Other arguments + * @returns {void} + * @this {Compilation} + */ + function(name, ...args) { + this.hooks[ + name.replace(/[- ]([a-z])/g, match => match[1].toUpperCase()) + ].call(...args); + }, + "Compilation.applyPlugins is deprecated. Use new API on `.hooks` instead" +); + +// TODO remove in webpack 5 +Object.defineProperty(Compilation.prototype, "moduleTemplate", { + configurable: false, + get: util.deprecate( + /** + * @deprecated + * @this {Compilation} + * @returns {TODO} module template + */ + function() { + return this.moduleTemplates.javascript; + }, + "Compilation.moduleTemplate: Use Compilation.moduleTemplates.javascript instead" + ), + set: util.deprecate( + /** + * @deprecated + * @param {ModuleTemplate} value Template value + * @this {Compilation} + * @returns {void} + */ + function(value) { + this.moduleTemplates.javascript = value; + }, + "Compilation.moduleTemplate: Use Compilation.moduleTemplates.javascript instead." + ) +}); + +module.exports = Compilation; |