NormalModuleFactory.js 39 KB

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