NormalModuleFactory.js 38 KB

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