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
JavaScript
/*
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