NormalModuleFactory.js 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044
  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. AsyncSeriesBailHook,
  9. AsyncSeriesHook,
  10. HookMap,
  11. SyncBailHook,
  12. SyncHook,
  13. SyncWaterfallHook
  14. } = require("tapable");
  15. const ChunkGraph = require("./ChunkGraph");
  16. const Module = require("./Module");
  17. const ModuleFactory = require("./ModuleFactory");
  18. const ModuleGraph = require("./ModuleGraph");
  19. const { JAVASCRIPT_MODULE_TYPE_AUTO } = require("./ModuleTypeConstants");
  20. const NormalModule = require("./NormalModule");
  21. const { ImportPhaseUtils } = require("./dependencies/ImportPhase");
  22. const { getContext } = require("./loaders/LoaderRunner");
  23. const BasicEffectRulePlugin = require("./rules/BasicEffectRulePlugin");
  24. const BasicMatcherRulePlugin = require("./rules/BasicMatcherRulePlugin");
  25. const GlobMatcherRulePlugin = require("./rules/GlobMatcherRulePlugin");
  26. const ObjectMatcherRulePlugin = require("./rules/ObjectMatcherRulePlugin");
  27. const RuleSetCompiler = require("./rules/RuleSetCompiler");
  28. const UseEffectRulePlugin = require("./rules/UseEffectRulePlugin");
  29. const LazySet = require("./util/LazySet");
  30. const { getScheme } = require("./util/URLAbsoluteSpecifier");
  31. const { cachedCleverMerge, cachedSetProperty } = require("./util/cleverMerge");
  32. const {
  33. applyCaseCorrections,
  34. findCaseMismatch
  35. } = require("./util/findCaseMismatch");
  36. const { dirname, join, relative } = require("./util/fs");
  37. const {
  38. ABSOLUTE_PATH_REGEXP,
  39. WINDOWS_PATH_SEPARATOR_REGEXP,
  40. escapeHashInPathRequest,
  41. parseResource,
  42. parseResourceWithoutFragment
  43. } = require("./util/identifier");
  44. const matchAlias = require("./util/matchAlias");
  45. /** @typedef {import("enhanced-resolve").Resolver["options"]["alias"]} AliasOptions */
  46. /** @import { ResolveContext, ResolveRequest } from "enhanced-resolve" */
  47. /**
  48. * @import {
  49. * ModuleOptionsNormalized as ModuleOptions,
  50. * RuleSetRule,
  51. * JavascriptParserOptions,
  52. * EmptyGeneratorOptions,
  53. * JsonParserOptions,
  54. * JsonGeneratorOptions,
  55. * AssetParserOptions,
  56. * EmptyParserOptions,
  57. * AssetGeneratorOptions,
  58. * CssParserOptions,
  59. * CssModuleParserOptions,
  60. * CssGeneratorOptions,
  61. * CssModuleGeneratorOptions,
  62. * EmptyParserOptions as HtmlParserOptions,
  63. * HtmlGeneratorOptions
  64. * } from "../declarations/WebpackOptions"
  65. */
  66. /** @import { FileSystemDependencies } from "./Compilation" */
  67. /** @import Generator from "./Generator" */
  68. /**
  69. * @import {
  70. * ModuleFactoryCallback,
  71. * ModuleFactoryCreateData,
  72. * ModuleFactoryCreateDataContextInfo,
  73. * ModuleFactoryResult
  74. * } from "./ModuleFactory"
  75. */
  76. /**
  77. * @import {
  78. * GeneratorOptions,
  79. * LoaderItem,
  80. * NormalModuleCreateData,
  81. * ParserOptions
  82. * } from "./NormalModule"
  83. */
  84. /** @import Parser from "./Parser" */
  85. /** @import ResolverFactory, { ResolverWithOptions } from "./ResolverFactory" */
  86. /** @import ModuleDependency from "./dependencies/ModuleDependency" */
  87. /**
  88. * @import {
  89. * ImportPhaseType,
  90. * ImportPhaseName
  91. * } from "./dependencies/ImportPhase"
  92. */
  93. /**
  94. * @import JavascriptParser, {
  95. * ImportAttributes
  96. * } from "./javascript/JavascriptParser"
  97. */
  98. /** @import { RuleSetRules, RuleSet } from "./rules/RuleSetCompiler" */
  99. /** @import { InputFileSystem } from "./util/fs" */
  100. /** @import { AssociatedObjectForCache } from "./util/identifier" */
  101. /**
  102. * Defines the callback type used by this module.
  103. * @template T
  104. * @typedef {import("./Compiler").Callback<T>} Callback
  105. */
  106. /** @typedef {Pick<RuleSetRule, "type" | "sideEffects" | "parser" | "generator" | "resolve" | "layer" | "extractSourceMap">} ModuleSettings */
  107. /** @typedef {NormalModuleCreateData & { settings: ModuleSettings }} CreateData */
  108. /**
  109. * Defines the resolve data type used by this module.
  110. * @typedef {object} ResolveData
  111. * @property {ModuleFactoryCreateData["contextInfo"]} contextInfo
  112. * @property {ModuleFactoryCreateData["resolveOptions"]} resolveOptions
  113. * @property {string} context
  114. * @property {string} request
  115. * @property {ImportPhaseName=} phase
  116. * @property {ImportAttributes=} attributes
  117. * @property {ModuleDependency[]} dependencies
  118. * @property {string} dependencyType
  119. * @property {Partial<CreateData>} createData
  120. * @property {FileSystemDependencies} fileDependencies
  121. * @property {FileSystemDependencies} missingDependencies
  122. * @property {FileSystemDependencies} contextDependencies
  123. * @property {Module=} ignoredModule
  124. * @property {boolean} cacheable allow to use the unsafe cache
  125. */
  126. /**
  127. * Defines the resource data type used by this module.
  128. * @typedef {object} ResourceData
  129. * @property {string} resource
  130. * @property {string=} path
  131. * @property {string=} query
  132. * @property {string=} fragment
  133. * @property {string=} context
  134. */
  135. /**
  136. * Defines the resource scheme data type used by this module.
  137. * @typedef {object} ResourceSchemeData
  138. * @property {string=} mimetype mime type of the resource
  139. * @property {string=} parameters additional parameters for the resource
  140. * @property {"base64" | false=} encoding encoding of the resource
  141. * @property {string=} encodedContent encoded content of the resource
  142. */
  143. /** @typedef {ResourceData & { data: ResourceSchemeData & Partial<ResolveRequest> }} ResourceDataWithData */
  144. /**
  145. * Defines the parsed loader request type used by this module.
  146. * @typedef {object} ParsedLoaderRequest
  147. * @property {string} loader loader
  148. * @property {string | undefined} options options
  149. */
  150. /**
  151. * Dependencies captured while resolving a loader.
  152. * @typedef {object} ResolveDependencies
  153. * @property {LazySet<string>} fileDependencies file dependencies of the resolve
  154. * @property {LazySet<string>} contextDependencies context dependencies of the resolve
  155. * @property {LazySet<string>} missingDependencies missing dependencies of the resolve
  156. */
  157. /**
  158. * Memoized result of resolving a loader for a given (context, request).
  159. * @typedef {ResolveDependencies & { loader: string, query: string | undefined, type: string | undefined }} LoaderResolveCacheEntry
  160. */
  161. /**
  162. * @import {
  163. * JAVASCRIPT_MODULE_TYPE_DYNAMIC,
  164. * JAVASCRIPT_MODULE_TYPE_ESM,
  165. * JSON_MODULE_TYPE,
  166. * ASSET_MODULE_TYPE,
  167. * ASSET_MODULE_TYPE_INLINE,
  168. * ASSET_MODULE_TYPE_RESOURCE,
  169. * ASSET_MODULE_TYPE_SOURCE,
  170. * ASSET_MODULE_TYPE_BYTES,
  171. * WEBASSEMBLY_MODULE_TYPE_ASYNC,
  172. * WEBASSEMBLY_MODULE_TYPE_SYNC,
  173. * CSS_MODULE_TYPE,
  174. * CSS_MODULE_TYPE_GLOBAL,
  175. * CSS_MODULE_TYPE_MODULE,
  176. * CSS_MODULE_TYPE_AUTO,
  177. * HTML_MODULE_TYPE
  178. * } from "./ModuleTypeConstants"
  179. */
  180. /** @typedef {JAVASCRIPT_MODULE_TYPE_AUTO | JAVASCRIPT_MODULE_TYPE_DYNAMIC | JAVASCRIPT_MODULE_TYPE_ESM | JSON_MODULE_TYPE | ASSET_MODULE_TYPE | ASSET_MODULE_TYPE_INLINE | ASSET_MODULE_TYPE_RESOURCE | ASSET_MODULE_TYPE_SOURCE | WEBASSEMBLY_MODULE_TYPE_ASYNC | WEBASSEMBLY_MODULE_TYPE_SYNC | CSS_MODULE_TYPE | CSS_MODULE_TYPE_GLOBAL | CSS_MODULE_TYPE_MODULE | CSS_MODULE_TYPE_AUTO | HTML_MODULE_TYPE} KnownNormalModuleTypes */
  181. /** @typedef {KnownNormalModuleTypes | string} NormalModuleTypes */
  182. const EMPTY_RESOLVE_OPTIONS = {};
  183. /** @type {ParserOptions} */
  184. const EMPTY_PARSER_OPTIONS = {};
  185. /** @type {GeneratorOptions} */
  186. const EMPTY_GENERATOR_OPTIONS = {};
  187. /** @type {ParsedLoaderRequest[]} */
  188. const EMPTY_ELEMENTS = [];
  189. const MATCH_RESOURCE_REGEX = /^([^!]+)!=!/;
  190. const LEADING_DOT_EXTENSION_REGEX = /^[^.]/;
  191. // Nothing in `util/identifier` matches a request (as opposed to a path) shape.
  192. const RELATIVE_REQUEST_REGEXP = /^\.\.?[\\/]/;
  193. /**
  194. * Computes the edit distance of two strings, counting swapped neighbors as one
  195. * edit and giving up as soon as the distance exceeds `max`.
  196. * @param {string} a first string
  197. * @param {string} b second string
  198. * @param {number} max largest distance worth distinguishing
  199. * @returns {number} the distance, or a value greater than `max` when it exceeds `max`
  200. */
  201. const editDistance = (a, b, max) => {
  202. if (a === b) return 0;
  203. if (Math.abs(a.length - b.length) > max) return max + 1;
  204. /** @type {number[] | undefined} */
  205. let rowBeforePrevious;
  206. /** @type {number[]} */
  207. let previousRow = [];
  208. for (let column = 0; column <= b.length; column++) previousRow.push(column);
  209. for (let row = 1; row <= a.length; row++) {
  210. const currentRow = [row];
  211. let rowMinimum = row;
  212. for (let column = 1; column <= b.length; column++) {
  213. let distance = Math.min(
  214. previousRow[column - 1] + (a[row - 1] === b[column - 1] ? 0 : 1),
  215. previousRow[column] + 1,
  216. currentRow[column - 1] + 1
  217. );
  218. if (
  219. rowBeforePrevious &&
  220. a[row - 1] === b[column - 2] &&
  221. a[row - 2] === b[column - 1]
  222. ) {
  223. distance = Math.min(distance, rowBeforePrevious[column - 2] + 1);
  224. }
  225. currentRow.push(distance);
  226. if (distance < rowMinimum) rowMinimum = distance;
  227. }
  228. if (rowMinimum > max) return max + 1;
  229. rowBeforePrevious = previousRow;
  230. previousRow = currentRow;
  231. }
  232. return previousRow[b.length];
  233. };
  234. /**
  235. * Names an entry of a directory can be requested by: as written, and without
  236. * the extension the resolver would have appended.
  237. * @param {string} entry name of a directory entry
  238. * @param {Set<string>} extensions the resolvable extensions
  239. * @returns {string[]} names the entry answers to
  240. */
  241. const namesForEntry = (entry, extensions) => {
  242. const names = [entry];
  243. for (const extension of extensions) {
  244. if (entry.length > extension.length && entry.endsWith(extension)) {
  245. names.push(entry.slice(0, -extension.length));
  246. }
  247. }
  248. return names;
  249. };
  250. /**
  251. * Picks the entry of a directory a failed request most likely meant. Ties are
  252. * broken by name so the hint does not depend on the order the directory lists.
  253. * @param {string} requested the requested file name
  254. * @param {string[]} entries names in the directory
  255. * @param {Set<string>} extensions the resolvable extensions
  256. * @returns {{ entry: string, distance: number } | undefined} the closest entry and how far it is from the request
  257. */
  258. const findClosestEntry = (requested, entries, extensions) => {
  259. // A typo is only distinguishable from an unrelated name once the name is
  260. // long enough, so short requests are matched by casing alone.
  261. let allowedDistance = requested.length < 4 ? 0 : requested.length < 8 ? 1 : 2;
  262. const lowerCaseRequested = requested.toLowerCase();
  263. /** @type {{ entry: string, distance: number } | undefined} */
  264. let best;
  265. for (const entry of entries) {
  266. // The entry exists as requested, so the request failed for another reason.
  267. if (entry === requested) return;
  268. for (const name of namesForEntry(entry, extensions)) {
  269. const distance = editDistance(
  270. lowerCaseRequested,
  271. name.toLowerCase(),
  272. allowedDistance
  273. );
  274. if (distance > allowedDistance) continue;
  275. if (best && distance === allowedDistance && entry >= best.entry) continue;
  276. allowedDistance = distance;
  277. best = { entry, distance };
  278. }
  279. }
  280. return best;
  281. };
  282. /** Bounds the walk up the context when a module request is looked up. */
  283. const MAX_MODULE_LOOKUP_DIRECTORIES = 10;
  284. /** Bounds the paths a failed request is looked for under, aliases included. */
  285. const MAX_SUGGESTION_CANDIDATES = 20;
  286. /**
  287. * Directories a module request is looked up in, nearest first.
  288. * @param {InputFileSystem} fs input file system
  289. * @param {string} context directory the request comes from
  290. * @param {Iterable<string | string[]>} modules the `resolve.modules` entries
  291. * @returns {string[]} absolute directories
  292. */
  293. const moduleLookupDirectories = (fs, context, modules) => {
  294. /** @type {string[]} */
  295. const relativeNames = [];
  296. /** @type {string[]} */
  297. const directories = [];
  298. for (const entry of modules) {
  299. for (const name of Array.isArray(entry) ? entry : [entry]) {
  300. if (ABSOLUTE_PATH_REGEXP.test(name)) {
  301. directories.push(name);
  302. } else {
  303. relativeNames.push(name);
  304. }
  305. }
  306. }
  307. let current = context;
  308. while (
  309. relativeNames.length > 0 &&
  310. directories.length < MAX_MODULE_LOOKUP_DIRECTORIES
  311. ) {
  312. for (const name of relativeNames) directories.push(join(fs, current, name));
  313. const parent = dirname(fs, current);
  314. if (parent === current) break;
  315. current = parent;
  316. }
  317. return directories;
  318. };
  319. /**
  320. * Requests an alias could have turned this one into, so a hint can look where
  321. * the resolver looked. Mirrors `AliasPlugin`: a name matches the whole request
  322. * or a leading segment of it, and `onlyModule` restricts it to the former.
  323. * @param {string} request the request as written
  324. * @param {AliasOptions} aliases the `resolve.alias` entries
  325. * @returns {string[]} the requests the aliases map this one to
  326. */
  327. const aliasedRequests = (request, aliases) => {
  328. /** @type {string[]} */
  329. const requests = [];
  330. for (const { name, alias, onlyModule } of aliases) {
  331. if (!matchAlias(request, name, onlyModule)) continue;
  332. const tail = request.slice(name.length);
  333. for (const target of Array.isArray(alias) ? alias : [alias]) {
  334. // `false` tells the resolver to ignore the request, so it names no path
  335. if (typeof target === "string") requests.push(target + tail);
  336. }
  337. }
  338. return requests;
  339. };
  340. /**
  341. * Whether a request spells out directories of its own, as opposed to naming an
  342. * entry of the directory it is resolved against.
  343. * @param {string} prefix the request without its last segment
  344. * @returns {boolean} true when the prefix names at least one directory
  345. */
  346. const namesDirectories = (prefix) =>
  347. prefix
  348. .replace(WINDOWS_PATH_SEPARATOR_REGEXP, "/")
  349. .split("/")
  350. .some((segment) => segment !== "" && segment !== "." && segment !== "..");
  351. /**
  352. * Replays the file/context/missing dependencies captured by a loader resolve
  353. * into a resolve context, so memoized loaders still record everything needed
  354. * for watch invalidation.
  355. * @param {ResolveContext} resolveContext resolve context to add dependencies to
  356. * @param {ResolveDependencies} source captured resolve dependencies
  357. * @returns {void}
  358. */
  359. const replayResolveDependencies = (resolveContext, source) => {
  360. // On this path resolveContext always carries the three LazySet dependency
  361. // sets built in the `resolve` hook, so they are safe to treat as LazySet.
  362. /** @type {LazySet<string>} */
  363. (resolveContext.fileDependencies).addAll(source.fileDependencies);
  364. /** @type {LazySet<string>} */
  365. (resolveContext.contextDependencies).addAll(source.contextDependencies);
  366. /** @type {LazySet<string>} */
  367. (resolveContext.missingDependencies).addAll(source.missingDependencies);
  368. };
  369. /**
  370. * Returns ident.
  371. * @param {LoaderItem} data data
  372. * @returns {string} ident
  373. */
  374. const loaderToIdent = (data) => {
  375. if (!data.options) {
  376. return data.loader;
  377. }
  378. if (typeof data.options === "string") {
  379. return `${data.loader}?${data.options}`;
  380. }
  381. if (typeof data.options !== "object") {
  382. throw new Error("loader options must be string or object");
  383. }
  384. if (data.ident) {
  385. return `${data.loader}??${data.ident}`;
  386. }
  387. return `${data.loader}?${JSON.stringify(data.options)}`;
  388. };
  389. /**
  390. * Stringify loaders and resource.
  391. * @param {LoaderItem[]} loaders loaders
  392. * @param {string} resource resource
  393. * @returns {string} stringified loaders and resource
  394. */
  395. const stringifyLoadersAndResource = (loaders, resource) => {
  396. let str = "";
  397. for (const loader of loaders) {
  398. str += `${loaderToIdent(loader)}!`;
  399. }
  400. return str + resource;
  401. };
  402. /**
  403. * Checks whether it needs calls.
  404. * @param {number} times times
  405. * @param {(err?: null | Error) => void} callback callback
  406. * @returns {(err?: null | Error) => void} callback
  407. */
  408. const needCalls = (times, callback) => (err) => {
  409. if (--times === 0) {
  410. return callback(err);
  411. }
  412. if (err && times > 0) {
  413. times = Number.NaN;
  414. return callback(err);
  415. }
  416. };
  417. /**
  418. * Merges global options.
  419. * @template T
  420. * @template O
  421. * @param {T} globalOptions global options
  422. * @param {string} type type
  423. * @param {O} localOptions local options
  424. * @returns {T & O | T | O} result
  425. */
  426. const mergeGlobalOptions = (globalOptions, type, localOptions) => {
  427. const parts = type.split("/");
  428. /** @type {undefined | T} */
  429. let result;
  430. let current = "";
  431. for (const part of parts) {
  432. current = current ? `${current}/${part}` : part;
  433. const options =
  434. /** @type {T} */
  435. (globalOptions[/** @type {keyof T} */ (current)]);
  436. if (typeof options === "object") {
  437. result =
  438. result === undefined ? options : cachedCleverMerge(result, options);
  439. }
  440. }
  441. if (result === undefined) {
  442. return localOptions;
  443. }
  444. return cachedCleverMerge(result, localOptions);
  445. };
  446. // TODO webpack 6 remove
  447. /**
  448. * Deprecation changed hook message.
  449. * @template {import("tapable").Hook<EXPECTED_ANY, EXPECTED_ANY>} T
  450. * @param {string} name name
  451. * @param {T} hook hook
  452. * @returns {string} result
  453. */
  454. const deprecationChangedHookMessage = (name, hook) => {
  455. const names = hook.taps.map((tapped) => tapped.name).join(", ");
  456. return (
  457. `NormalModuleFactory.${name} (${names}) is no longer a waterfall hook, but a bailing hook instead. ` +
  458. "Do not return the passed object, but modify it instead. " +
  459. "Returning false will ignore the request and results in no module created."
  460. );
  461. };
  462. const ruleSetCompiler = new RuleSetCompiler([
  463. new BasicMatcherRulePlugin("test", "resource"),
  464. new GlobMatcherRulePlugin("glob", "resource"),
  465. new BasicMatcherRulePlugin("scheme"),
  466. new BasicMatcherRulePlugin("mimetype"),
  467. new BasicMatcherRulePlugin("dependency"),
  468. new BasicMatcherRulePlugin("include", "resource"),
  469. new BasicMatcherRulePlugin("exclude", "resource", true),
  470. new BasicMatcherRulePlugin("resource"),
  471. new BasicMatcherRulePlugin("resourceQuery"),
  472. new BasicMatcherRulePlugin("resourceFragment"),
  473. new BasicMatcherRulePlugin("realResource"),
  474. new BasicMatcherRulePlugin("issuer"),
  475. new BasicMatcherRulePlugin("compiler"),
  476. new BasicMatcherRulePlugin("issuerLayer"),
  477. new BasicMatcherRulePlugin("phase"),
  478. new ObjectMatcherRulePlugin("assert", "attributes", (value) => {
  479. if (value) {
  480. return (
  481. /** @type {ImportAttributes} */ (value)._isLegacyAssert !== undefined
  482. );
  483. }
  484. return false;
  485. }),
  486. new ObjectMatcherRulePlugin("with", "attributes", (value) => {
  487. if (value) {
  488. return !(/** @type {ImportAttributes} */ (value)._isLegacyAssert);
  489. }
  490. return false;
  491. }),
  492. new ObjectMatcherRulePlugin("descriptionData"),
  493. new BasicMatcherRulePlugin("descriptionRelativePath"),
  494. new BasicEffectRulePlugin("type"),
  495. new BasicEffectRulePlugin("sideEffects"),
  496. new BasicEffectRulePlugin("parser"),
  497. new BasicEffectRulePlugin("resolve"),
  498. new BasicEffectRulePlugin("generator"),
  499. new BasicEffectRulePlugin("layer"),
  500. new BasicEffectRulePlugin("extractSourceMap"),
  501. new UseEffectRulePlugin()
  502. ]);
  503. /** @import JavascriptGenerator from "./javascript/JavascriptGenerator" */
  504. /** @import JsonParser from "./json/JsonParser" */
  505. /** @import JsonGenerator from "./json/JsonGenerator" */
  506. /** @import AssetParser from "./asset/AssetParser" */
  507. /** @import AssetSourceParser from "./asset/AssetSourceParser" */
  508. /** @import AssetBytesParser from "./asset/AssetBytesParser" */
  509. /** @import AssetGenerator from "./asset/AssetGenerator" */
  510. /** @import AssetSourceGenerator from "./asset/AssetSourceGenerator" */
  511. /** @import AssetBytesGenerator from "./asset/AssetBytesGenerator" */
  512. /** @import AsyncWebAssemblyParser from "./wasm-async/AsyncWebAssemblyParser" */
  513. /** @import WebAssemblyParser from "./wasm-sync/WebAssemblyParser" */
  514. /** @import CssParser from "./css/CssParser" */
  515. /** @import CssGenerator from "./css/CssGenerator" */
  516. /** @import HtmlParser from "./html/HtmlParser" */
  517. /** @import HtmlGenerator from "./html/HtmlGenerator" */
  518. /* eslint-disable jsdoc/type-formatting */
  519. /**
  520. * Defines the shared type used by this module.
  521. * @typedef {[
  522. * [JAVASCRIPT_MODULE_TYPE_AUTO, JavascriptParser, JavascriptParserOptions, JavascriptGenerator, EmptyGeneratorOptions],
  523. * [JAVASCRIPT_MODULE_TYPE_DYNAMIC, JavascriptParser, JavascriptParserOptions, JavascriptGenerator, EmptyGeneratorOptions],
  524. * [JAVASCRIPT_MODULE_TYPE_ESM, JavascriptParser, JavascriptParserOptions, JavascriptGenerator, EmptyGeneratorOptions],
  525. * [JSON_MODULE_TYPE, JsonParser, JsonParserOptions, JsonGenerator, JsonGeneratorOptions],
  526. * [ASSET_MODULE_TYPE, AssetParser, AssetParserOptions, AssetGenerator, AssetGeneratorOptions],
  527. * [ASSET_MODULE_TYPE_INLINE, AssetParser, EmptyParserOptions, AssetGenerator, AssetGeneratorOptions],
  528. * [ASSET_MODULE_TYPE_RESOURCE, AssetParser, EmptyParserOptions, AssetGenerator, AssetGeneratorOptions],
  529. * [ASSET_MODULE_TYPE_SOURCE, AssetSourceParser, EmptyParserOptions, AssetSourceGenerator, EmptyGeneratorOptions],
  530. * [ASSET_MODULE_TYPE_BYTES, AssetBytesParser, EmptyParserOptions, AssetBytesGenerator, EmptyGeneratorOptions],
  531. * [WEBASSEMBLY_MODULE_TYPE_ASYNC, AsyncWebAssemblyParser, EmptyParserOptions, Generator, EmptyGeneratorOptions],
  532. * [WEBASSEMBLY_MODULE_TYPE_SYNC, WebAssemblyParser, EmptyParserOptions, Generator, EmptyGeneratorOptions],
  533. * [CSS_MODULE_TYPE, CssParser, CssParserOptions, CssGenerator, CssGeneratorOptions],
  534. * [CSS_MODULE_TYPE_AUTO, CssParser, CssModuleParserOptions, CssGenerator, CssModuleGeneratorOptions],
  535. * [CSS_MODULE_TYPE_MODULE, CssParser, CssModuleParserOptions, CssGenerator, CssModuleGeneratorOptions],
  536. * [CSS_MODULE_TYPE_GLOBAL, CssParser, CssModuleParserOptions, CssGenerator, CssModuleGeneratorOptions],
  537. * [HTML_MODULE_TYPE, HtmlParser, HtmlParserOptions, HtmlGenerator, HtmlGeneratorOptions],
  538. * [string, Parser, ParserOptions, Generator, GeneratorOptions],
  539. * ]} ParsersAndGeneratorsByTypes
  540. */
  541. /* eslint-enable jsdoc/type-formatting */
  542. /**
  543. * Defines the extract tuple elements type used by this module.
  544. * @template {unknown[]} T
  545. * @template {number[]} I
  546. * @typedef {{ [K in keyof I]: K extends keyof I ? I[K] extends keyof T ? T[I[K]] : never : never }} ExtractTupleElements
  547. */
  548. /**
  549. * Represents the normal module factory runtime component.
  550. * @template {unknown[]} T
  551. * @template {number[]} A
  552. * @template [R=void]
  553. * @typedef {T extends [infer Head extends [string, ...unknown[]], ...infer Tail extends [string, ...unknown[]][]] ? Record<Head[0], SyncBailHook<ExtractTupleElements<Head, A>, R extends number ? Head[R] : R>> & RecordFactoryFromTuple<Tail, A, R> : unknown } RecordFactoryFromTuple
  554. */
  555. /**
  556. * Maps each tuple in `T` to a record from its `[0]` key to its `[I]` value.
  557. * @template {unknown[]} T
  558. * @template {number} I
  559. * @typedef {T extends [infer Head extends [string, ...unknown[]], ...infer Tail extends [string, ...unknown[]][]] ? Record<Head[0], I extends keyof Head ? Head[I] : never> & TupleToTypeMap<Tail, I> : unknown } TupleToTypeMap
  560. */
  561. /** @typedef {TupleToTypeMap<ParsersAndGeneratorsByTypes, 1>} ParserByType */
  562. /** @typedef {TupleToTypeMap<ParsersAndGeneratorsByTypes, 2>} ParserOptionsByType */
  563. /** @typedef {TupleToTypeMap<ParsersAndGeneratorsByTypes, 3>} GeneratorByType */
  564. /** @typedef {TupleToTypeMap<ParsersAndGeneratorsByTypes, 4>} GeneratorOptionsByType */
  565. class NormalModuleFactory extends ModuleFactory {
  566. /**
  567. * Creates an instance of NormalModuleFactory.
  568. * @param {object} param params
  569. * @param {string=} param.context context
  570. * @param {InputFileSystem} param.fs file system
  571. * @param {ResolverFactory} param.resolverFactory resolverFactory
  572. * @param {ModuleOptions} param.options options
  573. * @param {AssociatedObjectForCache} param.associatedObjectForCache an object to which the cache will be attached
  574. */
  575. constructor({
  576. context,
  577. fs,
  578. resolverFactory,
  579. options,
  580. associatedObjectForCache
  581. }) {
  582. super();
  583. this.hooks = Object.freeze({
  584. /** @type {AsyncSeriesBailHook<[ResolveData], Module | false | void>} */
  585. resolve: new AsyncSeriesBailHook(["resolveData"]),
  586. /** @type {HookMap<AsyncSeriesBailHook<[ResourceDataWithData, ResolveData], true | void>>} */
  587. resolveForScheme: new HookMap(
  588. () => new AsyncSeriesBailHook(["resourceData", "resolveData"])
  589. ),
  590. /**
  591. * @type {HookMap<AsyncSeriesBailHook<[ResourceDataWithData, ResolveData], true | void>>}
  592. * @since 5.49.0
  593. */
  594. resolveInScheme: new HookMap(
  595. () => new AsyncSeriesBailHook(["resourceData", "resolveData"])
  596. ),
  597. /** @type {AsyncSeriesBailHook<[ResolveData], Module | undefined>} */
  598. factorize: new AsyncSeriesBailHook(["resolveData"]),
  599. /** @type {AsyncSeriesBailHook<[ResolveData], false | void>} */
  600. beforeResolve: new AsyncSeriesBailHook(["resolveData"]),
  601. /** @type {AsyncSeriesBailHook<[ResolveData], false | void>} */
  602. afterResolve: new AsyncSeriesBailHook(["resolveData"]),
  603. /** @type {AsyncSeriesBailHook<[CreateData, ResolveData], Module | void>} */
  604. createModule: new AsyncSeriesBailHook(["createData", "resolveData"]),
  605. /** @type {SyncWaterfallHook<[Module, CreateData, ResolveData]>} */
  606. module: new SyncWaterfallHook(["module", "createData", "resolveData"]),
  607. /**
  608. * @type {HookMap<AsyncSeriesHook<[]>>}
  609. * @since 5.110.0
  610. */
  611. prepareModuleType: new HookMap(() => new AsyncSeriesHook([])),
  612. /** @type {import("tapable").TypedHookMap<RecordFactoryFromTuple<ParsersAndGeneratorsByTypes, [2], 1>>} */
  613. createParser: new HookMap(() => new SyncBailHook(["parserOptions"])),
  614. /** @type {import("tapable").TypedHookMap<RecordFactoryFromTuple<ParsersAndGeneratorsByTypes, [1, 2]>>} */
  615. parser: new HookMap(() => new SyncHook(["parser", "parserOptions"])),
  616. /** @type {import("tapable").TypedHookMap<RecordFactoryFromTuple<ParsersAndGeneratorsByTypes, [4], 3>>} */
  617. createGenerator: new HookMap(
  618. () => new SyncBailHook(["generatorOptions"])
  619. ),
  620. /** @type {import("tapable").TypedHookMap<RecordFactoryFromTuple<ParsersAndGeneratorsByTypes, [3, 4]>>} */
  621. generator: new HookMap(
  622. () => new SyncHook(["generator", "generatorOptions"])
  623. ),
  624. /**
  625. * @type {HookMap<SyncBailHook<[CreateData, ResolveData], Module | void>>}
  626. * @since 5.81.0
  627. */
  628. createModuleClass: new HookMap(
  629. () => new SyncBailHook(["createData", "resolveData"])
  630. )
  631. });
  632. /** @type {ResolverFactory} */
  633. this.resolverFactory = resolverFactory;
  634. /** @type {RuleSet} */
  635. this.ruleSet = ruleSetCompiler.compile([
  636. {
  637. rules: /** @type {RuleSetRules} */ (options.defaultRules)
  638. },
  639. {
  640. rules: /** @type {RuleSetRules} */ (options.rules)
  641. }
  642. ]);
  643. /** @type {string} */
  644. this.context = context || "";
  645. /** @type {InputFileSystem} */
  646. this.fs = fs;
  647. this._globalParserOptions = options.parser;
  648. this._globalGeneratorOptions = options.generator;
  649. /** @type {Map<string, WeakMap<ParserOptions, Parser>>} */
  650. this.parserCache = new Map();
  651. /** @type {Map<string, WeakMap<GeneratorOptions, Generator>>} */
  652. this.generatorCache = new Map();
  653. // `true` once a type is prepared, otherwise the calls waiting on the
  654. // in-flight `prepareModuleType` for it
  655. /** @type {Map<string, true | Callback<void>[]>} */
  656. this._moduleTypePreparation = new Map();
  657. /** @type {Set<Module>} */
  658. this._restoredUnsafeCacheEntries = new Set();
  659. /** @type {Map<string, LoaderResolveCacheEntry>} */
  660. this._loaderResolveCache = new Map();
  661. /** @type {(resource: string) => import("./util/identifier").ParsedResource} */
  662. const cacheParseResource = parseResource.bindCache(
  663. associatedObjectForCache
  664. );
  665. const cachedParseResourceWithoutFragment =
  666. parseResourceWithoutFragment.bindCache(associatedObjectForCache);
  667. this._parseResourceWithoutFragment = cachedParseResourceWithoutFragment;
  668. this.hooks.factorize.tapAsync(
  669. {
  670. name: "NormalModuleFactory",
  671. stage: 100
  672. },
  673. (resolveData, callback) => {
  674. this.hooks.resolve.callAsync(resolveData, (err, result) => {
  675. if (err) return callback(err);
  676. // Ignored
  677. if (result === false) return callback();
  678. // direct module
  679. if (result instanceof Module) return callback(null, result);
  680. if (typeof result === "object") {
  681. throw new Error(
  682. `${deprecationChangedHookMessage(
  683. "resolve",
  684. this.hooks.resolve
  685. )} Returning a Module object will result in this module used as result.`
  686. );
  687. }
  688. this.hooks.afterResolve.callAsync(resolveData, (err, result) => {
  689. if (err) return callback(err);
  690. if (typeof result === "object") {
  691. throw new Error(
  692. deprecationChangedHookMessage(
  693. "afterResolve",
  694. this.hooks.afterResolve
  695. )
  696. );
  697. }
  698. // Ignored
  699. if (result === false) return callback();
  700. const createData =
  701. /** @type {CreateData} */
  702. (resolveData.createData);
  703. this.hooks.createModule.callAsync(
  704. createData,
  705. resolveData,
  706. (err, createdModule) => {
  707. if (!createdModule) {
  708. if (!resolveData.request) {
  709. return callback(new Error("Empty dependency (no request)"));
  710. }
  711. // TODO webpack 6 make it required and move javascript/wasm/asset properties to own module
  712. createdModule = this.hooks.createModuleClass
  713. .for(createData.settings.type)
  714. .call(createData, resolveData);
  715. if (!createdModule) {
  716. createdModule = /** @type {Module} */ (
  717. new NormalModule(createData)
  718. );
  719. }
  720. }
  721. createdModule = this.hooks.module.call(
  722. createdModule,
  723. createData,
  724. resolveData
  725. );
  726. return callback(null, createdModule);
  727. }
  728. );
  729. });
  730. });
  731. }
  732. );
  733. this.hooks.resolve.tapAsync(
  734. {
  735. name: "NormalModuleFactory",
  736. stage: 100
  737. },
  738. (data, callback) => {
  739. const {
  740. contextInfo,
  741. context,
  742. dependencies,
  743. dependencyType,
  744. request,
  745. phase,
  746. attributes,
  747. resolveOptions,
  748. fileDependencies,
  749. missingDependencies,
  750. contextDependencies
  751. } = data;
  752. const loaderResolver = this.getResolver("loader");
  753. /** @type {ResourceData | undefined} */
  754. let matchResourceData;
  755. /** @type {string} */
  756. let unresolvedResource;
  757. /** @type {ParsedLoaderRequest[]} */
  758. let elements;
  759. let noPreAutoLoaders = false;
  760. let noAutoLoaders = false;
  761. let noPrePostAutoLoaders = false;
  762. const contextScheme = getScheme(context);
  763. /** @type {string | undefined} */
  764. let scheme = getScheme(request);
  765. if (!scheme) {
  766. /** @type {string} */
  767. let requestWithoutMatchResource = request;
  768. const matchResourceMatch = MATCH_RESOURCE_REGEX.exec(request);
  769. if (matchResourceMatch) {
  770. let matchResource = matchResourceMatch[1];
  771. // Check if matchResource starts with ./ or ../
  772. if (matchResource.charCodeAt(0) === 46) {
  773. // 46 is "."
  774. const secondChar = matchResource.charCodeAt(1);
  775. if (
  776. secondChar === 47 || // 47 is "/"
  777. (secondChar === 46 && matchResource.charCodeAt(2) === 47) // "../"
  778. ) {
  779. // Resolve relative path against context
  780. matchResource = join(this.fs, context, matchResource);
  781. }
  782. }
  783. matchResourceData = {
  784. ...cacheParseResource(matchResource),
  785. resource: matchResource
  786. };
  787. requestWithoutMatchResource = request.slice(
  788. matchResourceMatch[0].length
  789. );
  790. }
  791. scheme = getScheme(requestWithoutMatchResource);
  792. if (!scheme && !contextScheme) {
  793. const firstChar = requestWithoutMatchResource.charCodeAt(0);
  794. const secondChar = requestWithoutMatchResource.charCodeAt(1);
  795. noPreAutoLoaders = firstChar === 45 && secondChar === 33; // startsWith "-!"
  796. noAutoLoaders = noPreAutoLoaders || firstChar === 33; // startsWith "!"
  797. noPrePostAutoLoaders = firstChar === 33 && secondChar === 33; // startsWith "!!";
  798. const rawElements = requestWithoutMatchResource
  799. .slice(
  800. noPreAutoLoaders || noPrePostAutoLoaders
  801. ? 2
  802. : noAutoLoaders
  803. ? 1
  804. : 0
  805. )
  806. .split(/!+/);
  807. unresolvedResource = /** @type {string} */ (rawElements.pop());
  808. elements = rawElements.map((el) => {
  809. const { path, query } = cachedParseResourceWithoutFragment(el);
  810. return {
  811. loader: path,
  812. options: query ? query.slice(1) : undefined
  813. };
  814. });
  815. scheme = getScheme(unresolvedResource);
  816. } else {
  817. unresolvedResource = requestWithoutMatchResource;
  818. elements = EMPTY_ELEMENTS;
  819. }
  820. } else {
  821. unresolvedResource = request;
  822. elements = EMPTY_ELEMENTS;
  823. }
  824. /** @type {ResolveContext} */
  825. const resolveContext = {
  826. fileDependencies,
  827. missingDependencies,
  828. contextDependencies
  829. };
  830. /** @type {ResourceDataWithData} */
  831. let resourceData;
  832. /** @type {undefined | LoaderItem[]} */
  833. let loaders;
  834. const continueCallback = needCalls(2, (err) => {
  835. if (err) return callback(err);
  836. // translate option idents
  837. try {
  838. for (const item of /** @type {LoaderItem[]} */ (loaders)) {
  839. if (typeof item.options === "string" && item.options[0] === "?") {
  840. const ident = item.options.slice(1);
  841. if (ident === "[[missing ident]]") {
  842. throw new Error(
  843. "No ident is provided by referenced loader. " +
  844. "When using a function for Rule.use in config you need to " +
  845. "provide an 'ident' property for referenced loader options."
  846. );
  847. }
  848. item.options = this.ruleSet.references.get(ident);
  849. if (item.options === undefined) {
  850. throw new Error(
  851. "Invalid ident is provided by referenced loader"
  852. );
  853. }
  854. item.ident = ident;
  855. }
  856. }
  857. } catch (identErr) {
  858. return callback(/** @type {Error} */ (identErr));
  859. }
  860. if (!resourceData) {
  861. // ignored
  862. return callback(null, dependencies[0].createIgnoredModule(context));
  863. }
  864. const userRequest =
  865. (matchResourceData !== undefined
  866. ? `${matchResourceData.resource}!=!`
  867. : "") +
  868. stringifyLoadersAndResource(
  869. /** @type {LoaderItem[]} */ (loaders),
  870. resourceData.resource
  871. );
  872. /** @type {ModuleSettings} */
  873. const settings = {};
  874. /** @type {LoaderItem[]} */
  875. const useLoadersPost = [];
  876. /** @type {LoaderItem[]} */
  877. const useLoaders = [];
  878. /** @type {LoaderItem[]} */
  879. const useLoadersPre = [];
  880. // handle .webpack[] suffix
  881. /** @type {string} */
  882. let resource;
  883. /** @type {RegExpExecArray | null} */
  884. let match;
  885. if (
  886. matchResourceData &&
  887. typeof (resource = matchResourceData.resource) === "string" &&
  888. (match = /\.webpack\[([^\]]+)\]$/.exec(resource))
  889. ) {
  890. settings.type = match[1];
  891. matchResourceData.resource = matchResourceData.resource.slice(
  892. 0,
  893. -settings.type.length - 10
  894. );
  895. } else {
  896. settings.type = JAVASCRIPT_MODULE_TYPE_AUTO;
  897. const resourceDataForRules = matchResourceData || resourceData;
  898. const result = this.ruleSet.exec({
  899. resource: resourceDataForRules.path,
  900. realResource: resourceData.path,
  901. resourceQuery: resourceDataForRules.query,
  902. resourceFragment: resourceDataForRules.fragment,
  903. scheme,
  904. phase,
  905. attributes,
  906. mimetype: matchResourceData
  907. ? ""
  908. : resourceData.data.mimetype || "",
  909. dependency: dependencyType,
  910. descriptionData: matchResourceData
  911. ? undefined
  912. : resourceData.data.descriptionFileData,
  913. descriptionRelativePath: matchResourceData
  914. ? undefined
  915. : resourceData.data.relativePath,
  916. issuer: contextInfo.issuer,
  917. compiler: contextInfo.compiler,
  918. issuerLayer: contextInfo.issuerLayer || ""
  919. });
  920. for (const r of result) {
  921. // https://github.com/webpack/webpack/issues/16466
  922. // if a request exists PrePostAutoLoaders, should disable modifying Rule.type
  923. if (r.type === "type" && noPrePostAutoLoaders) {
  924. continue;
  925. }
  926. if (r.type === "use") {
  927. if (!noAutoLoaders && !noPrePostAutoLoaders) {
  928. useLoaders.push(r.value);
  929. }
  930. } else if (r.type === "use-post") {
  931. if (!noPrePostAutoLoaders) {
  932. useLoadersPost.push(r.value);
  933. }
  934. } else if (r.type === "use-pre") {
  935. if (!noPreAutoLoaders && !noPrePostAutoLoaders) {
  936. useLoadersPre.push(r.value);
  937. }
  938. } else if (
  939. typeof r.value === "object" &&
  940. r.value !== null &&
  941. typeof settings[
  942. /** @type {keyof ModuleSettings} */
  943. (r.type)
  944. ] === "object" &&
  945. settings[/** @type {keyof ModuleSettings} */ (r.type)] !== null
  946. ) {
  947. const type = /** @type {keyof ModuleSettings} */ (r.type);
  948. settings[type] = cachedCleverMerge(settings[type], r.value);
  949. } else {
  950. const type = /** @type {keyof ModuleSettings} */ (r.type);
  951. settings[type] = r.value;
  952. }
  953. }
  954. }
  955. /** @type {undefined | LoaderItem[]} */
  956. let postLoaders;
  957. /** @type {undefined | LoaderItem[]} */
  958. let normalLoaders;
  959. /** @type {undefined | LoaderItem[]} */
  960. let preLoaders;
  961. const continueCallback = needCalls(3, (err) => {
  962. if (err) {
  963. return callback(err);
  964. }
  965. const allLoaders = /** @type {LoaderItem[]} */ (postLoaders);
  966. if (matchResourceData === undefined) {
  967. for (const loader of /** @type {LoaderItem[]} */ (loaders)) {
  968. allLoaders.push(loader);
  969. }
  970. for (const loader of /** @type {LoaderItem[]} */ (
  971. normalLoaders
  972. )) {
  973. allLoaders.push(loader);
  974. }
  975. } else {
  976. for (const loader of /** @type {LoaderItem[]} */ (
  977. normalLoaders
  978. )) {
  979. allLoaders.push(loader);
  980. }
  981. for (const loader of /** @type {LoaderItem[]} */ (loaders)) {
  982. allLoaders.push(loader);
  983. }
  984. }
  985. for (const loader of /** @type {LoaderItem[]} */ (preLoaders)) {
  986. allLoaders.push(loader);
  987. }
  988. const type = /** @type {NormalModuleTypes} */ (settings.type);
  989. const resolveOptions = settings.resolve;
  990. const layer = settings.layer;
  991. const applyCreateData = () => {
  992. try {
  993. Object.assign(data.createData, {
  994. layer:
  995. layer === undefined
  996. ? contextInfo.issuerLayer || null
  997. : layer,
  998. request: stringifyLoadersAndResource(
  999. allLoaders,
  1000. resourceData.resource
  1001. ),
  1002. userRequest,
  1003. rawRequest: request,
  1004. loaders: allLoaders,
  1005. resource: resourceData.resource,
  1006. context:
  1007. resourceData.context || getContext(resourceData.resource),
  1008. matchResource: matchResourceData
  1009. ? matchResourceData.resource
  1010. : undefined,
  1011. resourceResolveData: resourceData.data,
  1012. settings,
  1013. type,
  1014. parser: this.getParser(type, settings.parser),
  1015. parserOptions: settings.parser,
  1016. generator: this.getGenerator(type, settings.generator),
  1017. generatorOptions: settings.generator,
  1018. resolveOptions,
  1019. extractSourceMap: settings.extractSourceMap || false
  1020. });
  1021. } catch (createDataErr) {
  1022. return callback(/** @type {Error} */ (createDataErr));
  1023. }
  1024. callback();
  1025. };
  1026. this._prepareModuleType(type, (err) => {
  1027. if (err) return callback(err);
  1028. applyCreateData();
  1029. });
  1030. });
  1031. this.resolveRequestArray(
  1032. contextInfo,
  1033. this.context,
  1034. useLoadersPost,
  1035. loaderResolver,
  1036. resolveContext,
  1037. (err, result) => {
  1038. postLoaders = result;
  1039. continueCallback(err);
  1040. }
  1041. );
  1042. this.resolveRequestArray(
  1043. contextInfo,
  1044. this.context,
  1045. useLoaders,
  1046. loaderResolver,
  1047. resolveContext,
  1048. (err, result) => {
  1049. normalLoaders = result;
  1050. continueCallback(err);
  1051. }
  1052. );
  1053. this.resolveRequestArray(
  1054. contextInfo,
  1055. this.context,
  1056. useLoadersPre,
  1057. loaderResolver,
  1058. resolveContext,
  1059. (err, result) => {
  1060. preLoaders = result;
  1061. continueCallback(err);
  1062. }
  1063. );
  1064. });
  1065. this.resolveRequestArray(
  1066. contextInfo,
  1067. contextScheme ? this.context : context,
  1068. /** @type {LoaderItem[]} */ (elements),
  1069. loaderResolver,
  1070. resolveContext,
  1071. (err, result) => {
  1072. if (err) return continueCallback(err);
  1073. loaders = result;
  1074. continueCallback();
  1075. }
  1076. );
  1077. /**
  1078. * Processes the provided string.
  1079. * @param {string} context context
  1080. */
  1081. const defaultResolve = (context) => {
  1082. if (/^(?:$|\?)/.test(unresolvedResource)) {
  1083. resourceData = {
  1084. ...cacheParseResource(unresolvedResource),
  1085. resource: unresolvedResource,
  1086. data: {}
  1087. };
  1088. continueCallback();
  1089. }
  1090. // resource without scheme and with path
  1091. else {
  1092. const normalResolver = this.getResolver(
  1093. "normal",
  1094. dependencyType
  1095. ? cachedSetProperty(
  1096. resolveOptions || EMPTY_RESOLVE_OPTIONS,
  1097. "dependencyType",
  1098. dependencyType
  1099. )
  1100. : resolveOptions
  1101. );
  1102. this.resolveResource(
  1103. contextInfo,
  1104. context,
  1105. escapeHashInPathRequest(unresolvedResource),
  1106. normalResolver,
  1107. resolveContext,
  1108. (err, _resolvedResource, resolvedResourceResolveData) => {
  1109. if (err) return continueCallback(err);
  1110. if (_resolvedResource !== false) {
  1111. const resolvedResource =
  1112. /** @type {string} */
  1113. (_resolvedResource);
  1114. resourceData = {
  1115. ...cacheParseResource(resolvedResource),
  1116. resource: resolvedResource,
  1117. data:
  1118. /** @type {ResolveRequest} */
  1119. (resolvedResourceResolveData)
  1120. };
  1121. }
  1122. continueCallback();
  1123. }
  1124. );
  1125. }
  1126. };
  1127. // resource with scheme
  1128. if (scheme) {
  1129. resourceData = {
  1130. resource: unresolvedResource,
  1131. data: {},
  1132. path: undefined,
  1133. query: undefined,
  1134. fragment: undefined,
  1135. context: undefined
  1136. };
  1137. this.hooks.resolveForScheme
  1138. .for(scheme)
  1139. .callAsync(resourceData, data, (err) => {
  1140. if (err) return continueCallback(err);
  1141. continueCallback();
  1142. });
  1143. }
  1144. // resource within scheme
  1145. else if (contextScheme) {
  1146. resourceData = {
  1147. resource: unresolvedResource,
  1148. data: {},
  1149. path: undefined,
  1150. query: undefined,
  1151. fragment: undefined,
  1152. context: undefined
  1153. };
  1154. this.hooks.resolveInScheme
  1155. .for(contextScheme)
  1156. .callAsync(resourceData, data, (err, handled) => {
  1157. if (err) return continueCallback(err);
  1158. if (!handled) return defaultResolve(this.context);
  1159. continueCallback();
  1160. });
  1161. }
  1162. // resource without scheme and without path
  1163. else {
  1164. defaultResolve(context);
  1165. }
  1166. }
  1167. );
  1168. }
  1169. cleanupForCache() {
  1170. for (const module of this._restoredUnsafeCacheEntries) {
  1171. ChunkGraph.clearChunkGraphForModule(module);
  1172. ModuleGraph.clearModuleGraphForModule(module);
  1173. module.cleanupForCache();
  1174. }
  1175. }
  1176. /**
  1177. * Processes the provided data.
  1178. * @param {ModuleFactoryCreateData} data data object
  1179. * @param {ModuleFactoryCallback} callback callback
  1180. * @returns {void}
  1181. */
  1182. create(data, callback) {
  1183. const dependencies = /** @type {ModuleDependency[]} */ (data.dependencies);
  1184. const context = data.context || this.context;
  1185. const resolveOptions = data.resolveOptions || EMPTY_RESOLVE_OPTIONS;
  1186. const dependency = dependencies[0];
  1187. const request = dependency.request;
  1188. const attributes =
  1189. /** @type {ModuleDependency & { attributes: ImportAttributes }} */
  1190. (dependency).attributes;
  1191. const phase =
  1192. typeof (
  1193. /** @type {ModuleDependency & { phase?: ImportPhaseType }} */
  1194. (dependency).phase
  1195. ) === "number"
  1196. ? ImportPhaseUtils.stringify(
  1197. /** @type {ModuleDependency & { phase?: ImportPhaseType }} */
  1198. (dependency).phase
  1199. )
  1200. : "evaluation";
  1201. const dependencyType = dependency.category || "";
  1202. const contextInfo = data.contextInfo;
  1203. /** @type {FileSystemDependencies} */
  1204. const fileDependencies = new LazySet();
  1205. /** @type {FileSystemDependencies} */
  1206. const missingDependencies = new LazySet();
  1207. /** @type {FileSystemDependencies} */
  1208. const contextDependencies = new LazySet();
  1209. /** @type {ResolveData} */
  1210. const resolveData = {
  1211. contextInfo,
  1212. resolveOptions,
  1213. context,
  1214. request,
  1215. phase,
  1216. attributes,
  1217. dependencies,
  1218. dependencyType,
  1219. fileDependencies,
  1220. missingDependencies,
  1221. contextDependencies,
  1222. createData: {},
  1223. cacheable: true
  1224. };
  1225. this.hooks.beforeResolve.callAsync(resolveData, (err, result) => {
  1226. if (err) {
  1227. return callback(err, {
  1228. fileDependencies,
  1229. missingDependencies,
  1230. contextDependencies,
  1231. cacheable: false
  1232. });
  1233. }
  1234. // Ignored
  1235. if (result === false) {
  1236. /** @type {ModuleFactoryResult} * */
  1237. const factoryResult = {
  1238. fileDependencies,
  1239. missingDependencies,
  1240. contextDependencies,
  1241. cacheable: resolveData.cacheable
  1242. };
  1243. if (resolveData.ignoredModule) {
  1244. factoryResult.module = resolveData.ignoredModule;
  1245. }
  1246. return callback(null, factoryResult);
  1247. }
  1248. if (typeof result === "object") {
  1249. throw new Error(
  1250. deprecationChangedHookMessage(
  1251. "beforeResolve",
  1252. this.hooks.beforeResolve
  1253. )
  1254. );
  1255. }
  1256. this.hooks.factorize.callAsync(resolveData, (err, module) => {
  1257. if (err) {
  1258. return callback(err, {
  1259. fileDependencies,
  1260. missingDependencies,
  1261. contextDependencies,
  1262. cacheable: false
  1263. });
  1264. }
  1265. /** @type {ModuleFactoryResult} * */
  1266. const factoryResult = {
  1267. module,
  1268. fileDependencies,
  1269. missingDependencies,
  1270. contextDependencies,
  1271. cacheable: resolveData.cacheable
  1272. };
  1273. callback(null, factoryResult);
  1274. });
  1275. });
  1276. }
  1277. /**
  1278. * Processes the provided context info.
  1279. * @param {ModuleFactoryCreateDataContextInfo} contextInfo context info
  1280. * @param {string} context context
  1281. * @param {string} unresolvedResource unresolved resource
  1282. * @param {ResolverWithOptions} resolver resolver
  1283. * @param {ResolveContext} resolveContext resolver context
  1284. * @param {(err: null | Error, res?: string | false, req?: ResolveRequest) => void} callback callback
  1285. */
  1286. resolveResource(
  1287. contextInfo,
  1288. context,
  1289. unresolvedResource,
  1290. resolver,
  1291. resolveContext,
  1292. callback
  1293. ) {
  1294. resolver.resolve(
  1295. contextInfo,
  1296. context,
  1297. unresolvedResource,
  1298. resolveContext,
  1299. (err, resolvedResource, resolvedResourceResolveData) => {
  1300. if (err) {
  1301. return this._resolveResourceErrorHints(
  1302. err,
  1303. contextInfo,
  1304. context,
  1305. unresolvedResource,
  1306. resolver,
  1307. resolveContext,
  1308. (err2, hints) => {
  1309. if (err2) {
  1310. err.message += `
  1311. A fatal error happened during resolving additional hints for this error: ${err2.message}`;
  1312. err.stack += `
  1313. A fatal error happened during resolving additional hints for this error:
  1314. ${err2.stack}`;
  1315. return callback(err);
  1316. }
  1317. if (hints && hints.length > 0) {
  1318. err.message += `
  1319. ${hints.join("\n\n")}`;
  1320. }
  1321. // Check if the extension is missing a leading dot (e.g. "js" instead of ".js")
  1322. let appendResolveExtensionsHint = false;
  1323. const specifiedExtensions = [...resolver.options.extensions];
  1324. const expectedExtensions = specifiedExtensions.map(
  1325. (extension) => {
  1326. if (LEADING_DOT_EXTENSION_REGEX.test(extension)) {
  1327. appendResolveExtensionsHint = true;
  1328. return `.${extension}`;
  1329. }
  1330. return extension;
  1331. }
  1332. );
  1333. if (appendResolveExtensionsHint) {
  1334. err.message += `\nDid you miss the leading dot in 'resolve.extensions'? Did you mean '${JSON.stringify(
  1335. expectedExtensions
  1336. )}' instead of '${JSON.stringify(specifiedExtensions)}'?`;
  1337. }
  1338. callback(err);
  1339. }
  1340. );
  1341. }
  1342. callback(err, resolvedResource, resolvedResourceResolveData);
  1343. }
  1344. );
  1345. }
  1346. /**
  1347. * Resolve resource error hints.
  1348. * @param {Error} error error
  1349. * @param {ModuleFactoryCreateDataContextInfo} contextInfo context info
  1350. * @param {string} context context
  1351. * @param {string} unresolvedResource unresolved resource
  1352. * @param {ResolverWithOptions} resolver resolver
  1353. * @param {ResolveContext} resolveContext resolver context
  1354. * @param {Callback<string[]>} callback callback
  1355. * @private
  1356. */
  1357. _resolveResourceErrorHints(
  1358. error,
  1359. contextInfo,
  1360. context,
  1361. unresolvedResource,
  1362. resolver,
  1363. resolveContext,
  1364. callback
  1365. ) {
  1366. /**
  1367. * Names the entry of the request's directory it comes closest to, for
  1368. * requests that failed over a typo or the casing of a file name. A module
  1369. * request is looked up in the module directories, so a wrongly cased
  1370. * package name is named too, and a directory the request spells out is
  1371. * itself matched by casing.
  1372. * @param {(hint?: string) => void} callback callback
  1373. * @returns {void}
  1374. */
  1375. const suggestClosestEntry = (callback) => {
  1376. const {
  1377. path: requestPath,
  1378. query,
  1379. fragment
  1380. } = parseResource(unresolvedResource);
  1381. /**
  1382. * Absolute paths the resolver would have tried for a request, in the
  1383. * order it tries them.
  1384. * @param {string} request a request
  1385. * @returns {string[]} absolute paths
  1386. */
  1387. const lookupPaths = (request) => {
  1388. if (ABSOLUTE_PATH_REGEXP.test(request)) {
  1389. const paths = [request];
  1390. const firstChar = request.charCodeAt(0);
  1391. // A request rooted at '/' is looked up under 'resolve.roots' as well
  1392. if (firstChar === 47 /* "/" */ || firstChar === 92 /* "\\" */) {
  1393. for (const root of resolver.options.roots) {
  1394. paths.push(join(this.fs, root, request.slice(1)));
  1395. }
  1396. }
  1397. return paths;
  1398. }
  1399. if (RELATIVE_REQUEST_REGEXP.test(request)) {
  1400. return [join(this.fs, context, request)];
  1401. }
  1402. /** @type {string[]} */
  1403. const paths = [];
  1404. // 'preferRelative' lets a module request resolve next to its origin too
  1405. if (resolver.options.preferRelative) {
  1406. paths.push(join(this.fs, context, request));
  1407. }
  1408. for (const directory of moduleLookupDirectories(
  1409. this.fs,
  1410. context,
  1411. resolver.options.modules
  1412. )) {
  1413. paths.push(join(this.fs, directory, request));
  1414. }
  1415. return paths;
  1416. };
  1417. const absoluteRequests = lookupPaths(requestPath);
  1418. // Each path costs a readdir, a casing walk and a resolve, so a large
  1419. // alias map must not turn one failed request into unbounded work
  1420. for (const aliased of aliasedRequests(
  1421. requestPath,
  1422. resolver.options.alias
  1423. )) {
  1424. if (absoluteRequests.length >= MAX_SUGGESTION_CANDIDATES) break;
  1425. for (const path of lookupPaths(aliased)) absoluteRequests.push(path);
  1426. }
  1427. if (absoluteRequests.length > MAX_SUGGESTION_CANDIDATES) {
  1428. absoluteRequests.length = MAX_SUGGESTION_CANDIDATES;
  1429. }
  1430. /**
  1431. * @param {string} prefix the request without its last segment, with the real casing
  1432. * @param {string} requestedName the last segment of the request
  1433. * @param {string[]} entries entries of the directory it was looked up in
  1434. * @param {boolean} directoryCorrected whether the casing of `prefix` had to be corrected
  1435. * @param {() => void} giveUp continues the search when nothing is close enough
  1436. * @returns {void}
  1437. */
  1438. const suggest = (
  1439. prefix,
  1440. requestedName,
  1441. entries,
  1442. directoryCorrected,
  1443. giveUp
  1444. ) => {
  1445. const closest =
  1446. directoryCorrected && entries.includes(requestedName)
  1447. ? { entry: requestedName, distance: 0 }
  1448. : findClosestEntry(
  1449. requestedName,
  1450. entries,
  1451. resolver.options.extensions
  1452. );
  1453. if (!closest) return giveUp();
  1454. const existing = prefix + closest.entry;
  1455. // The query and fragment select what to do with the file, so they
  1456. // belong in a request the user is meant to copy — but not in a
  1457. // sentence about what is on disk
  1458. const suggestion = existing + query + fragment;
  1459. const characters =
  1460. closest.distance === 1
  1461. ? "1 character"
  1462. : `${closest.distance} characters`;
  1463. // A corrected directory is not part of `entry`, so the whole path has
  1464. // to be named for the sentence to account for every difference
  1465. const difference =
  1466. closest.distance === 0
  1467. ? directoryCorrected
  1468. ? `'${existing}' exists and differs from the request only in casing. Case-sensitive filesystems (most Linux ones) fail to resolve it, even when the same build succeeds on a case-insensitive one.`
  1469. : `'${closest.entry}' exists in that directory and differs from the request only in casing. Case-sensitive filesystems (most Linux ones) fail to resolve it, even when the same build succeeds on a case-insensitive one.`
  1470. : directoryCorrected
  1471. ? `'${prefix}' differs from the request only in casing, and '${closest.entry}' is the closest name in it. It differs from the request by ${characters}, ignoring case and file extension.`
  1472. : `'${closest.entry}' is the closest name in that directory. It differs from the request by ${characters}, ignoring case and file extension.`;
  1473. // Offering a request that fails too would only be a second error, so the
  1474. // resolver has the last word. That keeps the hint aligned with every
  1475. // resolve option, not just the ones consulted to find the entry.
  1476. resolver.resolve(
  1477. contextInfo,
  1478. context,
  1479. suggestion,
  1480. resolveContext,
  1481. (suggestionError, resolved) => {
  1482. if (suggestionError || !resolved) return giveUp();
  1483. callback(`Did you mean '${suggestion}'?
  1484. ${difference}`);
  1485. }
  1486. );
  1487. };
  1488. /**
  1489. * @param {number} index index into `absoluteRequests`
  1490. * @returns {void}
  1491. */
  1492. const next = (index) => {
  1493. if (index >= absoluteRequests.length) return callback();
  1494. const absoluteRequest = absoluteRequests[index];
  1495. const directory = dirname(this.fs, absoluteRequest);
  1496. const requestedName = relative(this.fs, directory, absoluteRequest);
  1497. // A request ending in '.' or '..' names a directory, not an entry in one.
  1498. if (!requestPath.endsWith(requestedName)) return next(index + 1);
  1499. const prefix = requestPath.slice(
  1500. 0,
  1501. requestPath.length - requestedName.length
  1502. );
  1503. this.fs.readdir(directory, (err, entries) => {
  1504. if (!err && entries) {
  1505. return suggest(
  1506. prefix,
  1507. requestedName,
  1508. /** @type {string[]} */ (entries),
  1509. false,
  1510. () => next(index + 1)
  1511. );
  1512. }
  1513. // The directory is missing, so only a directory the request spells
  1514. // out itself can be the wrongly cased one
  1515. if (!namesDirectories(prefix)) return next(index + 1);
  1516. findCaseMismatch(this.fs, directory, (mismatch) => {
  1517. if (!mismatch) return next(index + 1);
  1518. const fixedPrefix = applyCaseCorrections(
  1519. prefix,
  1520. mismatch.corrections
  1521. );
  1522. if (fixedPrefix === undefined) return next(index + 1);
  1523. this.fs.readdir(mismatch.path, (realError, realEntries) => {
  1524. if (realError || !realEntries) return next(index + 1);
  1525. suggest(
  1526. fixedPrefix,
  1527. requestedName,
  1528. /** @type {string[]} */ (realEntries),
  1529. true,
  1530. () => next(index + 1)
  1531. );
  1532. });
  1533. });
  1534. });
  1535. };
  1536. next(0);
  1537. };
  1538. asyncLib.parallel(
  1539. [
  1540. (callback) => {
  1541. if (!resolver.options.fullySpecified) return callback();
  1542. resolver
  1543. .withOptions({
  1544. fullySpecified: false
  1545. })
  1546. .resolve(
  1547. contextInfo,
  1548. context,
  1549. unresolvedResource,
  1550. resolveContext,
  1551. (err, resolvedResource) => {
  1552. if (!err && resolvedResource) {
  1553. const resource = parseResource(resolvedResource).path.replace(
  1554. /^.*[\\/]/,
  1555. ""
  1556. );
  1557. return callback(
  1558. null,
  1559. `Did you mean '${resource}'?
  1560. BREAKING CHANGE: The request '${unresolvedResource}' failed to resolve only because it was resolved as fully specified
  1561. (probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
  1562. The extension in the request is mandatory for it to be fully specified.
  1563. Add the extension to the request.`
  1564. );
  1565. }
  1566. callback();
  1567. }
  1568. );
  1569. },
  1570. (callback) => {
  1571. if (!resolver.options.enforceExtension) return callback();
  1572. resolver
  1573. .withOptions({
  1574. enforceExtension: false,
  1575. extensions: []
  1576. })
  1577. .resolve(
  1578. contextInfo,
  1579. context,
  1580. unresolvedResource,
  1581. resolveContext,
  1582. (err, resolvedResource) => {
  1583. if (!err && resolvedResource) {
  1584. let hint = "";
  1585. const match = /\.[^.]+(?:\?|$)/.exec(unresolvedResource);
  1586. if (match) {
  1587. const fixedRequest = unresolvedResource.replace(
  1588. /(\.[^.]+)(\?|$)/,
  1589. "$2"
  1590. );
  1591. hint = resolver.options.extensions.has(match[1])
  1592. ? `Did you mean '${fixedRequest}'?`
  1593. : `Did you mean '${fixedRequest}'? Also note that '${match[1]}' is not in 'resolve.extensions' yet and need to be added for this to work?`;
  1594. } else {
  1595. hint =
  1596. "Did you mean to omit the extension or to remove 'resolve.enforceExtension'?";
  1597. }
  1598. return callback(
  1599. null,
  1600. `The request '${unresolvedResource}' failed to resolve only because 'resolve.enforceExtension' was specified.
  1601. ${hint}
  1602. Including the extension in the request is no longer possible. Did you mean to enforce including the extension in requests with 'resolve.extensions: []' instead?`
  1603. );
  1604. }
  1605. callback();
  1606. }
  1607. );
  1608. },
  1609. (callback) => {
  1610. if (
  1611. /^\.\.?\//.test(unresolvedResource) ||
  1612. resolver.options.preferRelative
  1613. ) {
  1614. return callback();
  1615. }
  1616. resolver.resolve(
  1617. contextInfo,
  1618. context,
  1619. `./${unresolvedResource}`,
  1620. resolveContext,
  1621. (err, resolvedResource) => {
  1622. if (err || !resolvedResource) return callback();
  1623. const moduleDirectories = resolver.options.modules
  1624. .map((m) => (Array.isArray(m) ? m.join(", ") : m))
  1625. .join(", ");
  1626. callback(
  1627. null,
  1628. `Did you mean './${unresolvedResource}'?
  1629. Requests that should resolve in the current directory need to start with './'.
  1630. Requests that start with a name are treated as module requests and resolve within module directories (${moduleDirectories}).
  1631. If changing the source code is not an option there is also a resolve options called 'preferRelative' which tries to resolve these kind of requests in the current directory too.`
  1632. );
  1633. }
  1634. );
  1635. }
  1636. ],
  1637. (err, hints) => {
  1638. if (err) return callback(err);
  1639. const resolveHints = /** @type {string[]} */ (hints).filter(Boolean);
  1640. // The hints above explain why an existing file was rejected and name it
  1641. // themselves, so only look for a similar name when none of them applied.
  1642. if (resolveHints.length > 0) return callback(null, resolveHints);
  1643. suggestClosestEntry((hint) => callback(null, hint ? [hint] : []));
  1644. }
  1645. );
  1646. }
  1647. /**
  1648. * Resolves request array.
  1649. * @param {ModuleFactoryCreateDataContextInfo} contextInfo context info
  1650. * @param {string} context context
  1651. * @param {LoaderItem[]} array array
  1652. * @param {ResolverWithOptions} resolver resolver
  1653. * @param {ResolveContext} resolveContext resolve context
  1654. * @param {Callback<LoaderItem[]>} callback callback
  1655. * @returns {void} result
  1656. */
  1657. resolveRequestArray(
  1658. contextInfo,
  1659. context,
  1660. array,
  1661. resolver,
  1662. resolveContext,
  1663. callback
  1664. ) {
  1665. // LoaderItem
  1666. if (array.length === 0) return callback(null, array);
  1667. asyncLib.map(
  1668. array,
  1669. /**
  1670. * Handles the callback logic for this hook.
  1671. * @param {LoaderItem} item item
  1672. * @param {Callback<LoaderItem>} callback callback
  1673. * @returns {void}
  1674. */
  1675. (item, callback) => {
  1676. // Loaders resolve identically for a given (context, request), so memoize
  1677. // the result and the resolve's dependencies to skip repeated resolves.
  1678. const cacheKey = `${context}\n${item.loader}`;
  1679. const cached = this._loaderResolveCache.get(cacheKey);
  1680. if (cached !== undefined) {
  1681. replayResolveDependencies(resolveContext, cached);
  1682. return callback(null, {
  1683. loader: cached.loader,
  1684. type: cached.type,
  1685. options:
  1686. item.options === undefined
  1687. ? cached.query
  1688. ? cached.query.slice(1)
  1689. : undefined
  1690. : item.options,
  1691. ident: item.options === undefined ? undefined : item.ident
  1692. });
  1693. }
  1694. // Capture this resolve's dependencies in dedicated sets so a future hit
  1695. // can replay them; also feed them back into the caller's resolveContext.
  1696. const captureContext = {
  1697. ...resolveContext,
  1698. fileDependencies: new LazySet(),
  1699. contextDependencies: new LazySet(),
  1700. missingDependencies: new LazySet()
  1701. };
  1702. resolver.resolve(
  1703. contextInfo,
  1704. context,
  1705. item.loader,
  1706. captureContext,
  1707. (err, result, resolveRequest) => {
  1708. // Merge the captured dependencies back so a failed or successful
  1709. // resolve records them for watch invalidation just like before.
  1710. replayResolveDependencies(resolveContext, captureContext);
  1711. if (
  1712. err &&
  1713. /^[^/]*$/.test(item.loader) &&
  1714. !item.loader.endsWith("-loader")
  1715. ) {
  1716. return resolver.resolve(
  1717. contextInfo,
  1718. context,
  1719. `${item.loader}-loader`,
  1720. resolveContext,
  1721. (err2) => {
  1722. if (!err2) {
  1723. err.message =
  1724. `${err.message}\n` +
  1725. "BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffix when using loaders.\n" +
  1726. ` You need to specify '${item.loader}-loader' instead of '${item.loader}',\n` +
  1727. " see https://webpack.js.org/migrate/3/#automatic-loader-module-name-extension-removed";
  1728. }
  1729. callback(err);
  1730. }
  1731. );
  1732. }
  1733. if (err) return callback(err);
  1734. const parsedResult = this._parseResourceWithoutFragment(
  1735. /** @type {string} */
  1736. (result)
  1737. );
  1738. const type = /\.mjs$/i.test(parsedResult.path)
  1739. ? "module"
  1740. : /\.cjs$/i.test(parsedResult.path)
  1741. ? "commonjs"
  1742. : /** @type {ResolveRequest} */
  1743. (resolveRequest).descriptionFileData === undefined
  1744. ? undefined
  1745. : /** @type {string} */
  1746. (
  1747. /** @type {ResolveRequest} */
  1748. (resolveRequest).descriptionFileData.type
  1749. );
  1750. this._loaderResolveCache.set(cacheKey, {
  1751. loader: parsedResult.path,
  1752. query: parsedResult.query,
  1753. type,
  1754. fileDependencies: captureContext.fileDependencies,
  1755. contextDependencies: captureContext.contextDependencies,
  1756. missingDependencies: captureContext.missingDependencies
  1757. });
  1758. /** @type {LoaderItem} */
  1759. const resolved = {
  1760. loader: parsedResult.path,
  1761. type,
  1762. options:
  1763. item.options === undefined
  1764. ? parsedResult.query
  1765. ? parsedResult.query.slice(1)
  1766. : undefined
  1767. : item.options,
  1768. ident: item.options === undefined ? undefined : item.ident
  1769. };
  1770. return callback(null, resolved);
  1771. }
  1772. );
  1773. },
  1774. (err, value) => {
  1775. callback(
  1776. /** @type {Error | null} */ (err),
  1777. /** @type {(LoaderItem)[]} */ (value)
  1778. );
  1779. }
  1780. );
  1781. }
  1782. /**
  1783. * Runs `prepareModuleType` for a type once, so a plugin can load what its
  1784. * synchronous `createParser`/`createGenerator` tap needs. Calls back
  1785. * synchronously for a type nothing taps or that is already prepared.
  1786. * @param {string} type module type
  1787. * @param {Callback<void>} callback signals the type is ready
  1788. * @returns {void}
  1789. */
  1790. _prepareModuleType(type, callback) {
  1791. const state = this._moduleTypePreparation.get(type);
  1792. if (state === true) return callback(null);
  1793. if (state !== undefined) {
  1794. state.push(callback);
  1795. return;
  1796. }
  1797. const hook = this.hooks.prepareModuleType.get(type);
  1798. if (hook === undefined) {
  1799. this._moduleTypePreparation.set(type, true);
  1800. return callback(null);
  1801. }
  1802. /** @type {Callback<void>[]} */
  1803. const waiting = [];
  1804. this._moduleTypePreparation.set(type, waiting);
  1805. hook.callAsync((err) => {
  1806. // a failed preparation must not be remembered as done
  1807. if (err) this._moduleTypePreparation.delete(type);
  1808. else this._moduleTypePreparation.set(type, true);
  1809. // every queued factorize must be resumed, so one callback throwing
  1810. // cannot starve the rest — the first throw is rethrown afterwards
  1811. waiting.unshift(callback);
  1812. const error = err || null;
  1813. /** @type {Error | undefined} */
  1814. let thrown;
  1815. for (const waitingCallback of waiting) {
  1816. try {
  1817. waitingCallback(error);
  1818. } catch (err_) {
  1819. if (thrown === undefined) {
  1820. thrown = /** @type {Error} */ (err_);
  1821. }
  1822. }
  1823. }
  1824. if (thrown !== undefined) throw thrown;
  1825. });
  1826. }
  1827. /**
  1828. * Returns parser.
  1829. * @template {string} T
  1830. * @param {T} type type
  1831. * @param {ParserOptions} parserOptions parser options
  1832. * @returns {ParserByType[T]} parser
  1833. */
  1834. getParser(type, parserOptions = EMPTY_PARSER_OPTIONS) {
  1835. let cache = this.parserCache.get(type);
  1836. if (cache === undefined) {
  1837. cache = new WeakMap();
  1838. this.parserCache.set(type, cache);
  1839. }
  1840. let parser = cache.get(parserOptions);
  1841. if (parser === undefined) {
  1842. parser = this.createParser(type, parserOptions);
  1843. cache.set(parserOptions, parser);
  1844. }
  1845. return /** @type {ParserByType[T]} */ (parser);
  1846. }
  1847. /**
  1848. * Creates a parser from the provided type.
  1849. * @template {string} T
  1850. * @param {T} type type
  1851. * @param {ParserOptions} parserOptions parser options
  1852. * @returns {ParserByType[T]} parser
  1853. */
  1854. createParser(type, parserOptions = {}) {
  1855. parserOptions = mergeGlobalOptions(
  1856. this._globalParserOptions,
  1857. type,
  1858. parserOptions
  1859. );
  1860. const parser = this.hooks.createParser.for(type).call(parserOptions);
  1861. if (!parser) {
  1862. throw new Error(`No parser registered for ${type}`);
  1863. }
  1864. this.hooks.parser.for(type).call(parser, parserOptions);
  1865. return /** @type {ParserByType[T]} */ (parser);
  1866. }
  1867. /**
  1868. * Returns generator.
  1869. * @template {string} T
  1870. * @param {T} type type of generator
  1871. * @param {GeneratorOptions} generatorOptions generator options
  1872. * @returns {GeneratorByType[T]} generator
  1873. */
  1874. getGenerator(type, generatorOptions = EMPTY_GENERATOR_OPTIONS) {
  1875. let cache = this.generatorCache.get(type);
  1876. if (cache === undefined) {
  1877. cache = new WeakMap();
  1878. this.generatorCache.set(type, cache);
  1879. }
  1880. let generator = cache.get(generatorOptions);
  1881. if (generator === undefined) {
  1882. generator = this.createGenerator(type, generatorOptions);
  1883. cache.set(generatorOptions, generator);
  1884. }
  1885. return /** @type {GeneratorByType[T]} */ (generator);
  1886. }
  1887. /**
  1888. * Creates a generator.
  1889. * @template {string} T
  1890. * @param {T} type type of generator
  1891. * @param {GeneratorOptions} generatorOptions generator options
  1892. * @returns {GeneratorByType[T]} generator
  1893. */
  1894. createGenerator(type, generatorOptions = {}) {
  1895. generatorOptions = mergeGlobalOptions(
  1896. this._globalGeneratorOptions,
  1897. type,
  1898. generatorOptions
  1899. );
  1900. const generator = this.hooks.createGenerator
  1901. .for(type)
  1902. .call(generatorOptions);
  1903. if (!generator) {
  1904. throw new Error(`No generator registered for ${type}`);
  1905. }
  1906. this.hooks.generator.for(type).call(generator, generatorOptions);
  1907. return /** @type {GeneratorByType[T]} */ (generator);
  1908. }
  1909. /**
  1910. * Returns the resolver.
  1911. * @param {Parameters<ResolverFactory["get"]>[0]} type type of resolver
  1912. * @param {Parameters<ResolverFactory["get"]>[1]=} resolveOptions options
  1913. * @returns {ReturnType<ResolverFactory["get"]>} the resolver
  1914. */
  1915. getResolver(type, resolveOptions) {
  1916. return this.resolverFactory.get(type, resolveOptions);
  1917. }
  1918. }
  1919. module.exports = NormalModuleFactory;