NormalModuleFactory.js 32 KB

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