DefaultStatsFactoryPlugin.js 80 KB

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