UNPKG

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