UNPKG

react-kiwi-dropdown

Version:

| Name | Type | Description | | --------------------------- | -------------------- | ----------- | | options | array | | selectedOption | string | | onCha

1,714 lines (1,536 loc) 74.4 kB
/* 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 AsyncDependencyToInitialChunkError = require("./AsyncDependencyToInitialChunkError"); const Stats = require("./Stats"); const Semaphore = require("./util/Semaphore"); const createHash = require("./util/createHash"); const Queue = require("./util/Queue"); const SortableSet = require("./util/SortableSet"); const GraphHelpers = require("./GraphHelpers"); const ModuleDependency = require("./dependencies/ModuleDependency"); const compareLocations = require("./compareLocations"); /** @typedef {import("./Module")} Module */ /** @typedef {import("./Compiler")} Compiler */ /** @typedef {import("webpack-sources").Source} Source */ /** @typedef {import("./WebpackError")} WebpackError */ /** @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 */ /** * @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; }; /** * @template T * @param {Set<T>} a first set * @param {Set<T>} b second set * @returns {number} cmp */ const bySetSize = (a, b) => { return a.size - b.size; }; /** * @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); } }; 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 {SyncHook<Module[]>} */ finishModules: new SyncHook(["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" ]), // 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 = []; 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 {WebpackError[]} */ this.errors = []; /** @type {WebpackError[]} */ this.warnings = []; /** @type {Compilation[]} */ this.children = []; /** @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); } /** * @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 (currentProfile) { const afterBuilding = Date.now(); currentProfile.building = afterBuilding - afterFactory; } 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 (currentProfile) { const afterBuilding = Date.now(); currentProfile.building = afterBuilding - afterFactory; } 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() { const modules = this.modules; this.hooks.finishModules.call(modules); for (let index = 0; index < modules.length; index++) { const module = modules[index]; this.reportDependencyErrorsAndWarnings(module, [module]); } } 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 = {}; 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); } this.processDependenciesBlocksForChunkGroups(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); } /** * This method creates the Chunk graph from the Module graph * @private * @param {TODO[]} inputChunkGroups chunk groups which are processed * @returns {void} */ processDependenciesBlocksForChunkGroups(inputChunkGroups) { // Process is splitting into two parts: // Part one traverse the module graph and builds a very basic chunks graph // in chunkDependencies. // Part two traverse every possible way through the basic chunk graph and // tracks the available modules. While traversing it connects chunks with // eachother and Blocks with Chunks. It stops traversing when all modules // for a chunk are already available. So it doesn't connect unneeded chunks. /** @type {Map<ChunkGroup, {block: AsyncDependenciesBlock, chunkGroup: ChunkGroup, couldBeFiltered: boolean}[]>} */ const chunkDependencies = new Map(); const allCreatedChunkGroups = new Set(); // PREPARE /** @type {Map<DependenciesBlock, { modules: Module[], blocks: AsyncDependenciesBlock[]}>} */ const blockInfoMap = new Map(); /** * @param {Dependency} d dependency to iterate over * @returns {void} */ const iteratorDependency = d => { // We skip Dependencies without Reference const ref = this.getDependencyReference(currentModule, d); if (!ref) { return; } // We skip Dependencies without Module pointer const refModule = ref.module; if (!refModule) { return; } // We skip weak Dependencies if (ref.weak) { return; } blockInfoModules.add(refModule); }; /** * @param {AsyncDependenciesBlock} b blocks to prepare * @returns {void} */ const iteratorBlockPrepare = b => { blockInfoBlocks.push(b); blockQueue.push(b); }; /** @type {Module} */ let currentModule; /** @type {DependenciesBlock} */ let block; /** @type {DependenciesBlock[]} */ let blockQueue; /** @type {Set<Module>} */ let blockInfoModules; /** @type {AsyncDependenciesBlock[]} */ let blockInfoBlocks; for (const module of this.modules) { blockQueue = [module]; currentModule = module; while (blockQueue.length > 0) { block = blockQueue.pop(); blockInfoModules = new Set(); blockInfoBlocks = []; if (block.variables) { iterationBlockVariable(block.variables, iteratorDependency); } if (block.dependencies) { iterationOfArrayCallback(block.dependencies, iteratorDependency); } if (block.blocks) { iterationOfArrayCallback(block.blocks, iteratorBlockPrepare); } const blockInfo = { modules: Array.from(blockInfoModules), blocks: blockInfoBlocks }; blockInfoMap.set(block, blockInfo); } } // PART ONE /** @type {Map<ChunkGroup, { index: number, index2: number }>} */ const chunkGroupCounters = new Map(); for (const chunkGroup of inputChunkGroups) { chunkGroupCounters.set(chunkGroup, { index: 0, index2: 0 }); } let nextFreeModuleIndex = 0; let nextFreeModuleIndex2 = 0; /** @type {Map<DependenciesBlock, ChunkGroup>} */ const blockChunkGroups = new Map(); /** @type {Set<DependenciesBlock>} */ const blocksWithNestedBlocks = new Set(); const ADD_AND_ENTER_MODULE = 0; const ENTER_MODULE = 1; const PROCESS_BLOCK = 2; const LEAVE_MODULE = 3; /** * @typedef {Object} QueueItem * @property {number} action * @property {DependenciesBlock} block * @property {Module} module * @property {Chunk} chunk * @property {ChunkGroup} chunkGroup */ /** * @param {ChunkGroup} chunkGroup chunk group * @returns {QueueItem} queue item */ const chunkGroupToQueueItem = chunkGroup => ({ action: ENTER_MODULE, block: chunkGroup.chunks[0].entryModule, module: chunkGroup.chunks[0].entryModule, chunk: chunkGroup.chunks[0], chunkGroup }); // Start with the provided modules/chunks /** @type {QueueItem[]} */ let queue = inputChunkGroups.map(chunkGroupToQueueItem).reverse(); /** @type {QueueItem[]} */ let queueDelayed = []; /** @type {Module} */ let module; /** @type {Chunk} */ let chunk; /** @type {ChunkGroup} */ let chunkGroup; // For each async Block in graph /** * @param {AsyncDependenciesBlock} b iterating over each Async DepBlock * @returns {void} */ const iteratorBlock = b => { // 1. We create a chunk for this Block // but only once (blockChunkGroups map) let c = blockChunkGroups.get(b); if (c === undefined) { c = this.namedChunkGroups.get(b.chunkName); if (c && c.isInitial()) { this.errors.push( new AsyncDependencyToInitialChunkError(b.chunkName, module, b.loc) ); c = chunkGroup; } else { c = this.addChunkInGroup( b.groupOptions || b.chunkName, module, b.loc, b.request ); chunkGroupCounters.set(c, { index: 0, index2: 0 }); blockChunkGroups.set(b, c); allCreatedChunkGroups.add(c); } } else { // TODO webpack 5 remove addOptions check if (c.addOptions) c.addOptions(b.groupOptions); c.addOrigin(module, b.loc, b.request); } // 2. We store the Block+Chunk mapping as dependency for the chunk let deps = chunkDependencies.get(chunkGroup); if (!deps) chunkDependencies.set(chunkGroup, (deps = [])); deps.push({ block: b, chunkGroup: c, couldBeFiltered: true }); // 3. We enqueue the DependenciesBlock for traversal queueDelayed.push({ action: PROCESS_BLOCK, block: b, module: module, chunk: c.chunks[0], chunkGroup: c }); }; // Iterative traversal of the Module graph // Recursive would be simpler to write but could result in Stack Overflows while (queue.length) { while (queue.length) { const queue