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,567 lines (1,520 loc) 82 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 { compareLocations, compareChunksById, compareNumbers, compareIds, concatComparators, compareSelect, compareModulesByIdentifier } = require("../util/comparators"); const { makePathsRelative, parseResource } = require("../util/identifier"); /** @typedef {import("webpack-sources").Source} Source */ /** @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").BuildInfo} BuildInfo */ /** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */ /** @typedef {import("../ModuleProfile")} ModuleProfile */ /** @typedef {import("../RequestShortener")} RequestShortener */ /** @typedef {import("../TemplatedPathPlugin").TemplatePath} TemplatePath */ /** @typedef {import("../WebpackError")} WebpackError */ /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */ /** @typedef {import("./StatsFactory")} StatsFactory */ /** @typedef {import("./StatsFactory").StatsFactoryContext} StatsFactoryContext */ /** * @template T * @typedef {import("../util/comparators").Comparator<T>} Comparator<T> */ /** * @template T, R * @typedef {import("../util/smartGrouping").GroupConfig<T, R>} GroupConfig */ /** @typedef {KnownStatsCompilation & Record<string, EXPECTED_ANY>} StatsCompilation */ /** * @typedef {object} KnownStatsCompilation * @property {Record<string, 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 */ /** * @typedef {object} KnownStatsLogging * @property {StatsLoggingEntry[]} entries * @property {number} filteredEntries * @property {boolean} debug */ /** @typedef {KnownStatsLoggingEntry & Record<string, EXPECTED_ANY>} StatsLoggingEntry */ /** * @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 {ChunkId} KnownStatsAssetChunk */ /** @typedef {ChunkName} KnownStatsAssetChunkName */ /** @typedef {string} KnownStatsAssetChunkIdHint */ /** * @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 {KnownStatsAssetChunk[]=} chunks * @property {KnownStatsAssetChunkName[]=} chunkNames * @property {KnownStatsAssetChunkIdHint[]=} chunkIdHints * @property {KnownStatsAssetChunk[]=} auxiliaryChunks * @property {KnownStatsAssetChunkName[]=} auxiliaryChunkNames * @property {KnownStatsAssetChunkIdHint[]=} auxiliaryChunkIdHints * @property {number=} filteredRelated * @property {boolean=} isOverSizeLimit */ /** @typedef {KnownStatsChunkGroup & Record<string, EXPECTED_ANY>} StatsChunkGroup */ /** * @typedef {object} KnownStatsChunkGroup * @property {(string | null)=} name * @property {(string | number)[]=} 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 {{ [x: string]: StatsChunkGroup[] }=} children * @property {{ [x: string]: string[] }=} childAssets * @property {boolean=} isOverSizeLimit */ /** @typedef {Module[]} ModuleIssuerPath */ /** @typedef {KnownStatsModule & Record<string, EXPECTED_ANY>} StatsModule */ /** * @typedef {object} KnownStatsModule * @property {string=} type * @property {string=} moduleType * @property {(string | null)=} layer * @property {string=} identifier * @property {string=} name * @property {(string | null)=} nameForCondition * @property {number=} index * @property {number=} preOrderIndex * @property {number=} index2 * @property {number=} postOrderIndex * @property {number=} size * @property {{ [x: 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 {string | number=} id * @property {string | number | null=} issuerId * @property {(string | number)[]=} chunks * @property {(string | number)[]=} 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 | string[])=} usedExports * @property {(string[] | 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 */ /** * @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 */ /** * @typedef {object} KnownStatsModuleIssuer * @property {string} identifier * @property {string} name * @property {(string|number)=} id * @property {StatsProfile} profile */ /** @typedef {KnownStatsModuleReason & Record<string, EXPECTED_ANY>} StatsModuleReason */ /** * @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 {(string | number | null)=} moduleId * @property {(string | number | null)=} resolvedModuleId */ /** @typedef {KnownStatsChunk & Record<string, EXPECTED_ANY>} StatsChunk */ /** * @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 {(string|number)=} id * @property {(string|number)[]=} siblings * @property {(string|number)[]=} parents * @property {(string|number)[]=} children * @property {StatsModule[]=} modules * @property {number=} filteredModules * @property {StatsChunkOrigin[]=} origins */ /** @typedef {KnownStatsChunkOrigin & Record<string, EXPECTED_ANY>} StatsChunkOrigin */ /** * @typedef {object} KnownStatsChunkOrigin * @property {string} module * @property {string} moduleIdentifier * @property {string} moduleName * @property {string} loc * @property {string} request * @property {(string | number)=} moduleId */ /** @typedef {KnownStatsModuleTraceItem & Record<string, EXPECTED_ANY>} StatsModuleTraceItem */ /** * @typedef {object} KnownStatsModuleTraceItem * @property {string=} originIdentifier * @property {string=} originName * @property {string=} moduleIdentifier * @property {string=} moduleName * @property {StatsModuleTraceDependency[]=} dependencies * @property {(string|number)=} originId * @property {(string|number)=} moduleId */ /** @typedef {KnownStatsModuleTraceDependency & Record<string, EXPECTED_ANY>} StatsModuleTraceDependency */ /** * @typedef {object} KnownStatsModuleTraceDependency * @property {string=} loc */ /** @typedef {KnownStatsError & Record<string, EXPECTED_ANY>} StatsError */ /** * @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 {string|number=} 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 */ /** * @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 */ /** * @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 */ /** * @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 Array.from(set); }; /** * @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); /** @template T @template R @typedef {{ [P in keyof T]: R }} MappedValues<T, R> */ /** * @template {object} T * @template {object} R * @param {T} obj object to be mapped * @param {function(T[keyof T], keyof T): R} fn mapping function * @returns {MappedValues<T, R>} mapped object */ const mapObject = (obj, fn) => { const newObj = Object.create(null); for (const key of Object.keys(obj)) { newObj[key] = fn( obj[/** @type {keyof T} */ (key)], /** @type {keyof T} */ (key) ); } return newObj; }; /** * @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; }; /** @typedef {Error & { cause?: unknown }} ErrorWithCause */ /** @typedef {Error & { errors: EXPECTED_ANY[] }} AggregateError */ /** @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, cachedGetWarnings }, { errorDetails } ) => { if ( typeof error !== "string" && (errorDetails === true || (type.endsWith(".error") && cachedGetErrors(compilation).length < 3)) ) { object.details = /** @type {WebpackError} */ (error).details; } }, errorStack: (object, error) => { if (typeof error !== "string") { 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 ); } } }; /** @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) { 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) { 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 = {}; 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; } 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 = /** @type {string} */ (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( /** @type {TemplatePath} */ (compilation.outputOptions.publicPath) ); }, outputPath: (object, compilation) => { object.outputPath = /** @type {string} */ ( 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) { 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) { 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`, Array.from(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`, Array.from(compilation.chunks), context ); }, modules: (object, compilation, context, options, factory) => { const { type } = context; const array = Array.from(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" && 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" && 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( /** * @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, { compilation, 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 => Array.from(c.idNameHints), compareIds ); object.auxiliaryChunkNames = uniqueOrderedArray( auxiliaryChunks, c => (c.name ? [c.name] : []), compareIds ); object.auxiliaryChunkIdHints = uniqueOrderedArray( auxiliaryChunks, c => Array.from(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); /** * @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 Array.from(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`, Array.from(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 = Array.from(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: Array.from(chunk.idNameHints), runtime: chunk.runtime === undefined ? undefined : typeof chunk.runtime === "string" ? [makePathsRelative(chunk.runtime)] : Array.from(chunk.runtime.sort(), makePathsRelative), files: Array.from(chunk.files), auxiliaryFiles: Array.from(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, { compilation: { chunkGraph } }) => { /** @type {Set<string|number>} */ const parents = new Set(); /** @type {Set<string|number>} */ const children = new Set(); /** @type {Set<string|number>} */ const siblings = new Set(); for (const chunkGroup of chunk.groupsIterable) { for (const parentGroup of chunkGroup.parentsIterable) { for (const chunk of parentGroup.chunks) { parents.add(/** @type {ChunkId} */ (chunk.id)); } } for (const childGroup of chunkGroup.childrenIterable) { for (const chunk of childGroup.chunks) { children.add(/** @type {ChunkId} */ (chunk.id)); } } for (const sibling of chunkGroup.chunks) { if (sibling !== chunk) siblings.add(/** @type {ChunkId} */ (sibling.id)); } } object.siblings = Array.from(siblings).sort(compareIds); object.parents = Array.from(parents).sort(compareIds); object.children = Array.from(children).sort(compareIds); }, chunkModules: (object, chunk, context, options, factory) => { const { type, compilation: { chunkGraph } } = context; const array = chunkGraph.getChunkModules(chunk); const groupedModules = factory.create(`${type}.modules`, array, { ...context, runtime: chunk.runtime, rootModules: new Set(chunkGraph.getChunkRootModules(chunk)) }); const limited = spaceLimited(groupedModules, options.chunkModulesSpace); object.modules = limited.children; object.filteredModules