UNPKG

71.9 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const util = require("util");
9const ModuleDependency = require("../dependencies/ModuleDependency");
10const formatLocation = require("../formatLocation");
11const { LogType } = require("../logging/Logger");
12const AggressiveSplittingPlugin = require("../optimize/AggressiveSplittingPlugin");
13const SizeLimitsPlugin = require("../performance/SizeLimitsPlugin");
14const { countIterable } = require("../util/IterableHelpers");
15const {
16 compareLocations,
17 compareChunksById,
18 compareNumbers,
19 compareIds,
20 concatComparators,
21 compareSelect,
22 compareModulesByIdentifier
23} = require("../util/comparators");
24const { makePathsRelative, parseResource } = require("../util/identifier");
25
26/** @typedef {import("webpack-sources").Source} Source */
27/** @typedef {import("../Chunk")} Chunk */
28/** @typedef {import("../ChunkGroup")} ChunkGroup */
29/** @typedef {import("../ChunkGroup").OriginRecord} OriginRecord */
30/** @typedef {import("../Compilation")} Compilation */
31/** @typedef {import("../Compilation").Asset} Asset */
32/** @typedef {import("../Compilation").AssetInfo} AssetInfo */
33/** @typedef {import("../Compilation").NormalizedStatsOptions} NormalizedStatsOptions */
34/** @typedef {import("../Compiler")} Compiler */
35/** @typedef {import("../Dependency")} Dependency */
36/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
37/** @typedef {import("../Module")} Module */
38/** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */
39/** @typedef {import("../ModuleProfile")} ModuleProfile */
40/** @typedef {import("../RequestShortener")} RequestShortener */
41/** @typedef {import("../WebpackError")} WebpackError */
42/** @template T @typedef {import("../util/comparators").Comparator<T>} Comparator<T> */
43/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
44/** @typedef {import("../util/smartGrouping").GroupConfig<any, object>} GroupConfig */
45/** @typedef {import("./StatsFactory")} StatsFactory */
46/** @typedef {import("./StatsFactory").StatsFactoryContext} StatsFactoryContext */
47
48/** @typedef {KnownStatsCompilation & Record<string, any>} StatsCompilation */
49/**
50 * @typedef {Object} KnownStatsCompilation
51 * @property {any=} env
52 * @property {string=} name
53 * @property {string=} hash
54 * @property {string=} version
55 * @property {number=} time
56 * @property {number=} builtAt
57 * @property {boolean=} needAdditionalPass
58 * @property {string=} publicPath
59 * @property {string=} outputPath
60 * @property {Record<string, string[]>=} assetsByChunkName
61 * @property {StatsAsset[]=} assets
62 * @property {number=} filteredAssets
63 * @property {StatsChunk[]=} chunks
64 * @property {StatsModule[]=} modules
65 * @property {number=} filteredModules
66 * @property {Record<string, StatsChunkGroup>=} entrypoints
67 * @property {Record<string, StatsChunkGroup>=} namedChunkGroups
68 * @property {StatsError[]=} errors
69 * @property {number=} errorsCount
70 * @property {StatsError[]=} warnings
71 * @property {number=} warningsCount
72 * @property {StatsCompilation[]=} children
73 * @property {Record<string, StatsLogging>=} logging
74 */
75
76/** @typedef {KnownStatsLogging & Record<string, any>} StatsLogging */
77/**
78 * @typedef {Object} KnownStatsLogging
79 * @property {StatsLoggingEntry[]} entries
80 * @property {number} filteredEntries
81 * @property {boolean} debug
82 */
83
84/** @typedef {KnownStatsLoggingEntry & Record<string, any>} StatsLoggingEntry */
85/**
86 * @typedef {Object} KnownStatsLoggingEntry
87 * @property {string} type
88 * @property {string} message
89 * @property {string[]=} trace
90 * @property {StatsLoggingEntry[]=} children
91 * @property {any[]=} args
92 * @property {number=} time
93 */
94
95/** @typedef {KnownStatsAsset & Record<string, any>} StatsAsset */
96/**
97 * @typedef {Object} KnownStatsAsset
98 * @property {string} type
99 * @property {string} name
100 * @property {AssetInfo} info
101 * @property {number} size
102 * @property {boolean} emitted
103 * @property {boolean} comparedForEmit
104 * @property {boolean} cached
105 * @property {StatsAsset[]=} related
106 * @property {(string|number)[]=} chunkNames
107 * @property {(string|number)[]=} chunkIdHints
108 * @property {(string|number)[]=} chunks
109 * @property {(string|number)[]=} auxiliaryChunkNames
110 * @property {(string|number)[]=} auxiliaryChunks
111 * @property {(string|number)[]=} auxiliaryChunkIdHints
112 * @property {number=} filteredRelated
113 * @property {boolean=} isOverSizeLimit
114 */
115
116/** @typedef {KnownStatsChunkGroup & Record<string, any>} StatsChunkGroup */
117/**
118 * @typedef {Object} KnownStatsChunkGroup
119 * @property {string=} name
120 * @property {(string|number)[]=} chunks
121 * @property {({ name: string, size?: number })[]=} assets
122 * @property {number=} filteredAssets
123 * @property {number=} assetsSize
124 * @property {({ name: string, size?: number })[]=} auxiliaryAssets
125 * @property {number=} filteredAuxiliaryAssets
126 * @property {number=} auxiliaryAssetsSize
127 * @property {{ [x: string]: StatsChunkGroup[] }=} children
128 * @property {{ [x: string]: string[] }=} childAssets
129 * @property {boolean=} isOverSizeLimit
130 */
131
132/** @typedef {KnownStatsModule & Record<string, any>} StatsModule */
133/**
134 * @typedef {Object} KnownStatsModule
135 * @property {string=} type
136 * @property {string=} moduleType
137 * @property {string=} layer
138 * @property {string=} identifier
139 * @property {string=} name
140 * @property {string=} nameForCondition
141 * @property {number=} index
142 * @property {number=} preOrderIndex
143 * @property {number=} index2
144 * @property {number=} postOrderIndex
145 * @property {number=} size
146 * @property {{[x: string]: number}=} sizes
147 * @property {boolean=} cacheable
148 * @property {boolean=} built
149 * @property {boolean=} codeGenerated
150 * @property {boolean=} buildTimeExecuted
151 * @property {boolean=} cached
152 * @property {boolean=} optional
153 * @property {boolean=} orphan
154 * @property {string|number=} id
155 * @property {string|number=} issuerId
156 * @property {(string|number)[]=} chunks
157 * @property {(string|number)[]=} assets
158 * @property {boolean=} dependent
159 * @property {string=} issuer
160 * @property {string=} issuerName
161 * @property {StatsModuleIssuer[]=} issuerPath
162 * @property {boolean=} failed
163 * @property {number=} errors
164 * @property {number=} warnings
165 * @property {StatsProfile=} profile
166 * @property {StatsModuleReason[]=} reasons
167 * @property {(boolean | string[])=} usedExports
168 * @property {string[]=} providedExports
169 * @property {string[]=} optimizationBailout
170 * @property {number=} depth
171 * @property {StatsModule[]=} modules
172 * @property {number=} filteredModules
173 * @property {ReturnType<Source["source"]>=} source
174 */
175
176/** @typedef {KnownStatsProfile & Record<string, any>} StatsProfile */
177/**
178 * @typedef {Object} KnownStatsProfile
179 * @property {number} total
180 * @property {number} resolving
181 * @property {number} restoring
182 * @property {number} building
183 * @property {number} integration
184 * @property {number} storing
185 * @property {number} additionalResolving
186 * @property {number} additionalIntegration
187 * @property {number} factory
188 * @property {number} dependencies
189 */
190
191/** @typedef {KnownStatsModuleIssuer & Record<string, any>} StatsModuleIssuer */
192/**
193 * @typedef {Object} KnownStatsModuleIssuer
194 * @property {string=} identifier
195 * @property {string=} name
196 * @property {(string|number)=} id
197 * @property {StatsProfile=} profile
198 */
199
200/** @typedef {KnownStatsModuleReason & Record<string, any>} StatsModuleReason */
201/**
202 * @typedef {Object} KnownStatsModuleReason
203 * @property {string=} moduleIdentifier
204 * @property {string=} module
205 * @property {string=} moduleName
206 * @property {string=} resolvedModuleIdentifier
207 * @property {string=} resolvedModule
208 * @property {string=} type
209 * @property {boolean} active
210 * @property {string=} explanation
211 * @property {string=} userRequest
212 * @property {string=} loc
213 * @property {(string|number)=} moduleId
214 * @property {(string|number)=} resolvedModuleId
215 */
216
217/** @typedef {KnownStatsChunk & Record<string, any>} StatsChunk */
218/**
219 * @typedef {Object} KnownStatsChunk
220 * @property {boolean} rendered
221 * @property {boolean} initial
222 * @property {boolean} entry
223 * @property {boolean} recorded
224 * @property {string=} reason
225 * @property {number} size
226 * @property {Record<string, number>=} sizes
227 * @property {string[]=} names
228 * @property {string[]=} idHints
229 * @property {string[]=} runtime
230 * @property {string[]=} files
231 * @property {string[]=} auxiliaryFiles
232 * @property {string} hash
233 * @property {Record<string, (string|number)[]>=} childrenByOrder
234 * @property {(string|number)=} id
235 * @property {(string|number)[]=} siblings
236 * @property {(string|number)[]=} parents
237 * @property {(string|number)[]=} children
238 * @property {StatsModule[]=} modules
239 * @property {number=} filteredModules
240 * @property {StatsChunkOrigin[]=} origins
241 */
242
243/** @typedef {KnownStatsChunkOrigin & Record<string, any>} StatsChunkOrigin */
244/**
245 * @typedef {Object} KnownStatsChunkOrigin
246 * @property {string=} module
247 * @property {string=} moduleIdentifier
248 * @property {string=} moduleName
249 * @property {string=} loc
250 * @property {string=} request
251 * @property {(string|number)=} moduleId
252 */
253
254/** @typedef {KnownStatsModuleTraceItem & Record<string, any>} StatsModuleTraceItem */
255/**
256 * @typedef {Object} KnownStatsModuleTraceItem
257 * @property {string=} originIdentifier
258 * @property {string=} originName
259 * @property {string=} moduleIdentifier
260 * @property {string=} moduleName
261 * @property {StatsModuleTraceDependency[]=} dependencies
262 * @property {(string|number)=} originId
263 * @property {(string|number)=} moduleId
264 */
265
266/** @typedef {KnownStatsModuleTraceDependency & Record<string, any>} StatsModuleTraceDependency */
267/**
268 * @typedef {Object} KnownStatsModuleTraceDependency
269 * @property {string=} loc
270 */
271
272/** @typedef {KnownStatsError & Record<string, any>} StatsError */
273/**
274 * @typedef {Object} KnownStatsError
275 * @property {string} message
276 * @property {string=} chunkName
277 * @property {boolean=} chunkEntry
278 * @property {boolean=} chunkInitial
279 * @property {string=} file
280 * @property {string=} moduleIdentifier
281 * @property {string=} moduleName
282 * @property {string=} loc
283 * @property {string|number=} chunkId
284 * @property {string|number=} moduleId
285 * @property {StatsModuleTraceItem[]=} moduleTrace
286 * @property {any=} details
287 * @property {string=} stack
288 */
289
290/** @typedef {Asset & { type: string, related: PreprocessedAsset[] }} PreprocessedAsset */
291
292/**
293 * @template T
294 * @template O
295 * @typedef {Record<string, (object: O, data: T, context: StatsFactoryContext, options: NormalizedStatsOptions, factory: StatsFactory) => void>} ExtractorsByOption
296 */
297
298/**
299 * @typedef {Object} SimpleExtractors
300 * @property {ExtractorsByOption<Compilation, StatsCompilation>} compilation
301 * @property {ExtractorsByOption<PreprocessedAsset, StatsAsset>} asset
302 * @property {ExtractorsByOption<PreprocessedAsset, StatsAsset>} asset$visible
303 * @property {ExtractorsByOption<{ name: string, chunkGroup: ChunkGroup }, StatsChunkGroup>} chunkGroup
304 * @property {ExtractorsByOption<Module, StatsModule>} module
305 * @property {ExtractorsByOption<Module, StatsModule>} module$visible
306 * @property {ExtractorsByOption<Module, StatsModuleIssuer>} moduleIssuer
307 * @property {ExtractorsByOption<ModuleProfile, StatsProfile>} profile
308 * @property {ExtractorsByOption<ModuleGraphConnection, StatsModuleReason>} moduleReason
309 * @property {ExtractorsByOption<Chunk, StatsChunk>} chunk
310 * @property {ExtractorsByOption<OriginRecord, StatsChunkOrigin>} chunkOrigin
311 * @property {ExtractorsByOption<WebpackError, StatsError>} error
312 * @property {ExtractorsByOption<WebpackError, StatsError>} warning
313 * @property {ExtractorsByOption<{ origin: Module, module: Module }, StatsModuleTraceItem>} moduleTraceItem
314 * @property {ExtractorsByOption<Dependency, StatsModuleTraceDependency>} moduleTraceDependency
315 */
316
317/**
318 * @template T
319 * @template I
320 * @param {Iterable<T>} items items to select from
321 * @param {function(T): Iterable<I>} selector selector function to select values from item
322 * @returns {I[]} array of values
323 */
324const uniqueArray = (items, selector) => {
325 /** @type {Set<I>} */
326 const set = new Set();
327 for (const item of items) {
328 for (const i of selector(item)) {
329 set.add(i);
330 }
331 }
332 return Array.from(set);
333};
334
335/**
336 * @template T
337 * @template I
338 * @param {Iterable<T>} items items to select from
339 * @param {function(T): Iterable<I>} selector selector function to select values from item
340 * @param {Comparator<I>} comparator comparator function
341 * @returns {I[]} array of values
342 */
343const uniqueOrderedArray = (items, selector, comparator) => {
344 return uniqueArray(items, selector).sort(comparator);
345};
346
347/** @template T @template R @typedef {{ [P in keyof T]: R }} MappedValues<T, R> */
348
349/**
350 * @template T
351 * @template R
352 * @param {T} obj object to be mapped
353 * @param {function(T[keyof T], keyof T): R} fn mapping function
354 * @returns {MappedValues<T, R>} mapped object
355 */
356const mapObject = (obj, fn) => {
357 const newObj = Object.create(null);
358 for (const key of Object.keys(obj)) {
359 newObj[key] = fn(obj[key], /** @type {keyof T} */ (key));
360 }
361 return newObj;
362};
363
364/**
365 * @param {Compilation} compilation the compilation
366 * @param {function(Compilation, string): any[]} getItems get items
367 * @returns {number} total number
368 */
369const countWithChildren = (compilation, getItems) => {
370 let count = getItems(compilation, "").length;
371 for (const child of compilation.children) {
372 count += countWithChildren(child, (c, type) =>
373 getItems(c, `.children[].compilation${type}`)
374 );
375 }
376 return count;
377};
378
379/** @type {ExtractorsByOption<WebpackError | string, StatsError>} */
380const EXTRACT_ERROR = {
381 _: (object, error, context, { requestShortener }) => {
382 // TODO webpack 6 disallow strings in the errors/warnings list
383 if (typeof error === "string") {
384 object.message = error;
385 } else {
386 if (error.chunk) {
387 object.chunkName = error.chunk.name;
388 object.chunkEntry = error.chunk.hasRuntime();
389 object.chunkInitial = error.chunk.canBeInitial();
390 }
391 if (error.file) {
392 object.file = error.file;
393 }
394 if (error.module) {
395 object.moduleIdentifier = error.module.identifier();
396 object.moduleName = error.module.readableIdentifier(requestShortener);
397 }
398 if (error.loc) {
399 object.loc = formatLocation(error.loc);
400 }
401 object.message = error.message;
402 }
403 },
404 ids: (object, error, { compilation: { chunkGraph } }) => {
405 if (typeof error !== "string") {
406 if (error.chunk) {
407 object.chunkId = error.chunk.id;
408 }
409 if (error.module) {
410 object.moduleId = chunkGraph.getModuleId(error.module);
411 }
412 }
413 },
414 moduleTrace: (object, error, context, options, factory) => {
415 if (typeof error !== "string" && error.module) {
416 const {
417 type,
418 compilation: { moduleGraph }
419 } = context;
420 /** @type {Set<Module>} */
421 const visitedModules = new Set();
422 const moduleTrace = [];
423 let current = error.module;
424 while (current) {
425 if (visitedModules.has(current)) break; // circular (technically impossible, but how knows)
426 visitedModules.add(current);
427 const origin = moduleGraph.getIssuer(current);
428 if (!origin) break;
429 moduleTrace.push({ origin, module: current });
430 current = origin;
431 }
432 object.moduleTrace = factory.create(
433 `${type}.moduleTrace`,
434 moduleTrace,
435 context
436 );
437 }
438 },
439 errorDetails: (
440 object,
441 error,
442 { type, compilation, cachedGetErrors, cachedGetWarnings },
443 { errorDetails }
444 ) => {
445 if (
446 typeof error !== "string" &&
447 (errorDetails === true ||
448 (type.endsWith(".error") && cachedGetErrors(compilation).length < 3))
449 ) {
450 object.details = error.details;
451 }
452 },
453 errorStack: (object, error) => {
454 if (typeof error !== "string") {
455 object.stack = error.stack;
456 }
457 }
458};
459
460/** @type {SimpleExtractors} */
461const SIMPLE_EXTRACTORS = {
462 compilation: {
463 _: (object, compilation, context, options) => {
464 if (!context.makePathsRelative) {
465 context.makePathsRelative = makePathsRelative.bindContextCache(
466 compilation.compiler.context,
467 compilation.compiler.root
468 );
469 }
470 if (!context.cachedGetErrors) {
471 const map = new WeakMap();
472 context.cachedGetErrors = compilation => {
473 return (
474 map.get(compilation) ||
475 (errors => (map.set(compilation, errors), errors))(
476 compilation.getErrors()
477 )
478 );
479 };
480 }
481 if (!context.cachedGetWarnings) {
482 const map = new WeakMap();
483 context.cachedGetWarnings = compilation => {
484 return (
485 map.get(compilation) ||
486 (warnings => (map.set(compilation, warnings), warnings))(
487 compilation.getWarnings()
488 )
489 );
490 };
491 }
492 if (compilation.name) {
493 object.name = compilation.name;
494 }
495 if (compilation.needAdditionalPass) {
496 object.needAdditionalPass = true;
497 }
498
499 const { logging, loggingDebug, loggingTrace } = options;
500 if (logging || (loggingDebug && loggingDebug.length > 0)) {
501 const util = require("util");
502 object.logging = {};
503 let acceptedTypes;
504 let collapsedGroups = false;
505 switch (logging) {
506 default:
507 acceptedTypes = new Set();
508 break;
509 case "error":
510 acceptedTypes = new Set([LogType.error]);
511 break;
512 case "warn":
513 acceptedTypes = new Set([LogType.error, LogType.warn]);
514 break;
515 case "info":
516 acceptedTypes = new Set([
517 LogType.error,
518 LogType.warn,
519 LogType.info
520 ]);
521 break;
522 case "log":
523 acceptedTypes = new Set([
524 LogType.error,
525 LogType.warn,
526 LogType.info,
527 LogType.log,
528 LogType.group,
529 LogType.groupEnd,
530 LogType.groupCollapsed,
531 LogType.clear
532 ]);
533 break;
534 case "verbose":
535 acceptedTypes = new Set([
536 LogType.error,
537 LogType.warn,
538 LogType.info,
539 LogType.log,
540 LogType.group,
541 LogType.groupEnd,
542 LogType.groupCollapsed,
543 LogType.profile,
544 LogType.profileEnd,
545 LogType.time,
546 LogType.status,
547 LogType.clear
548 ]);
549 collapsedGroups = true;
550 break;
551 }
552 const cachedMakePathsRelative = makePathsRelative.bindContextCache(
553 options.context,
554 compilation.compiler.root
555 );
556 let depthInCollapsedGroup = 0;
557 for (const [origin, logEntries] of compilation.logging) {
558 const debugMode = loggingDebug.some(fn => fn(origin));
559 if (logging === false && !debugMode) continue;
560 /** @type {KnownStatsLoggingEntry[]} */
561 const groupStack = [];
562 /** @type {KnownStatsLoggingEntry[]} */
563 const rootList = [];
564 let currentList = rootList;
565 let processedLogEntries = 0;
566 for (const entry of logEntries) {
567 let type = entry.type;
568 if (!debugMode && !acceptedTypes.has(type)) continue;
569
570 // Expand groups in verbose and debug modes
571 if (
572 type === LogType.groupCollapsed &&
573 (debugMode || collapsedGroups)
574 )
575 type = LogType.group;
576
577 if (depthInCollapsedGroup === 0) {
578 processedLogEntries++;
579 }
580
581 if (type === LogType.groupEnd) {
582 groupStack.pop();
583 if (groupStack.length > 0) {
584 currentList = groupStack[groupStack.length - 1].children;
585 } else {
586 currentList = rootList;
587 }
588 if (depthInCollapsedGroup > 0) depthInCollapsedGroup--;
589 continue;
590 }
591 let message = undefined;
592 if (entry.type === LogType.time) {
593 message = `${entry.args[0]}: ${
594 entry.args[1] * 1000 + entry.args[2] / 1000000
595 } ms`;
596 } else if (entry.args && entry.args.length > 0) {
597 message = util.format(entry.args[0], ...entry.args.slice(1));
598 }
599 /** @type {KnownStatsLoggingEntry} */
600 const newEntry = {
601 ...entry,
602 type,
603 message,
604 trace: loggingTrace ? entry.trace : undefined,
605 children:
606 type === LogType.group || type === LogType.groupCollapsed
607 ? []
608 : undefined
609 };
610 currentList.push(newEntry);
611 if (newEntry.children) {
612 groupStack.push(newEntry);
613 currentList = newEntry.children;
614 if (depthInCollapsedGroup > 0) {
615 depthInCollapsedGroup++;
616 } else if (type === LogType.groupCollapsed) {
617 depthInCollapsedGroup = 1;
618 }
619 }
620 }
621 let name = cachedMakePathsRelative(origin).replace(/\|/g, " ");
622 if (name in object.logging) {
623 let i = 1;
624 while (`${name}#${i}` in object.logging) {
625 i++;
626 }
627 name = `${name}#${i}`;
628 }
629 object.logging[name] = {
630 entries: rootList,
631 filteredEntries: logEntries.length - processedLogEntries,
632 debug: debugMode
633 };
634 }
635 }
636 },
637 hash: (object, compilation) => {
638 object.hash = compilation.hash;
639 },
640 version: object => {
641 object.version = require("../../package.json").version;
642 },
643 env: (object, compilation, context, { _env }) => {
644 object.env = _env;
645 },
646 timings: (object, compilation) => {
647 object.time = compilation.endTime - compilation.startTime;
648 },
649 builtAt: (object, compilation) => {
650 object.builtAt = compilation.endTime;
651 },
652 publicPath: (object, compilation) => {
653 object.publicPath = compilation.getPath(
654 compilation.outputOptions.publicPath
655 );
656 },
657 outputPath: (object, compilation) => {
658 object.outputPath = compilation.outputOptions.path;
659 },
660 assets: (object, compilation, context, options, factory) => {
661 const { type } = context;
662 /** @type {Map<string, Chunk[]>} */
663 const compilationFileToChunks = new Map();
664 /** @type {Map<string, Chunk[]>} */
665 const compilationAuxiliaryFileToChunks = new Map();
666 for (const chunk of compilation.chunks) {
667 for (const file of chunk.files) {
668 let array = compilationFileToChunks.get(file);
669 if (array === undefined) {
670 array = [];
671 compilationFileToChunks.set(file, array);
672 }
673 array.push(chunk);
674 }
675 for (const file of chunk.auxiliaryFiles) {
676 let array = compilationAuxiliaryFileToChunks.get(file);
677 if (array === undefined) {
678 array = [];
679 compilationAuxiliaryFileToChunks.set(file, array);
680 }
681 array.push(chunk);
682 }
683 }
684 /** @type {Map<string, PreprocessedAsset>} */
685 const assetMap = new Map();
686 /** @type {Set<PreprocessedAsset>} */
687 const assets = new Set();
688 for (const asset of compilation.getAssets()) {
689 /** @type {PreprocessedAsset} */
690 const item = {
691 ...asset,
692 type: "asset",
693 related: undefined
694 };
695 assets.add(item);
696 assetMap.set(asset.name, item);
697 }
698 for (const item of assetMap.values()) {
699 const related = item.info.related;
700 if (!related) continue;
701 for (const type of Object.keys(related)) {
702 const relatedEntry = related[type];
703 const deps = Array.isArray(relatedEntry)
704 ? relatedEntry
705 : [relatedEntry];
706 for (const dep of deps) {
707 const depItem = assetMap.get(dep);
708 if (!depItem) continue;
709 assets.delete(depItem);
710 depItem.type = type;
711 item.related = item.related || [];
712 item.related.push(depItem);
713 }
714 }
715 }
716
717 object.assetsByChunkName = {};
718 for (const [file, chunks] of compilationFileToChunks) {
719 for (const chunk of chunks) {
720 const name = chunk.name;
721 if (!name) continue;
722 if (
723 !Object.prototype.hasOwnProperty.call(
724 object.assetsByChunkName,
725 name
726 )
727 ) {
728 object.assetsByChunkName[name] = [];
729 }
730 object.assetsByChunkName[name].push(file);
731 }
732 }
733
734 const groupedAssets = factory.create(
735 `${type}.assets`,
736 Array.from(assets),
737 {
738 ...context,
739 compilationFileToChunks,
740 compilationAuxiliaryFileToChunks
741 }
742 );
743 const limited = spaceLimited(groupedAssets, options.assetsSpace);
744 object.assets = limited.children;
745 object.filteredAssets = limited.filteredChildren;
746 },
747 chunks: (object, compilation, context, options, factory) => {
748 const { type } = context;
749 object.chunks = factory.create(
750 `${type}.chunks`,
751 Array.from(compilation.chunks),
752 context
753 );
754 },
755 modules: (object, compilation, context, options, factory) => {
756 const { type } = context;
757 const array = Array.from(compilation.modules);
758 const groupedModules = factory.create(`${type}.modules`, array, context);
759 const limited = spaceLimited(groupedModules, options.modulesSpace);
760 object.modules = limited.children;
761 object.filteredModules = limited.filteredChildren;
762 },
763 entrypoints: (
764 object,
765 compilation,
766 context,
767 { entrypoints, chunkGroups, chunkGroupAuxiliary, chunkGroupChildren },
768 factory
769 ) => {
770 const { type } = context;
771 const array = Array.from(compilation.entrypoints, ([key, value]) => ({
772 name: key,
773 chunkGroup: value
774 }));
775 if (entrypoints === "auto" && !chunkGroups) {
776 if (array.length > 5) return;
777 if (
778 !chunkGroupChildren &&
779 array.every(({ chunkGroup }) => {
780 if (chunkGroup.chunks.length !== 1) return false;
781 const chunk = chunkGroup.chunks[0];
782 return (
783 chunk.files.size === 1 &&
784 (!chunkGroupAuxiliary || chunk.auxiliaryFiles.size === 0)
785 );
786 })
787 ) {
788 return;
789 }
790 }
791 object.entrypoints = factory.create(
792 `${type}.entrypoints`,
793 array,
794 context
795 );
796 },
797 chunkGroups: (object, compilation, context, options, factory) => {
798 const { type } = context;
799 const array = Array.from(
800 compilation.namedChunkGroups,
801 ([key, value]) => ({
802 name: key,
803 chunkGroup: value
804 })
805 );
806 object.namedChunkGroups = factory.create(
807 `${type}.namedChunkGroups`,
808 array,
809 context
810 );
811 },
812 errors: (object, compilation, context, options, factory) => {
813 const { type, cachedGetErrors } = context;
814 object.errors = factory.create(
815 `${type}.errors`,
816 cachedGetErrors(compilation),
817 context
818 );
819 },
820 errorsCount: (object, compilation, { cachedGetErrors }) => {
821 object.errorsCount = countWithChildren(compilation, c =>
822 cachedGetErrors(c)
823 );
824 },
825 warnings: (object, compilation, context, options, factory) => {
826 const { type, cachedGetWarnings } = context;
827 object.warnings = factory.create(
828 `${type}.warnings`,
829 cachedGetWarnings(compilation),
830 context
831 );
832 },
833 warningsCount: (
834 object,
835 compilation,
836 context,
837 { warningsFilter },
838 factory
839 ) => {
840 const { type, cachedGetWarnings } = context;
841 object.warningsCount = countWithChildren(compilation, (c, childType) => {
842 if (!warningsFilter && warningsFilter.length === 0)
843 return cachedGetWarnings(c);
844 return factory
845 .create(`${type}${childType}.warnings`, cachedGetWarnings(c), context)
846 .filter(warning => {
847 const warningString = Object.keys(warning)
848 .map(key => `${warning[key]}`)
849 .join("\n");
850 return !warningsFilter.some(filter =>
851 filter(warning, warningString)
852 );
853 });
854 });
855 },
856 errorDetails: (
857 object,
858 compilation,
859 { cachedGetErrors, cachedGetWarnings },
860 { errorDetails, errors, warnings }
861 ) => {
862 if (errorDetails === "auto") {
863 if (warnings) {
864 const warnings = cachedGetWarnings(compilation);
865 object.filteredWarningDetailsCount = warnings
866 .map(e => typeof e !== "string" && e.details)
867 .filter(Boolean).length;
868 }
869 if (errors) {
870 const errors = cachedGetErrors(compilation);
871 if (errors.length >= 3) {
872 object.filteredErrorDetailsCount = errors
873 .map(e => typeof e !== "string" && e.details)
874 .filter(Boolean).length;
875 }
876 }
877 }
878 },
879 children: (object, compilation, context, options, factory) => {
880 const { type } = context;
881 object.children = factory.create(
882 `${type}.children`,
883 compilation.children,
884 context
885 );
886 }
887 },
888 asset: {
889 _: (object, asset, context, options, factory) => {
890 const { compilation } = context;
891 object.type = asset.type;
892 object.name = asset.name;
893 object.size = asset.source.size();
894 object.emitted = compilation.emittedAssets.has(asset.name);
895 object.comparedForEmit = compilation.comparedForEmitAssets.has(
896 asset.name
897 );
898 const cached = !object.emitted && !object.comparedForEmit;
899 object.cached = cached;
900 object.info = asset.info;
901 if (!cached || options.cachedAssets) {
902 Object.assign(
903 object,
904 factory.create(`${context.type}$visible`, asset, context)
905 );
906 }
907 }
908 },
909 asset$visible: {
910 _: (
911 object,
912 asset,
913 { compilation, compilationFileToChunks, compilationAuxiliaryFileToChunks }
914 ) => {
915 const chunks = compilationFileToChunks.get(asset.name) || [];
916 const auxiliaryChunks =
917 compilationAuxiliaryFileToChunks.get(asset.name) || [];
918 object.chunkNames = uniqueOrderedArray(
919 chunks,
920 c => (c.name ? [c.name] : []),
921 compareIds
922 );
923 object.chunkIdHints = uniqueOrderedArray(
924 chunks,
925 c => Array.from(c.idNameHints),
926 compareIds
927 );
928 object.auxiliaryChunkNames = uniqueOrderedArray(
929 auxiliaryChunks,
930 c => (c.name ? [c.name] : []),
931 compareIds
932 );
933 object.auxiliaryChunkIdHints = uniqueOrderedArray(
934 auxiliaryChunks,
935 c => Array.from(c.idNameHints),
936 compareIds
937 );
938 object.filteredRelated = asset.related ? asset.related.length : undefined;
939 },
940 relatedAssets: (object, asset, context, options, factory) => {
941 const { type } = context;
942 object.related = factory.create(
943 `${type.slice(0, -8)}.related`,
944 asset.related,
945 context
946 );
947 object.filteredRelated = asset.related
948 ? asset.related.length - object.related.length
949 : undefined;
950 },
951 ids: (
952 object,
953 asset,
954 { compilationFileToChunks, compilationAuxiliaryFileToChunks }
955 ) => {
956 const chunks = compilationFileToChunks.get(asset.name) || [];
957 const auxiliaryChunks =
958 compilationAuxiliaryFileToChunks.get(asset.name) || [];
959 object.chunks = uniqueOrderedArray(chunks, c => c.ids, compareIds);
960 object.auxiliaryChunks = uniqueOrderedArray(
961 auxiliaryChunks,
962 c => c.ids,
963 compareIds
964 );
965 },
966 performance: (object, asset) => {
967 object.isOverSizeLimit = SizeLimitsPlugin.isOverSizeLimit(asset.source);
968 }
969 },
970 chunkGroup: {
971 _: (
972 object,
973 { name, chunkGroup },
974 { compilation, compilation: { moduleGraph, chunkGraph } },
975 { ids, chunkGroupAuxiliary, chunkGroupChildren, chunkGroupMaxAssets }
976 ) => {
977 const children =
978 chunkGroupChildren &&
979 chunkGroup.getChildrenByOrders(moduleGraph, chunkGraph);
980 /**
981 * @param {string} name Name
982 * @returns {{ name: string, size: number }} Asset object
983 */
984 const toAsset = name => {
985 const asset = compilation.getAsset(name);
986 return {
987 name,
988 size: asset ? asset.info.size : -1
989 };
990 };
991 /** @type {(total: number, asset: { size: number }) => number} */
992 const sizeReducer = (total, { size }) => total + size;
993 const assets = uniqueArray(chunkGroup.chunks, c => c.files).map(toAsset);
994 const auxiliaryAssets = uniqueOrderedArray(
995 chunkGroup.chunks,
996 c => c.auxiliaryFiles,
997 compareIds
998 ).map(toAsset);
999 const assetsSize = assets.reduce(sizeReducer, 0);
1000 const auxiliaryAssetsSize = auxiliaryAssets.reduce(sizeReducer, 0);
1001 /** @type {KnownStatsChunkGroup} */
1002 const statsChunkGroup = {
1003 name,
1004 chunks: ids ? chunkGroup.chunks.map(c => c.id) : undefined,
1005 assets: assets.length <= chunkGroupMaxAssets ? assets : undefined,
1006 filteredAssets:
1007 assets.length <= chunkGroupMaxAssets ? 0 : assets.length,
1008 assetsSize,
1009 auxiliaryAssets:
1010 chunkGroupAuxiliary && auxiliaryAssets.length <= chunkGroupMaxAssets
1011 ? auxiliaryAssets
1012 : undefined,
1013 filteredAuxiliaryAssets:
1014 chunkGroupAuxiliary && auxiliaryAssets.length <= chunkGroupMaxAssets
1015 ? 0
1016 : auxiliaryAssets.length,
1017 auxiliaryAssetsSize,
1018 children: children
1019 ? mapObject(children, groups =>
1020 groups.map(group => {
1021 const assets = uniqueArray(group.chunks, c => c.files).map(
1022 toAsset
1023 );
1024 const auxiliaryAssets = uniqueOrderedArray(
1025 group.chunks,
1026 c => c.auxiliaryFiles,
1027 compareIds
1028 ).map(toAsset);
1029
1030 /** @type {KnownStatsChunkGroup} */
1031 const childStatsChunkGroup = {
1032 name: group.name,
1033 chunks: ids ? group.chunks.map(c => c.id) : undefined,
1034 assets:
1035 assets.length <= chunkGroupMaxAssets ? assets : undefined,
1036 filteredAssets:
1037 assets.length <= chunkGroupMaxAssets ? 0 : assets.length,
1038 auxiliaryAssets:
1039 chunkGroupAuxiliary &&
1040 auxiliaryAssets.length <= chunkGroupMaxAssets
1041 ? auxiliaryAssets
1042 : undefined,
1043 filteredAuxiliaryAssets:
1044 chunkGroupAuxiliary &&
1045 auxiliaryAssets.length <= chunkGroupMaxAssets
1046 ? 0
1047 : auxiliaryAssets.length
1048 };
1049
1050 return childStatsChunkGroup;
1051 })
1052 )
1053 : undefined,
1054 childAssets: children
1055 ? mapObject(children, groups => {
1056 /** @type {Set<string>} */
1057 const set = new Set();
1058 for (const group of groups) {
1059 for (const chunk of group.chunks) {
1060 for (const asset of chunk.files) {
1061 set.add(asset);
1062 }
1063 }
1064 }
1065 return Array.from(set);
1066 })
1067 : undefined
1068 };
1069 Object.assign(object, statsChunkGroup);
1070 },
1071 performance: (object, { chunkGroup }) => {
1072 object.isOverSizeLimit = SizeLimitsPlugin.isOverSizeLimit(chunkGroup);
1073 }
1074 },
1075 module: {
1076 _: (object, module, context, options, factory) => {
1077 const { compilation, type } = context;
1078 const built = compilation.builtModules.has(module);
1079 const codeGenerated = compilation.codeGeneratedModules.has(module);
1080 const buildTimeExecuted =
1081 compilation.buildTimeExecutedModules.has(module);
1082 /** @type {{[x: string]: number}} */
1083 const sizes = {};
1084 for (const sourceType of module.getSourceTypes()) {
1085 sizes[sourceType] = module.size(sourceType);
1086 }
1087 /** @type {KnownStatsModule} */
1088 const statsModule = {
1089 type: "module",
1090 moduleType: module.type,
1091 layer: module.layer,
1092 size: module.size(),
1093 sizes,
1094 built,
1095 codeGenerated,
1096 buildTimeExecuted,
1097 cached: !built && !codeGenerated
1098 };
1099 Object.assign(object, statsModule);
1100
1101 if (built || codeGenerated || options.cachedModules) {
1102 Object.assign(
1103 object,
1104 factory.create(`${type}$visible`, module, context)
1105 );
1106 }
1107 }
1108 },
1109 module$visible: {
1110 _: (object, module, context, { requestShortener }, factory) => {
1111 const { compilation, type, rootModules } = context;
1112 const { moduleGraph } = compilation;
1113 /** @type {Module[]} */
1114 const path = [];
1115 const issuer = moduleGraph.getIssuer(module);
1116 let current = issuer;
1117 while (current) {
1118 path.push(current);
1119 current = moduleGraph.getIssuer(current);
1120 }
1121 path.reverse();
1122 const profile = moduleGraph.getProfile(module);
1123 const errors = module.getErrors();
1124 const errorsCount = errors !== undefined ? countIterable(errors) : 0;
1125 const warnings = module.getWarnings();
1126 const warningsCount =
1127 warnings !== undefined ? countIterable(warnings) : 0;
1128 /** @type {{[x: string]: number}} */
1129 const sizes = {};
1130 for (const sourceType of module.getSourceTypes()) {
1131 sizes[sourceType] = module.size(sourceType);
1132 }
1133 /** @type {KnownStatsModule} */
1134 const statsModule = {
1135 identifier: module.identifier(),
1136 name: module.readableIdentifier(requestShortener),
1137 nameForCondition: module.nameForCondition(),
1138 index: moduleGraph.getPreOrderIndex(module),
1139 preOrderIndex: moduleGraph.getPreOrderIndex(module),
1140 index2: moduleGraph.getPostOrderIndex(module),
1141 postOrderIndex: moduleGraph.getPostOrderIndex(module),
1142 cacheable: module.buildInfo.cacheable,
1143 optional: module.isOptional(moduleGraph),
1144 orphan:
1145 !type.endsWith("module.modules[].module$visible") &&
1146 compilation.chunkGraph.getNumberOfModuleChunks(module) === 0,
1147 dependent: rootModules ? !rootModules.has(module) : undefined,
1148 issuer: issuer && issuer.identifier(),
1149 issuerName: issuer && issuer.readableIdentifier(requestShortener),
1150 issuerPath:
1151 issuer &&
1152 factory.create(`${type.slice(0, -8)}.issuerPath`, path, context),
1153 failed: errorsCount > 0,
1154 errors: errorsCount,
1155 warnings: warningsCount
1156 };
1157 Object.assign(object, statsModule);
1158 if (profile) {
1159 object.profile = factory.create(
1160 `${type.slice(0, -8)}.profile`,
1161 profile,
1162 context
1163 );
1164 }
1165 },
1166 ids: (object, module, { compilation: { chunkGraph, moduleGraph } }) => {
1167 object.id = chunkGraph.getModuleId(module);
1168 const issuer = moduleGraph.getIssuer(module);
1169 object.issuerId = issuer && chunkGraph.getModuleId(issuer);
1170 object.chunks = Array.from(
1171 chunkGraph.getOrderedModuleChunksIterable(module, compareChunksById),
1172 chunk => chunk.id
1173 );
1174 },
1175 moduleAssets: (object, module) => {
1176 object.assets = module.buildInfo.assets
1177 ? Object.keys(module.buildInfo.assets)
1178 : [];
1179 },
1180 reasons: (object, module, context, options, factory) => {
1181 const {
1182 type,
1183 compilation: { moduleGraph }
1184 } = context;
1185 const groupsReasons = factory.create(
1186 `${type.slice(0, -8)}.reasons`,
1187 Array.from(moduleGraph.getIncomingConnections(module)),
1188 context
1189 );
1190 const limited = spaceLimited(groupsReasons, options.reasonsSpace);
1191 object.reasons = limited.children;
1192 object.filteredReasons = limited.filteredChildren;
1193 },
1194 usedExports: (
1195 object,
1196 module,
1197 { runtime, compilation: { moduleGraph } }
1198 ) => {
1199 const usedExports = moduleGraph.getUsedExports(module, runtime);
1200 if (usedExports === null) {
1201 object.usedExports = null;
1202 } else if (typeof usedExports === "boolean") {
1203 object.usedExports = usedExports;
1204 } else {
1205 object.usedExports = Array.from(usedExports);
1206 }
1207 },
1208 providedExports: (object, module, { compilation: { moduleGraph } }) => {
1209 const providedExports = moduleGraph.getProvidedExports(module);
1210 object.providedExports = Array.isArray(providedExports)
1211 ? providedExports
1212 : null;
1213 },
1214 optimizationBailout: (
1215 object,
1216 module,
1217 { compilation: { moduleGraph } },
1218 { requestShortener }
1219 ) => {
1220 object.optimizationBailout = moduleGraph
1221 .getOptimizationBailout(module)
1222 .map(item => {
1223 if (typeof item === "function") return item(requestShortener);
1224 return item;
1225 });
1226 },
1227 depth: (object, module, { compilation: { moduleGraph } }) => {
1228 object.depth = moduleGraph.getDepth(module);
1229 },
1230 nestedModules: (object, module, context, options, factory) => {
1231 const { type } = context;
1232 const innerModules = /** @type {Module & { modules?: Module[] }} */ (
1233 module
1234 ).modules;
1235 if (Array.isArray(innerModules)) {
1236 const groupedModules = factory.create(
1237 `${type.slice(0, -8)}.modules`,
1238 innerModules,
1239 context
1240 );
1241 const limited = spaceLimited(
1242 groupedModules,
1243 options.nestedModulesSpace
1244 );
1245 object.modules = limited.children;
1246 object.filteredModules = limited.filteredChildren;
1247 }
1248 },
1249 source: (object, module) => {
1250 const originalSource = module.originalSource();
1251 if (originalSource) {
1252 object.source = originalSource.source();
1253 }
1254 }
1255 },
1256 profile: {
1257 _: (object, profile) => {
1258 /** @type {KnownStatsProfile} */
1259 const statsProfile = {
1260 total:
1261 profile.factory +
1262 profile.restoring +
1263 profile.integration +
1264 profile.building +
1265 profile.storing,
1266 resolving: profile.factory,
1267 restoring: profile.restoring,
1268 building: profile.building,
1269 integration: profile.integration,
1270 storing: profile.storing,
1271 additionalResolving: profile.additionalFactories,
1272 additionalIntegration: profile.additionalIntegration,
1273 // TODO remove this in webpack 6
1274 factory: profile.factory,
1275 // TODO remove this in webpack 6
1276 dependencies: profile.additionalFactories
1277 };
1278 Object.assign(object, statsProfile);
1279 }
1280 },
1281 moduleIssuer: {
1282 _: (object, module, context, { requestShortener }, factory) => {
1283 const { compilation, type } = context;
1284 const { moduleGraph } = compilation;
1285 const profile = moduleGraph.getProfile(module);
1286 /** @type {KnownStatsModuleIssuer} */
1287 const statsModuleIssuer = {
1288 identifier: module.identifier(),
1289 name: module.readableIdentifier(requestShortener)
1290 };
1291 Object.assign(object, statsModuleIssuer);
1292 if (profile) {
1293 object.profile = factory.create(`${type}.profile`, profile, context);
1294 }
1295 },
1296 ids: (object, module, { compilation: { chunkGraph } }) => {
1297 object.id = chunkGraph.getModuleId(module);
1298 }
1299 },
1300 moduleReason: {
1301 _: (object, reason, { runtime }, { requestShortener }) => {
1302 const dep = reason.dependency;
1303 const moduleDep =
1304 dep && dep instanceof ModuleDependency ? dep : undefined;
1305 /** @type {KnownStatsModuleReason} */
1306 const statsModuleReason = {
1307 moduleIdentifier: reason.originModule
1308 ? reason.originModule.identifier()
1309 : null,
1310 module: reason.originModule
1311 ? reason.originModule.readableIdentifier(requestShortener)
1312 : null,
1313 moduleName: reason.originModule
1314 ? reason.originModule.readableIdentifier(requestShortener)
1315 : null,
1316 resolvedModuleIdentifier: reason.resolvedOriginModule
1317 ? reason.resolvedOriginModule.identifier()
1318 : null,
1319 resolvedModule: reason.resolvedOriginModule
1320 ? reason.resolvedOriginModule.readableIdentifier(requestShortener)
1321 : null,
1322 type: reason.dependency ? reason.dependency.type : null,
1323 active: reason.isActive(runtime),
1324 explanation: reason.explanation,
1325 userRequest: (moduleDep && moduleDep.userRequest) || null
1326 };
1327 Object.assign(object, statsModuleReason);
1328 if (reason.dependency) {
1329 const locInfo = formatLocation(reason.dependency.loc);
1330 if (locInfo) {
1331 object.loc = locInfo;
1332 }
1333 }
1334 },
1335 ids: (object, reason, { compilation: { chunkGraph } }) => {
1336 object.moduleId = reason.originModule
1337 ? chunkGraph.getModuleId(reason.originModule)
1338 : null;
1339 object.resolvedModuleId = reason.resolvedOriginModule
1340 ? chunkGraph.getModuleId(reason.resolvedOriginModule)
1341 : null;
1342 }
1343 },
1344 chunk: {
1345 _: (object, chunk, { makePathsRelative, compilation: { chunkGraph } }) => {
1346 const childIdByOrder = chunk.getChildIdsByOrders(chunkGraph);
1347
1348 /** @type {KnownStatsChunk} */
1349 const statsChunk = {
1350 rendered: chunk.rendered,
1351 initial: chunk.canBeInitial(),
1352 entry: chunk.hasRuntime(),
1353 recorded: AggressiveSplittingPlugin.wasChunkRecorded(chunk),
1354 reason: chunk.chunkReason,
1355 size: chunkGraph.getChunkModulesSize(chunk),
1356 sizes: chunkGraph.getChunkModulesSizes(chunk),
1357 names: chunk.name ? [chunk.name] : [],
1358 idHints: Array.from(chunk.idNameHints),
1359 runtime:
1360 chunk.runtime === undefined
1361 ? undefined
1362 : typeof chunk.runtime === "string"
1363 ? [makePathsRelative(chunk.runtime)]
1364 : Array.from(chunk.runtime.sort(), makePathsRelative),
1365 files: Array.from(chunk.files),
1366 auxiliaryFiles: Array.from(chunk.auxiliaryFiles).sort(compareIds),
1367 hash: chunk.renderedHash,
1368 childrenByOrder: childIdByOrder
1369 };
1370 Object.assign(object, statsChunk);
1371 },
1372 ids: (object, chunk) => {
1373 object.id = chunk.id;
1374 },
1375 chunkRelations: (object, chunk, { compilation: { chunkGraph } }) => {
1376 /** @type {Set<string|number>} */
1377 const parents = new Set();
1378 /** @type {Set<string|number>} */
1379 const children = new Set();
1380 /** @type {Set<string|number>} */
1381 const siblings = new Set();
1382
1383 for (const chunkGroup of chunk.groupsIterable) {
1384 for (const parentGroup of chunkGroup.parentsIterable) {
1385 for (const chunk of parentGroup.chunks) {
1386 parents.add(chunk.id);
1387 }
1388 }
1389 for (const childGroup of chunkGroup.childrenIterable) {
1390 for (const chunk of childGroup.chunks) {
1391 children.add(chunk.id);
1392 }
1393 }
1394 for (const sibling of chunkGroup.chunks) {
1395 if (sibling !== chunk) siblings.add(sibling.id);
1396 }
1397 }
1398 object.siblings = Array.from(siblings).sort(compareIds);
1399 object.parents = Array.from(parents).sort(compareIds);
1400 object.children = Array.from(children).sort(compareIds);
1401 },
1402 chunkModules: (object, chunk, context, options, factory) => {
1403 const {
1404 type,
1405 compilation: { chunkGraph }
1406 } = context;
1407 const array = chunkGraph.getChunkModules(chunk);
1408 const groupedModules = factory.create(`${type}.modules`, array, {
1409 ...context,
1410 runtime: chunk.runtime,
1411 rootModules: new Set(chunkGraph.getChunkRootModules(chunk))
1412 });
1413 const limited = spaceLimited(groupedModules, options.chunkModulesSpace);
1414 object.modules = limited.children;
1415 object.filteredModules = limited.filteredChildren;
1416 },
1417 chunkOrigins: (object, chunk, context, options, factory) => {
1418 const {
1419 type,
1420 compilation: { chunkGraph }
1421 } = context;
1422 /** @type {Set<string>} */
1423 const originsKeySet = new Set();
1424 const origins = [];
1425 for (const g of chunk.groupsIterable) {
1426 origins.push(...g.origins);
1427 }
1428 const array = origins.filter(origin => {
1429 const key = [
1430 origin.module ? chunkGraph.getModuleId(origin.module) : undefined,
1431 formatLocation(origin.loc),
1432 origin.request
1433 ].join();
1434 if (originsKeySet.has(key)) return false;
1435 originsKeySet.add(key);
1436 return true;
1437 });
1438 object.origins = factory.create(`${type}.origins`, array, context);
1439 }
1440 },
1441 chunkOrigin: {
1442 _: (object, origin, context, { requestShortener }) => {
1443 /** @type {KnownStatsChunkOrigin} */
1444 const statsChunkOrigin = {
1445 module: origin.module ? origin.module.identifier() : "",
1446 moduleIdentifier: origin.module ? origin.module.identifier() : "",
1447 moduleName: origin.module
1448 ? origin.module.readableIdentifier(requestShortener)
1449 : "",
1450 loc: formatLocation(origin.loc),
1451 request: origin.request
1452 };
1453 Object.assign(object, statsChunkOrigin);
1454 },
1455 ids: (object, origin, { compilation: { chunkGraph } }) => {
1456 object.moduleId = origin.module
1457 ? chunkGraph.getModuleId(origin.module)
1458 : undefined;
1459 }
1460 },
1461 error: EXTRACT_ERROR,
1462 warning: EXTRACT_ERROR,
1463 moduleTraceItem: {
1464 _: (object, { origin, module }, context, { requestShortener }, factory) => {
1465 const {
1466 type,
1467 compilation: { moduleGraph }
1468 } = context;
1469 object.originIdentifier = origin.identifier();
1470 object.originName = origin.readableIdentifier(requestShortener);
1471 object.moduleIdentifier = module.identifier();
1472 object.moduleName = module.readableIdentifier(requestShortener);
1473 const dependencies = Array.from(
1474 moduleGraph.getIncomingConnections(module)
1475 )
1476 .filter(c => c.resolvedOriginModule === origin && c.dependency)
1477 .map(c => c.dependency);
1478 object.dependencies = factory.create(
1479 `${type}.dependencies`,
1480 Array.from(new Set(dependencies)),
1481 context
1482 );
1483 },
1484 ids: (object, { origin, module }, { compilation: { chunkGraph } }) => {
1485 object.originId = chunkGraph.getModuleId(origin);
1486 object.moduleId = chunkGraph.getModuleId(module);
1487 }
1488 },
1489 moduleTraceDependency: {
1490 _: (object, dependency) => {
1491 object.loc = formatLocation(dependency.loc);
1492 }
1493 }
1494};
1495
1496/** @type {Record<string, Record<string, (thing: any, context: StatsFactoryContext, options: NormalizedStatsOptions) => boolean | undefined>>} */
1497const FILTER = {
1498 "module.reasons": {
1499 "!orphanModules": (reason, { compilation: { chunkGraph } }) => {
1500 if (
1501 reason.originModule &&
1502 chunkGraph.getNumberOfModuleChunks(reason.originModule) === 0
1503 ) {
1504 return false;
1505 }
1506 }
1507 }
1508};
1509
1510/** @type {Record<string, Record<string, (thing: Object, context: StatsFactoryContext, options: NormalizedStatsOptions) => boolean | undefined>>} */
1511const FILTER_RESULTS = {
1512 "compilation.warnings": {
1513 warningsFilter: util.deprecate(
1514 (warning, context, { warningsFilter }) => {
1515 const warningString = Object.keys(warning)
1516 .map(key => `${warning[key]}`)
1517 .join("\n");
1518 return !warningsFilter.some(filter => filter(warning, warningString));
1519 },
1520 "config.stats.warningsFilter is deprecated in favor of config.ignoreWarnings",
1521 "DEP_WEBPACK_STATS_WARNINGS_FILTER"
1522 )
1523 }
1524};
1525
1526/** @type {Record<string, (comparators: Function[], context: StatsFactoryContext) => void>} */
1527const MODULES_SORTER = {
1528 _: (comparators, { compilation: { moduleGraph } }) => {
1529 comparators.push(
1530 compareSelect(
1531 /**
1532 * @param {Module} m module
1533 * @returns {number} depth
1534 */
1535 m => moduleGraph.getDepth(m),
1536 compareNumbers
1537 ),
1538 compareSelect(
1539 /**
1540 * @param {Module} m module
1541 * @returns {number} index
1542 */
1543 m => moduleGraph.getPreOrderIndex(m),
1544 compareNumbers
1545 ),
1546 compareSelect(
1547 /**
1548 * @param {Module} m module
1549 * @returns {string} identifier
1550 */
1551 m => m.identifier(),
1552 compareIds
1553 )
1554 );
1555 }
1556};
1557
1558/** @type {Record<string, Record<string, (comparators: Function[], context: StatsFactoryContext) => void>>} */
1559const SORTERS = {
1560 "compilation.chunks": {
1561 _: comparators => {
1562 comparators.push(compareSelect(c => c.id, compareIds));
1563 }
1564 },
1565 "compilation.modules": MODULES_SORTER,
1566 "chunk.rootModules": MODULES_SORTER,
1567 "chunk.modules": MODULES_SORTER,
1568 "module.modules": MODULES_SORTER,
1569 "module.reasons": {
1570 _: (comparators, { compilation: { chunkGraph } }) => {
1571 comparators.push(
1572 compareSelect(x => x.originModule, compareModulesByIdentifier)
1573 );
1574 comparators.push(
1575 compareSelect(x => x.resolvedOriginModule, compareModulesByIdentifier)
1576 );
1577 comparators.push(
1578 compareSelect(
1579 x => x.dependency,
1580 concatComparators(
1581 compareSelect(
1582 /**
1583 * @param {Dependency} x dependency
1584 * @returns {DependencyLocation} location
1585 */
1586 x => x.loc,
1587 compareLocations
1588 ),
1589 compareSelect(x => x.type, compareIds)
1590 )
1591 )
1592 );
1593 }
1594 },
1595 "chunk.origins": {
1596 _: (comparators, { compilation: { chunkGraph } }) => {
1597 comparators.push(
1598 compareSelect(
1599 origin =>
1600 origin.module ? chunkGraph.getModuleId(origin.module) : undefined,
1601 compareIds
1602 ),
1603 compareSelect(origin => formatLocation(origin.loc), compareIds),
1604 compareSelect(origin => origin.request, compareIds)
1605 );
1606 }
1607 }
1608};
1609
1610const getItemSize = item => {
1611 // Each item takes 1 line
1612 // + the size of the children
1613 // + 1 extra line when it has children and filteredChildren
1614 return !item.children
1615 ? 1
1616 : item.filteredChildren
1617 ? 2 + getTotalSize(item.children)
1618 : 1 + getTotalSize(item.children);
1619};
1620
1621const getTotalSize = children => {
1622 let size = 0;
1623 for (const child of children) {
1624 size += getItemSize(child);
1625 }
1626 return size;
1627};
1628
1629const getTotalItems = children => {
1630 let count = 0;
1631 for (const child of children) {
1632 if (!child.children && !child.filteredChildren) {
1633 count++;
1634 } else {
1635 if (child.children) count += getTotalItems(child.children);
1636 if (child.filteredChildren) count += child.filteredChildren;
1637 }
1638 }
1639 return count;
1640};
1641
1642const collapse = children => {
1643 // After collapse each child must take exactly one line
1644 const newChildren = [];
1645 for (const child of children) {
1646 if (child.children) {
1647 let filteredChildren = child.filteredChildren || 0;
1648 filteredChildren += getTotalItems(child.children);
1649 newChildren.push({
1650 ...child,
1651 children: undefined,
1652 filteredChildren
1653 });
1654 } else {
1655 newChildren.push(child);
1656 }
1657 }
1658 return newChildren;
1659};
1660
1661const spaceLimited = (
1662 itemsAndGroups,
1663 max,
1664 filteredChildrenLineReserved = false
1665) => {
1666 if (max < 1) {
1667 return {
1668 children: undefined,
1669 filteredChildren: getTotalItems(itemsAndGroups)
1670 };
1671 }
1672 /** @type {any[] | undefined} */
1673 let children = undefined;
1674 /** @type {number | undefined} */
1675 let filteredChildren = undefined;
1676 // This are the groups, which take 1+ lines each
1677 const groups = [];
1678 // The sizes of the groups are stored in groupSizes
1679 const groupSizes = [];
1680 // This are the items, which take 1 line each
1681 const items = [];
1682 // The total of group sizes
1683 let groupsSize = 0;
1684
1685 for (const itemOrGroup of itemsAndGroups) {
1686 // is item
1687 if (!itemOrGroup.children && !itemOrGroup.filteredChildren) {
1688 items.push(itemOrGroup);
1689 } else {
1690 groups.push(itemOrGroup);
1691 const size = getItemSize(itemOrGroup);
1692 groupSizes.push(size);
1693 groupsSize += size;
1694 }
1695 }
1696
1697 if (groupsSize + items.length <= max) {
1698 // The total size in the current state fits into the max
1699 // keep all
1700 children = groups.length > 0 ? groups.concat(items) : items;
1701 } else if (groups.length === 0) {
1702 // slice items to max
1703 // inner space marks that lines for filteredChildren already reserved
1704 const limit = max - (filteredChildrenLineReserved ? 0 : 1);
1705 filteredChildren = items.length - limit;
1706 items.length = limit;
1707 children = items;
1708 } else {
1709 // limit is the size when all groups are collapsed
1710 const limit =
1711 groups.length +
1712 (filteredChildrenLineReserved || items.length === 0 ? 0 : 1);
1713 if (limit < max) {
1714 // calculate how much we are over the size limit
1715 // this allows to approach the limit faster
1716 let oversize;
1717 // If each group would take 1 line the total would be below the maximum
1718 // collapse some groups, keep items
1719 while (
1720 (oversize =
1721 groupsSize +
1722 items.length +
1723 (filteredChildren && !filteredChildrenLineReserved ? 1 : 0) -
1724 max) > 0
1725 ) {
1726 // Find the maximum group and process only this one
1727 const maxGroupSize = Math.max(...groupSizes);
1728 if (maxGroupSize < items.length) {
1729 filteredChildren = items.length;
1730 items.length = 0;
1731 continue;
1732 }
1733 for (let i = 0; i < groups.length; i++) {
1734 if (groupSizes[i] === maxGroupSize) {
1735 const group = groups[i];
1736 // run this algorithm recursively and limit the size of the children to
1737 // current size - oversize / number of groups
1738 // So it should always end up being smaller
1739 const headerSize = group.filteredChildren ? 2 : 1;
1740 const limited = spaceLimited(
1741 group.children,
1742 maxGroupSize -
1743 // we should use ceil to always feet in max
1744 Math.ceil(oversize / groups.length) -
1745 // we substitute size of group head
1746 headerSize,
1747 headerSize === 2
1748 );
1749 groups[i] = {
1750 ...group,
1751 children: limited.children,
1752 filteredChildren: limited.filteredChildren
1753 ? (group.filteredChildren || 0) + limited.filteredChildren
1754 : group.filteredChildren
1755 };
1756 const newSize = getItemSize(groups[i]);
1757 groupsSize -= maxGroupSize - newSize;
1758 groupSizes[i] = newSize;
1759 break;
1760 }
1761 }
1762 }
1763 children = groups.concat(items);
1764 } else if (limit === max) {
1765 // If we have only enough space to show one line per group and one line for the filtered items
1766 // collapse all groups and items
1767 children = collapse(groups);
1768 filteredChildren = items.length;
1769 } else {
1770 // If we have no space
1771 // collapse complete group
1772 filteredChildren = getTotalItems(itemsAndGroups);
1773 }
1774 }
1775
1776 return {
1777 children,
1778 filteredChildren
1779 };
1780};
1781
1782const assetGroup = (children, assets) => {
1783 let size = 0;
1784 for (const asset of children) {
1785 size += asset.size;
1786 }
1787 return {
1788 size
1789 };
1790};
1791
1792const moduleGroup = (children, modules) => {
1793 let size = 0;
1794 const sizes = {};
1795 for (const module of children) {
1796 size += module.size;
1797 for (const key of Object.keys(module.sizes)) {
1798 sizes[key] = (sizes[key] || 0) + module.sizes[key];
1799 }
1800 }
1801 return {
1802 size,
1803 sizes
1804 };
1805};
1806
1807const reasonGroup = (children, reasons) => {
1808 let active = false;
1809 for (const reason of children) {
1810 active = active || reason.active;
1811 }
1812 return {
1813 active
1814 };
1815};
1816
1817const GROUP_EXTENSION_REGEXP = /(\.[^.]+?)(?:\?|(?: \+ \d+ modules?)?$)/;
1818const GROUP_PATH_REGEXP = /(.+)[/\\][^/\\]+?(?:\?|(?: \+ \d+ modules?)?$)/;
1819
1820/** @type {Record<string, (groupConfigs: GroupConfig[], context: StatsFactoryContext, options: NormalizedStatsOptions) => void>} */
1821const ASSETS_GROUPERS = {
1822 _: (groupConfigs, context, options) => {
1823 const groupByFlag = (name, exclude) => {
1824 groupConfigs.push({
1825 getKeys: asset => {
1826 return asset[name] ? ["1"] : undefined;
1827 },
1828 getOptions: () => {
1829 return {
1830 groupChildren: !exclude,
1831 force: exclude
1832 };
1833 },
1834 createGroup: (key, children, assets) => {
1835 return exclude
1836 ? {
1837 type: "assets by status",
1838 [name]: !!key,
1839 filteredChildren: assets.length,
1840 ...assetGroup(children, assets)
1841 }
1842 : {
1843 type: "assets by status",
1844 [name]: !!key,
1845 children,
1846 ...assetGroup(children, assets)
1847 };
1848 }
1849 });
1850 };
1851 const {
1852 groupAssetsByEmitStatus,
1853 groupAssetsByPath,
1854 groupAssetsByExtension
1855 } = options;
1856 if (groupAssetsByEmitStatus) {
1857 groupByFlag("emitted");
1858 groupByFlag("comparedForEmit");
1859 groupByFlag("isOverSizeLimit");
1860 }
1861 if (groupAssetsByEmitStatus || !options.cachedAssets) {
1862 groupByFlag("cached", !options.cachedAssets);
1863 }
1864 if (groupAssetsByPath || groupAssetsByExtension) {
1865 groupConfigs.push({
1866 getKeys: asset => {
1867 const extensionMatch =
1868 groupAssetsByExtension && GROUP_EXTENSION_REGEXP.exec(asset.name);
1869 const extension = extensionMatch ? extensionMatch[1] : "";
1870 const pathMatch =
1871 groupAssetsByPath && GROUP_PATH_REGEXP.exec(asset.name);
1872 const path = pathMatch ? pathMatch[1].split(/[/\\]/) : [];
1873 const keys = [];
1874 if (groupAssetsByPath) {
1875 keys.push(".");
1876 if (extension)
1877 keys.push(
1878 path.length
1879 ? `${path.join("/")}/*${extension}`
1880 : `*${extension}`
1881 );
1882 while (path.length > 0) {
1883 keys.push(path.join("/") + "/");
1884 path.pop();
1885 }
1886 } else {
1887 if (extension) keys.push(`*${extension}`);
1888 }
1889 return keys;
1890 },
1891 createGroup: (key, children, assets) => {
1892 return {
1893 type: groupAssetsByPath ? "assets by path" : "assets by extension",
1894 name: key,
1895 children,
1896 ...assetGroup(children, assets)
1897 };
1898 }
1899 });
1900 }
1901 },
1902 groupAssetsByInfo: (groupConfigs, context, options) => {
1903 const groupByAssetInfoFlag = name => {
1904 groupConfigs.push({
1905 getKeys: asset => {
1906 return asset.info && asset.info[name] ? ["1"] : undefined;
1907 },
1908 createGroup: (key, children, assets) => {
1909 return {
1910 type: "assets by info",
1911 info: {
1912 [name]: !!key
1913 },
1914 children,
1915 ...assetGroup(children, assets)
1916 };
1917 }
1918 });
1919 };
1920 groupByAssetInfoFlag("immutable");
1921 groupByAssetInfoFlag("development");
1922 groupByAssetInfoFlag("hotModuleReplacement");
1923 },
1924 groupAssetsByChunk: (groupConfigs, context, options) => {
1925 const groupByNames = name => {
1926 groupConfigs.push({
1927 getKeys: asset => {
1928 return asset[name];
1929 },
1930 createGroup: (key, children, assets) => {
1931 return {
1932 type: "assets by chunk",
1933 [name]: [key],
1934 children,
1935 ...assetGroup(children, assets)
1936 };
1937 }
1938 });
1939 };
1940 groupByNames("chunkNames");
1941 groupByNames("auxiliaryChunkNames");
1942 groupByNames("chunkIdHints");
1943 groupByNames("auxiliaryChunkIdHints");
1944 },
1945 excludeAssets: (groupConfigs, context, { excludeAssets }) => {
1946 groupConfigs.push({
1947 getKeys: asset => {
1948 const ident = asset.name;
1949 const excluded = excludeAssets.some(fn => fn(ident, asset));
1950 if (excluded) return ["excluded"];
1951 },
1952 getOptions: () => ({
1953 groupChildren: false,
1954 force: true
1955 }),
1956 createGroup: (key, children, assets) => ({
1957 type: "hidden assets",
1958 filteredChildren: assets.length,
1959 ...assetGroup(children, assets)
1960 })
1961 });
1962 }
1963};
1964
1965/** @type {function("module" | "chunk" | "root-of-chunk" | "nested"): Record<string, (groupConfigs: GroupConfig[], context: StatsFactoryContext, options: NormalizedStatsOptions) => void>} */
1966const MODULES_GROUPERS = type => ({
1967 _: (groupConfigs, context, options) => {
1968 const groupByFlag = (name, type, exclude) => {
1969 groupConfigs.push({
1970 getKeys: module => {
1971 return module[name] ? ["1"] : undefined;
1972 },
1973 getOptions: () => {
1974 return {
1975 groupChildren: !exclude,
1976 force: exclude
1977 };
1978 },
1979 createGroup: (key, children, modules) => {
1980 return {
1981 type,
1982 [name]: !!key,
1983 ...(exclude ? { filteredChildren: modules.length } : { children }),
1984 ...moduleGroup(children, modules)
1985 };
1986 }
1987 });
1988 };
1989 const {
1990 groupModulesByCacheStatus,
1991 groupModulesByLayer,
1992 groupModulesByAttributes,
1993 groupModulesByType,
1994 groupModulesByPath,
1995 groupModulesByExtension
1996 } = options;
1997 if (groupModulesByAttributes) {
1998 groupByFlag("errors", "modules with errors");
1999 groupByFlag("warnings", "modules with warnings");
2000 groupByFlag("assets", "modules with assets");
2001 groupByFlag("optional", "optional modules");
2002 }
2003 if (groupModulesByCacheStatus) {
2004 groupByFlag("cacheable", "cacheable modules");
2005 groupByFlag("built", "built modules");
2006 groupByFlag("codeGenerated", "code generated modules");
2007 }
2008 if (groupModulesByCacheStatus || !options.cachedModules) {
2009 groupByFlag("cached", "cached modules", !options.cachedModules);
2010 }
2011 if (groupModulesByAttributes || !options.orphanModules) {
2012 groupByFlag("orphan", "orphan modules", !options.orphanModules);
2013 }
2014 if (groupModulesByAttributes || !options.dependentModules) {
2015 groupByFlag("dependent", "dependent modules", !options.dependentModules);
2016 }
2017 if (groupModulesByType || !options.runtimeModules) {
2018 groupConfigs.push({
2019 getKeys: module => {
2020 if (!module.moduleType) return;
2021 if (groupModulesByType) {
2022 return [module.moduleType.split("/", 1)[0]];
2023 } else if (module.moduleType === "runtime") {
2024 return ["runtime"];
2025 }
2026 },
2027 getOptions: key => {
2028 const exclude = key === "runtime" && !options.runtimeModules;
2029 return {
2030 groupChildren: !exclude,
2031 force: exclude
2032 };
2033 },
2034 createGroup: (key, children, modules) => {
2035 const exclude = key === "runtime" && !options.runtimeModules;
2036 return {
2037 type: `${key} modules`,
2038 moduleType: key,
2039 ...(exclude ? { filteredChildren: modules.length } : { children }),
2040 ...moduleGroup(children, modules)
2041 };
2042 }
2043 });
2044 }
2045 if (groupModulesByLayer) {
2046 groupConfigs.push({
2047 getKeys: module => {
2048 return [module.layer];
2049 },
2050 createGroup: (key, children, modules) => {
2051 return {
2052 type: "modules by layer",
2053 layer: key,
2054 children,
2055 ...moduleGroup(children, modules)
2056 };
2057 }
2058 });
2059 }
2060 if (groupModulesByPath || groupModulesByExtension) {
2061 groupConfigs.push({
2062 getKeys: module => {
2063 if (!module.name) return;
2064 const resource = parseResource(module.name.split("!").pop()).path;
2065 const dataUrl = /^data:[^,;]+/.exec(resource);
2066 if (dataUrl) return [dataUrl[0]];
2067 const extensionMatch =
2068 groupModulesByExtension && GROUP_EXTENSION_REGEXP.exec(resource);
2069 const extension = extensionMatch ? extensionMatch[1] : "";
2070 const pathMatch =
2071 groupModulesByPath && GROUP_PATH_REGEXP.exec(resource);
2072 const path = pathMatch ? pathMatch[1].split(/[/\\]/) : [];
2073 const keys = [];
2074 if (groupModulesByPath) {
2075 if (extension)
2076 keys.push(
2077 path.length
2078 ? `${path.join("/")}/*${extension}`
2079 : `*${extension}`
2080 );
2081 while (path.length > 0) {
2082 keys.push(path.join("/") + "/");
2083 path.pop();
2084 }
2085 } else {
2086 if (extension) keys.push(`*${extension}`);
2087 }
2088 return keys;
2089 },
2090 createGroup: (key, children, modules) => {
2091 const isDataUrl = key.startsWith("data:");
2092 return {
2093 type: isDataUrl
2094 ? "modules by mime type"
2095 : groupModulesByPath
2096 ? "modules by path"
2097 : "modules by extension",
2098 name: isDataUrl ? key.slice(/* 'data:'.length */ 5) : key,
2099 children,
2100 ...moduleGroup(children, modules)
2101 };
2102 }
2103 });
2104 }
2105 },
2106 excludeModules: (groupConfigs, context, { excludeModules }) => {
2107 groupConfigs.push({
2108 getKeys: module => {
2109 const name = module.name;
2110 if (name) {
2111 const excluded = excludeModules.some(fn => fn(name, module, type));
2112 if (excluded) return ["1"];
2113 }
2114 },
2115 getOptions: () => ({
2116 groupChildren: false,
2117 force: true
2118 }),
2119 createGroup: (key, children, modules) => ({
2120 type: "hidden modules",
2121 filteredChildren: children.length,
2122 ...moduleGroup(children, modules)
2123 })
2124 });
2125 }
2126});
2127
2128/** @type {Record<string, Record<string, (groupConfigs: GroupConfig[], context: StatsFactoryContext, options: NormalizedStatsOptions) => void>>} */
2129const RESULT_GROUPERS = {
2130 "compilation.assets": ASSETS_GROUPERS,
2131 "asset.related": ASSETS_GROUPERS,
2132 "compilation.modules": MODULES_GROUPERS("module"),
2133 "chunk.modules": MODULES_GROUPERS("chunk"),
2134 "chunk.rootModules": MODULES_GROUPERS("root-of-chunk"),
2135 "module.modules": MODULES_GROUPERS("nested"),
2136 "module.reasons": {
2137 groupReasonsByOrigin: groupConfigs => {
2138 groupConfigs.push({
2139 getKeys: reason => {
2140 return [reason.module];
2141 },
2142 createGroup: (key, children, reasons) => {
2143 return {
2144 type: "from origin",
2145 module: key,
2146 children,
2147 ...reasonGroup(children, reasons)
2148 };
2149 }
2150 });
2151 }
2152 }
2153};
2154
2155// remove a prefixed "!" that can be specified to reverse sort order
2156const normalizeFieldKey = field => {
2157 if (field[0] === "!") {
2158 return field.slice(1);
2159 }
2160 return field;
2161};
2162
2163// if a field is prefixed by a "!" reverse sort order
2164const sortOrderRegular = field => {
2165 if (field[0] === "!") {
2166 return false;
2167 }
2168 return true;
2169};
2170
2171/**
2172 * @param {string} field field name
2173 * @returns {function(Object, Object): number} comparators
2174 */
2175const sortByField = field => {
2176 if (!field) {
2177 /**
2178 * @param {any} a first
2179 * @param {any} b second
2180 * @returns {-1|0|1} zero
2181 */
2182 const noSort = (a, b) => 0;
2183 return noSort;
2184 }
2185
2186 const fieldKey = normalizeFieldKey(field);
2187
2188 let sortFn = compareSelect(m => m[fieldKey], compareIds);
2189
2190 // if a field is prefixed with a "!" the sort is reversed!
2191 const sortIsRegular = sortOrderRegular(field);
2192
2193 if (!sortIsRegular) {
2194 const oldSortFn = sortFn;
2195 sortFn = (a, b) => oldSortFn(b, a);
2196 }
2197
2198 return sortFn;
2199};
2200
2201const ASSET_SORTERS = {
2202 /** @type {(comparators: Function[], context: StatsFactoryContext, options: NormalizedStatsOptions) => void} */
2203 assetsSort: (comparators, context, { assetsSort }) => {
2204 comparators.push(sortByField(assetsSort));
2205 },
2206 _: comparators => {
2207 comparators.push(compareSelect(a => a.name, compareIds));
2208 }
2209};
2210
2211/** @type {Record<string, Record<string, (comparators: Function[], context: StatsFactoryContext, options: NormalizedStatsOptions) => void>>} */
2212const RESULT_SORTERS = {
2213 "compilation.chunks": {
2214 chunksSort: (comparators, context, { chunksSort }) => {
2215 comparators.push(sortByField(chunksSort));
2216 }
2217 },
2218 "compilation.modules": {
2219 modulesSort: (comparators, context, { modulesSort }) => {
2220 comparators.push(sortByField(modulesSort));
2221 }
2222 },
2223 "chunk.modules": {
2224 chunkModulesSort: (comparators, context, { chunkModulesSort }) => {
2225 comparators.push(sortByField(chunkModulesSort));
2226 }
2227 },
2228 "module.modules": {
2229 nestedModulesSort: (comparators, context, { nestedModulesSort }) => {
2230 comparators.push(sortByField(nestedModulesSort));
2231 }
2232 },
2233 "compilation.assets": ASSET_SORTERS,
2234 "asset.related": ASSET_SORTERS
2235};
2236
2237/**
2238 * @param {Record<string, Record<string, Function>>} config the config see above
2239 * @param {NormalizedStatsOptions} options stats options
2240 * @param {function(string, Function): void} fn handler function called for every active line in config
2241 * @returns {void}
2242 */
2243const iterateConfig = (config, options, fn) => {
2244 for (const hookFor of Object.keys(config)) {
2245 const subConfig = config[hookFor];
2246 for (const option of Object.keys(subConfig)) {
2247 if (option !== "_") {
2248 if (option.startsWith("!")) {
2249 if (options[option.slice(1)]) continue;
2250 } else {
2251 const value = options[option];
2252 if (
2253 value === false ||
2254 value === undefined ||
2255 (Array.isArray(value) && value.length === 0)
2256 )
2257 continue;
2258 }
2259 }
2260 fn(hookFor, subConfig[option]);
2261 }
2262 }
2263};
2264
2265/** @type {Record<string, string>} */
2266const ITEM_NAMES = {
2267 "compilation.children[]": "compilation",
2268 "compilation.modules[]": "module",
2269 "compilation.entrypoints[]": "chunkGroup",
2270 "compilation.namedChunkGroups[]": "chunkGroup",
2271 "compilation.errors[]": "error",
2272 "compilation.warnings[]": "warning",
2273 "chunk.modules[]": "module",
2274 "chunk.rootModules[]": "module",
2275 "chunk.origins[]": "chunkOrigin",
2276 "compilation.chunks[]": "chunk",
2277 "compilation.assets[]": "asset",
2278 "asset.related[]": "asset",
2279 "module.issuerPath[]": "moduleIssuer",
2280 "module.reasons[]": "moduleReason",
2281 "module.modules[]": "module",
2282 "module.children[]": "module",
2283 "moduleTrace[]": "moduleTraceItem",
2284 "moduleTraceItem.dependencies[]": "moduleTraceDependency"
2285};
2286
2287/**
2288 * @param {Object[]} items items to be merged
2289 * @returns {Object} an object
2290 */
2291const mergeToObject = items => {
2292 const obj = Object.create(null);
2293 for (const item of items) {
2294 obj[item.name] = item;
2295 }
2296 return obj;
2297};
2298
2299/** @type {Record<string, (items: Object[]) => any>} */
2300const MERGER = {
2301 "compilation.entrypoints": mergeToObject,
2302 "compilation.namedChunkGroups": mergeToObject
2303};
2304
2305class DefaultStatsFactoryPlugin {
2306 /**
2307 * Apply the plugin
2308 * @param {Compiler} compiler the compiler instance
2309 * @returns {void}
2310 */
2311 apply(compiler) {
2312 compiler.hooks.compilation.tap("DefaultStatsFactoryPlugin", compilation => {
2313 compilation.hooks.statsFactory.tap(
2314 "DefaultStatsFactoryPlugin",
2315 (stats, options, context) => {
2316 iterateConfig(SIMPLE_EXTRACTORS, options, (hookFor, fn) => {
2317 stats.hooks.extract
2318 .for(hookFor)
2319 .tap("DefaultStatsFactoryPlugin", (obj, data, ctx) =>
2320 fn(obj, data, ctx, options, stats)
2321 );
2322 });
2323 iterateConfig(FILTER, options, (hookFor, fn) => {
2324 stats.hooks.filter
2325 .for(hookFor)
2326 .tap("DefaultStatsFactoryPlugin", (item, ctx, idx, i) =>
2327 fn(item, ctx, options, idx, i)
2328 );
2329 });
2330 iterateConfig(FILTER_RESULTS, options, (hookFor, fn) => {
2331 stats.hooks.filterResults
2332 .for(hookFor)
2333 .tap("DefaultStatsFactoryPlugin", (item, ctx, idx, i) =>
2334 fn(item, ctx, options, idx, i)
2335 );
2336 });
2337 iterateConfig(SORTERS, options, (hookFor, fn) => {
2338 stats.hooks.sort
2339 .for(hookFor)
2340 .tap("DefaultStatsFactoryPlugin", (comparators, ctx) =>
2341 fn(comparators, ctx, options)
2342 );
2343 });
2344 iterateConfig(RESULT_SORTERS, options, (hookFor, fn) => {
2345 stats.hooks.sortResults
2346 .for(hookFor)
2347 .tap("DefaultStatsFactoryPlugin", (comparators, ctx) =>
2348 fn(comparators, ctx, options)
2349 );
2350 });
2351 iterateConfig(RESULT_GROUPERS, options, (hookFor, fn) => {
2352 stats.hooks.groupResults
2353 .for(hookFor)
2354 .tap("DefaultStatsFactoryPlugin", (groupConfigs, ctx) =>
2355 fn(groupConfigs, ctx, options)
2356 );
2357 });
2358 for (const key of Object.keys(ITEM_NAMES)) {
2359 const itemName = ITEM_NAMES[key];
2360 stats.hooks.getItemName
2361 .for(key)
2362 .tap("DefaultStatsFactoryPlugin", () => itemName);
2363 }
2364 for (const key of Object.keys(MERGER)) {
2365 const merger = MERGER[key];
2366 stats.hooks.merge.for(key).tap("DefaultStatsFactoryPlugin", merger);
2367 }
2368 if (options.children) {
2369 if (Array.isArray(options.children)) {
2370 stats.hooks.getItemFactory
2371 .for("compilation.children[].compilation")
2372 .tap("DefaultStatsFactoryPlugin", (comp, { _index: idx }) => {
2373 if (idx < options.children.length) {
2374 return compilation.createStatsFactory(
2375 compilation.createStatsOptions(
2376 options.children[idx],
2377 context
2378 )
2379 );
2380 }
2381 });
2382 } else if (options.children !== true) {
2383 const childFactory = compilation.createStatsFactory(
2384 compilation.createStatsOptions(options.children, context)
2385 );
2386 stats.hooks.getItemFactory
2387 .for("compilation.children[].compilation")
2388 .tap("DefaultStatsFactoryPlugin", () => {
2389 return childFactory;
2390 });
2391 }
2392 }
2393 }
2394 );
2395 });
2396 }
2397}
2398module.exports = DefaultStatsFactoryPlugin;