NormalModuleFactory.js 47 KB

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