NormalModuleFactory.js 33 KB

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