You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

2514 lines
71 KiB

  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const asyncLib = require("neo-async");
  7. const util = require("util");
  8. const { CachedSource } = require("webpack-sources");
  9. const {
  10. Tapable,
  11. SyncHook,
  12. SyncBailHook,
  13. SyncWaterfallHook,
  14. AsyncSeriesHook
  15. } = require("tapable");
  16. const EntryModuleNotFoundError = require("./EntryModuleNotFoundError");
  17. const ModuleNotFoundError = require("./ModuleNotFoundError");
  18. const ModuleDependencyWarning = require("./ModuleDependencyWarning");
  19. const ModuleDependencyError = require("./ModuleDependencyError");
  20. const ChunkGroup = require("./ChunkGroup");
  21. const Chunk = require("./Chunk");
  22. const Entrypoint = require("./Entrypoint");
  23. const MainTemplate = require("./MainTemplate");
  24. const ChunkTemplate = require("./ChunkTemplate");
  25. const HotUpdateChunkTemplate = require("./HotUpdateChunkTemplate");
  26. const ModuleTemplate = require("./ModuleTemplate");
  27. const RuntimeTemplate = require("./RuntimeTemplate");
  28. const ChunkRenderError = require("./ChunkRenderError");
  29. const AsyncDependencyToInitialChunkError = require("./AsyncDependencyToInitialChunkError");
  30. const Stats = require("./Stats");
  31. const Semaphore = require("./util/Semaphore");
  32. const createHash = require("./util/createHash");
  33. const Queue = require("./util/Queue");
  34. const SortableSet = require("./util/SortableSet");
  35. const GraphHelpers = require("./GraphHelpers");
  36. const ModuleDependency = require("./dependencies/ModuleDependency");
  37. const compareLocations = require("./compareLocations");
  38. /** @typedef {import("./Module")} Module */
  39. /** @typedef {import("./Compiler")} Compiler */
  40. /** @typedef {import("webpack-sources").Source} Source */
  41. /** @typedef {import("./WebpackError")} WebpackError */
  42. /** @typedef {import("./DependenciesBlockVariable")} DependenciesBlockVariable */
  43. /** @typedef {import("./dependencies/SingleEntryDependency")} SingleEntryDependency */
  44. /** @typedef {import("./dependencies/MultiEntryDependency")} MultiEntryDependency */
  45. /** @typedef {import("./dependencies/DllEntryDependency")} DllEntryDependency */
  46. /** @typedef {import("./dependencies/DependencyReference")} DependencyReference */
  47. /** @typedef {import("./DependenciesBlock")} DependenciesBlock */
  48. /** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
  49. /** @typedef {import("./Dependency")} Dependency */
  50. /** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
  51. /** @typedef {import("./Dependency").DependencyTemplate} DependencyTemplate */
  52. /** @typedef {import("./util/createHash").Hash} Hash */
  53. // TODO use @callback
  54. /** @typedef {{[assetName: string]: Source}} CompilationAssets */
  55. /** @typedef {(err: Error|null, result?: Module) => void } ModuleCallback */
  56. /** @typedef {(err?: Error|null, result?: Module) => void } ModuleChainCallback */
  57. /** @typedef {(module: Module) => void} OnModuleCallback */
  58. /** @typedef {(err?: Error|null) => void} Callback */
  59. /** @typedef {(d: Dependency) => any} DepBlockVarDependenciesCallback */
  60. /** @typedef {new (...args: any[]) => Dependency} DepConstructor */
  61. /** @typedef {{apply: () => void}} Plugin */
  62. /**
  63. * @typedef {Object} ModuleFactoryCreateDataContextInfo
  64. * @property {string} issuer
  65. * @property {string} compiler
  66. */
  67. /**
  68. * @typedef {Object} ModuleFactoryCreateData
  69. * @property {ModuleFactoryCreateDataContextInfo} contextInfo
  70. * @property {any=} resolveOptions
  71. * @property {string} context
  72. * @property {Dependency[]} dependencies
  73. */
  74. /**
  75. * @typedef {Object} ModuleFactory
  76. * @property {(data: ModuleFactoryCreateData, callback: ModuleCallback) => any} create
  77. */
  78. /**
  79. * @typedef {Object} SortedDependency
  80. * @property {ModuleFactory} factory
  81. * @property {Dependency[]} dependencies
  82. */
  83. /**
  84. * @typedef {Object} AvailableModulesChunkGroupMapping
  85. * @property {ChunkGroup} chunkGroup
  86. * @property {Set<Module>} availableModules
  87. */
  88. /**
  89. * @typedef {Object} DependenciesBlockLike
  90. * @property {Dependency[]} dependencies
  91. * @property {AsyncDependenciesBlock[]} blocks
  92. * @property {DependenciesBlockVariable[]} variables
  93. */
  94. /**
  95. * @param {Chunk} a first chunk to sort by id
  96. * @param {Chunk} b second chunk to sort by id
  97. * @returns {-1|0|1} sort value
  98. */
  99. const byId = (a, b) => {
  100. if (a.id !== null && b.id !== null) {
  101. if (a.id < b.id) return -1;
  102. if (a.id > b.id) return 1;
  103. }
  104. return 0;
  105. };
  106. /**
  107. * @param {Module} a first module to sort by
  108. * @param {Module} b second module to sort by
  109. * @returns {-1|0|1} sort value
  110. */
  111. const byIdOrIdentifier = (a, b) => {
  112. if (a.id < b.id) return -1;
  113. if (a.id > b.id) return 1;
  114. const identA = a.identifier();
  115. const identB = b.identifier();
  116. if (identA < identB) return -1;
  117. if (identA > identB) return 1;
  118. return 0;
  119. };
  120. /**
  121. * @param {Module} a first module to sort by
  122. * @param {Module} b second module to sort by
  123. * @returns {-1|0|1} sort value
  124. */
  125. const byIndexOrIdentifier = (a, b) => {
  126. if (a.index < b.index) return -1;
  127. if (a.index > b.index) return 1;
  128. const identA = a.identifier();
  129. const identB = b.identifier();
  130. if (identA < identB) return -1;
  131. if (identA > identB) return 1;
  132. return 0;
  133. };
  134. /**
  135. * @param {Compilation} a first compilation to sort by
  136. * @param {Compilation} b second compilation to sort by
  137. * @returns {-1|0|1} sort value
  138. */
  139. const byNameOrHash = (a, b) => {
  140. if (a.name < b.name) return -1;
  141. if (a.name > b.name) return 1;
  142. if (a.fullHash < b.fullHash) return -1;
  143. if (a.fullHash > b.fullHash) return 1;
  144. return 0;
  145. };
  146. /**
  147. * @param {DependenciesBlockVariable[]} variables DepBlock Variables to iterate over
  148. * @param {DepBlockVarDependenciesCallback} fn callback to apply on iterated elements
  149. * @returns {void}
  150. */
  151. const iterationBlockVariable = (variables, fn) => {
  152. for (
  153. let indexVariable = 0;
  154. indexVariable < variables.length;
  155. indexVariable++
  156. ) {
  157. const varDep = variables[indexVariable].dependencies;
  158. for (let indexVDep = 0; indexVDep < varDep.length; indexVDep++) {
  159. fn(varDep[indexVDep]);
  160. }
  161. }
  162. };
  163. /**
  164. * @template T
  165. * @param {T[]} arr array of elements to iterate over
  166. * @param {function(T): void} fn callback applied to each element
  167. * @returns {void}
  168. */
  169. const iterationOfArrayCallback = (arr, fn) => {
  170. for (let index = 0; index < arr.length; index++) {
  171. fn(arr[index]);
  172. }
  173. };
  174. /**
  175. * @template T
  176. * @param {Set<T>} set set to add items to
  177. * @param {Set<T>} otherSet set to add items from
  178. * @returns {void}
  179. */
  180. const addAllToSet = (set, otherSet) => {
  181. for (const item of otherSet) {
  182. set.add(item);
  183. }
  184. };
  185. class Compilation extends Tapable {
  186. /**
  187. * Creates an instance of Compilation.
  188. * @param {Compiler} compiler the compiler which created the compilation
  189. */
  190. constructor(compiler) {
  191. super();
  192. this.hooks = {
  193. /** @type {SyncHook<Module>} */
  194. buildModule: new SyncHook(["module"]),
  195. /** @type {SyncHook<Module>} */
  196. rebuildModule: new SyncHook(["module"]),
  197. /** @type {SyncHook<Module, Error>} */
  198. failedModule: new SyncHook(["module", "error"]),
  199. /** @type {SyncHook<Module>} */
  200. succeedModule: new SyncHook(["module"]),
  201. /** @type {SyncWaterfallHook<DependencyReference, Dependency, Module>} */
  202. dependencyReference: new SyncWaterfallHook([
  203. "dependencyReference",
  204. "dependency",
  205. "module"
  206. ]),
  207. /** @type {SyncHook<Module[]>} */
  208. finishModules: new SyncHook(["modules"]),
  209. /** @type {SyncHook<Module>} */
  210. finishRebuildingModule: new SyncHook(["module"]),
  211. /** @type {SyncHook} */
  212. unseal: new SyncHook([]),
  213. /** @type {SyncHook} */
  214. seal: new SyncHook([]),
  215. /** @type {SyncHook} */
  216. beforeChunks: new SyncHook([]),
  217. /** @type {SyncHook<Chunk[]>} */
  218. afterChunks: new SyncHook(["chunks"]),
  219. /** @type {SyncBailHook<Module[]>} */
  220. optimizeDependenciesBasic: new SyncBailHook(["modules"]),
  221. /** @type {SyncBailHook<Module[]>} */
  222. optimizeDependencies: new SyncBailHook(["modules"]),
  223. /** @type {SyncBailHook<Module[]>} */
  224. optimizeDependenciesAdvanced: new SyncBailHook(["modules"]),
  225. /** @type {SyncBailHook<Module[]>} */
  226. afterOptimizeDependencies: new SyncHook(["modules"]),
  227. /** @type {SyncHook} */
  228. optimize: new SyncHook([]),
  229. /** @type {SyncBailHook<Module[]>} */
  230. optimizeModulesBasic: new SyncBailHook(["modules"]),
  231. /** @type {SyncBailHook<Module[]>} */
  232. optimizeModules: new SyncBailHook(["modules"]),
  233. /** @type {SyncBailHook<Module[]>} */
  234. optimizeModulesAdvanced: new SyncBailHook(["modules"]),
  235. /** @type {SyncHook<Module[]>} */
  236. afterOptimizeModules: new SyncHook(["modules"]),
  237. /** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
  238. optimizeChunksBasic: new SyncBailHook(["chunks", "chunkGroups"]),
  239. /** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
  240. optimizeChunks: new SyncBailHook(["chunks", "chunkGroups"]),
  241. /** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
  242. optimizeChunksAdvanced: new SyncBailHook(["chunks", "chunkGroups"]),
  243. /** @type {SyncHook<Chunk[], ChunkGroup[]>} */
  244. afterOptimizeChunks: new SyncHook(["chunks", "chunkGroups"]),
  245. /** @type {AsyncSeriesHook<Chunk[], Module[]>} */
  246. optimizeTree: new AsyncSeriesHook(["chunks", "modules"]),
  247. /** @type {SyncHook<Chunk[], Module[]>} */
  248. afterOptimizeTree: new SyncHook(["chunks", "modules"]),
  249. /** @type {SyncBailHook<Chunk[], Module[]>} */
  250. optimizeChunkModulesBasic: new SyncBailHook(["chunks", "modules"]),
  251. /** @type {SyncBailHook<Chunk[], Module[]>} */
  252. optimizeChunkModules: new SyncBailHook(["chunks", "modules"]),
  253. /** @type {SyncBailHook<Chunk[], Module[]>} */
  254. optimizeChunkModulesAdvanced: new SyncBailHook(["chunks", "modules"]),
  255. /** @type {SyncHook<Chunk[], Module[]>} */
  256. afterOptimizeChunkModules: new SyncHook(["chunks", "modules"]),
  257. /** @type {SyncBailHook} */
  258. shouldRecord: new SyncBailHook([]),
  259. /** @type {SyncHook<Module[], any>} */
  260. reviveModules: new SyncHook(["modules", "records"]),
  261. /** @type {SyncHook<Module[]>} */
  262. optimizeModuleOrder: new SyncHook(["modules"]),
  263. /** @type {SyncHook<Module[]>} */
  264. advancedOptimizeModuleOrder: new SyncHook(["modules"]),
  265. /** @type {SyncHook<Module[]>} */
  266. beforeModuleIds: new SyncHook(["modules"]),
  267. /** @type {SyncHook<Module[]>} */
  268. moduleIds: new SyncHook(["modules"]),
  269. /** @type {SyncHook<Module[]>} */
  270. optimizeModuleIds: new SyncHook(["modules"]),
  271. /** @type {SyncHook<Module[]>} */
  272. afterOptimizeModuleIds: new SyncHook(["modules"]),
  273. /** @type {SyncHook<Chunk[], any>} */
  274. reviveChunks: new SyncHook(["chunks", "records"]),
  275. /** @type {SyncHook<Chunk[]>} */
  276. optimizeChunkOrder: new SyncHook(["chunks"]),
  277. /** @type {SyncHook<Chunk[]>} */
  278. beforeChunkIds: new SyncHook(["chunks"]),
  279. /** @type {SyncHook<Chunk[]>} */
  280. optimizeChunkIds: new SyncHook(["chunks"]),
  281. /** @type {SyncHook<Chunk[]>} */
  282. afterOptimizeChunkIds: new SyncHook(["chunks"]),
  283. /** @type {SyncHook<Module[], any>} */
  284. recordModules: new SyncHook(["modules", "records"]),
  285. /** @type {SyncHook<Chunk[], any>} */
  286. recordChunks: new SyncHook(["chunks", "records"]),
  287. /** @type {SyncHook} */
  288. beforeHash: new SyncHook([]),
  289. /** @type {SyncHook<Chunk>} */
  290. contentHash: new SyncHook(["chunk"]),
  291. /** @type {SyncHook} */
  292. afterHash: new SyncHook([]),
  293. /** @type {SyncHook<any>} */
  294. recordHash: new SyncHook(["records"]),
  295. /** @type {SyncHook<Compilation, any>} */
  296. record: new SyncHook(["compilation", "records"]),
  297. /** @type {SyncHook} */
  298. beforeModuleAssets: new SyncHook([]),
  299. /** @type {SyncBailHook} */
  300. shouldGenerateChunkAssets: new SyncBailHook([]),
  301. /** @type {SyncHook} */
  302. beforeChunkAssets: new SyncHook([]),
  303. /** @type {SyncHook<Chunk[]>} */
  304. additionalChunkAssets: new SyncHook(["chunks"]),
  305. /** @type {AsyncSeriesHook} */
  306. additionalAssets: new AsyncSeriesHook([]),
  307. /** @type {AsyncSeriesHook<Chunk[]>} */
  308. optimizeChunkAssets: new AsyncSeriesHook(["chunks"]),
  309. /** @type {SyncHook<Chunk[]>} */
  310. afterOptimizeChunkAssets: new SyncHook(["chunks"]),
  311. /** @type {AsyncSeriesHook<CompilationAssets>} */
  312. optimizeAssets: new AsyncSeriesHook(["assets"]),
  313. /** @type {SyncHook<CompilationAssets>} */
  314. afterOptimizeAssets: new SyncHook(["assets"]),
  315. /** @type {SyncBailHook} */
  316. needAdditionalSeal: new SyncBailHook([]),
  317. /** @type {AsyncSeriesHook} */
  318. afterSeal: new AsyncSeriesHook([]),
  319. /** @type {SyncHook<Chunk, Hash>} */
  320. chunkHash: new SyncHook(["chunk", "chunkHash"]),
  321. /** @type {SyncHook<Module, string>} */
  322. moduleAsset: new SyncHook(["module", "filename"]),
  323. /** @type {SyncHook<Chunk, string>} */
  324. chunkAsset: new SyncHook(["chunk", "filename"]),
  325. /** @type {SyncWaterfallHook<string, TODO>} */
  326. assetPath: new SyncWaterfallHook(["filename", "data"]), // TODO MainTemplate
  327. /** @type {SyncBailHook} */
  328. needAdditionalPass: new SyncBailHook([]),
  329. /** @type {SyncHook<Compiler, string, number>} */
  330. childCompiler: new SyncHook([
  331. "childCompiler",
  332. "compilerName",
  333. "compilerIndex"
  334. ]),
  335. // TODO the following hooks are weirdly located here
  336. // TODO move them for webpack 5
  337. /** @type {SyncHook<object, Module>} */
  338. normalModuleLoader: new SyncHook(["loaderContext", "module"]),
  339. /** @type {SyncBailHook<Chunk[]>} */
  340. optimizeExtractedChunksBasic: new SyncBailHook(["chunks"]),
  341. /** @type {SyncBailHook<Chunk[]>} */
  342. optimizeExtractedChunks: new SyncBailHook(["chunks"]),
  343. /** @type {SyncBailHook<Chunk[]>} */
  344. optimizeExtractedChunksAdvanced: new SyncBailHook(["chunks"]),
  345. /** @type {SyncHook<Chunk[]>} */
  346. afterOptimizeExtractedChunks: new SyncHook(["chunks"])
  347. };
  348. this._pluginCompat.tap("Compilation", options => {
  349. switch (options.name) {
  350. case "optimize-tree":
  351. case "additional-assets":
  352. case "optimize-chunk-assets":
  353. case "optimize-assets":
  354. case "after-seal":
  355. options.async = true;
  356. break;
  357. }
  358. });
  359. /** @type {string=} */
  360. this.name = undefined;
  361. /** @type {Compiler} */
  362. this.compiler = compiler;
  363. this.resolverFactory = compiler.resolverFactory;
  364. this.inputFileSystem = compiler.inputFileSystem;
  365. this.requestShortener = compiler.requestShortener;
  366. const options = (this.options = compiler.options);
  367. this.outputOptions = options && options.output;
  368. /** @type {boolean=} */
  369. this.bail = options && options.bail;
  370. this.profile = options && options.profile;
  371. this.performance = options && options.performance;
  372. this.mainTemplate = new MainTemplate(this.outputOptions);
  373. this.chunkTemplate = new ChunkTemplate(this.outputOptions);
  374. this.hotUpdateChunkTemplate = new HotUpdateChunkTemplate(
  375. this.outputOptions
  376. );
  377. this.runtimeTemplate = new RuntimeTemplate(
  378. this.outputOptions,
  379. this.requestShortener
  380. );
  381. this.moduleTemplates = {
  382. javascript: new ModuleTemplate(this.runtimeTemplate, "javascript"),
  383. webassembly: new ModuleTemplate(this.runtimeTemplate, "webassembly")
  384. };
  385. this.semaphore = new Semaphore(options.parallelism || 100);
  386. this.entries = [];
  387. /** @private @type {{name: string, request: string, module: Module}[]} */
  388. this._preparedEntrypoints = [];
  389. this.entrypoints = new Map();
  390. /** @type {Chunk[]} */
  391. this.chunks = [];
  392. /** @type {ChunkGroup[]} */
  393. this.chunkGroups = [];
  394. /** @type {Map<string, ChunkGroup>} */
  395. this.namedChunkGroups = new Map();
  396. /** @type {Map<string, Chunk>} */
  397. this.namedChunks = new Map();
  398. /** @type {Module[]} */
  399. this.modules = [];
  400. /** @private @type {Map<string, Module>} */
  401. this._modules = new Map();
  402. this.cache = null;
  403. this.records = null;
  404. /** @type {string[]} */
  405. this.additionalChunkAssets = [];
  406. /** @type {CompilationAssets} */
  407. this.assets = {};
  408. /** @type {WebpackError[]} */
  409. this.errors = [];
  410. /** @type {WebpackError[]} */
  411. this.warnings = [];
  412. /** @type {Compilation[]} */
  413. this.children = [];
  414. /** @type {Map<DepConstructor, ModuleFactory>} */
  415. this.dependencyFactories = new Map();
  416. /** @type {Map<DepConstructor, DependencyTemplate>} */
  417. this.dependencyTemplates = new Map();
  418. // TODO refactor this in webpack 5 to a custom DependencyTemplates class with a hash property
  419. // @ts-ignore
  420. this.dependencyTemplates.set("hash", "");
  421. this.childrenCounters = {};
  422. /** @type {Set<number|string>} */
  423. this.usedChunkIds = null;
  424. /** @type {Set<number>} */
  425. this.usedModuleIds = null;
  426. /** @type {Map<string, number>=} */
  427. this.fileTimestamps = undefined;
  428. /** @type {Map<string, number>=} */
  429. this.contextTimestamps = undefined;
  430. /** @type {Set<string>=} */
  431. this.compilationDependencies = undefined;
  432. /** @private @type {Map<Module, Callback[]>} */
  433. this._buildingModules = new Map();
  434. /** @private @type {Map<Module, Callback[]>} */
  435. this._rebuildingModules = new Map();
  436. }
  437. getStats() {
  438. return new Stats(this);
  439. }
  440. /**
  441. * @typedef {Object} AddModuleResult
  442. * @property {Module} module the added or existing module
  443. * @property {boolean} issuer was this the first request for this module
  444. * @property {boolean} build should the module be build
  445. * @property {boolean} dependencies should dependencies be walked
  446. */
  447. /**
  448. * @param {Module} module module to be added that was created
  449. * @param {any=} cacheGroup cacheGroup it is apart of
  450. * @returns {AddModuleResult} returns meta about whether or not the module had built
  451. * had an issuer, or any dependnecies
  452. */
  453. addModule(module, cacheGroup) {
  454. const identifier = module.identifier();
  455. const alreadyAddedModule = this._modules.get(identifier);
  456. if (alreadyAddedModule) {
  457. return {
  458. module: alreadyAddedModule,
  459. issuer: false,
  460. build: false,
  461. dependencies: false
  462. };
  463. }
  464. const cacheName = (cacheGroup || "m") + identifier;
  465. if (this.cache && this.cache[cacheName]) {
  466. const cacheModule = this.cache[cacheName];
  467. if (typeof cacheModule.updateCacheModule === "function") {
  468. cacheModule.updateCacheModule(module);
  469. }
  470. let rebuild = true;
  471. if (this.fileTimestamps && this.contextTimestamps) {
  472. rebuild = cacheModule.needRebuild(
  473. this.fileTimestamps,
  474. this.contextTimestamps
  475. );
  476. }
  477. if (!rebuild) {
  478. cacheModule.disconnect();
  479. this._modules.set(identifier, cacheModule);
  480. this.modules.push(cacheModule);
  481. for (const err of cacheModule.errors) {
  482. this.errors.push(err);
  483. }
  484. for (const err of cacheModule.warnings) {
  485. this.warnings.push(err);
  486. }
  487. return {
  488. module: cacheModule,
  489. issuer: true,
  490. build: false,
  491. dependencies: true
  492. };
  493. }
  494. cacheModule.unbuild();
  495. module = cacheModule;
  496. }
  497. this._modules.set(identifier, module);
  498. if (this.cache) {
  499. this.cache[cacheName] = module;
  500. }
  501. this.modules.push(module);
  502. return {
  503. module: module,
  504. issuer: true,
  505. build: true,
  506. dependencies: true
  507. };
  508. }
  509. /**
  510. * Fetches a module from a compilation by its identifier
  511. * @param {Module} module the module provided
  512. * @returns {Module} the module requested
  513. */
  514. getModule(module) {
  515. const identifier = module.identifier();
  516. return this._modules.get(identifier);
  517. }
  518. /**
  519. * Attempts to search for a module by its identifier
  520. * @param {string} identifier identifier (usually path) for module
  521. * @returns {Module|undefined} attempt to search for module and return it, else undefined
  522. */
  523. findModule(identifier) {
  524. return this._modules.get(identifier);
  525. }
  526. /**
  527. * @param {Module} module module with its callback list
  528. * @param {Callback} callback the callback function
  529. * @returns {void}
  530. */
  531. waitForBuildingFinished(module, callback) {
  532. let callbackList = this._buildingModules.get(module);
  533. if (callbackList) {
  534. callbackList.push(() => callback());
  535. } else {
  536. process.nextTick(callback);
  537. }
  538. }
  539. /**
  540. * Builds the module object
  541. *
  542. * @param {Module} module module to be built
  543. * @param {boolean} optional optional flag
  544. * @param {Module=} origin origin module this module build was requested from
  545. * @param {Dependency[]=} dependencies optional dependencies from the module to be built
  546. * @param {TODO} thisCallback the callback
  547. * @returns {TODO} returns the callback function with results
  548. */
  549. buildModule(module, optional, origin, dependencies, thisCallback) {
  550. let callbackList = this._buildingModules.get(module);
  551. if (callbackList) {
  552. callbackList.push(thisCallback);
  553. return;
  554. }
  555. this._buildingModules.set(module, (callbackList = [thisCallback]));
  556. const callback = err => {
  557. this._buildingModules.delete(module);
  558. for (const cb of callbackList) {
  559. cb(err);
  560. }
  561. };
  562. this.hooks.buildModule.call(module);
  563. module.build(
  564. this.options,
  565. this,
  566. this.resolverFactory.get("normal", module.resolveOptions),
  567. this.inputFileSystem,
  568. error => {
  569. const errors = module.errors;
  570. for (let indexError = 0; indexError < errors.length; indexError++) {
  571. const err = errors[indexError];
  572. err.origin = origin;
  573. err.dependencies = dependencies;
  574. if (optional) {
  575. this.warnings.push(err);
  576. } else {
  577. this.errors.push(err);
  578. }
  579. }
  580. const warnings = module.warnings;
  581. for (
  582. let indexWarning = 0;
  583. indexWarning < warnings.length;
  584. indexWarning++
  585. ) {
  586. const war = warnings[indexWarning];
  587. war.origin = origin;
  588. war.dependencies = dependencies;
  589. this.warnings.push(war);
  590. }
  591. module.dependencies.sort((a, b) => compareLocations(a.loc, b.loc));
  592. if (error) {
  593. this.hooks.failedModule.call(module, error);
  594. return callback(error);
  595. }
  596. this.hooks.succeedModule.call(module);
  597. return callback();
  598. }
  599. );
  600. }
  601. /**
  602. * @param {Module} module to be processed for deps
  603. * @param {ModuleCallback} callback callback to be triggered
  604. * @returns {void}
  605. */
  606. processModuleDependencies(module, callback) {
  607. const dependencies = new Map();
  608. const addDependency = dep => {
  609. const resourceIdent = dep.getResourceIdentifier();
  610. if (resourceIdent) {
  611. const factory = this.dependencyFactories.get(dep.constructor);
  612. if (factory === undefined) {
  613. throw new Error(
  614. `No module factory available for dependency type: ${
  615. dep.constructor.name
  616. }`
  617. );
  618. }
  619. let innerMap = dependencies.get(factory);
  620. if (innerMap === undefined) {
  621. dependencies.set(factory, (innerMap = new Map()));
  622. }
  623. let list = innerMap.get(resourceIdent);
  624. if (list === undefined) innerMap.set(resourceIdent, (list = []));
  625. list.push(dep);
  626. }
  627. };
  628. const addDependenciesBlock = block => {
  629. if (block.dependencies) {
  630. iterationOfArrayCallback(block.dependencies, addDependency);
  631. }
  632. if (block.blocks) {
  633. iterationOfArrayCallback(block.blocks, addDependenciesBlock);
  634. }
  635. if (block.variables) {
  636. iterationBlockVariable(block.variables, addDependency);
  637. }
  638. };
  639. try {
  640. addDependenciesBlock(module);
  641. } catch (e) {
  642. callback(e);
  643. }
  644. const sortedDependencies = [];
  645. for (const pair1 of dependencies) {
  646. for (const pair2 of pair1[1]) {
  647. sortedDependencies.push({
  648. factory: pair1[0],
  649. dependencies: pair2[1]
  650. });
  651. }
  652. }
  653. this.addModuleDependencies(
  654. module,
  655. sortedDependencies,
  656. this.bail,
  657. null,
  658. true,
  659. callback
  660. );
  661. }
  662. /**
  663. * @param {Module} module module to add deps to
  664. * @param {SortedDependency[]} dependencies set of sorted dependencies to iterate through
  665. * @param {(boolean|null)=} bail whether to bail or not
  666. * @param {TODO} cacheGroup optional cacheGroup
  667. * @param {boolean} recursive whether it is recursive traversal
  668. * @param {function} callback callback for when dependencies are finished being added
  669. * @returns {void}
  670. */
  671. addModuleDependencies(
  672. module,
  673. dependencies,
  674. bail,
  675. cacheGroup,
  676. recursive,
  677. callback
  678. ) {
  679. const start = this.profile && Date.now();
  680. const currentProfile = this.profile && {};
  681. asyncLib.forEach(
  682. dependencies,
  683. (item, callback) => {
  684. const dependencies = item.dependencies;
  685. const errorAndCallback = err => {
  686. err.origin = module;
  687. err.dependencies = dependencies;
  688. this.errors.push(err);
  689. if (bail) {
  690. callback(err);
  691. } else {
  692. callback();
  693. }
  694. };
  695. const warningAndCallback = err => {
  696. err.origin = module;
  697. this.warnings.push(err);
  698. callback();
  699. };
  700. const semaphore = this.semaphore;
  701. semaphore.acquire(() => {
  702. const factory = item.factory;
  703. factory.create(
  704. {
  705. contextInfo: {
  706. issuer: module.nameForCondition && module.nameForCondition(),
  707. compiler: this.compiler.name
  708. },
  709. resolveOptions: module.resolveOptions,
  710. context: module.context,
  711. dependencies: dependencies
  712. },
  713. (err, dependentModule) => {
  714. let afterFactory;
  715. const isOptional = () => {
  716. return dependencies.every(d => d.optional);
  717. };
  718. const errorOrWarningAndCallback = err => {
  719. if (isOptional()) {
  720. return warningAndCallback(err);
  721. } else {
  722. return errorAndCallback(err);
  723. }
  724. };
  725. if (err) {
  726. semaphore.release();
  727. return errorOrWarningAndCallback(
  728. new ModuleNotFoundError(module, err)
  729. );
  730. }
  731. if (!dependentModule) {
  732. semaphore.release();
  733. return process.nextTick(callback);
  734. }
  735. if (currentProfile) {
  736. afterFactory = Date.now();
  737. currentProfile.factory = afterFactory - start;
  738. }
  739. const iterationDependencies = depend => {
  740. for (let index = 0; index < depend.length; index++) {
  741. const dep = depend[index];
  742. dep.module = dependentModule;
  743. dependentModule.addReason(module, dep);
  744. }
  745. };
  746. const addModuleResult = this.addModule(
  747. dependentModule,
  748. cacheGroup
  749. );
  750. dependentModule = addModuleResult.module;
  751. iterationDependencies(dependencies);
  752. const afterBuild = () => {
  753. if (currentProfile) {
  754. const afterBuilding = Date.now();
  755. currentProfile.building = afterBuilding - afterFactory;
  756. }
  757. if (recursive && addModuleResult.dependencies) {
  758. this.processModuleDependencies(dependentModule, callback);
  759. } else {
  760. return callback();
  761. }
  762. };
  763. if (addModuleResult.issuer) {
  764. if (currentProfile) {
  765. dependentModule.profile = currentProfile;
  766. }
  767. dependentModule.issuer = module;
  768. } else {
  769. if (this.profile) {
  770. if (module.profile) {
  771. const time = Date.now() - start;
  772. if (
  773. !module.profile.dependencies ||
  774. time > module.profile.dependencies
  775. ) {
  776. module.profile.dependencies = time;
  777. }
  778. }
  779. }
  780. }
  781. if (addModuleResult.build) {
  782. this.buildModule(
  783. dependentModule,
  784. isOptional(),
  785. module,
  786. dependencies,
  787. err => {
  788. if (err) {
  789. semaphore.release();
  790. return errorOrWarningAndCallback(err);
  791. }
  792. if (currentProfile) {
  793. const afterBuilding = Date.now();
  794. currentProfile.building = afterBuilding - afterFactory;
  795. }
  796. semaphore.release();
  797. afterBuild();
  798. }
  799. );
  800. } else {
  801. semaphore.release();
  802. this.waitForBuildingFinished(dependentModule, afterBuild);
  803. }
  804. }
  805. );
  806. });
  807. },
  808. err => {
  809. // In V8, the Error objects keep a reference to the functions on the stack. These warnings &
  810. // errors are created inside closures that keep a reference to the Compilation, so errors are
  811. // leaking the Compilation object.
  812. if (err) {
  813. // eslint-disable-next-line no-self-assign
  814. err.stack = err.stack;
  815. return callback(err);
  816. }
  817. return process.nextTick(callback);
  818. }
  819. );
  820. }
  821. /**
  822. *
  823. * @param {string} context context string path
  824. * @param {Dependency} dependency dependency used to create Module chain
  825. * @param {OnModuleCallback} onModule function invoked on modules creation
  826. * @param {ModuleChainCallback} callback callback for when module chain is complete
  827. * @returns {void} will throw if dependency instance is not a valid Dependency
  828. */
  829. _addModuleChain(context, dependency, onModule, callback) {
  830. const start = this.profile && Date.now();
  831. const currentProfile = this.profile && {};
  832. const errorAndCallback = this.bail
  833. ? err => {
  834. callback(err);
  835. }
  836. : err => {
  837. err.dependencies = [dependency];
  838. this.errors.push(err);
  839. callback();
  840. };
  841. if (
  842. typeof dependency !== "object" ||
  843. dependency === null ||
  844. !dependency.constructor
  845. ) {
  846. throw new Error("Parameter 'dependency' must be a Dependency");
  847. }
  848. const Dep = /** @type {DepConstructor} */ (dependency.constructor);
  849. const moduleFactory = this.dependencyFactories.get(Dep);
  850. if (!moduleFactory) {
  851. throw new Error(
  852. `No dependency factory available for this dependency type: ${
  853. dependency.constructor.name
  854. }`
  855. );
  856. }
  857. this.semaphore.acquire(() => {
  858. moduleFactory.create(
  859. {
  860. contextInfo: {
  861. issuer: "",
  862. compiler: this.compiler.name
  863. },
  864. context: context,
  865. dependencies: [dependency]
  866. },
  867. (err, module) => {
  868. if (err) {
  869. this.semaphore.release();
  870. return errorAndCallback(new EntryModuleNotFoundError(err));
  871. }
  872. let afterFactory;
  873. if (currentProfile) {
  874. afterFactory = Date.now();
  875. currentProfile.factory = afterFactory - start;
  876. }
  877. const addModuleResult = this.addModule(module);
  878. module = addModuleResult.module;
  879. onModule(module);
  880. dependency.module = module;
  881. module.addReason(null, dependency);
  882. const afterBuild = () => {
  883. if (currentProfile) {
  884. const afterBuilding = Date.now();
  885. currentProfile.building = afterBuilding - afterFactory;
  886. }
  887. if (addModuleResult.dependencies) {
  888. this.processModuleDependencies(module, err => {
  889. if (err) return callback(err);
  890. callback(null, module);
  891. });
  892. } else {
  893. return callback(null, module);
  894. }
  895. };
  896. if (addModuleResult.issuer) {
  897. if (currentProfile) {
  898. module.profile = currentProfile;
  899. }
  900. }
  901. if (addModuleResult.build) {
  902. this.buildModule(module, false, null, null, err => {
  903. if (err) {
  904. this.semaphore.release();
  905. return errorAndCallback(err);
  906. }
  907. if (currentProfile) {
  908. const afterBuilding = Date.now();
  909. currentProfile.building = afterBuilding - afterFactory;
  910. }
  911. this.semaphore.release();
  912. afterBuild();
  913. });
  914. } else {
  915. this.semaphore.release();
  916. this.waitForBuildingFinished(module, afterBuild);
  917. }
  918. }
  919. );
  920. });
  921. }
  922. /**
  923. *
  924. * @param {string} context context path for entry
  925. * @param {Dependency} entry entry dependency being created
  926. * @param {string} name name of entry
  927. * @param {ModuleCallback} callback callback function
  928. * @returns {void} returns
  929. */
  930. addEntry(context, entry, name, callback) {
  931. const slot = {
  932. name: name,
  933. // TODO webpack 5 remove `request`
  934. request: null,
  935. module: null
  936. };
  937. if (entry instanceof ModuleDependency) {
  938. slot.request = entry.request;
  939. }
  940. // TODO webpack 5: merge modules instead when multiple entry modules are supported
  941. const idx = this._preparedEntrypoints.findIndex(slot => slot.name === name);
  942. if (idx >= 0) {
  943. // Overwrite existing entrypoint
  944. this._preparedEntrypoints[idx] = slot;
  945. } else {
  946. this._preparedEntrypoints.push(slot);
  947. }
  948. this._addModuleChain(
  949. context,
  950. entry,
  951. module => {
  952. this.entries.push(module);
  953. },
  954. (err, module) => {
  955. if (err) {
  956. return callback(err);
  957. }
  958. if (module) {
  959. slot.module = module;
  960. } else {
  961. const idx = this._preparedEntrypoints.indexOf(slot);
  962. if (idx >= 0) {
  963. this._preparedEntrypoints.splice(idx, 1);
  964. }
  965. }
  966. return callback(null, module);
  967. }
  968. );
  969. }
  970. /**
  971. * @param {string} context context path string
  972. * @param {Dependency} dependency dep used to create module
  973. * @param {ModuleCallback} callback module callback sending module up a level
  974. * @returns {void}
  975. */
  976. prefetch(context, dependency, callback) {
  977. this._addModuleChain(
  978. context,
  979. dependency,
  980. module => {
  981. module.prefetched = true;
  982. },
  983. callback
  984. );
  985. }
  986. /**
  987. * @param {Module} module module to be rebuilt
  988. * @param {Callback} thisCallback callback when module finishes rebuilding
  989. * @returns {void}
  990. */
  991. rebuildModule(module, thisCallback) {
  992. let callbackList = this._rebuildingModules.get(module);
  993. if (callbackList) {
  994. callbackList.push(thisCallback);
  995. return;
  996. }
  997. this._rebuildingModules.set(module, (callbackList = [thisCallback]));
  998. const callback = err => {
  999. this._rebuildingModules.delete(module);
  1000. for (const cb of callbackList) {
  1001. cb(err);
  1002. }
  1003. };
  1004. this.hooks.rebuildModule.call(module);
  1005. const oldDependencies = module.dependencies.slice();
  1006. const oldVariables = module.variables.slice();
  1007. const oldBlocks = module.blocks.slice();
  1008. module.unbuild();
  1009. this.buildModule(module, false, module, null, err => {
  1010. if (err) {
  1011. this.hooks.finishRebuildingModule.call(module);
  1012. return callback(err);
  1013. }
  1014. this.processModuleDependencies(module, err => {
  1015. if (err) return callback(err);
  1016. this.removeReasonsOfDependencyBlock(module, {
  1017. dependencies: oldDependencies,
  1018. variables: oldVariables,
  1019. blocks: oldBlocks
  1020. });
  1021. this.hooks.finishRebuildingModule.call(module);
  1022. callback();
  1023. });
  1024. });
  1025. }
  1026. finish() {
  1027. const modules = this.modules;
  1028. this.hooks.finishModules.call(modules);
  1029. for (let index = 0; index < modules.length; index++) {
  1030. const module = modules[index];
  1031. this.reportDependencyErrorsAndWarnings(module, [module]);
  1032. }
  1033. }
  1034. unseal() {
  1035. this.hooks.unseal.call();
  1036. this.chunks.length = 0;
  1037. this.chunkGroups.length = 0;
  1038. this.namedChunks.clear();
  1039. this.namedChunkGroups.clear();
  1040. this.additionalChunkAssets.length = 0;
  1041. this.assets = {};
  1042. for (const module of this.modules) {
  1043. module.unseal();
  1044. }
  1045. }
  1046. /**
  1047. * @param {Callback} callback signals when the seal method is finishes
  1048. * @returns {void}
  1049. */
  1050. seal(callback) {
  1051. this.hooks.seal.call();
  1052. while (
  1053. this.hooks.optimizeDependenciesBasic.call(this.modules) ||
  1054. this.hooks.optimizeDependencies.call(this.modules) ||
  1055. this.hooks.optimizeDependenciesAdvanced.call(this.modules)
  1056. ) {
  1057. /* empty */
  1058. }
  1059. this.hooks.afterOptimizeDependencies.call(this.modules);
  1060. this.hooks.beforeChunks.call();
  1061. for (const preparedEntrypoint of this._preparedEntrypoints) {
  1062. const module = preparedEntrypoint.module;
  1063. const name = preparedEntrypoint.name;
  1064. const chunk = this.addChunk(name);
  1065. const entrypoint = new Entrypoint(name);
  1066. entrypoint.setRuntimeChunk(chunk);
  1067. entrypoint.addOrigin(null, name, preparedEntrypoint.request);
  1068. this.namedChunkGroups.set(name, entrypoint);
  1069. this.entrypoints.set(name, entrypoint);
  1070. this.chunkGroups.push(entrypoint);
  1071. GraphHelpers.connectChunkGroupAndChunk(entrypoint, chunk);
  1072. GraphHelpers.connectChunkAndModule(chunk, module);
  1073. chunk.entryModule = module;
  1074. chunk.name = name;
  1075. this.assignDepth(module);
  1076. }
  1077. this.processDependenciesBlocksForChunkGroups(this.chunkGroups.slice());
  1078. this.sortModules(this.modules);
  1079. this.hooks.afterChunks.call(this.chunks);
  1080. this.hooks.optimize.call();
  1081. while (
  1082. this.hooks.optimizeModulesBasic.call(this.modules) ||
  1083. this.hooks.optimizeModules.call(this.modules) ||
  1084. this.hooks.optimizeModulesAdvanced.call(this.modules)
  1085. ) {
  1086. /* empty */
  1087. }
  1088. this.hooks.afterOptimizeModules.call(this.modules);
  1089. while (
  1090. this.hooks.optimizeChunksBasic.call(this.chunks, this.chunkGroups) ||
  1091. this.hooks.optimizeChunks.call(this.chunks, this.chunkGroups) ||
  1092. this.hooks.optimizeChunksAdvanced.call(this.chunks, this.chunkGroups)
  1093. ) {
  1094. /* empty */
  1095. }
  1096. this.hooks.afterOptimizeChunks.call(this.chunks, this.chunkGroups);
  1097. this.hooks.optimizeTree.callAsync(this.chunks, this.modules, err => {
  1098. if (err) {
  1099. return callback(err);
  1100. }
  1101. this.hooks.afterOptimizeTree.call(this.chunks, this.modules);
  1102. while (
  1103. this.hooks.optimizeChunkModulesBasic.call(this.chunks, this.modules) ||
  1104. this.hooks.optimizeChunkModules.call(this.chunks, this.modules) ||
  1105. this.hooks.optimizeChunkModulesAdvanced.call(this.chunks, this.modules)
  1106. ) {
  1107. /* empty */
  1108. }
  1109. this.hooks.afterOptimizeChunkModules.call(this.chunks, this.modules);
  1110. const shouldRecord = this.hooks.shouldRecord.call() !== false;
  1111. this.hooks.reviveModules.call(this.modules, this.records);
  1112. this.hooks.optimizeModuleOrder.call(this.modules);
  1113. this.hooks.advancedOptimizeModuleOrder.call(this.modules);
  1114. this.hooks.beforeModuleIds.call(this.modules);
  1115. this.hooks.moduleIds.call(this.modules);
  1116. this.applyModuleIds();
  1117. this.hooks.optimizeModuleIds.call(this.modules);
  1118. this.hooks.afterOptimizeModuleIds.call(this.modules);
  1119. this.sortItemsWithModuleIds();
  1120. this.hooks.reviveChunks.call(this.chunks, this.records);
  1121. this.hooks.optimizeChunkOrder.call(this.chunks);
  1122. this.hooks.beforeChunkIds.call(this.chunks);
  1123. this.applyChunkIds();
  1124. this.hooks.optimizeChunkIds.call(this.chunks);
  1125. this.hooks.afterOptimizeChunkIds.call(this.chunks);
  1126. this.sortItemsWithChunkIds();
  1127. if (shouldRecord) {
  1128. this.hooks.recordModules.call(this.modules, this.records);
  1129. this.hooks.recordChunks.call(this.chunks, this.records);
  1130. }
  1131. this.hooks.beforeHash.call();
  1132. this.createHash();
  1133. this.hooks.afterHash.call();
  1134. if (shouldRecord) {
  1135. this.hooks.recordHash.call(this.records);
  1136. }
  1137. this.hooks.beforeModuleAssets.call();
  1138. this.createModuleAssets();
  1139. if (this.hooks.shouldGenerateChunkAssets.call() !== false) {
  1140. this.hooks.beforeChunkAssets.call();
  1141. this.createChunkAssets();
  1142. }
  1143. this.hooks.additionalChunkAssets.call(this.chunks);
  1144. this.summarizeDependencies();
  1145. if (shouldRecord) {
  1146. this.hooks.record.call(this, this.records);
  1147. }
  1148. this.hooks.additionalAssets.callAsync(err => {
  1149. if (err) {
  1150. return callback(err);
  1151. }
  1152. this.hooks.optimizeChunkAssets.callAsync(this.chunks, err => {
  1153. if (err) {
  1154. return callback(err);
  1155. }
  1156. this.hooks.afterOptimizeChunkAssets.call(this.chunks);
  1157. this.hooks.optimizeAssets.callAsync(this.assets, err => {
  1158. if (err) {
  1159. return callback(err);
  1160. }
  1161. this.hooks.afterOptimizeAssets.call(this.assets);
  1162. if (this.hooks.needAdditionalSeal.call()) {
  1163. this.unseal();
  1164. return this.seal(callback);
  1165. }
  1166. return this.hooks.afterSeal.callAsync(callback);
  1167. });
  1168. });
  1169. });
  1170. });
  1171. }
  1172. /**
  1173. * @param {Module[]} modules the modules array on compilation to perform the sort for
  1174. * @returns {void}
  1175. */
  1176. sortModules(modules) {
  1177. // TODO webpack 5: this should only be enabled when `moduleIds: "natural"`
  1178. // TODO move it into a plugin (NaturalModuleIdsPlugin) and use this in WebpackOptionsApply
  1179. // TODO remove this method
  1180. modules.sort(byIndexOrIdentifier);
  1181. }
  1182. /**
  1183. * @param {Module} module moulde to report from
  1184. * @param {DependenciesBlock[]} blocks blocks to report from
  1185. * @returns {void}
  1186. */
  1187. reportDependencyErrorsAndWarnings(module, blocks) {
  1188. for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
  1189. const block = blocks[indexBlock];
  1190. const dependencies = block.dependencies;
  1191. for (let indexDep = 0; indexDep < dependencies.length; indexDep++) {
  1192. const d = dependencies[indexDep];
  1193. const warnings = d.getWarnings();
  1194. if (warnings) {
  1195. for (let indexWar = 0; indexWar < warnings.length; indexWar++) {
  1196. const w = warnings[indexWar];
  1197. const warning = new ModuleDependencyWarning(module, w, d.loc);
  1198. this.warnings.push(warning);
  1199. }
  1200. }
  1201. const errors = d.getErrors();
  1202. if (errors) {
  1203. for (let indexErr = 0; indexErr < errors.length; indexErr++) {
  1204. const e = errors[indexErr];
  1205. const error = new ModuleDependencyError(module, e, d.loc);
  1206. this.errors.push(error);
  1207. }
  1208. }
  1209. }
  1210. this.reportDependencyErrorsAndWarnings(module, block.blocks);
  1211. }
  1212. }
  1213. /**
  1214. * @param {TODO} groupOptions options for the chunk group
  1215. * @param {Module} module the module the references the chunk group
  1216. * @param {DependencyLocation} loc the location from with the chunk group is referenced (inside of module)
  1217. * @param {string} request the request from which the the chunk group is referenced
  1218. * @returns {ChunkGroup} the new or existing chunk group
  1219. */
  1220. addChunkInGroup(groupOptions, module, loc, request) {
  1221. if (typeof groupOptions === "string") {
  1222. groupOptions = { name: groupOptions };
  1223. }
  1224. const name = groupOptions.name;
  1225. if (name) {
  1226. const chunkGroup = this.namedChunkGroups.get(name);
  1227. if (chunkGroup !== undefined) {
  1228. chunkGroup.addOptions(groupOptions);
  1229. if (module) {
  1230. chunkGroup.addOrigin(module, loc, request);
  1231. }
  1232. return chunkGroup;
  1233. }
  1234. }
  1235. const chunkGroup = new ChunkGroup(groupOptions);
  1236. if (module) chunkGroup.addOrigin(module, loc, request);
  1237. const chunk = this.addChunk(name);
  1238. GraphHelpers.connectChunkGroupAndChunk(chunkGroup, chunk);
  1239. this.chunkGroups.push(chunkGroup);
  1240. if (name) {
  1241. this.namedChunkGroups.set(name, chunkGroup);
  1242. }
  1243. return chunkGroup;
  1244. }
  1245. /**
  1246. * This method first looks to see if a name is provided for a new chunk,
  1247. * and first looks to see if any named chunks already exist and reuse that chunk instead.
  1248. *
  1249. * @param {string=} name optional chunk name to be provided
  1250. * @returns {Chunk} create a chunk (invoked during seal event)
  1251. */
  1252. addChunk(name) {
  1253. if (name) {
  1254. const chunk = this.namedChunks.get(name);
  1255. if (chunk !== undefined) {
  1256. return chunk;
  1257. }
  1258. }
  1259. const chunk = new Chunk(name);
  1260. this.chunks.push(chunk);
  1261. if (name) {
  1262. this.namedChunks.set(name, chunk);
  1263. }
  1264. return chunk;
  1265. }
  1266. /**
  1267. * @param {Module} module module to assign depth
  1268. * @returns {void}
  1269. */
  1270. assignDepth(module) {
  1271. const queue = new Set([module]);
  1272. let depth;
  1273. module.depth = 0;
  1274. /**
  1275. * @param {Module} module module for processeing
  1276. * @returns {void}
  1277. */
  1278. const enqueueJob = module => {
  1279. const d = module.depth;
  1280. if (typeof d === "number" && d <= depth) return;
  1281. queue.add(module);
  1282. module.depth = depth;
  1283. };
  1284. /**
  1285. * @param {Dependency} dependency dependency to assign depth to
  1286. * @returns {void}
  1287. */
  1288. const assignDepthToDependency = dependency => {
  1289. if (dependency.module) {
  1290. enqueueJob(dependency.module);
  1291. }
  1292. };
  1293. /**
  1294. * @param {DependenciesBlock} block block to assign depth to
  1295. * @returns {void}
  1296. */
  1297. const assignDepthToDependencyBlock = block => {
  1298. if (block.variables) {
  1299. iterationBlockVariable(block.variables, assignDepthToDependency);
  1300. }
  1301. if (block.dependencies) {
  1302. iterationOfArrayCallback(block.dependencies, assignDepthToDependency);
  1303. }
  1304. if (block.blocks) {
  1305. iterationOfArrayCallback(block.blocks, assignDepthToDependencyBlock);
  1306. }
  1307. };
  1308. for (module of queue) {
  1309. queue.delete(module);
  1310. depth = module.depth;
  1311. depth++;
  1312. assignDepthToDependencyBlock(module);
  1313. }
  1314. }
  1315. /**
  1316. * @param {Module} module the module containing the dependency
  1317. * @param {Dependency} dependency the dependency
  1318. * @returns {DependencyReference} a reference for the dependency
  1319. */
  1320. getDependencyReference(module, dependency) {
  1321. // TODO remove dep.getReference existence check in webpack 5
  1322. if (typeof dependency.getReference !== "function") return null;
  1323. const ref = dependency.getReference();
  1324. if (!ref) return null;
  1325. return this.hooks.dependencyReference.call(ref, dependency, module);
  1326. }
  1327. /**
  1328. * This method creates the Chunk graph from the Module graph
  1329. * @private
  1330. * @param {TODO[]} inputChunkGroups chunk groups which are processed
  1331. * @returns {void}
  1332. */
  1333. processDependenciesBlocksForChunkGroups(inputChunkGroups) {
  1334. // Process is splitting into two parts:
  1335. // Part one traverse the module graph and builds a very basic chunks graph
  1336. // in chunkDependencies.
  1337. // Part two traverse every possible way through the basic chunk graph and
  1338. // tracks the available modules. While traversing it connects chunks with
  1339. // eachother and Blocks with Chunks. It stops traversing when all modules
  1340. // for a chunk are already available. So it doesn't connect unneeded chunks.
  1341. /** @type {Map<ChunkGroup, {block: AsyncDependenciesBlock, chunkGroup: ChunkGroup}[]>} */
  1342. const chunkDependencies = new Map();
  1343. const allCreatedChunkGroups = new Set();
  1344. // PREPARE
  1345. /** @type {Map<DependenciesBlock, { modules: Module[], blocks: AsyncDependenciesBlock[]}>} */
  1346. const blockInfoMap = new Map();
  1347. /**
  1348. * @param {Dependency} d dependency to iterate over
  1349. * @returns {void}
  1350. */
  1351. const iteratorDependency = d => {
  1352. // We skip Dependencies without Reference
  1353. const ref = this.getDependencyReference(currentModule, d);
  1354. if (!ref) {
  1355. return;
  1356. }
  1357. // We skip Dependencies without Module pointer
  1358. const refModule = ref.module;
  1359. if (!refModule) {
  1360. return;
  1361. }
  1362. // We skip weak Dependencies
  1363. if (ref.weak) {
  1364. return;
  1365. }
  1366. blockInfoModules.add(refModule);
  1367. };
  1368. /**
  1369. * @param {AsyncDependenciesBlock} b blocks to prepare
  1370. * @returns {void}
  1371. */
  1372. const iteratorBlockPrepare = b => {
  1373. blockInfoBlocks.push(b);
  1374. blockQueue.push(b);
  1375. };
  1376. /** @type {Module} */
  1377. let currentModule;
  1378. /** @type {DependenciesBlock} */
  1379. let block;
  1380. /** @type {DependenciesBlock[]} */
  1381. let blockQueue;
  1382. /** @type {Set<Module>} */
  1383. let blockInfoModules;
  1384. /** @type {AsyncDependenciesBlock[]} */
  1385. let blockInfoBlocks;
  1386. for (const module of this.modules) {
  1387. blockQueue = [module];
  1388. currentModule = module;
  1389. while (blockQueue.length > 0) {
  1390. block = blockQueue.pop();
  1391. blockInfoModules = new Set();
  1392. blockInfoBlocks = [];
  1393. if (block.variables) {
  1394. iterationBlockVariable(block.variables, iteratorDependency);
  1395. }
  1396. if (block.dependencies) {
  1397. iterationOfArrayCallback(block.dependencies, iteratorDependency);
  1398. }
  1399. if (block.blocks) {
  1400. iterationOfArrayCallback(block.blocks, iteratorBlockPrepare);
  1401. }
  1402. const blockInfo = {
  1403. modules: Array.from(blockInfoModules),
  1404. blocks: blockInfoBlocks
  1405. };
  1406. blockInfoMap.set(block, blockInfo);
  1407. }
  1408. }
  1409. // PART ONE
  1410. /** @type {Map<ChunkGroup, { index: number, index2: number }>} */
  1411. const chunkGroupCounters = new Map();
  1412. for (const chunkGroup of inputChunkGroups) {
  1413. chunkGroupCounters.set(chunkGroup, { index: 0, index2: 0 });
  1414. }
  1415. let nextFreeModuleIndex = 0;
  1416. let nextFreeModuleIndex2 = 0;
  1417. /** @type {Map<DependenciesBlock, ChunkGroup>} */
  1418. const blockChunkGroups = new Map();
  1419. /** @type {Set<DependenciesBlock>} */
  1420. const blocksWithNestedBlocks = new Set();
  1421. const ADD_AND_ENTER_MODULE = 0;
  1422. const ENTER_MODULE = 1;
  1423. const PROCESS_BLOCK = 2;
  1424. const LEAVE_MODULE = 3;
  1425. /**
  1426. * @typedef {Object} QueueItem
  1427. * @property {number} action
  1428. * @property {DependenciesBlock} block
  1429. * @property {Module} module
  1430. * @property {Chunk} chunk
  1431. * @property {ChunkGroup} chunkGroup
  1432. */
  1433. /**
  1434. * @param {ChunkGroup} chunkGroup chunk group
  1435. * @returns {QueueItem} queue item
  1436. */
  1437. const chunkGroupToQueueItem = chunkGroup => ({
  1438. action: ENTER_MODULE,
  1439. block: chunkGroup.chunks[0].entryModule,
  1440. module: chunkGroup.chunks[0].entryModule,
  1441. chunk: chunkGroup.chunks[0],
  1442. chunkGroup
  1443. });
  1444. // Start with the provided modules/chunks
  1445. /** @type {QueueItem[]} */
  1446. let queue = inputChunkGroups.map(chunkGroupToQueueItem).reverse();
  1447. /** @type {QueueItem[]} */
  1448. let queueDelayed = [];
  1449. /** @type {Module} */
  1450. let module;
  1451. /** @type {Chunk} */
  1452. let chunk;
  1453. /** @type {ChunkGroup} */
  1454. let chunkGroup;
  1455. // For each async Block in graph
  1456. /**
  1457. * @param {AsyncDependenciesBlock} b iterating over each Async DepBlock
  1458. * @returns {void}
  1459. */
  1460. const iteratorBlock = b => {
  1461. // 1. We create a chunk for this Block
  1462. // but only once (blockChunkGroups map)
  1463. let c = blockChunkGroups.get(b);
  1464. if (c === undefined) {
  1465. c = this.namedChunkGroups.get(b.chunkName);
  1466. if (c && c.isInitial()) {
  1467. this.errors.push(
  1468. new AsyncDependencyToInitialChunkError(b.chunkName, module, b.loc)
  1469. );
  1470. c = chunkGroup;
  1471. } else {
  1472. c = this.addChunkInGroup(
  1473. b.groupOptions || b.chunkName,
  1474. module,
  1475. b.loc,
  1476. b.request
  1477. );
  1478. chunkGroupCounters.set(c, { index: 0, index2: 0 });
  1479. blockChunkGroups.set(b, c);
  1480. allCreatedChunkGroups.add(c);
  1481. }
  1482. } else {
  1483. // TODO webpack 5 remove addOptions check
  1484. if (c.addOptions) c.addOptions(b.groupOptions);
  1485. c.addOrigin(module, b.loc, b.request);
  1486. }
  1487. // 2. We store the Block+Chunk mapping as dependency for the chunk
  1488. let deps = chunkDependencies.get(chunkGroup);
  1489. if (!deps) chunkDependencies.set(chunkGroup, (deps = []));
  1490. deps.push({
  1491. block: b,
  1492. chunkGroup: c
  1493. });
  1494. // 3. We enqueue the DependenciesBlock for traversal
  1495. queueDelayed.push({
  1496. action: PROCESS_BLOCK,
  1497. block: b,
  1498. module: module,
  1499. chunk: c.chunks[0],
  1500. chunkGroup: c
  1501. });
  1502. };
  1503. // Iterative traversal of the Module graph
  1504. // Recursive would be simpler to write but could result in Stack Overflows
  1505. while (queue.length) {
  1506. while (queue.length) {
  1507. const queueItem = queue.pop();
  1508. module = queueItem.module;
  1509. block = queueItem.block;
  1510. chunk = queueItem.chunk;
  1511. chunkGroup = queueItem.chunkGroup;
  1512. switch (queueItem.action) {
  1513. case ADD_AND_ENTER_MODULE: {
  1514. // We connect Module and Chunk when not already done
  1515. if (chunk.addModule(module)) {
  1516. module.addChunk(chunk);
  1517. } else {
  1518. // already connected, skip it
  1519. break;
  1520. }
  1521. }
  1522. // fallthrough
  1523. case ENTER_MODULE: {
  1524. if (chunkGroup !== undefined) {
  1525. const index = chunkGroup.getModuleIndex(module);
  1526. if (index === undefined) {
  1527. chunkGroup.setModuleIndex(
  1528. module,
  1529. chunkGroupCounters.get(chunkGroup).index++
  1530. );
  1531. }
  1532. }
  1533. if (module.index === null) {
  1534. module.index = nextFreeModuleIndex++;
  1535. }
  1536. queue.push({
  1537. action: LEAVE_MODULE,
  1538. block,
  1539. module,
  1540. chunk,
  1541. chunkGroup
  1542. });
  1543. }
  1544. // fallthrough
  1545. case PROCESS_BLOCK: {
  1546. // get prepared block info
  1547. const blockInfo = blockInfoMap.get(block);
  1548. // Traverse all referenced modules
  1549. for (let i = blockInfo.modules.length - 1; i >= 0; i--) {
  1550. const refModule = blockInfo.modules[i];
  1551. if (chunk.containsModule(refModule)) {
  1552. // skip early if already connected
  1553. continue;
  1554. }
  1555. // enqueue the add and enter to enter in the correct order
  1556. // this is relevant with circular dependencies
  1557. queue.push({
  1558. action: ADD_AND_ENTER_MODULE,
  1559. block: refModule,
  1560. module: refModule,
  1561. chunk,
  1562. chunkGroup
  1563. });
  1564. }
  1565. // Traverse all Blocks
  1566. iterationOfArrayCallback(blockInfo.blocks, iteratorBlock);
  1567. if (blockInfo.blocks.length > 0 && module !== block) {
  1568. blocksWithNestedBlocks.add(block);
  1569. }
  1570. break;
  1571. }
  1572. case LEAVE_MODULE: {
  1573. if (chunkGroup !== undefined) {
  1574. const index = chunkGroup.getModuleIndex2(module);
  1575. if (index === undefined) {
  1576. chunkGroup.setModuleIndex2(
  1577. module,
  1578. chunkGroupCounters.get(chunkGroup).index2++
  1579. );
  1580. }
  1581. }
  1582. if (module.index2 === null) {
  1583. module.index2 = nextFreeModuleIndex2++;
  1584. }
  1585. break;
  1586. }
  1587. }
  1588. }
  1589. const tempQueue = queue;
  1590. queue = queueDelayed.reverse();
  1591. queueDelayed = tempQueue;
  1592. }
  1593. // PART TWO
  1594. /** @type {Set<Module>} */
  1595. let availableModules;
  1596. let newAvailableModules;
  1597. /** @type {Queue<AvailableModulesChunkGroupMapping>} */
  1598. const queue2 = new Queue(
  1599. inputChunkGroups.map(chunkGroup => ({
  1600. chunkGroup,
  1601. availableModules: new Set()
  1602. }))
  1603. );
  1604. /**
  1605. * Helper function to check if all modules of a chunk are available
  1606. *
  1607. * @param {ChunkGroup} chunkGroup the chunkGroup to scan
  1608. * @param {Set<Module>} availableModules the comparitor set
  1609. * @returns {boolean} return true if all modules of a chunk are available
  1610. */
  1611. const areModulesAvailable = (chunkGroup, availableModules) => {
  1612. for (const chunk of chunkGroup.chunks) {
  1613. for (const module of chunk.modulesIterable) {
  1614. if (!availableModules.has(module)) return false;
  1615. }
  1616. }
  1617. return true;
  1618. };
  1619. // For each edge in the basic chunk graph
  1620. /**
  1621. * @param {TODO} dep the dependency used for filtering
  1622. * @returns {boolean} used to filter "edges" (aka Dependencies) that were pointing
  1623. * to modules that are already available. Also filters circular dependencies in the chunks graph
  1624. */
  1625. const filterFn = dep => {
  1626. const depChunkGroup = dep.chunkGroup;
  1627. if (blocksWithNestedBlocks.has(dep.block)) return true;
  1628. if (areModulesAvailable(depChunkGroup, newAvailableModules)) return false; // break all modules are already available
  1629. return true;
  1630. };
  1631. /** @type {Map<ChunkGroup, Set<Module>>} */
  1632. const minAvailableModulesMap = new Map();
  1633. // Iterative traversing of the basic chunk graph
  1634. while (queue2.length) {
  1635. const queueItem = queue2.dequeue();
  1636. chunkGroup = queueItem.chunkGroup;
  1637. availableModules = queueItem.availableModules;
  1638. // 1. Get minimal available modules
  1639. // It doesn't make sense to traverse a chunk again with more available modules.
  1640. // This step calculates the minimal available modules and skips traversal when
  1641. // the list didn't shrink.
  1642. let minAvailableModules = minAvailableModulesMap.get(chunkGroup);
  1643. if (minAvailableModules === undefined) {
  1644. minAvailableModulesMap.set(chunkGroup, new Set(availableModules));
  1645. } else {
  1646. let deletedModules = false;
  1647. for (const m of minAvailableModules) {
  1648. if (!availableModules.has(m)) {
  1649. minAvailableModules.delete(m);
  1650. deletedModules = true;
  1651. }
  1652. }
  1653. if (!deletedModules) continue;
  1654. availableModules = minAvailableModules;
  1655. }
  1656. // 2. Get the edges at this point of the graph
  1657. const deps = chunkDependencies.get(chunkGroup);
  1658. if (!deps) continue;
  1659. if (deps.length === 0) continue;
  1660. // 3. Create a new Set of available modules at this points
  1661. newAvailableModules = new Set(availableModules);
  1662. for (const chunk of chunkGroup.chunks) {
  1663. for (const m of chunk.modulesIterable) {
  1664. newAvailableModules.add(m);
  1665. }
  1666. }
  1667. // 4. Filter edges with available modules
  1668. const filteredDeps = deps.filter(filterFn);
  1669. // 5. Foreach remaining edge
  1670. const nextChunkGroups = new Set();
  1671. for (let i = 0; i < filteredDeps.length; i++) {
  1672. const dep = filteredDeps[i];
  1673. const depChunkGroup = dep.chunkGroup;
  1674. const depBlock = dep.block;
  1675. // 6. Connect block with chunk
  1676. GraphHelpers.connectDependenciesBlockAndChunkGroup(
  1677. depBlock,
  1678. depChunkGroup
  1679. );
  1680. // 7. Connect chunk with parent
  1681. GraphHelpers.connectChunkGroupParentAndChild(chunkGroup, depChunkGroup);
  1682. nextChunkGroups.add(depChunkGroup);
  1683. }
  1684. // 8. Enqueue further traversal
  1685. for (const nextChunkGroup of nextChunkGroups) {
  1686. queue2.enqueue({
  1687. chunkGroup: nextChunkGroup,
  1688. availableModules: newAvailableModules
  1689. });
  1690. }
  1691. }
  1692. // Remove all unconnected chunk groups
  1693. for (const chunkGroup of allCreatedChunkGroups) {
  1694. if (chunkGroup.getNumberOfParents() === 0) {
  1695. for (const chunk of chunkGroup.chunks) {
  1696. const idx = this.chunks.indexOf(chunk);
  1697. if (idx >= 0) this.chunks.splice(idx, 1);
  1698. chunk.remove("unconnected");
  1699. }
  1700. chunkGroup.remove("unconnected");
  1701. }
  1702. }
  1703. }
  1704. /**
  1705. *
  1706. * @param {Module} module module relationship for removal
  1707. * @param {DependenciesBlockLike} block //TODO: good description
  1708. * @returns {void}
  1709. */
  1710. removeReasonsOfDependencyBlock(module, block) {
  1711. const iteratorDependency = d => {
  1712. if (!d.module) {
  1713. return;
  1714. }
  1715. if (d.module.removeReason(module, d)) {
  1716. for (const chunk of d.module.chunksIterable) {
  1717. this.patchChunksAfterReasonRemoval(d.module, chunk);
  1718. }
  1719. }
  1720. };
  1721. if (block.blocks) {
  1722. iterationOfArrayCallback(block.blocks, block =>
  1723. this.removeReasonsOfDependencyBlock(module, block)
  1724. );
  1725. }
  1726. if (block.dependencies) {
  1727. iterationOfArrayCallback(block.dependencies, iteratorDependency);
  1728. }
  1729. if (block.variables) {
  1730. iterationBlockVariable(block.variables, iteratorDependency);
  1731. }
  1732. }
  1733. /**
  1734. * @param {Module} module module to patch tie
  1735. * @param {Chunk} chunk chunk to patch tie
  1736. * @returns {void}
  1737. */
  1738. patchChunksAfterReasonRemoval(module, chunk) {
  1739. if (!module.hasReasons()) {
  1740. this.removeReasonsOfDependencyBlock(module, module);
  1741. }
  1742. if (!module.hasReasonForChunk(chunk)) {
  1743. if (module.removeChunk(chunk)) {
  1744. this.removeChunkFromDependencies(module, chunk);
  1745. }
  1746. }
  1747. }
  1748. /**
  1749. *
  1750. * @param {DependenciesBlock} block block tie for Chunk
  1751. * @param {Chunk} chunk chunk to remove from dep
  1752. * @returns {void}
  1753. */
  1754. removeChunkFromDependencies(block, chunk) {
  1755. const iteratorDependency = d => {
  1756. if (!d.module) {
  1757. return;
  1758. }
  1759. this.patchChunksAfterReasonRemoval(d.module, chunk);
  1760. };
  1761. const blocks = block.blocks;
  1762. for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
  1763. const asyncBlock = blocks[indexBlock];
  1764. // Grab all chunks from the first Block's AsyncDepBlock
  1765. const chunks = asyncBlock.chunkGroup.chunks;
  1766. // For each chunk in chunkGroup
  1767. for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
  1768. const iteratedChunk = chunks[indexChunk];
  1769. asyncBlock.chunkGroup.removeChunk(iteratedChunk);
  1770. asyncBlock.chunkGroup.removeParent(iteratedChunk);
  1771. // Recurse
  1772. this.removeChunkFromDependencies(block, iteratedChunk);
  1773. }
  1774. }
  1775. if (block.dependencies) {
  1776. iterationOfArrayCallback(block.dependencies, iteratorDependency);
  1777. }
  1778. if (block.variables) {
  1779. iterationBlockVariable(block.variables, iteratorDependency);
  1780. }
  1781. }
  1782. applyModuleIds() {
  1783. const unusedIds = [];
  1784. let nextFreeModuleId = 0;
  1785. const usedIds = new Set();
  1786. if (this.usedModuleIds) {
  1787. for (const id of this.usedModuleIds) {
  1788. usedIds.add(id);
  1789. }
  1790. }
  1791. const modules1 = this.modules;
  1792. for (let indexModule1 = 0; indexModule1 < modules1.length; indexModule1++) {
  1793. const module1 = modules1[indexModule1];
  1794. if (module1.id !== null) {
  1795. usedIds.add(module1.id);
  1796. }
  1797. }
  1798. if (usedIds.size > 0) {
  1799. let usedIdMax = -1;
  1800. for (const usedIdKey of usedIds) {
  1801. if (typeof usedIdKey !== "number") {
  1802. continue;
  1803. }
  1804. usedIdMax = Math.max(usedIdMax, usedIdKey);
  1805. }
  1806. let lengthFreeModules = (nextFreeModuleId = usedIdMax + 1);
  1807. while (lengthFreeModules--) {
  1808. if (!usedIds.has(lengthFreeModules)) {
  1809. unusedIds.push(lengthFreeModules);
  1810. }
  1811. }
  1812. }
  1813. const modules2 = this.modules;
  1814. for (let indexModule2 = 0; indexModule2 < modules2.length; indexModule2++) {
  1815. const module2 = modules2[indexModule2];
  1816. if (module2.id === null) {
  1817. if (unusedIds.length > 0) {
  1818. module2.id = unusedIds.pop();
  1819. } else {
  1820. module2.id = nextFreeModuleId++;
  1821. }
  1822. }
  1823. }
  1824. }
  1825. applyChunkIds() {
  1826. /** @type {Set<number>} */
  1827. const usedIds = new Set();
  1828. // Get used ids from usedChunkIds property (i. e. from records)
  1829. if (this.usedChunkIds) {
  1830. for (const id of this.usedChunkIds) {
  1831. if (typeof id !== "number") {
  1832. continue;
  1833. }
  1834. usedIds.add(id);
  1835. }
  1836. }
  1837. // Get used ids from existing chunks
  1838. const chunks = this.chunks;
  1839. for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
  1840. const chunk = chunks[indexChunk];
  1841. const usedIdValue = chunk.id;
  1842. if (typeof usedIdValue !== "number") {
  1843. continue;
  1844. }
  1845. usedIds.add(usedIdValue);
  1846. }
  1847. // Calculate maximum assigned chunk id
  1848. let nextFreeChunkId = -1;
  1849. for (const id of usedIds) {
  1850. nextFreeChunkId = Math.max(nextFreeChunkId, id);
  1851. }
  1852. nextFreeChunkId++;
  1853. // Determine free chunk ids from 0 to maximum
  1854. /** @type {number[]} */
  1855. const unusedIds = [];
  1856. if (nextFreeChunkId > 0) {
  1857. let index = nextFreeChunkId;
  1858. while (index--) {
  1859. if (!usedIds.has(index)) {
  1860. unusedIds.push(index);
  1861. }
  1862. }
  1863. }
  1864. // Assign ids to chunk which has no id
  1865. for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
  1866. const chunk = chunks[indexChunk];
  1867. if (chunk.id === null) {
  1868. if (unusedIds.length > 0) {
  1869. chunk.id = unusedIds.pop();
  1870. } else {
  1871. chunk.id = nextFreeChunkId++;
  1872. }
  1873. }
  1874. if (!chunk.ids) {
  1875. chunk.ids = [chunk.id];
  1876. }
  1877. }
  1878. }
  1879. sortItemsWithModuleIds() {
  1880. this.modules.sort(byIdOrIdentifier);
  1881. const modules = this.modules;
  1882. for (let indexModule = 0; indexModule < modules.length; indexModule++) {
  1883. modules[indexModule].sortItems(false);
  1884. }
  1885. const chunks = this.chunks;
  1886. for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
  1887. chunks[indexChunk].sortItems();
  1888. }
  1889. }
  1890. sortItemsWithChunkIds() {
  1891. for (const chunkGroup of this.chunkGroups) {
  1892. chunkGroup.sortItems();
  1893. }
  1894. this.chunks.sort(byId);
  1895. for (
  1896. let indexModule = 0;
  1897. indexModule < this.modules.length;
  1898. indexModule++
  1899. ) {
  1900. this.modules[indexModule].sortItems(true);
  1901. }
  1902. const chunks = this.chunks;
  1903. for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
  1904. chunks[indexChunk].sortItems();
  1905. }
  1906. /**
  1907. * Used to sort errors and warnings in compilation. this.warnings, and
  1908. * this.errors contribute to the compilation hash and therefore should be
  1909. * updated whenever other references (having a chunk id) are sorted. This preserves the hash
  1910. * integrity
  1911. *
  1912. * @param {WebpackError} a first WebpackError instance (including subclasses)
  1913. * @param {WebpackError} b second WebpackError instance (including subclasses)
  1914. * @returns {-1|0|1} sort order index
  1915. */
  1916. const byMessage = (a, b) => {
  1917. const ma = `${a.message}`;
  1918. const mb = `${b.message}`;
  1919. if (ma < mb) return -1;
  1920. if (mb < ma) return 1;
  1921. return 0;
  1922. };
  1923. this.errors.sort(byMessage);
  1924. this.warnings.sort(byMessage);
  1925. this.children.sort(byNameOrHash);
  1926. }
  1927. summarizeDependencies() {
  1928. this.fileDependencies = new SortableSet(this.compilationDependencies);
  1929. this.contextDependencies = new SortableSet();
  1930. this.missingDependencies = new SortableSet();
  1931. for (
  1932. let indexChildren = 0;
  1933. indexChildren < this.children.length;
  1934. indexChildren++
  1935. ) {
  1936. const child = this.children[indexChildren];
  1937. addAllToSet(this.fileDependencies, child.fileDependencies);
  1938. addAllToSet(this.contextDependencies, child.contextDependencies);
  1939. addAllToSet(this.missingDependencies, child.missingDependencies);
  1940. }
  1941. for (
  1942. let indexModule = 0;
  1943. indexModule < this.modules.length;
  1944. indexModule++
  1945. ) {
  1946. const module = this.modules[indexModule];
  1947. if (module.buildInfo.fileDependencies) {
  1948. addAllToSet(this.fileDependencies, module.buildInfo.fileDependencies);
  1949. }
  1950. if (module.buildInfo.contextDependencies) {
  1951. addAllToSet(
  1952. this.contextDependencies,
  1953. module.buildInfo.contextDependencies
  1954. );
  1955. }
  1956. }
  1957. for (const error of this.errors) {
  1958. if (
  1959. typeof error.missing === "object" &&
  1960. error.missing &&
  1961. error.missing[Symbol.iterator]
  1962. ) {
  1963. addAllToSet(this.missingDependencies, error.missing);
  1964. }
  1965. }
  1966. this.fileDependencies.sort();
  1967. this.contextDependencies.sort();
  1968. this.missingDependencies.sort();
  1969. }
  1970. createHash() {
  1971. const outputOptions = this.outputOptions;
  1972. const hashFunction = outputOptions.hashFunction;
  1973. const hashDigest = outputOptions.hashDigest;
  1974. const hashDigestLength = outputOptions.hashDigestLength;
  1975. const hash = createHash(hashFunction);
  1976. if (outputOptions.hashSalt) {
  1977. hash.update(outputOptions.hashSalt);
  1978. }
  1979. this.mainTemplate.updateHash(hash);
  1980. this.chunkTemplate.updateHash(hash);
  1981. for (const key of Object.keys(this.moduleTemplates).sort()) {
  1982. this.moduleTemplates[key].updateHash(hash);
  1983. }
  1984. for (const child of this.children) {
  1985. hash.update(child.hash);
  1986. }
  1987. for (const warning of this.warnings) {
  1988. hash.update(`${warning.message}`);
  1989. }
  1990. for (const error of this.errors) {
  1991. hash.update(`${error.message}`);
  1992. }
  1993. const modules = this.modules;
  1994. for (let i = 0; i < modules.length; i++) {
  1995. const module = modules[i];
  1996. const moduleHash = createHash(hashFunction);
  1997. module.updateHash(moduleHash);
  1998. module.hash = moduleHash.digest(hashDigest);
  1999. module.renderedHash = module.hash.substr(0, hashDigestLength);
  2000. }
  2001. // clone needed as sort below is inplace mutation
  2002. const chunks = this.chunks.slice();
  2003. /**
  2004. * sort here will bring all "falsy" values to the beginning
  2005. * this is needed as the "hasRuntime()" chunks are dependent on the
  2006. * hashes of the non-runtime chunks.
  2007. */
  2008. chunks.sort((a, b) => {
  2009. const aEntry = a.hasRuntime();
  2010. const bEntry = b.hasRuntime();
  2011. if (aEntry && !bEntry) return 1;
  2012. if (!aEntry && bEntry) return -1;
  2013. return byId(a, b);
  2014. });
  2015. for (let i = 0; i < chunks.length; i++) {
  2016. const chunk = chunks[i];
  2017. const chunkHash = createHash(hashFunction);
  2018. if (outputOptions.hashSalt) {
  2019. chunkHash.update(outputOptions.hashSalt);
  2020. }
  2021. chunk.updateHash(chunkHash);
  2022. const template = chunk.hasRuntime()
  2023. ? this.mainTemplate
  2024. : this.chunkTemplate;
  2025. template.updateHashForChunk(
  2026. chunkHash,
  2027. chunk,
  2028. this.moduleTemplates.javascript,
  2029. this.dependencyTemplates
  2030. );
  2031. this.hooks.chunkHash.call(chunk, chunkHash);
  2032. chunk.hash = chunkHash.digest(hashDigest);
  2033. hash.update(chunk.hash);
  2034. chunk.renderedHash = chunk.hash.substr(0, hashDigestLength);
  2035. this.hooks.contentHash.call(chunk);
  2036. }
  2037. this.fullHash = hash.digest(hashDigest);
  2038. this.hash = this.fullHash.substr(0, hashDigestLength);
  2039. }
  2040. /**
  2041. * @param {string} update extra information
  2042. * @returns {void}
  2043. */
  2044. modifyHash(update) {
  2045. const outputOptions = this.outputOptions;
  2046. const hashFunction = outputOptions.hashFunction;
  2047. const hashDigest = outputOptions.hashDigest;
  2048. const hashDigestLength = outputOptions.hashDigestLength;
  2049. const hash = createHash(hashFunction);
  2050. hash.update(this.fullHash);
  2051. hash.update(update);
  2052. this.fullHash = hash.digest(hashDigest);
  2053. this.hash = this.fullHash.substr(0, hashDigestLength);
  2054. }
  2055. createModuleAssets() {
  2056. for (let i = 0; i < this.modules.length; i++) {
  2057. const module = this.modules[i];
  2058. if (module.buildInfo.assets) {
  2059. for (const assetName of Object.keys(module.buildInfo.assets)) {
  2060. const fileName = this.getPath(assetName);
  2061. this.assets[fileName] = module.buildInfo.assets[assetName];
  2062. this.hooks.moduleAsset.call(module, fileName);
  2063. }
  2064. }
  2065. }
  2066. }
  2067. createChunkAssets() {
  2068. const outputOptions = this.outputOptions;
  2069. const cachedSourceMap = new Map();
  2070. /** @type {Map<string, {hash: string, source: Source, chunk: Chunk}>} */
  2071. const alreadyWrittenFiles = new Map();
  2072. for (let i = 0; i < this.chunks.length; i++) {
  2073. const chunk = this.chunks[i];
  2074. chunk.files = [];
  2075. let source;
  2076. let file;
  2077. let filenameTemplate;
  2078. try {
  2079. const template = chunk.hasRuntime()
  2080. ? this.mainTemplate
  2081. : this.chunkTemplate;
  2082. const manifest = template.getRenderManifest({
  2083. chunk,
  2084. hash: this.hash,
  2085. fullHash: this.fullHash,
  2086. outputOptions,
  2087. moduleTemplates: this.moduleTemplates,
  2088. dependencyTemplates: this.dependencyTemplates
  2089. }); // [{ render(), filenameTemplate, pathOptions, identifier, hash }]
  2090. for (const fileManifest of manifest) {
  2091. const cacheName = fileManifest.identifier;
  2092. const usedHash = fileManifest.hash;
  2093. filenameTemplate = fileManifest.filenameTemplate;
  2094. file = this.getPath(filenameTemplate, fileManifest.pathOptions);
  2095. // check if the same filename was already written by another chunk
  2096. const alreadyWritten = alreadyWrittenFiles.get(file);
  2097. if (alreadyWritten !== undefined) {
  2098. if (alreadyWritten.hash === usedHash) {
  2099. if (this.cache) {
  2100. this.cache[cacheName] = {
  2101. hash: usedHash,
  2102. source: alreadyWritten.source
  2103. };
  2104. }
  2105. chunk.files.push(file);
  2106. this.hooks.chunkAsset.call(chunk, file);
  2107. continue;
  2108. } else {
  2109. throw new Error(
  2110. `Conflict: Multiple chunks emit assets to the same filename ${file}` +
  2111. ` (chunks ${alreadyWritten.chunk.id} and ${chunk.id})`
  2112. );
  2113. }
  2114. }
  2115. if (
  2116. this.cache &&
  2117. this.cache[cacheName] &&
  2118. this.cache[cacheName].hash === usedHash
  2119. ) {
  2120. source = this.cache[cacheName].source;
  2121. } else {
  2122. source = fileManifest.render();
  2123. // Ensure that source is a cached source to avoid additional cost because of repeated access
  2124. if (!(source instanceof CachedSource)) {
  2125. const cacheEntry = cachedSourceMap.get(source);
  2126. if (cacheEntry) {
  2127. source = cacheEntry;
  2128. } else {
  2129. const cachedSource = new CachedSource(source);
  2130. cachedSourceMap.set(source, cachedSource);
  2131. source = cachedSource;
  2132. }
  2133. }
  2134. if (this.cache) {
  2135. this.cache[cacheName] = {
  2136. hash: usedHash,
  2137. source
  2138. };
  2139. }
  2140. }
  2141. if (this.assets[file] && this.assets[file] !== source) {
  2142. throw new Error(
  2143. `Conflict: Multiple assets emit to the same filename ${file}`
  2144. );
  2145. }
  2146. this.assets[file] = source;
  2147. chunk.files.push(file);
  2148. this.hooks.chunkAsset.call(chunk, file);
  2149. alreadyWrittenFiles.set(file, {
  2150. hash: usedHash,
  2151. source,
  2152. chunk
  2153. });
  2154. }
  2155. } catch (err) {
  2156. this.errors.push(
  2157. new ChunkRenderError(chunk, file || filenameTemplate, err)
  2158. );
  2159. }
  2160. }
  2161. }
  2162. /**
  2163. * @param {string} filename used to get asset path with hash
  2164. * @param {TODO=} data // TODO: figure out this param type
  2165. * @returns {string} interpolated path
  2166. */
  2167. getPath(filename, data) {
  2168. data = data || {};
  2169. data.hash = data.hash || this.hash;
  2170. return this.mainTemplate.getAssetPath(filename, data);
  2171. }
  2172. /**
  2173. * This function allows you to run another instance of webpack inside of webpack however as
  2174. * a child with different settings and configurations (if desired) applied. It copies all hooks, plugins
  2175. * from parent (or top level compiler) and creates a child Compilation
  2176. *
  2177. * @param {string} name name of the child compiler
  2178. * @param {TODO} outputOptions // Need to convert config schema to types for this
  2179. * @param {Plugin[]} plugins webpack plugins that will be applied
  2180. * @returns {Compiler} creates a child Compiler instance
  2181. */
  2182. createChildCompiler(name, outputOptions, plugins) {
  2183. const idx = this.childrenCounters[name] || 0;
  2184. this.childrenCounters[name] = idx + 1;
  2185. return this.compiler.createChildCompiler(
  2186. this,
  2187. name,
  2188. idx,
  2189. outputOptions,
  2190. plugins
  2191. );
  2192. }
  2193. checkConstraints() {
  2194. /** @type {Set<number|string>} */
  2195. const usedIds = new Set();
  2196. const modules = this.modules;
  2197. for (let indexModule = 0; indexModule < modules.length; indexModule++) {
  2198. const moduleId = modules[indexModule].id;
  2199. if (moduleId === null) continue;
  2200. if (usedIds.has(moduleId)) {
  2201. throw new Error(`checkConstraints: duplicate module id ${moduleId}`);
  2202. }
  2203. usedIds.add(moduleId);
  2204. }
  2205. const chunks = this.chunks;
  2206. for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
  2207. const chunk = chunks[indexChunk];
  2208. if (chunks.indexOf(chunk) !== indexChunk) {
  2209. throw new Error(
  2210. `checkConstraints: duplicate chunk in compilation ${chunk.debugId}`
  2211. );
  2212. }
  2213. }
  2214. for (const chunkGroup of this.chunkGroups) {
  2215. chunkGroup.checkConstraints();
  2216. }
  2217. }
  2218. }
  2219. // TODO remove in webpack 5
  2220. Compilation.prototype.applyPlugins = util.deprecate(
  2221. /**
  2222. * @deprecated
  2223. * @param {string} name Name
  2224. * @param {any[]} args Other arguments
  2225. * @returns {void}
  2226. * @this {Compilation}
  2227. */
  2228. function(name, ...args) {
  2229. this.hooks[
  2230. name.replace(/[- ]([a-z])/g, match => match[1].toUpperCase())
  2231. ].call(...args);
  2232. },
  2233. "Compilation.applyPlugins is deprecated. Use new API on `.hooks` instead"
  2234. );
  2235. // TODO remove in webpack 5
  2236. Object.defineProperty(Compilation.prototype, "moduleTemplate", {
  2237. configurable: false,
  2238. get: util.deprecate(
  2239. /**
  2240. * @deprecated
  2241. * @this {Compilation}
  2242. * @returns {TODO} module template
  2243. */
  2244. function() {
  2245. return this.moduleTemplates.javascript;
  2246. },
  2247. "Compilation.moduleTemplate: Use Compilation.moduleTemplates.javascript instead"
  2248. ),
  2249. set: util.deprecate(
  2250. /**
  2251. * @deprecated
  2252. * @param {ModuleTemplate} value Template value
  2253. * @this {Compilation}
  2254. * @returns {void}
  2255. */
  2256. function(value) {
  2257. this.moduleTemplates.javascript = value;
  2258. },
  2259. "Compilation.moduleTemplate: Use Compilation.moduleTemplates.javascript instead."
  2260. )
  2261. });
  2262. module.exports = Compilation;