source: imaps-frontend/node_modules/webpack/lib/NormalModuleFactory.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 3 days ago

F4 Finalna Verzija

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