UNPKG

webpack

Version:

Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand. Supports loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.

1,564 lines (1,517 loc) 88.6 kB
/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ "use strict"; const util = require("util"); const { WEBPACK_MODULE_TYPE_RUNTIME } = require("../ModuleTypeConstants"); const ModuleDependency = require("../dependencies/ModuleDependency"); const formatLocation = require("../formatLocation"); const { LogType } = require("../logging/Logger"); const AggressiveSplittingPlugin = require("../optimize/AggressiveSplittingPlugin"); const SizeLimitsPlugin = require("../performance/SizeLimitsPlugin"); const { countIterable } = require("../util/IterableHelpers"); const { compareChunksById, compareIds, compareLocations, compareModulesByIdentifier, compareNumbers, compareSelect, concatComparators } = require("../util/comparators"); const { makePathsRelative, parseResource } = require("../util/identifier"); /** @typedef {import("webpack-sources").Source} Source */ /** @typedef {import("../../declarations/WebpackOptions").StatsValue} StatsValue */ /** @typedef {import("./StatsFactory")} StatsFactory */ /** @typedef {import("./StatsFactory").StatsFactoryContext} StatsFactoryContext */ /** @typedef {import("../Chunk")} Chunk */ /** @typedef {import("../Chunk").ChunkId} ChunkId */ /** @typedef {import("../Chunk").ChunkName} ChunkName */ /** @typedef {import("../ChunkGraph").ModuleId} ModuleId */ /** @typedef {import("../ChunkGroup")} ChunkGroup */ /** @typedef {import("../ChunkGroup").OriginRecord} OriginRecord */ /** @typedef {import("../Compilation")} Compilation */ /** @typedef {import("../Compilation").Asset} Asset */ /** @typedef {import("../Compilation").AssetInfo} AssetInfo */ /** @typedef {import("../Compilation").ExcludeModulesType} ExcludeModulesType */ /** @typedef {import("../Compilation").KnownNormalizedStatsOptions} KnownNormalizedStatsOptions */ /** @typedef {import("../Compilation").NormalizedStatsOptions} NormalizedStatsOptions */ /** @typedef {import("../Compiler")} Compiler */ /** @typedef {import("../Dependency")} Dependency */ /** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */ /** @typedef {import("../Module")} Module */ /** @typedef {import("../Module").NameForCondition} NameForCondition */ /** @typedef {import("../Module").BuildInfo} BuildInfo */ /** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */ /** @typedef {import("../ModuleProfile")} ModuleProfile */ /** @typedef {import("../WebpackError")} WebpackError */ /** @typedef {import("../serialization/AggregateErrorSerializer").AggregateError} AggregateError */ /** @typedef {import("../serialization/ErrorObjectSerializer").ErrorWithCause} ErrorWithCause */ /** @typedef {import("../ExportsInfo").ExportInfoName} ExportInfoName */ /** * Defines the shared type used by this module. * @template T * @typedef {import("../util/comparators").Comparator<T>} Comparator<T> */ /** * Defines the group config type used by this module. * @template I, G * @typedef {import("../util/smartGrouping").GroupConfig<I, G>} GroupConfig */ /** @typedef {KnownStatsCompilation & Record<string, EXPECTED_ANY>} StatsCompilation */ /** * Defines the known stats compilation type used by this module. * @typedef {object} KnownStatsCompilation * @property {EXPECTED_ANY=} env * @property {string=} name * @property {string=} hash * @property {string=} version * @property {number=} time * @property {number=} builtAt * @property {boolean=} needAdditionalPass * @property {string=} publicPath * @property {string=} outputPath * @property {Record<string, string[]>=} assetsByChunkName * @property {StatsAsset[]=} assets * @property {number=} filteredAssets * @property {StatsChunk[]=} chunks * @property {StatsModule[]=} modules * @property {number=} filteredModules * @property {Record<string, StatsChunkGroup>=} entrypoints * @property {Record<string, StatsChunkGroup>=} namedChunkGroups * @property {StatsError[]=} errors * @property {number=} errorsCount * @property {StatsError[]=} warnings * @property {number=} warningsCount * @property {StatsCompilation[]=} children * @property {Record<string, StatsLogging>=} logging * @property {number=} filteredWarningDetailsCount * @property {number=} filteredErrorDetailsCount */ /** @typedef {KnownStatsLogging & Record<string, EXPECTED_ANY>} StatsLogging */ /** * Defines the known stats logging type used by this module. * @typedef {object} KnownStatsLogging * @property {StatsLoggingEntry[]} entries * @property {number} filteredEntries * @property {boolean} debug */ /** @typedef {KnownStatsLoggingEntry & Record<string, EXPECTED_ANY>} StatsLoggingEntry */ /** * Defines the known stats logging entry type used by this module. * @typedef {object} KnownStatsLoggingEntry * @property {string} type * @property {string=} message * @property {string[]=} trace * @property {StatsLoggingEntry[]=} children * @property {EXPECTED_ANY[]=} args * @property {number=} time */ /** @typedef {KnownStatsAsset & Record<string, EXPECTED_ANY>} StatsAsset */ /** @typedef {string[]} ChunkIdHints */ /** * Defines the known stats asset type used by this module. * @typedef {object} KnownStatsAsset * @property {string} type * @property {string} name * @property {AssetInfo} info * @property {number} size * @property {boolean} emitted * @property {boolean} comparedForEmit * @property {boolean} cached * @property {StatsAsset[]=} related * @property {ChunkId[]=} chunks * @property {ChunkName[]=} chunkNames * @property {ChunkIdHints=} chunkIdHints * @property {ChunkId[]=} auxiliaryChunks * @property {ChunkName[]=} auxiliaryChunkNames * @property {ChunkIdHints=} auxiliaryChunkIdHints * @property {number=} filteredRelated * @property {boolean=} isOverSizeLimit */ /** @typedef {KnownStatsChunkGroup & Record<string, EXPECTED_ANY>} StatsChunkGroup */ /** * Defines the known stats chunk group type used by this module. * @typedef {object} KnownStatsChunkGroup * @property {ChunkName=} name * @property {ChunkId[]=} chunks * @property {({ name: string, size?: number })[]=} assets * @property {number=} filteredAssets * @property {number=} assetsSize * @property {({ name: string, size?: number })[]=} auxiliaryAssets * @property {number=} filteredAuxiliaryAssets * @property {number=} auxiliaryAssetsSize * @property {Record<string, StatsChunkGroup[]>=} children * @property {Record<string, string[]>=} childAssets * @property {boolean=} isOverSizeLimit */ /** @typedef {Module[]} ModuleIssuerPath */ /** @typedef {KnownStatsModule & Record<string, EXPECTED_ANY>} StatsModule */ /** * Defines the known stats module type used by this module. * @typedef {object} KnownStatsModule * @property {string=} type * @property {string=} moduleType * @property {(string | null)=} layer * @property {string=} identifier * @property {string=} name * @property {NameForCondition | null=} nameForCondition * @property {number=} index * @property {number=} preOrderIndex * @property {number=} index2 * @property {number=} postOrderIndex * @property {number=} size * @property {Record<string, number>=} sizes * @property {boolean=} cacheable * @property {boolean=} built * @property {boolean=} codeGenerated * @property {boolean=} buildTimeExecuted * @property {boolean=} cached * @property {boolean=} optional * @property {boolean=} orphan * @property {ModuleId=} id * @property {ModuleId | null=} issuerId * @property {ChunkId[]=} chunks * @property {string[]=} assets * @property {boolean=} dependent * @property {(string | null)=} issuer * @property {(string | null)=} issuerName * @property {StatsModuleIssuer[] | null=} issuerPath * @property {boolean=} failed * @property {number=} errors * @property {number=} warnings * @property {StatsProfile=} profile * @property {StatsModuleReason[]=} reasons * @property {boolean | null | ExportInfoName[]=} usedExports * @property {ExportInfoName[] | null=} providedExports * @property {string[]=} optimizationBailout * @property {(number | null)=} depth * @property {StatsModule[]=} modules * @property {number=} filteredModules * @property {ReturnType<Source["source"]>=} source */ /** @typedef {KnownStatsProfile & Record<string, EXPECTED_ANY>} StatsProfile */ /** * Defines the known stats profile type used by this module. * @typedef {object} KnownStatsProfile * @property {number} total * @property {number} resolving * @property {number} restoring * @property {number} building * @property {number} integration * @property {number} storing * @property {number} additionalResolving * @property {number} additionalIntegration * @property {number} factory * @property {number} dependencies */ /** @typedef {KnownStatsModuleIssuer & Record<string, EXPECTED_ANY>} StatsModuleIssuer */ /** * Defines the known stats module issuer type used by this module. * @typedef {object} KnownStatsModuleIssuer * @property {string} identifier * @property {string} name * @property {ModuleId=} id * @property {StatsProfile} profile */ /** @typedef {KnownStatsModuleReason & Record<string, EXPECTED_ANY>} StatsModuleReason */ /** * Defines the known stats module reason type used by this module. * @typedef {object} KnownStatsModuleReason * @property {string | null} moduleIdentifier * @property {string | null} module * @property {string | null} moduleName * @property {string | null} resolvedModuleIdentifier * @property {string | null} resolvedModule * @property {string | null} type * @property {boolean} active * @property {string | null} explanation * @property {string | null} userRequest * @property {(string | null)=} loc * @property {ModuleId | null=} moduleId * @property {ModuleId | null=} resolvedModuleId */ /** @typedef {KnownStatsChunk & Record<string, EXPECTED_ANY>} StatsChunk */ /** * Defines the known stats chunk type used by this module. * @typedef {object} KnownStatsChunk * @property {boolean} rendered * @property {boolean} initial * @property {boolean} entry * @property {boolean} recorded * @property {string=} reason * @property {number} size * @property {Record<string, number>} sizes * @property {string[]} names * @property {string[]} idHints * @property {string[]=} runtime * @property {string[]} files * @property {string[]} auxiliaryFiles * @property {string} hash * @property {Record<string, ChunkId[]>} childrenByOrder * @property {ChunkId=} id * @property {ChunkId[]=} siblings * @property {ChunkId[]=} parents * @property {ChunkId[]=} children * @property {StatsModule[]=} modules * @property {number=} filteredModules * @property {StatsChunkOrigin[]=} origins */ /** @typedef {KnownStatsChunkOrigin & Record<string, EXPECTED_ANY>} StatsChunkOrigin */ /** * Defines the known stats chunk origin type used by this module. * @typedef {object} KnownStatsChunkOrigin * @property {string} module * @property {string} moduleIdentifier * @property {string} moduleName * @property {string} loc * @property {string} request * @property {ModuleId=} moduleId */ /** @typedef {KnownStatsModuleTraceItem & Record<string, EXPECTED_ANY>} StatsModuleTraceItem */ /** * Defines the known stats module trace item type used by this module. * @typedef {object} KnownStatsModuleTraceItem * @property {string=} originIdentifier * @property {string=} originName * @property {string=} moduleIdentifier * @property {string=} moduleName * @property {StatsModuleTraceDependency[]=} dependencies * @property {ModuleId=} originId * @property {ModuleId=} moduleId */ /** @typedef {KnownStatsModuleTraceDependency & Record<string, EXPECTED_ANY>} StatsModuleTraceDependency */ /** * Defines the known stats module trace dependency type used by this module. * @typedef {object} KnownStatsModuleTraceDependency * @property {string=} loc */ /** @typedef {KnownStatsError & Record<string, EXPECTED_ANY>} StatsError */ /** * Defines the known stats error type used by this module. * @typedef {object} KnownStatsError * @property {string} message * @property {string=} chunkName * @property {boolean=} chunkEntry * @property {boolean=} chunkInitial * @property {string=} file * @property {string=} moduleIdentifier * @property {string=} moduleName * @property {string=} loc * @property {ChunkId=} chunkId * @property {ModuleId=} moduleId * @property {StatsModuleTraceItem[]=} moduleTrace * @property {string=} details * @property {string=} stack * @property {KnownStatsError=} cause * @property {KnownStatsError[]=} errors * @property {string=} compilerPath */ /** @typedef {Asset & { type: string, related: PreprocessedAsset[] | undefined }} PreprocessedAsset */ /** * Defines the extractors by option type used by this module. * @template T * @template O * @typedef {Record<string, (object: O, data: T, context: StatsFactoryContext, options: NormalizedStatsOptions, factory: StatsFactory) => void>} ExtractorsByOption */ /** @typedef {{ name: string, chunkGroup: ChunkGroup }} ChunkGroupInfoWithName */ /** @typedef {{ origin: Module, module: Module }} ModuleTrace */ /** * Defines the simple extractors type used by this module. * @typedef {object} SimpleExtractors * @property {ExtractorsByOption<Compilation, StatsCompilation>} compilation * @property {ExtractorsByOption<PreprocessedAsset, StatsAsset>} asset * @property {ExtractorsByOption<PreprocessedAsset, StatsAsset>} asset$visible * @property {ExtractorsByOption<ChunkGroupInfoWithName, StatsChunkGroup>} chunkGroup * @property {ExtractorsByOption<Module, StatsModule>} module * @property {ExtractorsByOption<Module, StatsModule>} module$visible * @property {ExtractorsByOption<Module, StatsModuleIssuer>} moduleIssuer * @property {ExtractorsByOption<ModuleProfile, StatsProfile>} profile * @property {ExtractorsByOption<ModuleGraphConnection, StatsModuleReason>} moduleReason * @property {ExtractorsByOption<Chunk, StatsChunk>} chunk * @property {ExtractorsByOption<OriginRecord, StatsChunkOrigin>} chunkOrigin * @property {ExtractorsByOption<WebpackError, StatsError>} error * @property {ExtractorsByOption<WebpackError, StatsError>} warning * @property {ExtractorsByOption<WebpackError, StatsError>} cause * @property {ExtractorsByOption<ModuleTrace, StatsModuleTraceItem>} moduleTraceItem * @property {ExtractorsByOption<Dependency, StatsModuleTraceDependency>} moduleTraceDependency */ /** * Returns array of values. * @template T * @template I * @param {Iterable<T>} items items to select from * @param {(item: T) => Iterable<I>} selector selector function to select values from item * @returns {I[]} array of values */ const uniqueArray = (items, selector) => { /** @type {Set<I>} */ const set = new Set(); for (const item of items) { for (const i of selector(item)) { set.add(i); } } return [...set]; }; /** * Unique ordered array. * @template T * @template I * @param {Iterable<T>} items items to select from * @param {(item: T) => Iterable<I>} selector selector function to select values from item * @param {Comparator<I>} comparator comparator function * @returns {I[]} array of values */ const uniqueOrderedArray = (items, selector, comparator) => uniqueArray(items, selector).sort(comparator); /** * Defines the shared type used by this module. * @template T * @template R * @typedef {{ [P in keyof T]: R }} MappedValues<T, R> */ /** * Returns mapped object. * @template {object} T * @template {object} R * @param {T} obj object to be mapped * @param {(value: T[keyof T], key: keyof T) => R} fn mapping function * @returns {MappedValues<T, R>} mapped object */ const mapObject = (obj, fn) => { /** @type {MappedValues<T, R>} */ const newObj = Object.create(null); for (const key of /** @type {(keyof T)[]} */ (Object.keys(obj))) { newObj[key] = fn(obj[key], key); } return newObj; }; /** * Count with children. * @template T * @param {Compilation} compilation the compilation * @param {(compilation: Compilation, name: string) => T[]} getItems get items * @returns {number} total number */ const countWithChildren = (compilation, getItems) => { let count = getItems(compilation, "").length; for (const child of compilation.children) { count += countWithChildren(child, (c, type) => getItems(c, `.children[].compilation${type}`) ); } return count; }; /** @type {ExtractorsByOption<string | ErrorWithCause | AggregateError | WebpackError, StatsError>} */ const EXTRACT_ERROR = { _: (object, error, context, { requestShortener }) => { // TODO webpack 6 disallow strings in the errors/warnings list if (typeof error === "string") { object.message = error; } else { if (/** @type {WebpackError} */ (error).chunk) { const chunk = /** @type {WebpackError} */ (error).chunk; object.chunkName = /** @type {string | undefined} */ (chunk.name); object.chunkEntry = chunk.hasRuntime(); object.chunkInitial = chunk.canBeInitial(); } if (/** @type {WebpackError} */ (error).file) { object.file = /** @type {WebpackError} */ (error).file; } if (/** @type {WebpackError} */ (error).module) { object.moduleIdentifier = /** @type {WebpackError} */ (error).module.identifier(); object.moduleName = /** @type {WebpackError} */ (error).module.readableIdentifier(requestShortener); } if (/** @type {WebpackError} */ (error).loc) { object.loc = formatLocation(/** @type {WebpackError} */ (error).loc); } object.message = error.message; } }, ids: (object, error, { compilation: { chunkGraph } }) => { if (typeof error !== "string") { if (/** @type {WebpackError} */ (error).chunk) { object.chunkId = /** @type {ChunkId} */ ( /** @type {WebpackError} */ (error).chunk.id ); } if (/** @type {WebpackError} */ (error).module) { object.moduleId = /** @type {ModuleId} */ (chunkGraph.getModuleId(/** @type {WebpackError} */ (error).module)); } } }, moduleTrace: (object, error, context, options, factory) => { if ( typeof error !== "string" && /** @type {WebpackError} */ (error).module ) { const { type, compilation: { moduleGraph } } = context; /** @type {Set<Module>} */ const visitedModules = new Set(); /** @type {ModuleTrace[]} */ const moduleTrace = []; let current = /** @type {WebpackError} */ (error).module; while (current) { if (visitedModules.has(current)) break; // circular (technically impossible, but how knows) visitedModules.add(current); const origin = moduleGraph.getIssuer(current); if (!origin) break; moduleTrace.push({ origin, module: current }); current = origin; } object.moduleTrace = factory.create( `${type}.moduleTrace`, moduleTrace, context ); } }, errorDetails: ( object, error, { type, compilation, cachedGetErrors }, { errorDetails } ) => { if ( typeof error !== "string" && (errorDetails === true || (type.endsWith(".error") && cachedGetErrors(compilation).length < 3)) ) { object.details = /** @type {WebpackError} */ (error).details; } }, errorStack: (object, error, _context, { errorStack }) => { if (typeof error !== "string" && errorStack) { object.stack = error.stack; } }, errorCause: (object, error, context, options, factory) => { if ( typeof error !== "string" && /** @type {ErrorWithCause} */ (error).cause ) { const rawCause = /** @type {ErrorWithCause} */ (error).cause; /** @type {Error} */ const cause = typeof rawCause === "string" ? /** @type {Error} */ ({ message: rawCause }) : /** @type {Error} */ (rawCause); const { type } = context; object.cause = factory.create(`${type}.cause`, cause, context); } }, errorErrors: (object, error, context, options, factory) => { if ( typeof error !== "string" && /** @type {AggregateError} */ (error).errors ) { const { type } = context; object.errors = factory.create( `${type}.errors`, /** @type {Error[]} */ (/** @type {AggregateError} */ (error).errors), context ); } } }; /** @typedef {((value: string) => boolean)} FilterItemTypeFn */ /** @type {SimpleExtractors} */ const SIMPLE_EXTRACTORS = { compilation: { _: (object, compilation, context, options) => { if (!context.makePathsRelative) { context.makePathsRelative = makePathsRelative.bindContextCache( compilation.compiler.context, compilation.compiler.root ); } if (!context.cachedGetErrors) { /** @type {WeakMap<Compilation, Error[]>} */ const map = new WeakMap(); context.cachedGetErrors = (compilation) => map.get(compilation) || // eslint-disable-next-line no-sequences ((errors) => (map.set(compilation, errors), errors))( compilation.getErrors() ); } if (!context.cachedGetWarnings) { /** @type {WeakMap<Compilation, Error[]>} */ const map = new WeakMap(); context.cachedGetWarnings = (compilation) => map.get(compilation) || // eslint-disable-next-line no-sequences ((warnings) => (map.set(compilation, warnings), warnings))( compilation.getWarnings() ); } if (compilation.name) { object.name = compilation.name; } if (compilation.needAdditionalPass) { object.needAdditionalPass = true; } const { logging, loggingDebug, loggingTrace } = options; if (logging || (loggingDebug && loggingDebug.length > 0)) { const util = require("util"); object.logging = {}; /** @type {Set<keyof LogType>} */ let acceptedTypes; let collapsedGroups = false; switch (logging) { case "error": acceptedTypes = new Set([LogType.error]); break; case "warn": acceptedTypes = new Set([LogType.error, LogType.warn]); break; case "info": acceptedTypes = new Set([ LogType.error, LogType.warn, LogType.info ]); break; case "log": acceptedTypes = new Set([ LogType.error, LogType.warn, LogType.info, LogType.log, LogType.group, LogType.groupEnd, LogType.groupCollapsed, LogType.clear ]); break; case "verbose": acceptedTypes = new Set([ LogType.error, LogType.warn, LogType.info, LogType.log, LogType.group, LogType.groupEnd, LogType.groupCollapsed, LogType.profile, LogType.profileEnd, LogType.time, LogType.status, LogType.clear ]); collapsedGroups = true; break; default: acceptedTypes = new Set(); break; } const cachedMakePathsRelative = makePathsRelative.bindContextCache( options.context, compilation.compiler.root ); let depthInCollapsedGroup = 0; for (const [origin, logEntries] of compilation.logging) { const debugMode = loggingDebug.some((fn) => fn(origin)); if (logging === false && !debugMode) continue; /** @type {KnownStatsLoggingEntry[]} */ const groupStack = []; /** @type {KnownStatsLoggingEntry[]} */ const rootList = []; let currentList = rootList; let processedLogEntries = 0; for (const entry of logEntries) { let type = entry.type; if (!debugMode && !acceptedTypes.has(type)) continue; // Expand groups in verbose and debug modes if ( type === LogType.groupCollapsed && (debugMode || collapsedGroups) ) { type = LogType.group; } if (depthInCollapsedGroup === 0) { processedLogEntries++; } if (type === LogType.groupEnd) { groupStack.pop(); currentList = groupStack.length > 0 ? /** @type {KnownStatsLoggingEntry[]} */ ( groupStack[groupStack.length - 1].children ) : rootList; if (depthInCollapsedGroup > 0) depthInCollapsedGroup--; continue; } /** @type {undefined | string} */ let message; if (entry.type === LogType.time) { const [label, first, second] = /** @type {[string, number, number]} */ (entry.args); message = `${label}: ${first * 1000 + second / 1000000} ms`; } else if (entry.args && entry.args.length > 0) { message = util.format(entry.args[0], ...entry.args.slice(1)); } /** @type {KnownStatsLoggingEntry} */ const newEntry = { ...entry, type, message, trace: loggingTrace ? entry.trace : undefined, children: type === LogType.group || type === LogType.groupCollapsed ? [] : undefined }; currentList.push(newEntry); if (newEntry.children) { groupStack.push(newEntry); currentList = newEntry.children; if (depthInCollapsedGroup > 0) { depthInCollapsedGroup++; } else if (type === LogType.groupCollapsed) { depthInCollapsedGroup = 1; } } } let name = cachedMakePathsRelative(origin).replace(/\|/g, " "); if (name in object.logging) { let i = 1; while (`${name}#${i}` in object.logging) { i++; } name = `${name}#${i}`; } object.logging[name] = { entries: rootList, filteredEntries: logEntries.length - processedLogEntries, debug: debugMode }; } } }, hash: (object, compilation) => { object.hash = compilation.hash; }, version: (object) => { object.version = require("../../package.json").version; }, env: (object, compilation, context, { _env }) => { object.env = _env; }, timings: (object, compilation) => { object.time = /** @type {number} */ (compilation.endTime) - /** @type {number} */ (compilation.startTime); }, builtAt: (object, compilation) => { object.builtAt = /** @type {number} */ (compilation.endTime); }, publicPath: (object, compilation) => { object.publicPath = compilation.getPath( compilation.outputOptions.publicPath ); }, outputPath: (object, compilation) => { object.outputPath = compilation.outputOptions.path; }, assets: (object, compilation, context, options, factory) => { const { type } = context; /** @type {Map<string, Chunk[]>} */ const compilationFileToChunks = new Map(); /** @type {Map<string, Chunk[]>} */ const compilationAuxiliaryFileToChunks = new Map(); for (const chunk of compilation.chunks) { for (const file of chunk.files) { let array = compilationFileToChunks.get(file); if (array === undefined) { array = []; compilationFileToChunks.set(file, array); } array.push(chunk); } for (const file of chunk.auxiliaryFiles) { let array = compilationAuxiliaryFileToChunks.get(file); if (array === undefined) { array = []; compilationAuxiliaryFileToChunks.set(file, array); } array.push(chunk); } } /** @type {Map<string, PreprocessedAsset>} */ const assetMap = new Map(); /** @type {Set<PreprocessedAsset>} */ const assets = new Set(); for (const asset of compilation.getAssets()) { /** @type {PreprocessedAsset} */ const item = { ...asset, type: "asset", related: undefined }; assets.add(item); assetMap.set(asset.name, item); } for (const item of assetMap.values()) { const related = item.info.related; if (!related) continue; for (const type of Object.keys(related)) { const relatedEntry = related[type]; const deps = Array.isArray(relatedEntry) ? relatedEntry : [relatedEntry]; for (const dep of deps) { if (!dep) continue; const depItem = assetMap.get(dep); if (!depItem) continue; assets.delete(depItem); depItem.type = type; item.related = item.related || []; item.related.push(depItem); } } } object.assetsByChunkName = {}; for (const [file, chunks] of [ ...compilationFileToChunks, ...compilationAuxiliaryFileToChunks ]) { for (const chunk of chunks) { const name = chunk.name; if (!name) continue; if ( !Object.prototype.hasOwnProperty.call( object.assetsByChunkName, name ) ) { object.assetsByChunkName[name] = []; } object.assetsByChunkName[name].push(file); } } const groupedAssets = factory.create(`${type}.assets`, [...assets], { ...context, compilationFileToChunks, compilationAuxiliaryFileToChunks }); const limited = spaceLimited( groupedAssets, /** @type {number} */ (options.assetsSpace) ); object.assets = limited.children; object.filteredAssets = limited.filteredChildren; }, chunks: (object, compilation, context, options, factory) => { const { type } = context; object.chunks = factory.create( `${type}.chunks`, [...compilation.chunks], context ); }, modules: (object, compilation, context, options, factory) => { const { type } = context; const array = [...compilation.modules]; const groupedModules = factory.create(`${type}.modules`, array, context); const limited = spaceLimited(groupedModules, options.modulesSpace); object.modules = limited.children; object.filteredModules = limited.filteredChildren; }, entrypoints: ( object, compilation, context, { entrypoints, chunkGroups, chunkGroupAuxiliary, chunkGroupChildren }, factory ) => { const { type } = context; /** @type {ChunkGroupInfoWithName[]} */ const array = Array.from(compilation.entrypoints, ([key, value]) => ({ name: key, chunkGroup: value })); if (entrypoints === "auto" && !chunkGroups) { if (array.length > 5) return; if ( !chunkGroupChildren && array.every(({ chunkGroup }) => { if (chunkGroup.chunks.length !== 1) return false; const chunk = chunkGroup.chunks[0]; return ( chunk.files.size === 1 && (!chunkGroupAuxiliary || chunk.auxiliaryFiles.size === 0) ); }) ) { return; } } object.entrypoints = factory.create( `${type}.entrypoints`, array, context ); }, chunkGroups: (object, compilation, context, options, factory) => { const { type } = context; const array = Array.from( compilation.namedChunkGroups, ([key, value]) => ({ name: key, chunkGroup: value }) ); object.namedChunkGroups = factory.create( `${type}.namedChunkGroups`, array, context ); }, errors: (object, compilation, context, options, factory) => { const { type, cachedGetErrors } = context; const rawErrors = cachedGetErrors(compilation); const factorizedErrors = factory.create( `${type}.errors`, cachedGetErrors(compilation), context ); let filtered = 0; if (options.errorDetails === "auto" && rawErrors.length >= 3) { filtered = rawErrors .map( (e) => typeof e !== "string" && /** @type {WebpackError} */ (e).details ) .filter(Boolean).length; } if ( options.errorDetails === true || !Number.isFinite(options.errorsSpace) ) { object.errors = factorizedErrors; if (filtered) object.filteredErrorDetailsCount = filtered; return; } const [errors, filteredBySpace] = errorsSpaceLimit( factorizedErrors, /** @type {number} */ (options.errorsSpace) ); object.filteredErrorDetailsCount = filtered + filteredBySpace; object.errors = errors; }, errorsCount: (object, compilation, { cachedGetErrors }) => { object.errorsCount = countWithChildren(compilation, (c) => cachedGetErrors(c) ); }, warnings: (object, compilation, context, options, factory) => { const { type, cachedGetWarnings } = context; const rawWarnings = factory.create( `${type}.warnings`, cachedGetWarnings(compilation), context ); let filtered = 0; if (options.errorDetails === "auto") { filtered = cachedGetWarnings(compilation) .map( (e) => typeof e !== "string" && /** @type {WebpackError} */ (e).details ) .filter(Boolean).length; } if ( options.errorDetails === true || !Number.isFinite(options.warningsSpace) ) { object.warnings = rawWarnings; if (filtered) object.filteredWarningDetailsCount = filtered; return; } const [warnings, filteredBySpace] = errorsSpaceLimit( rawWarnings, /** @type {number} */ (options.warningsSpace) ); object.filteredWarningDetailsCount = filtered + filteredBySpace; object.warnings = warnings; }, warningsCount: ( object, compilation, context, { warningsFilter }, factory ) => { const { type, cachedGetWarnings } = context; object.warningsCount = countWithChildren(compilation, (c, childType) => { if ( !warningsFilter && /** @type {KnownNormalizedStatsOptions["warningsFilter"]} */ (warningsFilter).length === 0 ) { // Type is wrong, because we don't need the real value for counting return /** @type {EXPECTED_ANY[]} */ (cachedGetWarnings(c)); } return factory .create(`${type}${childType}.warnings`, cachedGetWarnings(c), context) .filter( /** * Handles the warnings count callback for this hook. * @param {StatsError} warning warning * @returns {boolean} result */ (warning) => { const warningString = Object.keys(warning) .map( (key) => `${warning[/** @type {keyof KnownStatsError} */ (key)]}` ) .join("\n"); return !warningsFilter.some((filter) => filter(warning, warningString) ); } ); }); }, children: (object, compilation, context, options, factory) => { const { type } = context; object.children = factory.create( `${type}.children`, compilation.children, context ); } }, asset: { _: (object, asset, context, options, factory) => { const { compilation } = context; object.type = asset.type; object.name = asset.name; object.size = asset.source.size(); object.emitted = compilation.emittedAssets.has(asset.name); object.comparedForEmit = compilation.comparedForEmitAssets.has( asset.name ); const cached = !object.emitted && !object.comparedForEmit; object.cached = cached; object.info = asset.info; if (!cached || options.cachedAssets) { Object.assign( object, factory.create(`${context.type}$visible`, asset, context) ); } } }, asset$visible: { _: ( object, asset, { compilationFileToChunks, compilationAuxiliaryFileToChunks } ) => { const chunks = compilationFileToChunks.get(asset.name) || []; const auxiliaryChunks = compilationAuxiliaryFileToChunks.get(asset.name) || []; object.chunkNames = uniqueOrderedArray( chunks, (c) => (c.name ? [c.name] : []), compareIds ); object.chunkIdHints = uniqueOrderedArray( chunks, (c) => [...c.idNameHints], compareIds ); object.auxiliaryChunkNames = uniqueOrderedArray( auxiliaryChunks, (c) => (c.name ? [c.name] : []), compareIds ); object.auxiliaryChunkIdHints = uniqueOrderedArray( auxiliaryChunks, (c) => [...c.idNameHints], compareIds ); object.filteredRelated = asset.related ? asset.related.length : undefined; }, relatedAssets: (object, asset, context, options, factory) => { const { type } = context; object.related = factory.create( `${type.slice(0, -8)}.related`, asset.related || [], context ); object.filteredRelated = asset.related ? asset.related.length - /** @type {StatsAsset[]} */ (object.related).length : undefined; }, ids: ( object, asset, { compilationFileToChunks, compilationAuxiliaryFileToChunks } ) => { const chunks = compilationFileToChunks.get(asset.name) || []; const auxiliaryChunks = compilationAuxiliaryFileToChunks.get(asset.name) || []; object.chunks = uniqueOrderedArray( chunks, (c) => /** @type {ChunkId[]} */ (c.ids), compareIds ); object.auxiliaryChunks = uniqueOrderedArray( auxiliaryChunks, (c) => /** @type {ChunkId[]} */ (c.ids), compareIds ); }, performance: (object, asset) => { object.isOverSizeLimit = SizeLimitsPlugin.isOverSizeLimit(asset.source); } }, chunkGroup: { _: ( object, { name, chunkGroup }, { compilation, compilation: { moduleGraph, chunkGraph } }, { ids, chunkGroupAuxiliary, chunkGroupChildren, chunkGroupMaxAssets } ) => { const children = chunkGroupChildren && chunkGroup.getChildrenByOrders(moduleGraph, chunkGraph); /** * Returns } Asset object. * @param {string} name Name * @returns {{ name: string, size: number }} Asset object */ const toAsset = (name) => { const asset = compilation.getAsset(name); return { name, size: /** @type {number} */ (asset ? asset.info.size : -1) }; }; /** @type {(total: number, asset: { size: number }) => number} */ const sizeReducer = (total, { size }) => total + size; const assets = uniqueArray(chunkGroup.chunks, (c) => c.files).map( toAsset ); const auxiliaryAssets = uniqueOrderedArray( chunkGroup.chunks, (c) => c.auxiliaryFiles, compareIds ).map(toAsset); const assetsSize = assets.reduce(sizeReducer, 0); const auxiliaryAssetsSize = auxiliaryAssets.reduce(sizeReducer, 0); /** @type {KnownStatsChunkGroup} */ const statsChunkGroup = { name, chunks: ids ? /** @type {ChunkId[]} */ (chunkGroup.chunks.map((c) => c.id)) : undefined, assets: assets.length <= chunkGroupMaxAssets ? assets : undefined, filteredAssets: assets.length <= chunkGroupMaxAssets ? 0 : assets.length, assetsSize, auxiliaryAssets: chunkGroupAuxiliary && auxiliaryAssets.length <= chunkGroupMaxAssets ? auxiliaryAssets : undefined, filteredAuxiliaryAssets: chunkGroupAuxiliary && auxiliaryAssets.length <= chunkGroupMaxAssets ? 0 : auxiliaryAssets.length, auxiliaryAssetsSize, children: children ? mapObject(children, (groups) => groups.map((group) => { const assets = uniqueArray(group.chunks, (c) => c.files).map( toAsset ); const auxiliaryAssets = uniqueOrderedArray( group.chunks, (c) => c.auxiliaryFiles, compareIds ).map(toAsset); /** @type {KnownStatsChunkGroup} */ const childStatsChunkGroup = { name: group.name, chunks: ids ? /** @type {ChunkId[]} */ (group.chunks.map((c) => c.id)) : undefined, assets: assets.length <= chunkGroupMaxAssets ? assets : undefined, filteredAssets: assets.length <= chunkGroupMaxAssets ? 0 : assets.length, auxiliaryAssets: chunkGroupAuxiliary && auxiliaryAssets.length <= chunkGroupMaxAssets ? auxiliaryAssets : undefined, filteredAuxiliaryAssets: chunkGroupAuxiliary && auxiliaryAssets.length <= chunkGroupMaxAssets ? 0 : auxiliaryAssets.length }; return childStatsChunkGroup; }) ) : undefined, childAssets: children ? mapObject(children, (groups) => { /** @type {Set<string>} */ const set = new Set(); for (const group of groups) { for (const chunk of group.chunks) { for (const asset of chunk.files) { set.add(asset); } } } return [...set]; }) : undefined }; Object.assign(object, statsChunkGroup); }, performance: (object, { chunkGroup }) => { object.isOverSizeLimit = SizeLimitsPlugin.isOverSizeLimit(chunkGroup); } }, module: { _: (object, module, context, options, factory) => { const { type } = context; const compilation = /** @type {Compilation} */ (context.compilation); const built = compilation.builtModules.has(module); const codeGenerated = compilation.codeGeneratedModules.has(module); const buildTimeExecuted = compilation.buildTimeExecutedModules.has(module); /** @type {{ [x: string]: number }} */ const sizes = {}; for (const sourceType of module.getSourceTypes()) { sizes[sourceType] = module.size(sourceType); } /** @type {KnownStatsModule} */ const statsModule = { type: "module", moduleType: module.type, layer: module.layer, size: module.size(), sizes, built, codeGenerated, buildTimeExecuted, cached: !built && !codeGenerated }; Object.assign(object, statsModule); if (built || codeGenerated || options.cachedModules) { Object.assign( object, factory.create(`${type}$visible`, module, context) ); } } }, module$visible: { _: (object, module, context, { requestShortener }, factory) => { const { type, rootModules } = context; const compilation = /** @type {Compilation} */ (context.compilation); const { moduleGraph } = compilation; /** @type {ModuleIssuerPath} */ const path = []; const issuer = moduleGraph.getIssuer(module); let current = issuer; while (current) { path.push(current); current = moduleGraph.getIssuer(current); } path.reverse(); const profile = moduleGraph.getProfile(module); const errors = module.getErrors(); const errorsCount = errors !== undefined ? countIterable(errors) : 0; const warnings = module.getWarnings(); const warningsCount = warnings !== undefined ? countIterable(warnings) : 0; /** @type {KnownStatsModule} */ const statsModule = { identifier: module.identifier(), name: module.readableIdentifier(requestShortener), nameForCondition: module.nameForCondition(), index: /** @type {number} */ (moduleGraph.getPreOrderIndex(module)), preOrderIndex: /** @type {number} */ ( moduleGraph.getPreOrderIndex(module) ), index2: /** @type {number} */ (moduleGraph.getPostOrderIndex(module)), postOrderIndex: /** @type {number} */ ( moduleGraph.getPostOrderIndex(module) ), cacheable: /** @type {BuildInfo} */ (module.buildInfo).cacheable, optional: module.isOptional(moduleGraph), orphan: !type.endsWith("module.modules[].module$visible") && compilation.chunkGraph.getNumberOfModuleChunks(module) === 0, dependent: rootModules ? !rootModules.has(module) : undefined, issuer: issuer && issuer.identifier(), issuerName: issuer && issuer.readableIdentifier(requestShortener), issuerPath: issuer && /** @type {StatsModuleIssuer[] | undefined} */ (factory.create(`${type.slice(0, -8)}.issuerPath`, path, context)), failed: errorsCount > 0, errors: errorsCount, warnings: warningsCount }; Object.assign(object, statsModule); if (profile) { object.profile = factory.create( `${type.slice(0, -8)}.profile`, profile, context ); } }, ids: (object, module, { compilation: { chunkGraph, moduleGraph } }) => { object.id = /** @type {ModuleId} */ (chunkGraph.getModuleId(module)); const issuer = moduleGraph.getIssuer(module); object.issuerId = issuer && chunkGraph.getModuleId(issuer); object.chunks = /** @type {ChunkId[]} */ ( Array.from( chunkGraph.getOrderedModuleChunksIterable( module, compareChunksById ), (chunk) => chunk.id ) ); }, moduleAssets: (object, module) => { object.assets = /** @type {BuildInfo} */ (module.buildInfo).assets ? Object.keys(/** @type {BuildInfo} */ (module.buildInfo).assets) : []; }, reasons: (object, module, context, options, factory) => { const { type, compilation: { moduleGraph } } = context; const groupsReasons = factory.create( `${type.slice(0, -8)}.reasons`, [...moduleGraph.getIncomingConnections(module)], context ); const limited = spaceLimited( groupsReasons, /** @type {number} */ (options.reasonsSpace) ); object.reasons = limited.children; object.filteredReasons = limited.filteredChildren; }, usedExports: ( object, module, { runtime, compilation: { moduleGraph } } ) => { const usedExports = moduleGraph.getUsedExports(module, runtime); if (usedExports === null) { object.usedExports = null; } else if (typeof usedExports === "boolean") { object.usedExports = usedExports; } else { object.usedExports = [...usedExports]; } }, providedExports: (object, module, { compilation: { moduleGraph } }) => { const providedExports = moduleGraph.getProvidedExports(module); object.providedExports = Array.isArray(providedExports) ? providedExports : null; }, optimizationBailout: ( object, module, { compilation: { moduleGraph } }, { requestShortener } ) => { object.optimizationBailout = moduleGraph .getOptimizationBailout(module) .map((item) => { if (typeof item === "function") return item(requestShortener); return item; }); }, depth: (object, module, { compilation: { moduleGraph } }) => { object.depth = moduleGraph.getDepth(module); }, nestedModules: (object, module, context, options, factory) => { const { type } = context; const innerModules = /** @type {Module & { modules?: Module[] }} */ ( module ).modules; if (Array.isArray(innerModules)) { const groupedModules = factory.create( `${type.slice(0, -8)}.modules`, innerModules, context ); const limited = spaceLimited( groupedModules, options.nestedModulesSpace ); object.modules = limited.children; object.filteredModules = limited.filteredChildren; } }, source: (object, module) => { const originalSource = module.originalSource(); if (originalSource) { object.source = originalSource.source(); } } }, profile: { _: (object, profile) => { /** @type {KnownStatsProfile} */ const statsProfile = { total: profile.factory + profile.restoring + profile.integration + profile.building + profile.storing, resolving: profile.factory, restoring: profile.restoring, building: profile.building, integration: profile.integration, storing: profile.storing, additionalResolving: profile.additionalFactories, additionalIntegration: profile.additionalIntegration, // TODO remove this in webpack 6 factory: profile.factory, // TODO remove this in webpack 6 dependencies: profile.additionalFactories }; Object.assign(object, statsProfile); } }, moduleIssuer: { _: (object, module, context, { requestShortener }, factory) => { const { type } = context; const compilation = /** @type {Compilation} */ (context.compilation); const { moduleGraph } = compilation; const profile = moduleGraph.getProfile(module); /** @type {Partial<KnownStatsModuleIssuer>} */ const statsModuleIssuer = { identifier: module.identifier(), name: module.readableIdentifier(requestShortener) }; Object.assign(object, statsModuleIssuer); if (profile) { object.profile = factory.create(`${type}.profile`, profile, context); } }, ids: (object, module, { compilation: { chunkGraph } }) => { object.id = /** @type {ModuleId} */ (chunkGraph.getModuleId(module)); } }, moduleReason: { _: (object, reason, { runtime }, { requestShortener }) => { const dep = reason.dependency; const moduleDep = dep && dep instanceof ModuleDependency ? dep : undefined; /** @type {KnownStatsModuleReason} */ const statsModuleReason = { moduleIdentifier: reason.originModule ? reason.originModule.identifier() : null, module: reason.originModule ? reason.originModule.readableIdentifier(requestShortener) : null, moduleName: reason.originModule ? reason.originModule.readableIdentifier(requestShortener) : null, resolvedModuleIdentifier: reason.resolvedOriginModule ? reason.resolvedOriginModule.identifier() : null, resolvedModule: reason.resolvedOriginModule ? reason.resolvedOriginModule.readableIdentifier(requestShortener) : null, type: reason.dependency ? reason.dependency.type : null, active: reason.isActive(runtime), explanation: reason.explanation, userRequest: (moduleDep && moduleDep.userRequest) || null }; Object.assign(object, statsModuleReason); if (reason.dependency) { const locInfo = formatLocation(reason.dependency.loc); if (locInfo) { object.loc = locInfo; } } }, ids: (object, reason, { compilation: { chunkGraph } }) => { object.moduleId = reason.originModule ? chunkGraph.getModuleId(reason.originModule) : null; object.resolvedModuleId = reason.resolvedOriginModule ? chunkGraph.getModuleId(reason.resolvedOriginModule) : null; } }, chunk: { _: (object, chunk, { makePathsRelative, compilation: { chunkGraph } }) => { const childIdByOrder = chunk.getChildIdsByOrders(chunkGraph); /** @type {KnownStatsChunk} */ const statsChunk = { rendered: chunk.rendered, initial: chunk.canBeInitial(), entry: chunk.hasRuntime(), recorded: AggressiveSplittingPlugin.wasChunkRecorded(chunk), reason: chunk.chunkReason, size: chunkGraph.getChunkModulesSize(chunk), sizes: chunkGraph.getChunkModulesSizes(chunk), names: chunk.name ? [chunk.name] : [], idHints: [...chunk.idNameHints], runtime: chunk.runtime === undefined ? undefined : typeof chunk.runtime === "string" ? [makePathsRelative(chunk.runtime)] : Array.from(chunk.runtime.sort(), makePathsRelative), files: [...chunk.files], auxiliaryFiles: [...chunk.auxiliaryFiles].sort(compareIds), hash: /** @type {string} */ (chunk.renderedHash), childrenByOrder: childIdByOrder }; Object.assign(object, statsChunk); }, ids: (object, chunk) => { object.id = /** @type {ChunkId} */ (chunk.id); }, chunkRelations: (object, chunk, _context) => {