NormalModuleFactory.js 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { getContext } = require("loader-runner");
  7. const asyncLib = require("neo-async");
  8. const {
  9. AsyncSeriesBailHook,
  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 BasicEffectRulePlugin = require("./rules/BasicEffectRulePlugin");
  23. const BasicMatcherRulePlugin = require("./rules/BasicMatcherRulePlugin");
  24. const ObjectMatcherRulePlugin = require("./rules/ObjectMatcherRulePlugin");
  25. const RuleSetCompiler = require("./rules/RuleSetCompiler");
  26. const UseEffectRulePlugin = require("./rules/UseEffectRulePlugin");
  27. const LazySet = require("./util/LazySet");
  28. const { getScheme } = require("./util/URLAbsoluteSpecifier");
  29. const { cachedCleverMerge, cachedSetProperty } = require("./util/cleverMerge");
  30. const { join } = require("./util/fs");
  31. const {
  32. parseResource,
  33. parseResourceWithoutFragment
  34. } = require("./util/identifier");
  35. /** @typedef {import("enhanced-resolve").ResolveContext} ResolveContext */
  36. /** @typedef {import("enhanced-resolve").ResolveRequest} ResolveRequest */
  37. /** @typedef {import("../declarations/WebpackOptions").ModuleOptionsNormalized} ModuleOptions */
  38. /** @typedef {import("../declarations/WebpackOptions").RuleSetRule} RuleSetRule */
  39. /** @typedef {import("./Compilation").FileSystemDependencies} FileSystemDependencies */
  40. /** @typedef {import("./Generator")} Generator */
  41. /** @typedef {import("./ModuleFactory").ModuleFactoryCallback} ModuleFactoryCallback */
  42. /** @typedef {import("./ModuleFactory").ModuleFactoryCreateData} ModuleFactoryCreateData */
  43. /** @typedef {import("./ModuleFactory").ModuleFactoryCreateDataContextInfo} ModuleFactoryCreateDataContextInfo */
  44. /** @typedef {import("./ModuleFactory").ModuleFactoryResult} ModuleFactoryResult */
  45. /** @typedef {import("./NormalModule").GeneratorOptions} GeneratorOptions */
  46. /** @typedef {import("./NormalModule").LoaderItem} LoaderItem */
  47. /** @typedef {import("./NormalModule").NormalModuleCreateData} NormalModuleCreateData */
  48. /** @typedef {import("./NormalModule").ParserOptions} ParserOptions */
  49. /** @typedef {import("./Parser")} Parser */
  50. /** @typedef {import("./ResolverFactory")} ResolverFactory */
  51. /** @typedef {import("./ResolverFactory").ResolverWithOptions} ResolverWithOptions */
  52. /** @typedef {import("./dependencies/ModuleDependency")} ModuleDependency */
  53. /** @typedef {import("./dependencies/ImportPhase").ImportPhaseType} ImportPhaseType */
  54. /** @typedef {import("./dependencies/ImportPhase").ImportPhaseName} ImportPhaseName */
  55. /** @typedef {import("./javascript/JavascriptParser").ImportAttributes} ImportAttributes */
  56. /** @typedef {import("./rules/RuleSetCompiler").RuleSetRules} RuleSetRules */
  57. /** @typedef {import("./rules/RuleSetCompiler").RuleSet} RuleSet */
  58. /** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
  59. /** @typedef {import("./util/identifier").AssociatedObjectForCache} AssociatedObjectForCache */
  60. /**
  61. * Defines the callback type used by this module.
  62. * @template T
  63. * @typedef {import("./Compiler").Callback<T>} Callback
  64. */
  65. /** @typedef {Pick<RuleSetRule, "type" | "sideEffects" | "parser" | "generator" | "resolve" | "layer" | "extractSourceMap">} ModuleSettings */
  66. /** @typedef {Partial<NormalModuleCreateData & { settings: ModuleSettings }>} CreateData */
  67. /**
  68. * Defines the resolve data type used by this module.
  69. * @typedef {object} ResolveData
  70. * @property {ModuleFactoryCreateData["contextInfo"]} contextInfo
  71. * @property {ModuleFactoryCreateData["resolveOptions"]} resolveOptions
  72. * @property {string} context
  73. * @property {string} request
  74. * @property {ImportPhaseName=} phase
  75. * @property {ImportAttributes=} attributes
  76. * @property {ModuleDependency[]} dependencies
  77. * @property {string} dependencyType
  78. * @property {CreateData} createData
  79. * @property {FileSystemDependencies} fileDependencies
  80. * @property {FileSystemDependencies} missingDependencies
  81. * @property {FileSystemDependencies} contextDependencies
  82. * @property {Module=} ignoredModule
  83. * @property {boolean} cacheable allow to use the unsafe cache
  84. */
  85. /**
  86. * Defines the resource data type used by this module.
  87. * @typedef {object} ResourceData
  88. * @property {string} resource
  89. * @property {string=} path
  90. * @property {string=} query
  91. * @property {string=} fragment
  92. * @property {string=} context
  93. */
  94. /**
  95. * Defines the resource scheme data type used by this module.
  96. * @typedef {object} ResourceSchemeData
  97. * @property {string=} mimetype mime type of the resource
  98. * @property {string=} parameters additional parameters for the resource
  99. * @property {"base64" | false=} encoding encoding of the resource
  100. * @property {string=} encodedContent encoded content of the resource
  101. */
  102. /** @typedef {ResourceData & { data: ResourceSchemeData & Partial<ResolveRequest> }} ResourceDataWithData */
  103. /**
  104. * Defines the parsed loader request type used by this module.
  105. * @typedef {object} ParsedLoaderRequest
  106. * @property {string} loader loader
  107. * @property {string | undefined} options options
  108. */
  109. /** @typedef {import("./ModuleTypeConstants").JAVASCRIPT_MODULE_TYPE_AUTO} JAVASCRIPT_MODULE_TYPE_AUTO */
  110. /** @typedef {import("./ModuleTypeConstants").JAVASCRIPT_MODULE_TYPE_DYNAMIC} JAVASCRIPT_MODULE_TYPE_DYNAMIC */
  111. /** @typedef {import("./ModuleTypeConstants").JAVASCRIPT_MODULE_TYPE_ESM} JAVASCRIPT_MODULE_TYPE_ESM */
  112. /** @typedef {import("./ModuleTypeConstants").JSON_MODULE_TYPE} JSON_MODULE_TYPE */
  113. /** @typedef {import("./ModuleTypeConstants").ASSET_MODULE_TYPE} ASSET_MODULE_TYPE */
  114. /** @typedef {import("./ModuleTypeConstants").ASSET_MODULE_TYPE_INLINE} ASSET_MODULE_TYPE_INLINE */
  115. /** @typedef {import("./ModuleTypeConstants").ASSET_MODULE_TYPE_RESOURCE} ASSET_MODULE_TYPE_RESOURCE */
  116. /** @typedef {import("./ModuleTypeConstants").ASSET_MODULE_TYPE_SOURCE} ASSET_MODULE_TYPE_SOURCE */
  117. /** @typedef {import("./ModuleTypeConstants").ASSET_MODULE_TYPE_BYTES} ASSET_MODULE_TYPE_BYTES */
  118. /** @typedef {import("./ModuleTypeConstants").WEBASSEMBLY_MODULE_TYPE_ASYNC} WEBASSEMBLY_MODULE_TYPE_ASYNC */
  119. /** @typedef {import("./ModuleTypeConstants").WEBASSEMBLY_MODULE_TYPE_SYNC} WEBASSEMBLY_MODULE_TYPE_SYNC */
  120. /** @typedef {import("./ModuleTypeConstants").CSS_MODULE_TYPE} CSS_MODULE_TYPE */
  121. /** @typedef {import("./ModuleTypeConstants").CSS_MODULE_TYPE_GLOBAL} CSS_MODULE_TYPE_GLOBAL */
  122. /** @typedef {import("./ModuleTypeConstants").CSS_MODULE_TYPE_MODULE} CSS_MODULE_TYPE_MODULE */
  123. /** @typedef {import("./ModuleTypeConstants").CSS_MODULE_TYPE_AUTO} CSS_MODULE_TYPE_AUTO */
  124. /** @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} KnownNormalModuleTypes */
  125. /** @typedef {KnownNormalModuleTypes | string} NormalModuleTypes */
  126. const EMPTY_RESOLVE_OPTIONS = {};
  127. /** @type {ParserOptions} */
  128. const EMPTY_PARSER_OPTIONS = {};
  129. /** @type {GeneratorOptions} */
  130. const EMPTY_GENERATOR_OPTIONS = {};
  131. /** @type {ParsedLoaderRequest[]} */
  132. const EMPTY_ELEMENTS = [];
  133. const MATCH_RESOURCE_REGEX = /^([^!]+)!=!/;
  134. const LEADING_DOT_EXTENSION_REGEX = /^[^.]/;
  135. /**
  136. * Returns ident.
  137. * @param {LoaderItem} data data
  138. * @returns {string} ident
  139. */
  140. const loaderToIdent = (data) => {
  141. if (!data.options) {
  142. return data.loader;
  143. }
  144. if (typeof data.options === "string") {
  145. return `${data.loader}?${data.options}`;
  146. }
  147. if (typeof data.options !== "object") {
  148. throw new Error("loader options must be string or object");
  149. }
  150. if (data.ident) {
  151. return `${data.loader}??${data.ident}`;
  152. }
  153. return `${data.loader}?${JSON.stringify(data.options)}`;
  154. };
  155. /**
  156. * Stringify loaders and resource.
  157. * @param {LoaderItem[]} loaders loaders
  158. * @param {string} resource resource
  159. * @returns {string} stringified loaders and resource
  160. */
  161. const stringifyLoadersAndResource = (loaders, resource) => {
  162. let str = "";
  163. for (const loader of loaders) {
  164. str += `${loaderToIdent(loader)}!`;
  165. }
  166. return str + resource;
  167. };
  168. /**
  169. * Checks whether it needs calls.
  170. * @param {number} times times
  171. * @param {(err?: null | Error) => void} callback callback
  172. * @returns {(err?: null | Error) => void} callback
  173. */
  174. const needCalls = (times, callback) => (err) => {
  175. if (--times === 0) {
  176. return callback(err);
  177. }
  178. if (err && times > 0) {
  179. times = Number.NaN;
  180. return callback(err);
  181. }
  182. };
  183. /**
  184. * Merges global options.
  185. * @template T
  186. * @template O
  187. * @param {T} globalOptions global options
  188. * @param {string} type type
  189. * @param {O} localOptions local options
  190. * @returns {T & O | T | O} result
  191. */
  192. const mergeGlobalOptions = (globalOptions, type, localOptions) => {
  193. const parts = type.split("/");
  194. /** @type {undefined | T} */
  195. let result;
  196. let current = "";
  197. for (const part of parts) {
  198. current = current ? `${current}/${part}` : part;
  199. const options =
  200. /** @type {T} */
  201. (globalOptions[/** @type {keyof T} */ (current)]);
  202. if (typeof options === "object") {
  203. result =
  204. result === undefined ? options : cachedCleverMerge(result, options);
  205. }
  206. }
  207. if (result === undefined) {
  208. return localOptions;
  209. }
  210. return cachedCleverMerge(result, localOptions);
  211. };
  212. // TODO webpack 6 remove
  213. /**
  214. * Deprecation changed hook message.
  215. * @template {import("tapable").Hook<EXPECTED_ANY, EXPECTED_ANY>} T
  216. * @param {string} name name
  217. * @param {T} hook hook
  218. * @returns {string} result
  219. */
  220. const deprecationChangedHookMessage = (name, hook) => {
  221. const names = hook.taps.map((tapped) => tapped.name).join(", ");
  222. return (
  223. `NormalModuleFactory.${name} (${names}) is no longer a waterfall hook, but a bailing hook instead. ` +
  224. "Do not return the passed object, but modify it instead. " +
  225. "Returning false will ignore the request and results in no module created."
  226. );
  227. };
  228. const ruleSetCompiler = new RuleSetCompiler([
  229. new BasicMatcherRulePlugin("test", "resource"),
  230. new BasicMatcherRulePlugin("scheme"),
  231. new BasicMatcherRulePlugin("mimetype"),
  232. new BasicMatcherRulePlugin("dependency"),
  233. new BasicMatcherRulePlugin("include", "resource"),
  234. new BasicMatcherRulePlugin("exclude", "resource", true),
  235. new BasicMatcherRulePlugin("resource"),
  236. new BasicMatcherRulePlugin("resourceQuery"),
  237. new BasicMatcherRulePlugin("resourceFragment"),
  238. new BasicMatcherRulePlugin("realResource"),
  239. new BasicMatcherRulePlugin("issuer"),
  240. new BasicMatcherRulePlugin("compiler"),
  241. new BasicMatcherRulePlugin("issuerLayer"),
  242. new BasicMatcherRulePlugin("phase"),
  243. new ObjectMatcherRulePlugin("assert", "attributes", (value) => {
  244. if (value) {
  245. return (
  246. /** @type {ImportAttributes} */ (value)._isLegacyAssert !== undefined
  247. );
  248. }
  249. return false;
  250. }),
  251. new ObjectMatcherRulePlugin("with", "attributes", (value) => {
  252. if (value) {
  253. return !(/** @type {ImportAttributes} */ (value)._isLegacyAssert);
  254. }
  255. return false;
  256. }),
  257. new ObjectMatcherRulePlugin("descriptionData"),
  258. new BasicEffectRulePlugin("type"),
  259. new BasicEffectRulePlugin("sideEffects"),
  260. new BasicEffectRulePlugin("parser"),
  261. new BasicEffectRulePlugin("resolve"),
  262. new BasicEffectRulePlugin("generator"),
  263. new BasicEffectRulePlugin("layer"),
  264. new BasicEffectRulePlugin("extractSourceMap"),
  265. new UseEffectRulePlugin()
  266. ]);
  267. /** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */
  268. /** @typedef {import("../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
  269. /** @typedef {import("./javascript/JavascriptGenerator")} JavascriptGenerator */
  270. /** @typedef {import("../declarations/WebpackOptions").EmptyGeneratorOptions} EmptyGeneratorOptions */
  271. /** @typedef {import("./json/JsonParser")} JsonParser */
  272. /** @typedef {import("../declarations/WebpackOptions").JsonParserOptions} JsonParserOptions */
  273. /** @typedef {import("./json/JsonGenerator")} JsonGenerator */
  274. /** @typedef {import("../declarations/WebpackOptions").JsonGeneratorOptions} JsonGeneratorOptions */
  275. /** @typedef {import("./asset/AssetParser")} AssetParser */
  276. /** @typedef {import("./asset/AssetSourceParser")} AssetSourceParser */
  277. /** @typedef {import("./asset/AssetBytesParser")} AssetBytesParser */
  278. /** @typedef {import("../declarations/WebpackOptions").AssetParserOptions} AssetParserOptions */
  279. /** @typedef {import("../declarations/WebpackOptions").EmptyParserOptions} EmptyParserOptions */
  280. /** @typedef {import("./asset/AssetGenerator")} AssetGenerator */
  281. /** @typedef {import("../declarations/WebpackOptions").AssetGeneratorOptions} AssetGeneratorOptions */
  282. /** @typedef {import("../declarations/WebpackOptions").AssetInlineGeneratorOptions} AssetInlineGeneratorOptions */
  283. /** @typedef {import("../declarations/WebpackOptions").AssetResourceGeneratorOptions} AssetResourceGeneratorOptions */
  284. /** @typedef {import("./asset/AssetSourceGenerator")} AssetSourceGenerator */
  285. /** @typedef {import("./asset/AssetBytesGenerator")} AssetBytesGenerator */
  286. /** @typedef {import("./wasm-async/AsyncWebAssemblyParser")} AsyncWebAssemblyParser */
  287. /** @typedef {import("./wasm-sync/WebAssemblyParser")} WebAssemblyParser */
  288. /** @typedef {import("./css/CssParser")} CssParser */
  289. /** @typedef {import("../declarations/WebpackOptions").CssParserOptions} CssParserOptions */
  290. /** @typedef {import("../declarations/WebpackOptions").CssModuleParserOptions} CssModuleParserOptions */
  291. /** @typedef {import("./css/CssGenerator")} CssGenerator */
  292. /** @typedef {import("../declarations/WebpackOptions").CssGeneratorOptions} CssGeneratorOptions */
  293. /** @typedef {import("../declarations/WebpackOptions").CssModuleGeneratorOptions} CssModuleGeneratorOptions */
  294. /* eslint-disable jsdoc/type-formatting */
  295. /**
  296. * Defines the shared type used by this module.
  297. * @typedef {[
  298. * [JAVASCRIPT_MODULE_TYPE_AUTO, JavascriptParser, JavascriptParserOptions, JavascriptGenerator, EmptyGeneratorOptions],
  299. * [JAVASCRIPT_MODULE_TYPE_DYNAMIC, JavascriptParser, JavascriptParserOptions, JavascriptGenerator, EmptyGeneratorOptions],
  300. * [JAVASCRIPT_MODULE_TYPE_ESM, JavascriptParser, JavascriptParserOptions, JavascriptGenerator, EmptyGeneratorOptions],
  301. * [JSON_MODULE_TYPE, JsonParser, JsonParserOptions, JsonGenerator, JsonGeneratorOptions],
  302. * [ASSET_MODULE_TYPE, AssetParser, AssetParserOptions, AssetGenerator, AssetGeneratorOptions],
  303. * [ASSET_MODULE_TYPE_INLINE, AssetParser, EmptyParserOptions, AssetGenerator, AssetGeneratorOptions],
  304. * [ASSET_MODULE_TYPE_RESOURCE, AssetParser, EmptyParserOptions, AssetGenerator, AssetGeneratorOptions],
  305. * [ASSET_MODULE_TYPE_SOURCE, AssetSourceParser, EmptyParserOptions, AssetSourceGenerator, EmptyGeneratorOptions],
  306. * [ASSET_MODULE_TYPE_BYTES, AssetBytesParser, EmptyParserOptions, AssetBytesGenerator, EmptyGeneratorOptions],
  307. * [WEBASSEMBLY_MODULE_TYPE_ASYNC, AsyncWebAssemblyParser, EmptyParserOptions, Generator, EmptyParserOptions],
  308. * [WEBASSEMBLY_MODULE_TYPE_SYNC, WebAssemblyParser, EmptyParserOptions, Generator, EmptyParserOptions],
  309. * [CSS_MODULE_TYPE, CssParser, CssParserOptions, CssGenerator, CssGeneratorOptions],
  310. * [CSS_MODULE_TYPE_AUTO, CssParser, CssModuleParserOptions, CssGenerator, CssModuleGeneratorOptions],
  311. * [CSS_MODULE_TYPE_MODULE, CssParser, CssModuleParserOptions, CssGenerator, CssModuleGeneratorOptions],
  312. * [CSS_MODULE_TYPE_GLOBAL, CssParser, CssModuleParserOptions, CssGenerator, CssModuleGeneratorOptions],
  313. * [string, Parser, ParserOptions, Generator, GeneratorOptions],
  314. * ]} ParsersAndGeneratorsByTypes
  315. */
  316. /* eslint-enable jsdoc/type-formatting */
  317. /**
  318. * Defines the extract tuple elements type used by this module.
  319. * @template {unknown[]} T
  320. * @template {number[]} I
  321. * @typedef {{ [K in keyof I]: K extends keyof I ? I[K] extends keyof T ? T[I[K]] : never : never }} ExtractTupleElements
  322. */
  323. /**
  324. * Represents the normal module factory runtime component.
  325. * @template {unknown[]} T
  326. * @template {number[]} A
  327. * @template [R=void]
  328. * @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
  329. */
  330. class NormalModuleFactory extends ModuleFactory {
  331. /**
  332. * Creates an instance of NormalModuleFactory.
  333. * @param {object} param params
  334. * @param {string=} param.context context
  335. * @param {InputFileSystem} param.fs file system
  336. * @param {ResolverFactory} param.resolverFactory resolverFactory
  337. * @param {ModuleOptions} param.options options
  338. * @param {AssociatedObjectForCache} param.associatedObjectForCache an object to which the cache will be attached
  339. */
  340. constructor({
  341. context,
  342. fs,
  343. resolverFactory,
  344. options,
  345. associatedObjectForCache
  346. }) {
  347. super();
  348. this.hooks = Object.freeze({
  349. /** @type {AsyncSeriesBailHook<[ResolveData], Module | false | void>} */
  350. resolve: new AsyncSeriesBailHook(["resolveData"]),
  351. /** @type {HookMap<AsyncSeriesBailHook<[ResourceDataWithData, ResolveData], true | void>>} */
  352. resolveForScheme: new HookMap(
  353. () => new AsyncSeriesBailHook(["resourceData", "resolveData"])
  354. ),
  355. /** @type {HookMap<AsyncSeriesBailHook<[ResourceDataWithData, ResolveData], true | void>>} */
  356. resolveInScheme: new HookMap(
  357. () => new AsyncSeriesBailHook(["resourceData", "resolveData"])
  358. ),
  359. /** @type {AsyncSeriesBailHook<[ResolveData], Module | undefined>} */
  360. factorize: new AsyncSeriesBailHook(["resolveData"]),
  361. /** @type {AsyncSeriesBailHook<[ResolveData], false | void>} */
  362. beforeResolve: new AsyncSeriesBailHook(["resolveData"]),
  363. /** @type {AsyncSeriesBailHook<[ResolveData], false | void>} */
  364. afterResolve: new AsyncSeriesBailHook(["resolveData"]),
  365. /** @type {AsyncSeriesBailHook<[CreateData, ResolveData], Module | void>} */
  366. createModule: new AsyncSeriesBailHook(["createData", "resolveData"]),
  367. /** @type {SyncWaterfallHook<[Module, CreateData, ResolveData]>} */
  368. module: new SyncWaterfallHook(["module", "createData", "resolveData"]),
  369. /** @type {import("tapable").TypedHookMap<RecordFactoryFromTuple<ParsersAndGeneratorsByTypes, [2], 1>>} */
  370. createParser: new HookMap(() => new SyncBailHook(["parserOptions"])),
  371. /** @type {import("tapable").TypedHookMap<RecordFactoryFromTuple<ParsersAndGeneratorsByTypes, [1, 2]>>} */
  372. parser: new HookMap(() => new SyncHook(["parser", "parserOptions"])),
  373. /** @type {import("tapable").TypedHookMap<RecordFactoryFromTuple<ParsersAndGeneratorsByTypes, [4], 3>>} */
  374. createGenerator: new HookMap(
  375. () => new SyncBailHook(["generatorOptions"])
  376. ),
  377. /** @type {import("tapable").TypedHookMap<RecordFactoryFromTuple<ParsersAndGeneratorsByTypes, [3, 4]>>} */
  378. generator: new HookMap(
  379. () => new SyncHook(["generator", "generatorOptions"])
  380. ),
  381. /** @type {HookMap<SyncBailHook<[CreateData, ResolveData], Module | void>>} */
  382. createModuleClass: new HookMap(
  383. () => new SyncBailHook(["createData", "resolveData"])
  384. )
  385. });
  386. /** @type {ResolverFactory} */
  387. this.resolverFactory = resolverFactory;
  388. /** @type {RuleSet} */
  389. this.ruleSet = ruleSetCompiler.compile([
  390. {
  391. rules: /** @type {RuleSetRules} */ (options.defaultRules)
  392. },
  393. {
  394. rules: /** @type {RuleSetRules} */ (options.rules)
  395. }
  396. ]);
  397. /** @type {string} */
  398. this.context = context || "";
  399. /** @type {InputFileSystem} */
  400. this.fs = fs;
  401. this._globalParserOptions = options.parser;
  402. this._globalGeneratorOptions = options.generator;
  403. /** @type {Map<string, WeakMap<ParserOptions, Parser>>} */
  404. this.parserCache = new Map();
  405. /** @type {Map<string, WeakMap<GeneratorOptions, Generator>>} */
  406. this.generatorCache = new Map();
  407. /** @type {Set<Module>} */
  408. this._restoredUnsafeCacheEntries = new Set();
  409. /** @type {(resource: string) => import("./util/identifier").ParsedResource} */
  410. const cacheParseResource = parseResource.bindCache(
  411. associatedObjectForCache
  412. );
  413. const cachedParseResourceWithoutFragment =
  414. parseResourceWithoutFragment.bindCache(associatedObjectForCache);
  415. this._parseResourceWithoutFragment = cachedParseResourceWithoutFragment;
  416. this.hooks.factorize.tapAsync(
  417. {
  418. name: "NormalModuleFactory",
  419. stage: 100
  420. },
  421. (resolveData, callback) => {
  422. this.hooks.resolve.callAsync(resolveData, (err, result) => {
  423. if (err) return callback(err);
  424. // Ignored
  425. if (result === false) return callback();
  426. // direct module
  427. if (result instanceof Module) return callback(null, result);
  428. if (typeof result === "object") {
  429. throw new Error(
  430. `${deprecationChangedHookMessage(
  431. "resolve",
  432. this.hooks.resolve
  433. )} Returning a Module object will result in this module used as result.`
  434. );
  435. }
  436. this.hooks.afterResolve.callAsync(resolveData, (err, result) => {
  437. if (err) return callback(err);
  438. if (typeof result === "object") {
  439. throw new Error(
  440. deprecationChangedHookMessage(
  441. "afterResolve",
  442. this.hooks.afterResolve
  443. )
  444. );
  445. }
  446. // Ignored
  447. if (result === false) return callback();
  448. const createData = resolveData.createData;
  449. this.hooks.createModule.callAsync(
  450. createData,
  451. resolveData,
  452. (err, createdModule) => {
  453. if (!createdModule) {
  454. if (!resolveData.request) {
  455. return callback(new Error("Empty dependency (no request)"));
  456. }
  457. // TODO webpack 6 make it required and move javascript/wasm/asset properties to own module
  458. createdModule = this.hooks.createModuleClass
  459. .for(
  460. /** @type {ModuleSettings} */
  461. (createData.settings).type
  462. )
  463. .call(createData, resolveData);
  464. if (!createdModule) {
  465. createdModule = /** @type {Module} */ (
  466. new NormalModule(
  467. /** @type {NormalModuleCreateData} */
  468. (createData)
  469. )
  470. );
  471. }
  472. }
  473. createdModule = this.hooks.module.call(
  474. createdModule,
  475. createData,
  476. resolveData
  477. );
  478. return callback(null, createdModule);
  479. }
  480. );
  481. });
  482. });
  483. }
  484. );
  485. this.hooks.resolve.tapAsync(
  486. {
  487. name: "NormalModuleFactory",
  488. stage: 100
  489. },
  490. (data, callback) => {
  491. const {
  492. contextInfo,
  493. context,
  494. dependencies,
  495. dependencyType,
  496. request,
  497. phase,
  498. attributes,
  499. resolveOptions,
  500. fileDependencies,
  501. missingDependencies,
  502. contextDependencies
  503. } = data;
  504. const loaderResolver = this.getResolver("loader");
  505. /** @type {ResourceData | undefined} */
  506. let matchResourceData;
  507. /** @type {string} */
  508. let unresolvedResource;
  509. /** @type {ParsedLoaderRequest[]} */
  510. let elements;
  511. let noPreAutoLoaders = false;
  512. let noAutoLoaders = false;
  513. let noPrePostAutoLoaders = false;
  514. const contextScheme = getScheme(context);
  515. /** @type {string | undefined} */
  516. let scheme = getScheme(request);
  517. if (!scheme) {
  518. /** @type {string} */
  519. let requestWithoutMatchResource = request;
  520. const matchResourceMatch = MATCH_RESOURCE_REGEX.exec(request);
  521. if (matchResourceMatch) {
  522. let matchResource = matchResourceMatch[1];
  523. // Check if matchResource starts with ./ or ../
  524. if (matchResource.charCodeAt(0) === 46) {
  525. // 46 is "."
  526. const secondChar = matchResource.charCodeAt(1);
  527. if (
  528. secondChar === 47 || // 47 is "/"
  529. (secondChar === 46 && matchResource.charCodeAt(2) === 47) // "../"
  530. ) {
  531. // Resolve relative path against context
  532. matchResource = join(this.fs, context, matchResource);
  533. }
  534. }
  535. matchResourceData = {
  536. ...cacheParseResource(matchResource),
  537. resource: matchResource
  538. };
  539. requestWithoutMatchResource = request.slice(
  540. matchResourceMatch[0].length
  541. );
  542. }
  543. scheme = getScheme(requestWithoutMatchResource);
  544. if (!scheme && !contextScheme) {
  545. const firstChar = requestWithoutMatchResource.charCodeAt(0);
  546. const secondChar = requestWithoutMatchResource.charCodeAt(1);
  547. noPreAutoLoaders = firstChar === 45 && secondChar === 33; // startsWith "-!"
  548. noAutoLoaders = noPreAutoLoaders || firstChar === 33; // startsWith "!"
  549. noPrePostAutoLoaders = firstChar === 33 && secondChar === 33; // startsWith "!!";
  550. const rawElements = requestWithoutMatchResource
  551. .slice(
  552. noPreAutoLoaders || noPrePostAutoLoaders
  553. ? 2
  554. : noAutoLoaders
  555. ? 1
  556. : 0
  557. )
  558. .split(/!+/);
  559. unresolvedResource = /** @type {string} */ (rawElements.pop());
  560. elements = rawElements.map((el) => {
  561. const { path, query } = cachedParseResourceWithoutFragment(el);
  562. return {
  563. loader: path,
  564. options: query ? query.slice(1) : undefined
  565. };
  566. });
  567. scheme = getScheme(unresolvedResource);
  568. } else {
  569. unresolvedResource = requestWithoutMatchResource;
  570. elements = EMPTY_ELEMENTS;
  571. }
  572. } else {
  573. unresolvedResource = request;
  574. elements = EMPTY_ELEMENTS;
  575. }
  576. /** @type {ResolveContext} */
  577. const resolveContext = {
  578. fileDependencies,
  579. missingDependencies,
  580. contextDependencies
  581. };
  582. /** @type {ResourceDataWithData} */
  583. let resourceData;
  584. /** @type {undefined | LoaderItem[]} */
  585. let loaders;
  586. const continueCallback = needCalls(2, (err) => {
  587. if (err) return callback(err);
  588. // translate option idents
  589. try {
  590. for (const item of /** @type {LoaderItem[]} */ (loaders)) {
  591. if (typeof item.options === "string" && item.options[0] === "?") {
  592. const ident = item.options.slice(1);
  593. if (ident === "[[missing ident]]") {
  594. throw new Error(
  595. "No ident is provided by referenced loader. " +
  596. "When using a function for Rule.use in config you need to " +
  597. "provide an 'ident' property for referenced loader options."
  598. );
  599. }
  600. item.options = this.ruleSet.references.get(ident);
  601. if (item.options === undefined) {
  602. throw new Error(
  603. "Invalid ident is provided by referenced loader"
  604. );
  605. }
  606. item.ident = ident;
  607. }
  608. }
  609. } catch (identErr) {
  610. return callback(/** @type {Error} */ (identErr));
  611. }
  612. if (!resourceData) {
  613. // ignored
  614. return callback(null, dependencies[0].createIgnoredModule(context));
  615. }
  616. const userRequest =
  617. (matchResourceData !== undefined
  618. ? `${matchResourceData.resource}!=!`
  619. : "") +
  620. stringifyLoadersAndResource(
  621. /** @type {LoaderItem[]} */ (loaders),
  622. resourceData.resource
  623. );
  624. /** @type {ModuleSettings} */
  625. const settings = {};
  626. /** @type {LoaderItem[]} */
  627. const useLoadersPost = [];
  628. /** @type {LoaderItem[]} */
  629. const useLoaders = [];
  630. /** @type {LoaderItem[]} */
  631. const useLoadersPre = [];
  632. // handle .webpack[] suffix
  633. /** @type {string} */
  634. let resource;
  635. /** @type {RegExpExecArray | null} */
  636. let match;
  637. if (
  638. matchResourceData &&
  639. typeof (resource = matchResourceData.resource) === "string" &&
  640. (match = /\.webpack\[([^\]]+)\]$/.exec(resource))
  641. ) {
  642. settings.type = match[1];
  643. matchResourceData.resource = matchResourceData.resource.slice(
  644. 0,
  645. -settings.type.length - 10
  646. );
  647. } else {
  648. settings.type = JAVASCRIPT_MODULE_TYPE_AUTO;
  649. const resourceDataForRules = matchResourceData || resourceData;
  650. const result = this.ruleSet.exec({
  651. resource: resourceDataForRules.path,
  652. realResource: resourceData.path,
  653. resourceQuery: resourceDataForRules.query,
  654. resourceFragment: resourceDataForRules.fragment,
  655. scheme,
  656. phase,
  657. attributes,
  658. mimetype: matchResourceData
  659. ? ""
  660. : resourceData.data.mimetype || "",
  661. dependency: dependencyType,
  662. descriptionData: matchResourceData
  663. ? undefined
  664. : resourceData.data.descriptionFileData,
  665. issuer: contextInfo.issuer,
  666. compiler: contextInfo.compiler,
  667. issuerLayer: contextInfo.issuerLayer || ""
  668. });
  669. for (const r of result) {
  670. // https://github.com/webpack/webpack/issues/16466
  671. // if a request exists PrePostAutoLoaders, should disable modifying Rule.type
  672. if (r.type === "type" && noPrePostAutoLoaders) {
  673. continue;
  674. }
  675. if (r.type === "use") {
  676. if (!noAutoLoaders && !noPrePostAutoLoaders) {
  677. useLoaders.push(r.value);
  678. }
  679. } else if (r.type === "use-post") {
  680. if (!noPrePostAutoLoaders) {
  681. useLoadersPost.push(r.value);
  682. }
  683. } else if (r.type === "use-pre") {
  684. if (!noPreAutoLoaders && !noPrePostAutoLoaders) {
  685. useLoadersPre.push(r.value);
  686. }
  687. } else if (
  688. typeof r.value === "object" &&
  689. r.value !== null &&
  690. typeof settings[
  691. /** @type {keyof ModuleSettings} */
  692. (r.type)
  693. ] === "object" &&
  694. settings[/** @type {keyof ModuleSettings} */ (r.type)] !== null
  695. ) {
  696. const type = /** @type {keyof ModuleSettings} */ (r.type);
  697. settings[type] = cachedCleverMerge(settings[type], r.value);
  698. } else {
  699. const type = /** @type {keyof ModuleSettings} */ (r.type);
  700. settings[type] = r.value;
  701. }
  702. }
  703. }
  704. /** @type {undefined | LoaderItem[]} */
  705. let postLoaders;
  706. /** @type {undefined | LoaderItem[]} */
  707. let normalLoaders;
  708. /** @type {undefined | LoaderItem[]} */
  709. let preLoaders;
  710. const continueCallback = needCalls(3, (err) => {
  711. if (err) {
  712. return callback(err);
  713. }
  714. const allLoaders = /** @type {LoaderItem[]} */ (postLoaders);
  715. if (matchResourceData === undefined) {
  716. for (const loader of /** @type {LoaderItem[]} */ (loaders)) {
  717. allLoaders.push(loader);
  718. }
  719. for (const loader of /** @type {LoaderItem[]} */ (
  720. normalLoaders
  721. )) {
  722. allLoaders.push(loader);
  723. }
  724. } else {
  725. for (const loader of /** @type {LoaderItem[]} */ (
  726. normalLoaders
  727. )) {
  728. allLoaders.push(loader);
  729. }
  730. for (const loader of /** @type {LoaderItem[]} */ (loaders)) {
  731. allLoaders.push(loader);
  732. }
  733. }
  734. for (const loader of /** @type {LoaderItem[]} */ (preLoaders)) {
  735. allLoaders.push(loader);
  736. }
  737. const type = /** @type {NormalModuleTypes} */ (settings.type);
  738. const resolveOptions = settings.resolve;
  739. const layer = settings.layer;
  740. try {
  741. Object.assign(data.createData, {
  742. layer:
  743. layer === undefined ? contextInfo.issuerLayer || null : layer,
  744. request: stringifyLoadersAndResource(
  745. allLoaders,
  746. resourceData.resource
  747. ),
  748. userRequest,
  749. rawRequest: request,
  750. loaders: allLoaders,
  751. resource: resourceData.resource,
  752. context:
  753. resourceData.context || getContext(resourceData.resource),
  754. matchResource: matchResourceData
  755. ? matchResourceData.resource
  756. : undefined,
  757. resourceResolveData: resourceData.data,
  758. settings,
  759. type,
  760. parser: this.getParser(type, settings.parser),
  761. parserOptions: settings.parser,
  762. generator: this.getGenerator(type, settings.generator),
  763. generatorOptions: settings.generator,
  764. resolveOptions,
  765. extractSourceMap: settings.extractSourceMap || false
  766. });
  767. } catch (createDataErr) {
  768. return callback(/** @type {Error} */ (createDataErr));
  769. }
  770. callback();
  771. });
  772. this.resolveRequestArray(
  773. contextInfo,
  774. this.context,
  775. useLoadersPost,
  776. loaderResolver,
  777. resolveContext,
  778. (err, result) => {
  779. postLoaders = result;
  780. continueCallback(err);
  781. }
  782. );
  783. this.resolveRequestArray(
  784. contextInfo,
  785. this.context,
  786. useLoaders,
  787. loaderResolver,
  788. resolveContext,
  789. (err, result) => {
  790. normalLoaders = result;
  791. continueCallback(err);
  792. }
  793. );
  794. this.resolveRequestArray(
  795. contextInfo,
  796. this.context,
  797. useLoadersPre,
  798. loaderResolver,
  799. resolveContext,
  800. (err, result) => {
  801. preLoaders = result;
  802. continueCallback(err);
  803. }
  804. );
  805. });
  806. this.resolveRequestArray(
  807. contextInfo,
  808. contextScheme ? this.context : context,
  809. /** @type {LoaderItem[]} */ (elements),
  810. loaderResolver,
  811. resolveContext,
  812. (err, result) => {
  813. if (err) return continueCallback(err);
  814. loaders = result;
  815. continueCallback();
  816. }
  817. );
  818. /**
  819. * Processes the provided string.
  820. * @param {string} context context
  821. */
  822. const defaultResolve = (context) => {
  823. if (/^(?:$|\?)/.test(unresolvedResource)) {
  824. resourceData = {
  825. ...cacheParseResource(unresolvedResource),
  826. resource: unresolvedResource,
  827. data: {}
  828. };
  829. continueCallback();
  830. }
  831. // resource without scheme and with path
  832. else {
  833. const normalResolver = this.getResolver(
  834. "normal",
  835. dependencyType
  836. ? cachedSetProperty(
  837. resolveOptions || EMPTY_RESOLVE_OPTIONS,
  838. "dependencyType",
  839. dependencyType
  840. )
  841. : resolveOptions
  842. );
  843. this.resolveResource(
  844. contextInfo,
  845. context,
  846. unresolvedResource,
  847. normalResolver,
  848. resolveContext,
  849. (err, _resolvedResource, resolvedResourceResolveData) => {
  850. if (err) return continueCallback(err);
  851. if (_resolvedResource !== false) {
  852. const resolvedResource =
  853. /** @type {string} */
  854. (_resolvedResource);
  855. resourceData = {
  856. ...cacheParseResource(resolvedResource),
  857. resource: resolvedResource,
  858. data:
  859. /** @type {ResolveRequest} */
  860. (resolvedResourceResolveData)
  861. };
  862. }
  863. continueCallback();
  864. }
  865. );
  866. }
  867. };
  868. // resource with scheme
  869. if (scheme) {
  870. resourceData = {
  871. resource: unresolvedResource,
  872. data: {},
  873. path: undefined,
  874. query: undefined,
  875. fragment: undefined,
  876. context: undefined
  877. };
  878. this.hooks.resolveForScheme
  879. .for(scheme)
  880. .callAsync(resourceData, data, (err) => {
  881. if (err) return continueCallback(err);
  882. continueCallback();
  883. });
  884. }
  885. // resource within scheme
  886. else if (contextScheme) {
  887. resourceData = {
  888. resource: unresolvedResource,
  889. data: {},
  890. path: undefined,
  891. query: undefined,
  892. fragment: undefined,
  893. context: undefined
  894. };
  895. this.hooks.resolveInScheme
  896. .for(contextScheme)
  897. .callAsync(resourceData, data, (err, handled) => {
  898. if (err) return continueCallback(err);
  899. if (!handled) return defaultResolve(this.context);
  900. continueCallback();
  901. });
  902. }
  903. // resource without scheme and without path
  904. else {
  905. defaultResolve(context);
  906. }
  907. }
  908. );
  909. }
  910. cleanupForCache() {
  911. for (const module of this._restoredUnsafeCacheEntries) {
  912. ChunkGraph.clearChunkGraphForModule(module);
  913. ModuleGraph.clearModuleGraphForModule(module);
  914. module.cleanupForCache();
  915. }
  916. }
  917. /**
  918. * Processes the provided data.
  919. * @param {ModuleFactoryCreateData} data data object
  920. * @param {ModuleFactoryCallback} callback callback
  921. * @returns {void}
  922. */
  923. create(data, callback) {
  924. const dependencies = /** @type {ModuleDependency[]} */ (data.dependencies);
  925. const context = data.context || this.context;
  926. const resolveOptions = data.resolveOptions || EMPTY_RESOLVE_OPTIONS;
  927. const dependency = dependencies[0];
  928. const request = dependency.request;
  929. const attributes =
  930. /** @type {ModuleDependency & { attributes: ImportAttributes }} */
  931. (dependency).attributes;
  932. const phase =
  933. typeof (
  934. /** @type {ModuleDependency & { phase?: ImportPhaseType }} */
  935. (dependency).phase
  936. ) === "number"
  937. ? ImportPhaseUtils.stringify(
  938. /** @type {ModuleDependency & { phase?: ImportPhaseType }} */
  939. (dependency).phase
  940. )
  941. : "evaluation";
  942. const dependencyType = dependency.category || "";
  943. const contextInfo = data.contextInfo;
  944. /** @type {FileSystemDependencies} */
  945. const fileDependencies = new LazySet();
  946. /** @type {FileSystemDependencies} */
  947. const missingDependencies = new LazySet();
  948. /** @type {FileSystemDependencies} */
  949. const contextDependencies = new LazySet();
  950. /** @type {ResolveData} */
  951. const resolveData = {
  952. contextInfo,
  953. resolveOptions,
  954. context,
  955. request,
  956. phase,
  957. attributes,
  958. dependencies,
  959. dependencyType,
  960. fileDependencies,
  961. missingDependencies,
  962. contextDependencies,
  963. createData: {},
  964. cacheable: true
  965. };
  966. this.hooks.beforeResolve.callAsync(resolveData, (err, result) => {
  967. if (err) {
  968. return callback(err, {
  969. fileDependencies,
  970. missingDependencies,
  971. contextDependencies,
  972. cacheable: false
  973. });
  974. }
  975. // Ignored
  976. if (result === false) {
  977. /** @type {ModuleFactoryResult} * */
  978. const factoryResult = {
  979. fileDependencies,
  980. missingDependencies,
  981. contextDependencies,
  982. cacheable: resolveData.cacheable
  983. };
  984. if (resolveData.ignoredModule) {
  985. factoryResult.module = resolveData.ignoredModule;
  986. }
  987. return callback(null, factoryResult);
  988. }
  989. if (typeof result === "object") {
  990. throw new Error(
  991. deprecationChangedHookMessage(
  992. "beforeResolve",
  993. this.hooks.beforeResolve
  994. )
  995. );
  996. }
  997. this.hooks.factorize.callAsync(resolveData, (err, module) => {
  998. if (err) {
  999. return callback(err, {
  1000. fileDependencies,
  1001. missingDependencies,
  1002. contextDependencies,
  1003. cacheable: false
  1004. });
  1005. }
  1006. /** @type {ModuleFactoryResult} * */
  1007. const factoryResult = {
  1008. module,
  1009. fileDependencies,
  1010. missingDependencies,
  1011. contextDependencies,
  1012. cacheable: resolveData.cacheable
  1013. };
  1014. callback(null, factoryResult);
  1015. });
  1016. });
  1017. }
  1018. /**
  1019. * Processes the provided context info.
  1020. * @param {ModuleFactoryCreateDataContextInfo} contextInfo context info
  1021. * @param {string} context context
  1022. * @param {string} unresolvedResource unresolved resource
  1023. * @param {ResolverWithOptions} resolver resolver
  1024. * @param {ResolveContext} resolveContext resolver context
  1025. * @param {(err: null | Error, res?: string | false, req?: ResolveRequest) => void} callback callback
  1026. */
  1027. resolveResource(
  1028. contextInfo,
  1029. context,
  1030. unresolvedResource,
  1031. resolver,
  1032. resolveContext,
  1033. callback
  1034. ) {
  1035. resolver.resolve(
  1036. contextInfo,
  1037. context,
  1038. unresolvedResource,
  1039. resolveContext,
  1040. (err, resolvedResource, resolvedResourceResolveData) => {
  1041. if (err) {
  1042. return this._resolveResourceErrorHints(
  1043. err,
  1044. contextInfo,
  1045. context,
  1046. unresolvedResource,
  1047. resolver,
  1048. resolveContext,
  1049. (err2, hints) => {
  1050. if (err2) {
  1051. err.message += `
  1052. A fatal error happened during resolving additional hints for this error: ${err2.message}`;
  1053. err.stack += `
  1054. A fatal error happened during resolving additional hints for this error:
  1055. ${err2.stack}`;
  1056. return callback(err);
  1057. }
  1058. if (hints && hints.length > 0) {
  1059. err.message += `
  1060. ${hints.join("\n\n")}`;
  1061. }
  1062. // Check if the extension is missing a leading dot (e.g. "js" instead of ".js")
  1063. let appendResolveExtensionsHint = false;
  1064. const specifiedExtensions = [...resolver.options.extensions];
  1065. const expectedExtensions = specifiedExtensions.map(
  1066. (extension) => {
  1067. if (LEADING_DOT_EXTENSION_REGEX.test(extension)) {
  1068. appendResolveExtensionsHint = true;
  1069. return `.${extension}`;
  1070. }
  1071. return extension;
  1072. }
  1073. );
  1074. if (appendResolveExtensionsHint) {
  1075. err.message += `\nDid you miss the leading dot in 'resolve.extensions'? Did you mean '${JSON.stringify(
  1076. expectedExtensions
  1077. )}' instead of '${JSON.stringify(specifiedExtensions)}'?`;
  1078. }
  1079. callback(err);
  1080. }
  1081. );
  1082. }
  1083. callback(err, resolvedResource, resolvedResourceResolveData);
  1084. }
  1085. );
  1086. }
  1087. /**
  1088. * Resolve resource error hints.
  1089. * @param {Error} error error
  1090. * @param {ModuleFactoryCreateDataContextInfo} contextInfo context info
  1091. * @param {string} context context
  1092. * @param {string} unresolvedResource unresolved resource
  1093. * @param {ResolverWithOptions} resolver resolver
  1094. * @param {ResolveContext} resolveContext resolver context
  1095. * @param {Callback<string[]>} callback callback
  1096. * @private
  1097. */
  1098. _resolveResourceErrorHints(
  1099. error,
  1100. contextInfo,
  1101. context,
  1102. unresolvedResource,
  1103. resolver,
  1104. resolveContext,
  1105. callback
  1106. ) {
  1107. asyncLib.parallel(
  1108. [
  1109. (callback) => {
  1110. if (!resolver.options.fullySpecified) return callback();
  1111. resolver
  1112. .withOptions({
  1113. fullySpecified: false
  1114. })
  1115. .resolve(
  1116. contextInfo,
  1117. context,
  1118. unresolvedResource,
  1119. resolveContext,
  1120. (err, resolvedResource) => {
  1121. if (!err && resolvedResource) {
  1122. const resource = parseResource(resolvedResource).path.replace(
  1123. /^.*[\\/]/,
  1124. ""
  1125. );
  1126. return callback(
  1127. null,
  1128. `Did you mean '${resource}'?
  1129. BREAKING CHANGE: The request '${unresolvedResource}' failed to resolve only because it was resolved as fully specified
  1130. (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"').
  1131. The extension in the request is mandatory for it to be fully specified.
  1132. Add the extension to the request.`
  1133. );
  1134. }
  1135. callback();
  1136. }
  1137. );
  1138. },
  1139. (callback) => {
  1140. if (!resolver.options.enforceExtension) return callback();
  1141. resolver
  1142. .withOptions({
  1143. enforceExtension: false,
  1144. extensions: []
  1145. })
  1146. .resolve(
  1147. contextInfo,
  1148. context,
  1149. unresolvedResource,
  1150. resolveContext,
  1151. (err, resolvedResource) => {
  1152. if (!err && resolvedResource) {
  1153. let hint = "";
  1154. const match = /\.[^.]+(?:\?|$)/.exec(unresolvedResource);
  1155. if (match) {
  1156. const fixedRequest = unresolvedResource.replace(
  1157. /(\.[^.]+)(\?|$)/,
  1158. "$2"
  1159. );
  1160. hint = resolver.options.extensions.has(match[1])
  1161. ? `Did you mean '${fixedRequest}'?`
  1162. : `Did you mean '${fixedRequest}'? Also note that '${match[1]}' is not in 'resolve.extensions' yet and need to be added for this to work?`;
  1163. } else {
  1164. hint =
  1165. "Did you mean to omit the extension or to remove 'resolve.enforceExtension'?";
  1166. }
  1167. return callback(
  1168. null,
  1169. `The request '${unresolvedResource}' failed to resolve only because 'resolve.enforceExtension' was specified.
  1170. ${hint}
  1171. Including the extension in the request is no longer possible. Did you mean to enforce including the extension in requests with 'resolve.extensions: []' instead?`
  1172. );
  1173. }
  1174. callback();
  1175. }
  1176. );
  1177. },
  1178. (callback) => {
  1179. if (
  1180. /^\.\.?\//.test(unresolvedResource) ||
  1181. resolver.options.preferRelative
  1182. ) {
  1183. return callback();
  1184. }
  1185. resolver.resolve(
  1186. contextInfo,
  1187. context,
  1188. `./${unresolvedResource}`,
  1189. resolveContext,
  1190. (err, resolvedResource) => {
  1191. if (err || !resolvedResource) return callback();
  1192. const moduleDirectories = resolver.options.modules
  1193. .map((m) => (Array.isArray(m) ? m.join(", ") : m))
  1194. .join(", ");
  1195. callback(
  1196. null,
  1197. `Did you mean './${unresolvedResource}'?
  1198. Requests that should resolve in the current directory need to start with './'.
  1199. Requests that start with a name are treated as module requests and resolve within module directories (${moduleDirectories}).
  1200. 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.`
  1201. );
  1202. }
  1203. );
  1204. }
  1205. ],
  1206. (err, hints) => {
  1207. if (err) return callback(err);
  1208. callback(null, /** @type {string[]} */ (hints).filter(Boolean));
  1209. }
  1210. );
  1211. }
  1212. /**
  1213. * Resolves request array.
  1214. * @param {ModuleFactoryCreateDataContextInfo} contextInfo context info
  1215. * @param {string} context context
  1216. * @param {LoaderItem[]} array array
  1217. * @param {ResolverWithOptions} resolver resolver
  1218. * @param {ResolveContext} resolveContext resolve context
  1219. * @param {Callback<LoaderItem[]>} callback callback
  1220. * @returns {void} result
  1221. */
  1222. resolveRequestArray(
  1223. contextInfo,
  1224. context,
  1225. array,
  1226. resolver,
  1227. resolveContext,
  1228. callback
  1229. ) {
  1230. // LoaderItem
  1231. if (array.length === 0) return callback(null, array);
  1232. asyncLib.map(
  1233. array,
  1234. /**
  1235. * Handles the callback logic for this hook.
  1236. * @param {LoaderItem} item item
  1237. * @param {Callback<LoaderItem>} callback callback
  1238. */
  1239. (item, callback) => {
  1240. resolver.resolve(
  1241. contextInfo,
  1242. context,
  1243. item.loader,
  1244. resolveContext,
  1245. (err, result, resolveRequest) => {
  1246. if (
  1247. err &&
  1248. /^[^/]*$/.test(item.loader) &&
  1249. !item.loader.endsWith("-loader")
  1250. ) {
  1251. return resolver.resolve(
  1252. contextInfo,
  1253. context,
  1254. `${item.loader}-loader`,
  1255. resolveContext,
  1256. (err2) => {
  1257. if (!err2) {
  1258. err.message =
  1259. `${err.message}\n` +
  1260. "BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffix when using loaders.\n" +
  1261. ` You need to specify '${item.loader}-loader' instead of '${item.loader}',\n` +
  1262. " see https://webpack.js.org/migrate/3/#automatic-loader-module-name-extension-removed";
  1263. }
  1264. callback(err);
  1265. }
  1266. );
  1267. }
  1268. if (err) return callback(err);
  1269. const parsedResult = this._parseResourceWithoutFragment(
  1270. /** @type {string} */
  1271. (result)
  1272. );
  1273. const type = /\.mjs$/i.test(parsedResult.path)
  1274. ? "module"
  1275. : /\.cjs$/i.test(parsedResult.path)
  1276. ? "commonjs"
  1277. : /** @type {ResolveRequest} */
  1278. (resolveRequest).descriptionFileData === undefined
  1279. ? undefined
  1280. : /** @type {string} */
  1281. (
  1282. /** @type {ResolveRequest} */
  1283. (resolveRequest).descriptionFileData.type
  1284. );
  1285. /** @type {LoaderItem} */
  1286. const resolved = {
  1287. loader: parsedResult.path,
  1288. type,
  1289. options:
  1290. item.options === undefined
  1291. ? parsedResult.query
  1292. ? parsedResult.query.slice(1)
  1293. : undefined
  1294. : item.options,
  1295. ident: item.options === undefined ? undefined : item.ident
  1296. };
  1297. return callback(null, resolved);
  1298. }
  1299. );
  1300. },
  1301. (err, value) => {
  1302. callback(
  1303. /** @type {Error | null} */ (err),
  1304. /** @type {(LoaderItem)[]} */ (value)
  1305. );
  1306. }
  1307. );
  1308. }
  1309. /**
  1310. * Returns parser.
  1311. * @param {string} type type
  1312. * @param {ParserOptions} parserOptions parser options
  1313. * @returns {Parser} parser
  1314. */
  1315. getParser(type, parserOptions = EMPTY_PARSER_OPTIONS) {
  1316. let cache = this.parserCache.get(type);
  1317. if (cache === undefined) {
  1318. cache = new WeakMap();
  1319. this.parserCache.set(type, cache);
  1320. }
  1321. let parser = cache.get(parserOptions);
  1322. if (parser === undefined) {
  1323. parser = this.createParser(type, parserOptions);
  1324. cache.set(parserOptions, parser);
  1325. }
  1326. return parser;
  1327. }
  1328. /**
  1329. * Creates a parser from the provided type.
  1330. * @param {string} type type
  1331. * @param {ParserOptions} parserOptions parser options
  1332. * @returns {Parser} parser
  1333. */
  1334. createParser(type, parserOptions = {}) {
  1335. parserOptions = mergeGlobalOptions(
  1336. this._globalParserOptions,
  1337. type,
  1338. parserOptions
  1339. );
  1340. const parser = this.hooks.createParser.for(type).call(parserOptions);
  1341. if (!parser) {
  1342. throw new Error(`No parser registered for ${type}`);
  1343. }
  1344. this.hooks.parser.for(type).call(parser, parserOptions);
  1345. return parser;
  1346. }
  1347. /**
  1348. * Returns generator.
  1349. * @param {string} type type of generator
  1350. * @param {GeneratorOptions} generatorOptions generator options
  1351. * @returns {Generator} generator
  1352. */
  1353. getGenerator(type, generatorOptions = EMPTY_GENERATOR_OPTIONS) {
  1354. let cache = this.generatorCache.get(type);
  1355. if (cache === undefined) {
  1356. cache = new WeakMap();
  1357. this.generatorCache.set(type, cache);
  1358. }
  1359. let generator = cache.get(generatorOptions);
  1360. if (generator === undefined) {
  1361. generator = this.createGenerator(type, generatorOptions);
  1362. cache.set(generatorOptions, generator);
  1363. }
  1364. return generator;
  1365. }
  1366. /**
  1367. * Creates a generator.
  1368. * @param {string} type type of generator
  1369. * @param {GeneratorOptions} generatorOptions generator options
  1370. * @returns {Generator} generator
  1371. */
  1372. createGenerator(type, generatorOptions = {}) {
  1373. generatorOptions = mergeGlobalOptions(
  1374. this._globalGeneratorOptions,
  1375. type,
  1376. generatorOptions
  1377. );
  1378. const generator = this.hooks.createGenerator
  1379. .for(type)
  1380. .call(generatorOptions);
  1381. if (!generator) {
  1382. throw new Error(`No generator registered for ${type}`);
  1383. }
  1384. this.hooks.generator.for(type).call(generator, generatorOptions);
  1385. return generator;
  1386. }
  1387. /**
  1388. * Returns the resolver.
  1389. * @param {Parameters<ResolverFactory["get"]>[0]} type type of resolver
  1390. * @param {Parameters<ResolverFactory["get"]>[1]=} resolveOptions options
  1391. * @returns {ReturnType<ResolverFactory["get"]>} the resolver
  1392. */
  1393. getResolver(type, resolveOptions) {
  1394. return this.resolverFactory.get(type, resolveOptions);
  1395. }
  1396. }
  1397. module.exports = NormalModuleFactory;