NormalModuleFactory.js 38 KB

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