Compiler.js 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560
  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 {
  8. AsyncParallelHook,
  9. AsyncSeriesHook,
  10. SyncBailHook,
  11. SyncHook
  12. } = require("tapable");
  13. const { SizeOnlySource } = require("webpack-sources");
  14. const Cache = require("./Cache");
  15. const CacheFacade = require("./CacheFacade");
  16. const ChunkGraph = require("./ChunkGraph");
  17. const Compilation = require("./Compilation");
  18. const ContextModuleFactory = require("./ContextModuleFactory");
  19. const ModuleGraph = require("./ModuleGraph");
  20. const NormalModuleFactory = require("./NormalModuleFactory");
  21. const RequestShortener = require("./RequestShortener");
  22. const ResolverFactory = require("./ResolverFactory");
  23. const Stats = require("./Stats");
  24. const WebpackError = require("./errors/WebpackError");
  25. const { Logger } = require("./logging/Logger");
  26. const { dirname, join, mkdirp } = require("./util/fs");
  27. const {
  28. WINDOWS_ABS_PATH_REGEXP,
  29. makePathsRelative
  30. } = require("./util/identifier");
  31. const memoize = require("./util/memoize");
  32. const parseJson = require("./util/parseJson");
  33. const { isSourceEqual } = require("./util/source");
  34. const webpack = require(".");
  35. const getWatching = memoize(() => require("./Watching"));
  36. const getValidate = memoize(() => require("schema-utils").validate);
  37. const getConcurrentCompilationError = memoize(() =>
  38. require("./errors/ConcurrentCompilationError")
  39. );
  40. /** @import { Source } from "webpack-sources" */
  41. /**
  42. * @import {
  43. * EntryNormalized as Entry,
  44. * OutputNormalized as OutputOptions,
  45. * WatchOptions,
  46. * WebpackOptionsNormalized as WebpackOptions,
  47. * Plugins
  48. * } from "../declarations/WebpackOptions"
  49. */
  50. /** @import { WebpackPluginFunction, ErrorCallback } from "./webpack" */
  51. /** @import Chunk from "./Chunk" */
  52. /** @import Dependency from "./Dependency" */
  53. /**
  54. * @import {
  55. * ChunkHashes,
  56. * ChunkModuleHashes,
  57. * ChunkModuleIds,
  58. * ChunkRuntime,
  59. * FullHashChunkModuleHashes,
  60. * HotIndex
  61. * } from "./HotModuleReplacementPlugin"
  62. */
  63. /** @import Module, { BuildInfo } from "./Module" */
  64. /** @import Watching from "./Watching" */
  65. /** @import { RecordsChunks, RecordsModules } from "./RecordIdsPlugin" */
  66. /** @import { PlatformTargetProperties } from "./config/target" */
  67. /** @import { LoggingFunction } from "./logging/createConsoleLogger" */
  68. /** @import { SplitData } from "./optimize/AggressiveSplittingPlugin" */
  69. /**
  70. * @import {
  71. * IStats,
  72. * InputFileSystem,
  73. * IntermediateFileSystem,
  74. * OutputFileSystem,
  75. * TimeInfoEntries,
  76. * WatchFileSystem
  77. * } from "./util/fs"
  78. */
  79. /** @import { Schema, ValidationErrorConfiguration } from "schema-utils" */
  80. /**
  81. * Defines the compilation params type used by this module.
  82. * @typedef {object} CompilationParams
  83. * @property {NormalModuleFactory} normalModuleFactory
  84. * @property {ContextModuleFactory} contextModuleFactory
  85. */
  86. /**
  87. * Defines the callback type used by this module.
  88. * @template T
  89. * @template [R=void]
  90. * @typedef {import("./webpack").Callback<T, R>} Callback
  91. */
  92. /**
  93. * Defines the run as child callback callback.
  94. * @callback RunAsChildCallback
  95. * @param {Error | null} err
  96. * @param {Chunk[]=} entries
  97. * @param {Compilation=} compilation
  98. * @returns {void}
  99. */
  100. /**
  101. * Defines the known records type used by this module.
  102. * @typedef {object} KnownRecords
  103. * @property {SplitData[]=} aggressiveSplits
  104. * @property {RecordsChunks=} chunks
  105. * @property {RecordsModules=} modules
  106. * @property {string=} hash
  107. * @property {HotIndex=} hotIndex
  108. * @property {FullHashChunkModuleHashes=} fullHashChunkModuleHashes
  109. * @property {ChunkModuleHashes=} chunkModuleHashes
  110. * @property {ChunkHashes=} chunkHashes
  111. * @property {ChunkRuntime=} chunkRuntime
  112. * @property {ChunkModuleIds=} chunkModuleIds
  113. */
  114. /** @typedef {KnownRecords & Record<string, KnownRecords[]> & Record<string, EXPECTED_ANY>} Records */
  115. /**
  116. * Defines the asset emitted info type used by this module.
  117. * @typedef {object} AssetEmittedInfo
  118. * @property {Buffer} content
  119. * @property {Source} source
  120. * @property {Compilation} compilation
  121. * @property {string} outputPath
  122. * @property {string} targetPath
  123. */
  124. /** @typedef {{ sizeOnlySource: SizeOnlySource | undefined, writtenTo: Map<string, number> }} CacheEntry */
  125. /** @typedef {{ path: string, source: Source, size: number | undefined, waiting: ({ cacheEntry: CacheEntry, file: string }[] | undefined) }} SimilarEntry */
  126. /** @typedef {WeakMap<Dependency, Module>} WeakReferences */
  127. /** @typedef {import("./util/WeakTupleMap")<EXPECTED_ANY[], EXPECTED_ANY>} MemCache */
  128. /** @typedef {{ buildInfo: BuildInfo, references: WeakReferences | undefined, memCache: MemCache }} ModuleMemCachesItem */
  129. /**
  130. * Checks whether this object is sorted.
  131. * @template T
  132. * @param {T[]} array an array
  133. * @returns {boolean} true, if the array is sorted
  134. */
  135. const isSorted = (array) => {
  136. for (let i = 1; i < array.length; i++) {
  137. if (array[i - 1] > array[i]) return false;
  138. }
  139. return true;
  140. };
  141. /**
  142. * Returns the object with properties sorted by property name.
  143. * @template {object} T
  144. * @param {T} obj an object
  145. * @param {(keyof T)[]} keys the keys of the object
  146. * @returns {T} the object with properties sorted by property name
  147. */
  148. const sortObject = (obj, keys) => {
  149. const o = /** @type {T} */ ({});
  150. for (const k of keys.sort()) {
  151. o[k] = obj[k];
  152. }
  153. return o;
  154. };
  155. /**
  156. * Returns true, if the filename contains any hash.
  157. * @param {string} filename filename
  158. * @param {string | string[] | undefined} hashes list of hashes
  159. * @returns {boolean} true, if the filename contains any hash
  160. */
  161. const includesHash = (filename, hashes) => {
  162. if (!hashes) return false;
  163. if (Array.isArray(hashes)) {
  164. return hashes.some((hash) => filename.includes(hash));
  165. }
  166. return filename.includes(hashes);
  167. };
  168. // only reached by `watch()`, so a one-shot build never loads the watcher
  169. class Compiler {
  170. /**
  171. * Creates an instance of Compiler.
  172. * @param {string} context the compilation path
  173. * @param {WebpackOptions} options options
  174. */
  175. constructor(context, options = /** @type {WebpackOptions} */ ({})) {
  176. this.hooks = Object.freeze({
  177. /** @type {SyncHook<[]>} */
  178. initialize: new SyncHook([]),
  179. /** @type {SyncBailHook<[Compilation], boolean | void>} */
  180. shouldEmit: new SyncBailHook(["compilation"]),
  181. /** @type {AsyncSeriesHook<[Stats]>} */
  182. done: new AsyncSeriesHook(["stats"]),
  183. /** @type {SyncHook<[Stats]>} */
  184. afterDone: new SyncHook(["stats"]),
  185. /** @type {AsyncSeriesHook<[]>} */
  186. additionalPass: new AsyncSeriesHook([]),
  187. /** @type {AsyncSeriesHook<[Compiler]>} */
  188. beforeRun: new AsyncSeriesHook(["compiler"]),
  189. /** @type {AsyncSeriesHook<[Compiler]>} */
  190. run: new AsyncSeriesHook(["compiler"]),
  191. /** @type {AsyncSeriesHook<[Compilation]>} */
  192. emit: new AsyncSeriesHook(["compilation"]),
  193. /** @type {AsyncSeriesHook<[string, AssetEmittedInfo]>} */
  194. assetEmitted: new AsyncSeriesHook(["file", "info"]),
  195. /** @type {AsyncSeriesHook<[Compilation]>} */
  196. afterEmit: new AsyncSeriesHook(["compilation"]),
  197. /** @type {SyncHook<[Compilation, CompilationParams]>} */
  198. thisCompilation: new SyncHook(["compilation", "params"]),
  199. /** @type {SyncHook<[Compilation, CompilationParams]>} */
  200. compilation: new SyncHook(["compilation", "params"]),
  201. /** @type {SyncHook<[NormalModuleFactory]>} */
  202. normalModuleFactory: new SyncHook(["normalModuleFactory"]),
  203. /** @type {SyncHook<[ContextModuleFactory]>} */
  204. contextModuleFactory: new SyncHook(["contextModuleFactory"]),
  205. /** @type {AsyncSeriesHook<[CompilationParams]>} */
  206. beforeCompile: new AsyncSeriesHook(["params"]),
  207. /** @type {SyncHook<[CompilationParams]>} */
  208. compile: new SyncHook(["params"]),
  209. /** @type {AsyncParallelHook<[Compilation]>} */
  210. make: new AsyncParallelHook(["compilation"]),
  211. /** @type {AsyncParallelHook<[Compilation]>} */
  212. finishMake: new AsyncSeriesHook(["compilation"]),
  213. /** @type {AsyncSeriesHook<[Compilation]>} */
  214. afterCompile: new AsyncSeriesHook(["compilation"]),
  215. /**
  216. * @type {AsyncSeriesHook<[]>}
  217. * @since 5.67.0
  218. */
  219. readRecords: new AsyncSeriesHook([]),
  220. /**
  221. * @type {AsyncSeriesHook<[]>}
  222. * @since 5.67.0
  223. */
  224. emitRecords: new AsyncSeriesHook([]),
  225. /** @type {AsyncSeriesHook<[Compiler]>} */
  226. watchRun: new AsyncSeriesHook(["compiler"]),
  227. /** @type {SyncHook<[Error]>} */
  228. failed: new SyncHook(["error"]),
  229. /** @type {SyncHook<[string | null, number]>} */
  230. invalid: new SyncHook(["filename", "changeTime"]),
  231. /** @type {SyncHook<[]>} */
  232. watchClose: new SyncHook([]),
  233. /**
  234. * @type {AsyncSeriesHook<[]>}
  235. * @since 5.17.0
  236. */
  237. shutdown: new AsyncSeriesHook([]),
  238. /** @type {SyncBailHook<[string, string, EXPECTED_ANY[] | undefined], true | void>} */
  239. infrastructureLog: new SyncBailHook(["origin", "type", "args"]),
  240. // TODO the following hooks are weirdly located here
  241. // TODO move them for webpack 5
  242. /**
  243. * @type {SyncHook<[]>}
  244. * @since 5.106.0
  245. */
  246. validate: new SyncHook([]),
  247. /** @type {SyncHook<[]>} */
  248. environment: new SyncHook([]),
  249. /** @type {SyncHook<[]>} */
  250. afterEnvironment: new SyncHook([]),
  251. /** @type {SyncHook<[Compiler]>} */
  252. afterPlugins: new SyncHook(["compiler"]),
  253. /** @type {SyncHook<[Compiler]>} */
  254. afterResolvers: new SyncHook(["compiler"]),
  255. /** @type {SyncBailHook<[string, Entry], boolean | void>} */
  256. entryOption: new SyncBailHook(["context", "entry"])
  257. });
  258. this.webpack = webpack;
  259. /** @type {string | undefined} */
  260. this.name = undefined;
  261. /** @type {Compilation | undefined} */
  262. this.parentCompilation = undefined;
  263. /** @type {Compiler} */
  264. this.root = this;
  265. /** @type {string} */
  266. this.outputPath = "";
  267. /** @type {Watching | undefined} */
  268. this.watching = undefined;
  269. /** @type {OutputFileSystem | null} */
  270. this.outputFileSystem = null;
  271. /** @type {IntermediateFileSystem | null} */
  272. this.intermediateFileSystem = null;
  273. /** @type {InputFileSystem | null} */
  274. this.inputFileSystem = null;
  275. /** @type {WatchFileSystem | null} */
  276. this.watchFileSystem = null;
  277. /** @type {string | null} */
  278. this.recordsInputPath = null;
  279. /** @type {string | null} */
  280. this.recordsOutputPath = null;
  281. /** @type {Records} */
  282. this.records = {};
  283. /** @type {Set<string | RegExp>} */
  284. this.managedPaths = new Set();
  285. /** @type {Set<string | RegExp>} */
  286. this.unmanagedPaths = new Set();
  287. /** @type {Set<string | RegExp>} */
  288. this.immutablePaths = new Set();
  289. /** @type {ReadonlySet<string> | undefined} */
  290. this.modifiedFiles = undefined;
  291. /** @type {ReadonlySet<string> | undefined} */
  292. this.removedFiles = undefined;
  293. /** @type {TimeInfoEntries | undefined} */
  294. this.fileTimestamps = undefined;
  295. /** @type {TimeInfoEntries | undefined} */
  296. this.contextTimestamps = undefined;
  297. /** @type {number | undefined} */
  298. this.fsStartTime = undefined;
  299. /** @type {ResolverFactory} */
  300. this.resolverFactory = new ResolverFactory();
  301. /** @type {LoggingFunction | undefined} */
  302. this.infrastructureLogger = undefined;
  303. /** @type {Readonly<PlatformTargetProperties>} */
  304. this.platform = {
  305. web: null,
  306. browser: null,
  307. webworker: null,
  308. node: null,
  309. deno: null,
  310. bun: null,
  311. nwjs: null,
  312. electron: null,
  313. universal: null
  314. };
  315. this.options = options;
  316. /** @type {string} */
  317. this.context = context;
  318. /** @type {RequestShortener} */
  319. this.requestShortener = new RequestShortener(context, this.root);
  320. /** @type {Cache} */
  321. this.cache = new Cache();
  322. /** @type {Map<Module, ModuleMemCachesItem> | undefined} */
  323. this.moduleMemCaches = undefined;
  324. /** @type {string} */
  325. this.compilerPath = "";
  326. /** @type {boolean} */
  327. this.running = false;
  328. /** @type {boolean} */
  329. this.idle = false;
  330. /** @type {boolean} */
  331. this.watchMode = false;
  332. /** @type {boolean} */
  333. this._backCompat = this.options.experiments.backCompat !== false;
  334. /** @type {Compilation | undefined} */
  335. this._lastCompilation = undefined;
  336. /** @type {NormalModuleFactory | undefined} */
  337. this._lastNormalModuleFactory = undefined;
  338. /**
  339. * @private
  340. * @type {WeakMap<Source, CacheEntry>}
  341. */
  342. this._assetEmittingSourceCache = new WeakMap();
  343. /**
  344. * @private
  345. * @type {Map<string, number>}
  346. */
  347. this._assetEmittingWrittenFiles = new Map();
  348. /**
  349. * @private
  350. * @type {Set<string>}
  351. */
  352. this._assetEmittingPreviousFiles = new Set();
  353. }
  354. /**
  355. * Returns the cache facade instance.
  356. * @param {string} name cache name
  357. * @returns {CacheFacade} the cache facade instance
  358. */
  359. getCache(name) {
  360. return new CacheFacade(
  361. this.cache,
  362. `${this.compilerPath}${name}`,
  363. this.options.output.hashFunction
  364. );
  365. }
  366. /**
  367. * Gets infrastructure logger.
  368. * @param {string | (() => string)} name name of the logger, or function called once to get the logger name
  369. * @returns {Logger} a logger with that name
  370. */
  371. getInfrastructureLogger(name) {
  372. if (!name) {
  373. throw new TypeError(
  374. "Compiler.getInfrastructureLogger(name) called without a name"
  375. );
  376. }
  377. return new Logger(
  378. (type, args) => {
  379. if (typeof name === "function") {
  380. name = name();
  381. if (!name) {
  382. throw new TypeError(
  383. "Compiler.getInfrastructureLogger(name) called with a function not returning a name"
  384. );
  385. }
  386. }
  387. if (
  388. this.hooks.infrastructureLog.call(name, type, args) === undefined &&
  389. this.infrastructureLogger !== undefined
  390. ) {
  391. this.infrastructureLogger(name, type, args);
  392. }
  393. },
  394. (childName) => {
  395. if (typeof name === "function") {
  396. if (typeof childName === "function") {
  397. return this.getInfrastructureLogger(() => {
  398. if (typeof name === "function") {
  399. name = name();
  400. if (!name) {
  401. throw new TypeError(
  402. "Compiler.getInfrastructureLogger(name) called with a function not returning a name"
  403. );
  404. }
  405. }
  406. if (typeof childName === "function") {
  407. childName = childName();
  408. if (!childName) {
  409. throw new TypeError(
  410. "Logger.getChildLogger(name) called with a function not returning a name"
  411. );
  412. }
  413. }
  414. return `${name}/${childName}`;
  415. });
  416. }
  417. return this.getInfrastructureLogger(() => {
  418. if (typeof name === "function") {
  419. name = name();
  420. if (!name) {
  421. throw new TypeError(
  422. "Compiler.getInfrastructureLogger(name) called with a function not returning a name"
  423. );
  424. }
  425. }
  426. return `${name}/${childName}`;
  427. });
  428. }
  429. if (typeof childName === "function") {
  430. return this.getInfrastructureLogger(() => {
  431. if (typeof childName === "function") {
  432. childName = childName();
  433. if (!childName) {
  434. throw new TypeError(
  435. "Logger.getChildLogger(name) called with a function not returning a name"
  436. );
  437. }
  438. }
  439. return `${name}/${childName}`;
  440. });
  441. }
  442. return this.getInfrastructureLogger(`${name}/${childName}`);
  443. }
  444. );
  445. }
  446. // TODO webpack 6: solve this in a better way
  447. // e.g. move compilation specific info from Modules into ModuleGraph
  448. _cleanupLastCompilation() {
  449. if (this._lastCompilation !== undefined) {
  450. for (const childCompilation of this._lastCompilation.children) {
  451. for (const module of childCompilation.modules) {
  452. ChunkGraph.clearChunkGraphForModule(module);
  453. ModuleGraph.clearModuleGraphForModule(module);
  454. module.cleanupForCache();
  455. }
  456. for (const chunk of childCompilation.chunks) {
  457. ChunkGraph.clearChunkGraphForChunk(chunk);
  458. }
  459. }
  460. for (const module of this._lastCompilation.modules) {
  461. ChunkGraph.clearChunkGraphForModule(module);
  462. ModuleGraph.clearModuleGraphForModule(module);
  463. module.cleanupForCache();
  464. }
  465. for (const chunk of this._lastCompilation.chunks) {
  466. ChunkGraph.clearChunkGraphForChunk(chunk);
  467. }
  468. this._lastCompilation = undefined;
  469. }
  470. }
  471. // TODO webpack 6: solve this in a better way
  472. _cleanupLastNormalModuleFactory() {
  473. if (this._lastNormalModuleFactory !== undefined) {
  474. this._lastNormalModuleFactory.cleanupForCache();
  475. this._lastNormalModuleFactory = undefined;
  476. }
  477. }
  478. /**
  479. * Release fields on a finished compilation that nothing reads after emit,
  480. * so the heap can shrink while user code still holds the Stats reference.
  481. * Recurses into child compilations. Stats output is preserved — only
  482. * codeGen byproducts are dropped.
  483. * @param {Compilation} compilation finished compilation to slim down
  484. * @returns {void}
  485. */
  486. _releaseUnusedCompilationData(compilation) {
  487. for (const child of compilation.children) {
  488. this._releaseUnusedCompilationData(child);
  489. }
  490. // Rendered source per (module × runtime) — used only during seal/emit,
  491. // never read by Stats, and not serialized to the persistent cache.
  492. if (compilation.codeGenerationResults !== undefined) {
  493. compilation.codeGenerationResults.map.clear();
  494. }
  495. }
  496. /**
  497. * Returns a compiler watcher.
  498. * @param {WatchOptions} watchOptions the watcher's options
  499. * @param {Callback<Stats>} handler signals when the call finishes
  500. * @returns {Watching | undefined} a compiler watcher
  501. */
  502. watch(watchOptions, handler) {
  503. if (this.running) {
  504. handler(new (getConcurrentCompilationError())());
  505. return;
  506. }
  507. this.running = true;
  508. this.watchMode = true;
  509. const Watching = getWatching();
  510. this.watching = new Watching(this, watchOptions, handler);
  511. return this.watching;
  512. }
  513. /**
  514. * Processes the provided stat.
  515. * @param {Callback<Stats>} callback signals when the call finishes
  516. * @returns {void}
  517. */
  518. run(callback) {
  519. if (this.running) {
  520. callback(new (getConcurrentCompilationError())());
  521. return;
  522. }
  523. /** @type {Logger | undefined} */
  524. let logger;
  525. /**
  526. * Processes the provided err.
  527. * @param {Error | null} err error
  528. * @param {Stats=} stats stats
  529. */
  530. const finalCallback = (err, stats) => {
  531. if (logger) logger.time("beginIdle");
  532. this.idle = true;
  533. this.cache.beginIdle();
  534. if (logger) logger.timeEnd("beginIdle");
  535. this.running = false;
  536. if (err) {
  537. this.hooks.failed.call(err);
  538. }
  539. if (callback !== undefined) callback(err, stats);
  540. this.hooks.afterDone.call(/** @type {Stats} */ (stats));
  541. };
  542. const startTime = Date.now();
  543. this.running = true;
  544. /**
  545. * Processes the provided err.
  546. * @param {Error | null} err error
  547. * @param {Compilation=} _compilation compilation
  548. * @returns {void}
  549. */
  550. const onCompiled = (err, _compilation) => {
  551. if (err) return finalCallback(err);
  552. const compilation = /** @type {Compilation} */ (_compilation);
  553. if (this.hooks.shouldEmit.call(compilation) === false) {
  554. compilation.startTime = startTime;
  555. compilation.endTime = Date.now();
  556. const stats = new Stats(compilation);
  557. this.hooks.done.callAsync(stats, (err) => {
  558. if (err) return finalCallback(err);
  559. return finalCallback(null, stats);
  560. });
  561. return;
  562. }
  563. process.nextTick(() => {
  564. logger = compilation.getLogger("webpack.Compiler");
  565. logger.time("emitAssets");
  566. this.emitAssets(compilation, (err) => {
  567. /** @type {Logger} */
  568. (logger).timeEnd("emitAssets");
  569. if (err) return finalCallback(err);
  570. if (compilation.hooks.needAdditionalPass.call()) {
  571. compilation.needAdditionalPass = true;
  572. compilation.startTime = startTime;
  573. compilation.endTime = Date.now();
  574. /** @type {Logger} */
  575. (logger).time("done hook");
  576. const stats = new Stats(compilation);
  577. this.hooks.done.callAsync(stats, (err) => {
  578. /** @type {Logger} */
  579. (logger).timeEnd("done hook");
  580. if (err) return finalCallback(err);
  581. this.hooks.additionalPass.callAsync((err) => {
  582. if (err) return finalCallback(err);
  583. this.compile(onCompiled);
  584. });
  585. });
  586. return;
  587. }
  588. /** @type {Logger} */
  589. (logger).time("emitRecords");
  590. this.emitRecords((err) => {
  591. /** @type {Logger} */
  592. (logger).timeEnd("emitRecords");
  593. if (err) return finalCallback(err);
  594. compilation.startTime = startTime;
  595. compilation.endTime = Date.now();
  596. /** @type {Logger} */
  597. (logger).time("done hook");
  598. const stats = new Stats(compilation);
  599. this.hooks.done.callAsync(stats, (err) => {
  600. /** @type {Logger} */
  601. (logger).timeEnd("done hook");
  602. if (err) return finalCallback(err);
  603. this.cache.storeBuildDependencies(
  604. compilation.buildDependencies,
  605. (err) => {
  606. if (err) return finalCallback(err);
  607. return finalCallback(null, stats);
  608. }
  609. );
  610. });
  611. });
  612. });
  613. });
  614. };
  615. const run = () => {
  616. this.hooks.beforeRun.callAsync(this, (err) => {
  617. if (err) return finalCallback(err);
  618. this.hooks.run.callAsync(this, (err) => {
  619. if (err) return finalCallback(err);
  620. this.readRecords((err) => {
  621. if (err) return finalCallback(err);
  622. this.compile(onCompiled);
  623. });
  624. });
  625. });
  626. };
  627. if (this.idle) {
  628. this.cache.endIdle((err) => {
  629. if (err) return finalCallback(err);
  630. this.idle = false;
  631. run();
  632. });
  633. } else {
  634. run();
  635. }
  636. }
  637. /**
  638. * Processes the provided run as child callback.
  639. * @param {RunAsChildCallback} callback signals when the call finishes
  640. * @returns {void}
  641. */
  642. runAsChild(callback) {
  643. const startTime = Date.now();
  644. /**
  645. * Processes the provided err.
  646. * @param {Error | null} err error
  647. * @param {Chunk[]=} entries entries
  648. * @param {Compilation=} compilation compilation
  649. */
  650. const finalCallback = (err, entries, compilation) => {
  651. try {
  652. callback(err, entries, compilation);
  653. } catch (runAsChildErr) {
  654. const err = new WebpackError(
  655. `compiler.runAsChild callback error: ${runAsChildErr}`,
  656. { cause: runAsChildErr }
  657. );
  658. err.details = /** @type {Error} */ (runAsChildErr).stack;
  659. /** @type {Compilation} */
  660. (this.parentCompilation).errors.push(err);
  661. }
  662. };
  663. this.compile((err, _compilation) => {
  664. if (err) return finalCallback(err);
  665. const compilation = /** @type {Compilation} */ (_compilation);
  666. const parentCompilation = /** @type {Compilation} */ (
  667. this.parentCompilation
  668. );
  669. parentCompilation.children.push(compilation);
  670. for (const { name, source, info } of compilation.getAssets()) {
  671. parentCompilation.emitAsset(name, source, info);
  672. }
  673. /** @type {Chunk[]} */
  674. const entries = [];
  675. for (const ep of compilation.entrypoints.values()) {
  676. entries.push(...ep.chunks);
  677. }
  678. compilation.startTime = startTime;
  679. compilation.endTime = Date.now();
  680. return finalCallback(null, entries, compilation);
  681. });
  682. }
  683. purgeInputFileSystem() {
  684. if (this.inputFileSystem && this.inputFileSystem.purge) {
  685. this.inputFileSystem.purge();
  686. }
  687. }
  688. /**
  689. * Processes the provided compilation.
  690. * @param {Compilation} compilation the compilation
  691. * @param {ErrorCallback} callback signals when the assets are emitted
  692. * @returns {void}
  693. */
  694. emitAssets(compilation, callback) {
  695. /** @type {string} */
  696. let outputPath;
  697. /**
  698. * Processes the provided err.
  699. * @param {Error=} err error
  700. * @returns {void}
  701. */
  702. const emitFiles = (err) => {
  703. if (err) return callback(err);
  704. const assets = compilation.getAssets();
  705. compilation.assets = { ...compilation.assets };
  706. /** @type {Map<string, SimilarEntry>} */
  707. const caseInsensitiveMap = new Map();
  708. /** @type {Set<string>} */
  709. const allTargetPaths = new Set();
  710. asyncLib.forEachLimit(
  711. assets,
  712. 15,
  713. ({ name: file, source, info }, callback) => {
  714. let targetFile = file;
  715. let immutable = info.immutable;
  716. const queryOrHashStringIdx = targetFile.search(/[?#]/);
  717. if (queryOrHashStringIdx >= 0) {
  718. targetFile = targetFile.slice(0, queryOrHashStringIdx);
  719. // We may remove the hash, which is in the query string
  720. // So we recheck if the file is immutable
  721. // This doesn't cover all cases, but immutable is only a performance optimization anyway
  722. immutable =
  723. immutable &&
  724. (includesHash(targetFile, info.contenthash) ||
  725. includesHash(targetFile, info.chunkhash) ||
  726. includesHash(targetFile, info.modulehash) ||
  727. includesHash(targetFile, info.fullhash));
  728. }
  729. const fs = /** @type {OutputFileSystem} */ (this.outputFileSystem);
  730. // A Windows drive-absolute targetFile is written as-is; joining it onto
  731. // outputPath would produce an invalid path (e.g. C:\out\D:\file). A
  732. // leading "/" stays relative to outputPath (e.g. entry name "/dir/x").
  733. const targetPath = WINDOWS_ABS_PATH_REGEXP.test(targetFile)
  734. ? targetFile
  735. : join(fs, outputPath, targetFile);
  736. /**
  737. * Processes the provided err.
  738. * @param {Error=} err error
  739. * @returns {void}
  740. */
  741. const writeOut = (err) => {
  742. if (err) return callback(err);
  743. allTargetPaths.add(targetPath);
  744. // check if the target file has already been written by this Compiler
  745. const targetFileGeneration =
  746. this._assetEmittingWrittenFiles.get(targetPath);
  747. // create an cache entry for this Source if not already existing
  748. let cacheEntry = this._assetEmittingSourceCache.get(source);
  749. if (cacheEntry === undefined) {
  750. cacheEntry = {
  751. sizeOnlySource: undefined,
  752. /** @type {CacheEntry["writtenTo"]} */
  753. writtenTo: new Map()
  754. };
  755. this._assetEmittingSourceCache.set(source, cacheEntry);
  756. }
  757. /** @type {SimilarEntry | undefined} */
  758. let similarEntry;
  759. const checkSimilarFile = () => {
  760. const caseInsensitiveTargetPath = targetPath.toLowerCase();
  761. similarEntry = caseInsensitiveMap.get(caseInsensitiveTargetPath);
  762. if (similarEntry !== undefined) {
  763. const { path: other, source: otherSource } = similarEntry;
  764. if (isSourceEqual(otherSource, source)) {
  765. // Size may or may not be available at this point.
  766. // If it's not available add to "waiting" list and it will be updated once available
  767. if (similarEntry.size !== undefined) {
  768. updateWithReplacementSource(similarEntry.size);
  769. } else {
  770. if (!similarEntry.waiting) similarEntry.waiting = [];
  771. similarEntry.waiting.push({ file, cacheEntry });
  772. }
  773. alreadyWritten();
  774. } else {
  775. const err =
  776. new WebpackError(`Prevent writing to file that only differs in casing or query string from already written file.
  777. This will lead to a race-condition and corrupted files on case-insensitive file systems.
  778. ${targetPath}
  779. ${other}`);
  780. err.file = file;
  781. callback(err);
  782. }
  783. return true;
  784. }
  785. caseInsensitiveMap.set(
  786. caseInsensitiveTargetPath,
  787. (similarEntry = /** @type {SimilarEntry} */ ({
  788. path: targetPath,
  789. source,
  790. size: undefined,
  791. waiting: undefined
  792. }))
  793. );
  794. return false;
  795. };
  796. /**
  797. * get the binary (Buffer) content from the Source
  798. * @returns {Buffer} content for the source
  799. */
  800. const getContent = () => {
  801. if (typeof source.buffer === "function") {
  802. return source.buffer();
  803. }
  804. const bufferOrString = source.source();
  805. if (Buffer.isBuffer(bufferOrString)) {
  806. return bufferOrString;
  807. }
  808. return Buffer.from(bufferOrString, "utf8");
  809. };
  810. const alreadyWritten = () => {
  811. // cache the information that the Source has been already been written to that location
  812. if (targetFileGeneration === undefined) {
  813. const newGeneration = 1;
  814. this._assetEmittingWrittenFiles.set(targetPath, newGeneration);
  815. /** @type {CacheEntry} */
  816. (cacheEntry).writtenTo.set(targetPath, newGeneration);
  817. } else {
  818. /** @type {CacheEntry} */
  819. (cacheEntry).writtenTo.set(targetPath, targetFileGeneration);
  820. }
  821. callback();
  822. };
  823. /**
  824. * Write the file to output file system
  825. * @param {Buffer} content content to be written
  826. * @returns {void}
  827. */
  828. const doWrite = (content) => {
  829. /** @type {OutputFileSystem} */
  830. (this.outputFileSystem).writeFile(targetPath, content, (err) => {
  831. if (err) return callback(err);
  832. // information marker that the asset has been emitted
  833. compilation.emittedAssets.add(file);
  834. // cache the information that the Source has been written to that location
  835. const newGeneration =
  836. targetFileGeneration === undefined
  837. ? 1
  838. : targetFileGeneration + 1;
  839. /** @type {CacheEntry} */
  840. (cacheEntry).writtenTo.set(targetPath, newGeneration);
  841. this._assetEmittingWrittenFiles.set(targetPath, newGeneration);
  842. this.hooks.assetEmitted.callAsync(
  843. file,
  844. {
  845. content,
  846. source,
  847. outputPath,
  848. compilation,
  849. targetPath
  850. },
  851. callback
  852. );
  853. });
  854. };
  855. /**
  856. * Updates with replacement source.
  857. * @param {number} size size
  858. */
  859. const updateWithReplacementSource = (size) => {
  860. updateFileWithReplacementSource(
  861. file,
  862. /** @type {CacheEntry} */ (cacheEntry),
  863. size
  864. );
  865. /** @type {SimilarEntry} */
  866. (similarEntry).size = size;
  867. if (
  868. /** @type {SimilarEntry} */ (similarEntry).waiting !== undefined
  869. ) {
  870. for (const { file, cacheEntry } of /** @type {SimilarEntry} */ (
  871. similarEntry
  872. ).waiting) {
  873. updateFileWithReplacementSource(file, cacheEntry, size);
  874. }
  875. }
  876. };
  877. /**
  878. * Updates file with replacement source.
  879. * @param {string} file file
  880. * @param {CacheEntry} cacheEntry cache entry
  881. * @param {number} size size
  882. */
  883. const updateFileWithReplacementSource = (
  884. file,
  885. cacheEntry,
  886. size
  887. ) => {
  888. // Create a replacement resource which only allows to ask for size
  889. // This allows to GC all memory allocated by the Source
  890. // (expect when the Source is stored in any other cache)
  891. if (!cacheEntry.sizeOnlySource) {
  892. cacheEntry.sizeOnlySource = new SizeOnlySource(size);
  893. }
  894. compilation.updateAsset(file, cacheEntry.sizeOnlySource, {
  895. size
  896. });
  897. };
  898. /**
  899. * Process existing file.
  900. * @param {IStats} stats stats
  901. * @returns {void}
  902. */
  903. const processExistingFile = (stats) => {
  904. // skip emitting if it's already there and an immutable file
  905. if (immutable) {
  906. updateWithReplacementSource(/** @type {number} */ (stats.size));
  907. return alreadyWritten();
  908. }
  909. const content = getContent();
  910. updateWithReplacementSource(content.length);
  911. // if it exists and content on disk matches content
  912. // skip writing the same content again
  913. // (to keep mtime and don't trigger watchers)
  914. // for a fast negative match file size is compared first
  915. if (content.length === stats.size) {
  916. compilation.comparedForEmitAssets.add(file);
  917. return /** @type {OutputFileSystem} */ (
  918. this.outputFileSystem
  919. ).readFile(targetPath, (err, existingContent) => {
  920. if (
  921. err ||
  922. !content.equals(/** @type {Buffer} */ (existingContent))
  923. ) {
  924. return doWrite(content);
  925. }
  926. return alreadyWritten();
  927. });
  928. }
  929. return doWrite(content);
  930. };
  931. const processMissingFile = () => {
  932. const content = getContent();
  933. updateWithReplacementSource(content.length);
  934. return doWrite(content);
  935. };
  936. // if the target file has already been written
  937. if (targetFileGeneration !== undefined) {
  938. // check if the Source has been written to this target file
  939. const writtenGeneration = /** @type {CacheEntry} */ (
  940. cacheEntry
  941. ).writtenTo.get(targetPath);
  942. if (writtenGeneration === targetFileGeneration) {
  943. // if yes, we may skip writing the file
  944. // if it's already there
  945. // (we assume one doesn't modify files while the Compiler is running, other then removing them)
  946. if (this._assetEmittingPreviousFiles.has(targetPath)) {
  947. const sizeOnlySource = /** @type {SizeOnlySource} */ (
  948. /** @type {CacheEntry} */ (cacheEntry).sizeOnlySource
  949. );
  950. // We assume that assets from the last compilation say intact on disk (they are not removed)
  951. compilation.updateAsset(file, sizeOnlySource, {
  952. size: sizeOnlySource.size()
  953. });
  954. return callback();
  955. }
  956. // Settings immutable will make it accept file content without comparing when file exist
  957. immutable = true;
  958. } else if (!immutable) {
  959. if (checkSimilarFile()) return;
  960. // We wrote to this file before which has very likely a different content
  961. // skip comparing and assume content is different for performance
  962. // This case happens often during watch mode.
  963. return processMissingFile();
  964. }
  965. }
  966. if (checkSimilarFile()) return;
  967. if (this.options.output.compareBeforeEmit) {
  968. /** @type {OutputFileSystem} */
  969. (this.outputFileSystem).stat(targetPath, (err, stats) => {
  970. const exists = !err && /** @type {IStats} */ (stats).isFile();
  971. if (exists) {
  972. processExistingFile(/** @type {IStats} */ (stats));
  973. } else {
  974. processMissingFile();
  975. }
  976. });
  977. } else {
  978. processMissingFile();
  979. }
  980. };
  981. if (/\/|\\/.test(targetFile)) {
  982. const dir = dirname(fs, targetPath);
  983. mkdirp(fs, dir, writeOut);
  984. } else {
  985. writeOut();
  986. }
  987. },
  988. (err) => {
  989. // Clear map to free up memory
  990. caseInsensitiveMap.clear();
  991. if (err) {
  992. this._assetEmittingPreviousFiles.clear();
  993. return callback(err);
  994. }
  995. this._assetEmittingPreviousFiles = allTargetPaths;
  996. this.hooks.afterEmit.callAsync(compilation, (err) => {
  997. if (err) return callback(err);
  998. return callback(null);
  999. });
  1000. }
  1001. );
  1002. };
  1003. this.hooks.emit.callAsync(compilation, (err) => {
  1004. if (err) return callback(err);
  1005. outputPath = compilation.getPath(this.outputPath, {});
  1006. mkdirp(
  1007. /** @type {OutputFileSystem} */ (this.outputFileSystem),
  1008. outputPath,
  1009. emitFiles
  1010. );
  1011. });
  1012. }
  1013. /**
  1014. * Processes the provided error callback.
  1015. * @param {ErrorCallback} callback signals when the call finishes
  1016. * @returns {void}
  1017. */
  1018. emitRecords(callback) {
  1019. if (this.hooks.emitRecords.isUsed()) {
  1020. if (this.recordsOutputPath) {
  1021. asyncLib.parallel(
  1022. [
  1023. (cb) => this.hooks.emitRecords.callAsync(cb),
  1024. this._emitRecords.bind(this)
  1025. ],
  1026. (err) => callback(/** @type {Error | null} */ (err))
  1027. );
  1028. } else {
  1029. this.hooks.emitRecords.callAsync(callback);
  1030. }
  1031. } else if (this.recordsOutputPath) {
  1032. this._emitRecords(callback);
  1033. } else {
  1034. callback(null);
  1035. }
  1036. }
  1037. /**
  1038. * Processes the provided error callback.
  1039. * @param {ErrorCallback} callback signals when the call finishes
  1040. * @returns {void}
  1041. */
  1042. _emitRecords(callback) {
  1043. const writeFile = () => {
  1044. /** @type {OutputFileSystem} */
  1045. (this.outputFileSystem).writeFile(
  1046. /** @type {string} */ (this.recordsOutputPath),
  1047. JSON.stringify(
  1048. this.records,
  1049. (n, value) => {
  1050. if (
  1051. typeof value === "object" &&
  1052. value !== null &&
  1053. !Array.isArray(value)
  1054. ) {
  1055. const keys = Object.keys(value);
  1056. if (!isSorted(keys)) {
  1057. return sortObject(value, keys);
  1058. }
  1059. }
  1060. return value;
  1061. },
  1062. 2
  1063. ),
  1064. callback
  1065. );
  1066. };
  1067. const recordsOutputPathDirectory = dirname(
  1068. /** @type {OutputFileSystem} */
  1069. (this.outputFileSystem),
  1070. /** @type {string} */
  1071. (this.recordsOutputPath)
  1072. );
  1073. if (!recordsOutputPathDirectory) {
  1074. return writeFile();
  1075. }
  1076. mkdirp(
  1077. /** @type {OutputFileSystem} */ (this.outputFileSystem),
  1078. recordsOutputPathDirectory,
  1079. (err) => {
  1080. if (err) return callback(err);
  1081. writeFile();
  1082. }
  1083. );
  1084. }
  1085. /**
  1086. * Processes the provided error callback.
  1087. * @param {ErrorCallback} callback signals when the call finishes
  1088. * @returns {void}
  1089. */
  1090. readRecords(callback) {
  1091. if (this.hooks.readRecords.isUsed()) {
  1092. if (this.recordsInputPath) {
  1093. asyncLib.parallel(
  1094. [
  1095. (cb) => this.hooks.readRecords.callAsync(cb),
  1096. this._readRecords.bind(this)
  1097. ],
  1098. (err) => callback(/** @type {Error | null} */ (err))
  1099. );
  1100. } else {
  1101. this.records = {};
  1102. this.hooks.readRecords.callAsync(callback);
  1103. }
  1104. } else if (this.recordsInputPath) {
  1105. this._readRecords(callback);
  1106. } else {
  1107. this.records = {};
  1108. callback(null);
  1109. }
  1110. }
  1111. /**
  1112. * Processes the provided error callback.
  1113. * @param {ErrorCallback} callback signals when the call finishes
  1114. * @returns {void}
  1115. */
  1116. _readRecords(callback) {
  1117. if (!this.recordsInputPath) {
  1118. this.records = {};
  1119. return callback(null);
  1120. }
  1121. /** @type {InputFileSystem} */
  1122. (this.inputFileSystem).stat(this.recordsInputPath, (err) => {
  1123. // It doesn't exist
  1124. // We can ignore this.
  1125. if (err) return callback(null);
  1126. /** @type {InputFileSystem} */
  1127. (this.inputFileSystem).readFile(
  1128. /** @type {string} */
  1129. (this.recordsInputPath),
  1130. (err, content) => {
  1131. if (err) return callback(err);
  1132. try {
  1133. this.records =
  1134. /** @type {Records} */
  1135. (parseJson(/** @type {Buffer} */ (content).toString("utf8")));
  1136. } catch (parseErr) {
  1137. return callback(
  1138. new Error(
  1139. `Cannot parse records: ${
  1140. /** @type {Error} */ (parseErr).message
  1141. }`
  1142. )
  1143. );
  1144. }
  1145. return callback(null);
  1146. }
  1147. );
  1148. });
  1149. }
  1150. /**
  1151. * Creates a child compiler.
  1152. * @param {Compilation} compilation the compilation
  1153. * @param {string} compilerName the compiler's name
  1154. * @param {number} compilerIndex the compiler's index
  1155. * @param {Partial<OutputOptions>=} outputOptions the output options
  1156. * @param {Plugins=} plugins the plugins to apply
  1157. * @returns {Compiler} a child compiler
  1158. */
  1159. createChildCompiler(
  1160. compilation,
  1161. compilerName,
  1162. compilerIndex,
  1163. outputOptions,
  1164. plugins
  1165. ) {
  1166. const childCompiler = new Compiler(this.context, {
  1167. ...this.options,
  1168. output: {
  1169. ...this.options.output,
  1170. ...outputOptions
  1171. }
  1172. });
  1173. childCompiler.name = compilerName;
  1174. childCompiler.outputPath = this.outputPath;
  1175. childCompiler.inputFileSystem = this.inputFileSystem;
  1176. childCompiler.outputFileSystem = null;
  1177. childCompiler.resolverFactory = this.resolverFactory;
  1178. childCompiler.modifiedFiles = this.modifiedFiles;
  1179. childCompiler.removedFiles = this.removedFiles;
  1180. childCompiler.fileTimestamps = this.fileTimestamps;
  1181. childCompiler.contextTimestamps = this.contextTimestamps;
  1182. childCompiler.fsStartTime = this.fsStartTime;
  1183. childCompiler.cache = this.cache;
  1184. childCompiler.compilerPath = `${this.compilerPath}${compilerName}|${compilerIndex}|`;
  1185. childCompiler._backCompat = this._backCompat;
  1186. const relativeCompilerName = makePathsRelative(
  1187. this.context,
  1188. compilerName,
  1189. this.root
  1190. );
  1191. if (!this.records[relativeCompilerName]) {
  1192. this.records[relativeCompilerName] = [];
  1193. }
  1194. if (this.records[relativeCompilerName][compilerIndex]) {
  1195. childCompiler.records =
  1196. /** @type {Records} */
  1197. (this.records[relativeCompilerName][compilerIndex]);
  1198. } else {
  1199. this.records[relativeCompilerName].push((childCompiler.records = {}));
  1200. }
  1201. childCompiler.parentCompilation = compilation;
  1202. childCompiler.root = this.root;
  1203. if (Array.isArray(plugins)) {
  1204. for (const plugin of plugins) {
  1205. if (typeof plugin === "function") {
  1206. /** @type {WebpackPluginFunction} */
  1207. (plugin).call(childCompiler, childCompiler);
  1208. } else if (plugin) {
  1209. plugin.apply(childCompiler);
  1210. }
  1211. }
  1212. }
  1213. for (const name in this.hooks) {
  1214. if (
  1215. ![
  1216. "make",
  1217. "compile",
  1218. "emit",
  1219. "afterEmit",
  1220. "invalid",
  1221. "done",
  1222. "thisCompilation"
  1223. ].includes(name) &&
  1224. childCompiler.hooks[/** @type {keyof Compiler["hooks"]} */ (name)]
  1225. ) {
  1226. childCompiler.hooks[
  1227. /** @type {keyof Compiler["hooks"]} */
  1228. (name)
  1229. ].taps = [
  1230. ...this.hooks[
  1231. /** @type {keyof Compiler["hooks"]} */
  1232. (name)
  1233. ].taps
  1234. ];
  1235. }
  1236. }
  1237. compilation.hooks.childCompiler.call(
  1238. childCompiler,
  1239. compilerName,
  1240. compilerIndex
  1241. );
  1242. return childCompiler;
  1243. }
  1244. isChild() {
  1245. return Boolean(this.parentCompilation);
  1246. }
  1247. /**
  1248. * Creates a compilation.
  1249. * @param {CompilationParams} params the compilation parameters
  1250. * @returns {Compilation} compilation
  1251. */
  1252. createCompilation(params) {
  1253. this._cleanupLastCompilation();
  1254. return (this._lastCompilation = new Compilation(this, params));
  1255. }
  1256. /**
  1257. * Returns the created compilation.
  1258. * @param {CompilationParams} params the compilation parameters
  1259. * @returns {Compilation} the created compilation
  1260. */
  1261. newCompilation(params) {
  1262. const compilation = this.createCompilation(params);
  1263. compilation.name = this.name;
  1264. compilation.records = this.records;
  1265. this.hooks.thisCompilation.call(compilation, params);
  1266. this.hooks.compilation.call(compilation, params);
  1267. return compilation;
  1268. }
  1269. createNormalModuleFactory() {
  1270. this._cleanupLastNormalModuleFactory();
  1271. const normalModuleFactory = new NormalModuleFactory({
  1272. context: this.options.context,
  1273. fs: /** @type {InputFileSystem} */ (this.inputFileSystem),
  1274. resolverFactory: this.resolverFactory,
  1275. options: this.options.module,
  1276. associatedObjectForCache: this.root
  1277. });
  1278. this._lastNormalModuleFactory = normalModuleFactory;
  1279. this.hooks.normalModuleFactory.call(normalModuleFactory);
  1280. return normalModuleFactory;
  1281. }
  1282. createContextModuleFactory() {
  1283. const contextModuleFactory = new ContextModuleFactory(this.resolverFactory);
  1284. this.hooks.contextModuleFactory.call(contextModuleFactory);
  1285. return contextModuleFactory;
  1286. }
  1287. newCompilationParams() {
  1288. const params = {
  1289. normalModuleFactory: this.createNormalModuleFactory(),
  1290. contextModuleFactory: this.createContextModuleFactory()
  1291. };
  1292. return params;
  1293. }
  1294. /**
  1295. * Processes the provided compilation.
  1296. * @param {Callback<Compilation>} callback signals when the compilation finishes
  1297. * @returns {void}
  1298. */
  1299. compile(callback) {
  1300. const params = this.newCompilationParams();
  1301. this.hooks.beforeCompile.callAsync(params, (err) => {
  1302. if (err) return callback(err);
  1303. this.hooks.compile.call(params);
  1304. const compilation = this.newCompilation(params);
  1305. const logger = compilation.getLogger("webpack.Compiler");
  1306. logger.time("make hook");
  1307. this.hooks.make.callAsync(compilation, (err) => {
  1308. logger.timeEnd("make hook");
  1309. if (err) return callback(err);
  1310. logger.time("finish make hook");
  1311. this.hooks.finishMake.callAsync(compilation, (err) => {
  1312. logger.timeEnd("finish make hook");
  1313. if (err) return callback(err);
  1314. process.nextTick(() => {
  1315. logger.time("finish compilation");
  1316. compilation.finish((err) => {
  1317. logger.timeEnd("finish compilation");
  1318. if (err) return callback(err);
  1319. logger.time("seal compilation");
  1320. compilation.seal((err) => {
  1321. logger.timeEnd("seal compilation");
  1322. if (err) return callback(err);
  1323. logger.time("afterCompile hook");
  1324. this.hooks.afterCompile.callAsync(compilation, (err) => {
  1325. logger.timeEnd("afterCompile hook");
  1326. if (err) return callback(err);
  1327. return callback(null, compilation);
  1328. });
  1329. });
  1330. });
  1331. });
  1332. });
  1333. });
  1334. });
  1335. }
  1336. /**
  1337. * Processes the provided error callback.
  1338. * @param {ErrorCallback} callback signals when the compiler closes
  1339. * @returns {void}
  1340. */
  1341. close(callback) {
  1342. if (this.watching) {
  1343. // When there is still an active watching, close this first
  1344. this.watching.close((_err) => {
  1345. this.close(callback);
  1346. });
  1347. return;
  1348. }
  1349. this.hooks.shutdown.callAsync((err) => {
  1350. if (err) return callback(err);
  1351. // Defer a microtask so a close() made inside the run callback can't
  1352. // release codeGenerationResults before afterDone fires on the same stack.
  1353. const lastCompilation = this._lastCompilation;
  1354. if (lastCompilation !== undefined) {
  1355. Promise.resolve().then(() => {
  1356. this._releaseUnusedCompilationData(lastCompilation);
  1357. });
  1358. }
  1359. this._lastCompilation = undefined;
  1360. this._lastNormalModuleFactory = undefined;
  1361. this.cache.shutdown(callback);
  1362. });
  1363. }
  1364. /**
  1365. * Schema validation function with optional pre-compiled check
  1366. * @template {EXPECTED_OBJECT | EXPECTED_OBJECT[]} [T=EXPECTED_OBJECT]
  1367. * @param {Schema | (() => Schema)} schema schema
  1368. * @param {T} value value
  1369. * @param {ValidationErrorConfiguration=} options options
  1370. * @param {((value: T) => boolean)=} check options
  1371. */
  1372. validate(schema, value, options, check) {
  1373. // Avoid validation at all when disabled
  1374. if (this.options.validate === false) {
  1375. return;
  1376. }
  1377. /**
  1378. * Returns schema.
  1379. * @returns {Schema} schema
  1380. */
  1381. const getSchema = () => {
  1382. if (typeof schema === "function") {
  1383. return schema();
  1384. }
  1385. return schema;
  1386. };
  1387. // // If we have precompiled schema let's use it
  1388. if (check) {
  1389. if (!check(value)) {
  1390. getValidate()(getSchema(), value, options);
  1391. require("util").deprecate(
  1392. () => {},
  1393. "webpack bug: Pre-compiled schema reports error while real schema is happy. This has performance drawbacks.",
  1394. "DEP_WEBPACK_PRE_COMPILED_SCHEMA_INVALID"
  1395. )();
  1396. }
  1397. return;
  1398. }
  1399. // Otherwise let's standard validation
  1400. getValidate()(getSchema(), value, options);
  1401. }
  1402. }
  1403. module.exports = Compiler;