mirror of https://github.com/webpack/webpack.git
				
				
				
			
		
			
				
	
	
		
			853 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			853 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| /*
 | |
| 	MIT License http://www.opensource.org/licenses/mit-license.php
 | |
| 	Author Tobias Koppers @sokra
 | |
| */
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| const asyncLib = require("neo-async");
 | |
| const {
 | |
| 	AsyncSeriesBailHook,
 | |
| 	SyncWaterfallHook,
 | |
| 	SyncBailHook,
 | |
| 	SyncHook,
 | |
| 	HookMap
 | |
| } = require("tapable");
 | |
| const Module = require("./Module");
 | |
| const ModuleFactory = require("./ModuleFactory");
 | |
| const NormalModule = require("./NormalModule");
 | |
| const RawModule = require("./RawModule");
 | |
| const BasicEffectRulePlugin = require("./rules/BasicEffectRulePlugin");
 | |
| const BasicMatcherRulePlugin = require("./rules/BasicMatcherRulePlugin");
 | |
| const DescriptionDataMatcherRulePlugin = require("./rules/DescriptionDataMatcherRulePlugin");
 | |
| const RuleSetCompiler = require("./rules/RuleSetCompiler");
 | |
| const UseEffectRulePlugin = require("./rules/UseEffectRulePlugin");
 | |
| const LazySet = require("./util/LazySet");
 | |
| const { getScheme } = require("./util/URLAbsoluteSpecifier");
 | |
| const { cachedCleverMerge, cachedSetProperty } = require("./util/cleverMerge");
 | |
| const { join } = require("./util/fs");
 | |
| const { parseResource } = require("./util/identifier");
 | |
| 
 | |
| /** @typedef {import("../declarations/WebpackOptions").ModuleOptions} ModuleOptions */
 | |
| /** @typedef {import("./Generator")} Generator */
 | |
| /** @typedef {import("./ModuleFactory").ModuleFactoryCreateData} ModuleFactoryCreateData */
 | |
| /** @typedef {import("./ModuleFactory").ModuleFactoryResult} ModuleFactoryResult */
 | |
| /** @typedef {import("./Parser")} Parser */
 | |
| /** @typedef {import("./ResolverFactory")} ResolverFactory */
 | |
| /** @typedef {import("./dependencies/ModuleDependency")} ModuleDependency */
 | |
| /** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
 | |
| 
 | |
| /**
 | |
|  * @typedef {Object} ResolveData
 | |
|  * @property {ModuleFactoryCreateData["contextInfo"]} contextInfo
 | |
|  * @property {ModuleFactoryCreateData["resolveOptions"]} resolveOptions
 | |
|  * @property {string} context
 | |
|  * @property {string} request
 | |
|  * @property {ModuleDependency[]} dependencies
 | |
|  * @property {Object} createData
 | |
|  * @property {LazySet<string>} fileDependencies
 | |
|  * @property {LazySet<string>} missingDependencies
 | |
|  * @property {LazySet<string>} contextDependencies
 | |
|  * @property {boolean} cacheable allow to use the unsafe cache
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * @typedef {Object} ResourceData
 | |
|  * @property {string} resource
 | |
|  * @property {string} path
 | |
|  * @property {string} query
 | |
|  * @property {string} fragment
 | |
|  */
 | |
| 
 | |
| /** @typedef {ResourceData & { data: Record<string, any> }} ResourceDataWithData */
 | |
| 
 | |
| const EMPTY_RESOLVE_OPTIONS = {};
 | |
| 
 | |
| const MATCH_RESOURCE_REGEX = /^([^!]+)!=!/;
 | |
| 
 | |
| const loaderToIdent = data => {
 | |
| 	if (!data.options) {
 | |
| 		return data.loader;
 | |
| 	}
 | |
| 	if (typeof data.options === "string") {
 | |
| 		return data.loader + "?" + data.options;
 | |
| 	}
 | |
| 	if (typeof data.options !== "object") {
 | |
| 		throw new Error("loader options must be string or object");
 | |
| 	}
 | |
| 	if (data.ident) {
 | |
| 		return data.loader + "??" + data.ident;
 | |
| 	}
 | |
| 	return data.loader + "?" + JSON.stringify(data.options);
 | |
| };
 | |
| 
 | |
| const stringifyLoadersAndResource = (loaders, resource) => {
 | |
| 	let str = "";
 | |
| 	for (const loader of loaders) {
 | |
| 		str += loaderToIdent(loader) + "!";
 | |
| 	}
 | |
| 	return str + resource;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * @param {string} resultString resultString
 | |
|  * @returns {{loader: string, options: string|undefined}} parsed loader request
 | |
|  */
 | |
| const identToLoaderRequest = resultString => {
 | |
| 	const idx = resultString.indexOf("?");
 | |
| 	if (idx >= 0) {
 | |
| 		const loader = resultString.substr(0, idx);
 | |
| 		const options = resultString.substr(idx + 1);
 | |
| 		return {
 | |
| 			loader,
 | |
| 			options
 | |
| 		};
 | |
| 	} else {
 | |
| 		return {
 | |
| 			loader: resultString,
 | |
| 			options: undefined
 | |
| 		};
 | |
| 	}
 | |
| };
 | |
| 
 | |
| const needCalls = (times, callback) => {
 | |
| 	return err => {
 | |
| 		if (--times === 0) {
 | |
| 			return callback(err);
 | |
| 		}
 | |
| 		if (err && times > 0) {
 | |
| 			times = NaN;
 | |
| 			return callback(err);
 | |
| 		}
 | |
| 	};
 | |
| };
 | |
| 
 | |
| // TODO webpack 6 remove
 | |
| const deprecationChangedHookMessage = name =>
 | |
| 	`NormalModuleFactory.${name} is no longer a waterfall hook, but a bailing hook instead. ` +
 | |
| 	"Do not return the passed object, but modify it instead. " +
 | |
| 	"Returning false will ignore the request and results in no module created.";
 | |
| 
 | |
| const dependencyCache = new WeakMap();
 | |
| 
 | |
| const ruleSetCompiler = new RuleSetCompiler([
 | |
| 	new BasicMatcherRulePlugin("test", "resource"),
 | |
| 	new BasicMatcherRulePlugin("mimetype"),
 | |
| 	new BasicMatcherRulePlugin("dependency"),
 | |
| 	new BasicMatcherRulePlugin("include", "resource"),
 | |
| 	new BasicMatcherRulePlugin("exclude", "resource", true),
 | |
| 	new BasicMatcherRulePlugin("resource"),
 | |
| 	new BasicMatcherRulePlugin("resourceQuery"),
 | |
| 	new BasicMatcherRulePlugin("resourceFragment"),
 | |
| 	new BasicMatcherRulePlugin("realResource"),
 | |
| 	new BasicMatcherRulePlugin("issuer"),
 | |
| 	new BasicMatcherRulePlugin("compiler"),
 | |
| 	new DescriptionDataMatcherRulePlugin(),
 | |
| 	new BasicEffectRulePlugin("type"),
 | |
| 	new BasicEffectRulePlugin("sideEffects"),
 | |
| 	new BasicEffectRulePlugin("parser"),
 | |
| 	new BasicEffectRulePlugin("resolve"),
 | |
| 	new BasicEffectRulePlugin("generator"),
 | |
| 	new UseEffectRulePlugin()
 | |
| ]);
 | |
| 
 | |
| class NormalModuleFactory extends ModuleFactory {
 | |
| 	/**
 | |
| 	 * @param {Object} param params
 | |
| 	 * @param {string=} param.context context
 | |
| 	 * @param {InputFileSystem} param.fs file system
 | |
| 	 * @param {ResolverFactory} param.resolverFactory resolverFactory
 | |
| 	 * @param {ModuleOptions} param.options options
 | |
| 	 * @param {Object=} param.associatedObjectForCache an object to which the cache will be attached
 | |
| 	 */
 | |
| 	constructor({
 | |
| 		context,
 | |
| 		fs,
 | |
| 		resolverFactory,
 | |
| 		options,
 | |
| 		associatedObjectForCache
 | |
| 	}) {
 | |
| 		super();
 | |
| 		this.hooks = Object.freeze({
 | |
| 			/** @type {AsyncSeriesBailHook<[ResolveData], TODO>} */
 | |
| 			resolve: new AsyncSeriesBailHook(["resolveData"]),
 | |
| 			/** @type {HookMap<AsyncSeriesBailHook<[ResourceDataWithData, ResolveData], true | void>>} */
 | |
| 			resolveForScheme: new HookMap(
 | |
| 				() => new AsyncSeriesBailHook(["resourceData", "resolveData"])
 | |
| 			),
 | |
| 			/** @type {AsyncSeriesBailHook<[ResolveData], TODO>} */
 | |
| 			factorize: new AsyncSeriesBailHook(["resolveData"]),
 | |
| 			/** @type {AsyncSeriesBailHook<[ResolveData], TODO>} */
 | |
| 			beforeResolve: new AsyncSeriesBailHook(["resolveData"]),
 | |
| 			/** @type {AsyncSeriesBailHook<[ResolveData], TODO>} */
 | |
| 			afterResolve: new AsyncSeriesBailHook(["resolveData"]),
 | |
| 			/** @type {AsyncSeriesBailHook<[ResolveData["createData"], ResolveData], TODO>} */
 | |
| 			createModule: new AsyncSeriesBailHook(["createData", "resolveData"]),
 | |
| 			/** @type {SyncWaterfallHook<[Module, ResolveData["createData"], ResolveData], TODO>} */
 | |
| 			module: new SyncWaterfallHook(["module", "createData", "resolveData"]),
 | |
| 			createParser: new HookMap(() => new SyncBailHook(["parserOptions"])),
 | |
| 			parser: new HookMap(() => new SyncHook(["parser", "parserOptions"])),
 | |
| 			createGenerator: new HookMap(
 | |
| 				() => new SyncBailHook(["generatorOptions"])
 | |
| 			),
 | |
| 			generator: new HookMap(
 | |
| 				() => new SyncHook(["generator", "generatorOptions"])
 | |
| 			)
 | |
| 		});
 | |
| 		this.resolverFactory = resolverFactory;
 | |
| 		this.ruleSet = ruleSetCompiler.compile([
 | |
| 			{
 | |
| 				rules: options.defaultRules
 | |
| 			},
 | |
| 			{
 | |
| 				rules: options.rules
 | |
| 			}
 | |
| 		]);
 | |
| 		this.unsafeCache = !!options.unsafeCache;
 | |
| 		this.cachePredicate =
 | |
| 			typeof options.unsafeCache === "function"
 | |
| 				? options.unsafeCache
 | |
| 				: () => true;
 | |
| 		this.context = context || "";
 | |
| 		this.fs = fs;
 | |
| 		/**
 | |
| 		 * @type {Map<string, WeakMap<Object, TODO>>}
 | |
| 		 */
 | |
| 		this.parserCache = new Map();
 | |
| 		/**
 | |
| 		 * @type {Map<string, WeakMap<Object, Generator>>}
 | |
| 		 */
 | |
| 		this.generatorCache = new Map();
 | |
| 
 | |
| 		const cacheParseResource = parseResource.bindCache(
 | |
| 			associatedObjectForCache
 | |
| 		);
 | |
| 
 | |
| 		this.hooks.factorize.tapAsync(
 | |
| 			{
 | |
| 				name: "NormalModuleFactory",
 | |
| 				stage: 100
 | |
| 			},
 | |
| 			(resolveData, callback) => {
 | |
| 				this.hooks.resolve.callAsync(resolveData, (err, result) => {
 | |
| 					if (err) return callback(err);
 | |
| 
 | |
| 					// Ignored
 | |
| 					if (result === false) return callback();
 | |
| 
 | |
| 					// direct module
 | |
| 					if (result instanceof Module) return callback(null, result);
 | |
| 
 | |
| 					if (typeof result === "object")
 | |
| 						throw new Error(
 | |
| 							deprecationChangedHookMessage("resolve") +
 | |
| 								" Returning a Module object will result in this module used as result."
 | |
| 						);
 | |
| 
 | |
| 					this.hooks.afterResolve.callAsync(resolveData, (err, result) => {
 | |
| 						if (err) return callback(err);
 | |
| 
 | |
| 						if (typeof result === "object")
 | |
| 							throw new Error(deprecationChangedHookMessage("afterResolve"));
 | |
| 
 | |
| 						// Ignored
 | |
| 						if (result === false) return callback();
 | |
| 
 | |
| 						const createData = resolveData.createData;
 | |
| 
 | |
| 						this.hooks.createModule.callAsync(
 | |
| 							createData,
 | |
| 							resolveData,
 | |
| 							(err, createdModule) => {
 | |
| 								if (!createdModule) {
 | |
| 									if (!resolveData.request) {
 | |
| 										return callback(new Error("Empty dependency (no request)"));
 | |
| 									}
 | |
| 
 | |
| 									createdModule = new NormalModule(createData);
 | |
| 								}
 | |
| 
 | |
| 								createdModule = this.hooks.module.call(
 | |
| 									createdModule,
 | |
| 									createData,
 | |
| 									resolveData
 | |
| 								);
 | |
| 
 | |
| 								return callback(null, createdModule);
 | |
| 							}
 | |
| 						);
 | |
| 					});
 | |
| 				});
 | |
| 			}
 | |
| 		);
 | |
| 		this.hooks.resolve.tapAsync(
 | |
| 			{
 | |
| 				name: "NormalModuleFactory",
 | |
| 				stage: 100
 | |
| 			},
 | |
| 			(data, callback) => {
 | |
| 				const {
 | |
| 					contextInfo,
 | |
| 					context,
 | |
| 					dependencies,
 | |
| 					request,
 | |
| 					resolveOptions,
 | |
| 					fileDependencies,
 | |
| 					missingDependencies,
 | |
| 					contextDependencies
 | |
| 				} = data;
 | |
| 				const dependencyType =
 | |
| 					(dependencies.length > 0 && dependencies[0].category) || "";
 | |
| 				const loaderResolver = this.getResolver("loader");
 | |
| 
 | |
| 				/** @type {ResourceData | undefined} */
 | |
| 				let matchResourceData = undefined;
 | |
| 				/** @type {string} */
 | |
| 				let requestWithoutMatchResource = request;
 | |
| 				const matchResourceMatch = MATCH_RESOURCE_REGEX.exec(request);
 | |
| 				if (matchResourceMatch) {
 | |
| 					let matchResource = matchResourceMatch[1];
 | |
| 					if (matchResource.charCodeAt(0) === 46) {
 | |
| 						// 46 === ".", 47 === "/"
 | |
| 						const secondChar = matchResource.charCodeAt(1);
 | |
| 						if (
 | |
| 							secondChar === 47 ||
 | |
| 							(secondChar === 46 && matchResource.charCodeAt(2) === 47)
 | |
| 						) {
 | |
| 							// if matchResources startsWith ../ or ./
 | |
| 							matchResource = join(this.fs, context, matchResource);
 | |
| 						}
 | |
| 					}
 | |
| 					matchResourceData = {
 | |
| 						resource: matchResource,
 | |
| 						...cacheParseResource(matchResource)
 | |
| 					};
 | |
| 					requestWithoutMatchResource = request.substr(
 | |
| 						matchResourceMatch[0].length
 | |
| 					);
 | |
| 				}
 | |
| 
 | |
| 				const firstChar = requestWithoutMatchResource.charCodeAt(0);
 | |
| 				const secondChar = requestWithoutMatchResource.charCodeAt(1);
 | |
| 				const noPreAutoLoaders = firstChar === 45 && secondChar === 33; // startsWith "-!"
 | |
| 				const noAutoLoaders = noPreAutoLoaders || firstChar === 33; // startsWith "!"
 | |
| 				const noPrePostAutoLoaders = firstChar === 33 && secondChar === 33; // startsWith "!!";
 | |
| 				const rawElements = requestWithoutMatchResource
 | |
| 					.slice(
 | |
| 						noPreAutoLoaders || noPrePostAutoLoaders ? 2 : noAutoLoaders ? 1 : 0
 | |
| 					)
 | |
| 					.split(/!+/);
 | |
| 				const unresolvedResource = rawElements.pop();
 | |
| 				const elements = rawElements.map(identToLoaderRequest);
 | |
| 
 | |
| 				const resolveContext = {
 | |
| 					fileDependencies,
 | |
| 					missingDependencies,
 | |
| 					contextDependencies
 | |
| 				};
 | |
| 
 | |
| 				/** @type {ResourceDataWithData} */
 | |
| 				let resourceData;
 | |
| 				/** @type {string | undefined} */
 | |
| 				const scheme = getScheme(unresolvedResource);
 | |
| 
 | |
| 				let loaders;
 | |
| 
 | |
| 				const continueCallback = needCalls(2, err => {
 | |
| 					if (err) return callback(err);
 | |
| 
 | |
| 					// translate option idents
 | |
| 					try {
 | |
| 						for (const item of loaders) {
 | |
| 							if (typeof item.options === "string" && item.options[0] === "?") {
 | |
| 								const ident = item.options.substr(1);
 | |
| 								if (ident === "[[missing ident]]") {
 | |
| 									throw new Error(
 | |
| 										"No ident is provided by referenced loader. " +
 | |
| 											"When using a function for Rule.use in config you need to " +
 | |
| 											"provide an 'ident' property for referenced loader options."
 | |
| 									);
 | |
| 								}
 | |
| 								item.options = this.ruleSet.references.get(ident);
 | |
| 								if (item.options === undefined) {
 | |
| 									throw new Error(
 | |
| 										"Invalid ident is provided by referenced loader"
 | |
| 									);
 | |
| 								}
 | |
| 								item.ident = ident;
 | |
| 							}
 | |
| 						}
 | |
| 					} catch (e) {
 | |
| 						return callback(e);
 | |
| 					}
 | |
| 
 | |
| 					if (!resourceData) {
 | |
| 						// ignored
 | |
| 						return callback(
 | |
| 							null,
 | |
| 							new RawModule(
 | |
| 								"/* (ignored) */",
 | |
| 								`ignored|${request}`,
 | |
| 								`${request} (ignored)`
 | |
| 							)
 | |
| 						);
 | |
| 					}
 | |
| 
 | |
| 					const userRequest =
 | |
| 						(matchResourceData !== undefined
 | |
| 							? `${matchResourceData.resource}!=!`
 | |
| 							: "") +
 | |
| 						stringifyLoadersAndResource(loaders, resourceData.resource);
 | |
| 
 | |
| 					const resourceDataForRules = matchResourceData || resourceData;
 | |
| 					const result = this.ruleSet.exec({
 | |
| 						resource: resourceDataForRules.path,
 | |
| 						realResource: resourceData.path,
 | |
| 						resourceQuery: resourceDataForRules.query,
 | |
| 						resourceFragment: resourceDataForRules.fragment,
 | |
| 						mimetype: matchResourceData ? "" : resourceData.data.mimetype || "",
 | |
| 						dependency: dependencyType,
 | |
| 						descriptionData: matchResourceData
 | |
| 							? undefined
 | |
| 							: resourceData.data.descriptionFileData,
 | |
| 						issuer: contextInfo.issuer,
 | |
| 						compiler: contextInfo.compiler
 | |
| 					});
 | |
| 					const settings = {};
 | |
| 					const useLoadersPost = [];
 | |
| 					const useLoaders = [];
 | |
| 					const useLoadersPre = [];
 | |
| 					for (const r of result) {
 | |
| 						if (r.type === "use") {
 | |
| 							if (!noAutoLoaders && !noPrePostAutoLoaders) {
 | |
| 								useLoaders.push(r.value);
 | |
| 							}
 | |
| 						} else if (r.type === "use-post") {
 | |
| 							if (!noPrePostAutoLoaders) {
 | |
| 								useLoadersPost.push(r.value);
 | |
| 							}
 | |
| 						} else if (r.type === "use-pre") {
 | |
| 							if (!noPreAutoLoaders && !noPrePostAutoLoaders) {
 | |
| 								useLoadersPre.push(r.value);
 | |
| 							}
 | |
| 						} else if (
 | |
| 							typeof r.value === "object" &&
 | |
| 							r.value !== null &&
 | |
| 							typeof settings[r.type] === "object" &&
 | |
| 							settings[r.type] !== null
 | |
| 						) {
 | |
| 							settings[r.type] = cachedCleverMerge(settings[r.type], r.value);
 | |
| 						} else {
 | |
| 							settings[r.type] = r.value;
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					let postLoaders, normalLoaders, preLoaders;
 | |
| 
 | |
| 					const continueCallback = needCalls(3, err => {
 | |
| 						if (err) {
 | |
| 							return callback(err);
 | |
| 						}
 | |
| 						const allLoaders = postLoaders;
 | |
| 						if (matchResourceData === undefined) {
 | |
| 							for (const loader of loaders) allLoaders.push(loader);
 | |
| 							for (const loader of normalLoaders) allLoaders.push(loader);
 | |
| 						} else {
 | |
| 							for (const loader of normalLoaders) allLoaders.push(loader);
 | |
| 							for (const loader of loaders) allLoaders.push(loader);
 | |
| 						}
 | |
| 						for (const loader of preLoaders) allLoaders.push(loader);
 | |
| 						const type = settings.type;
 | |
| 						const resolveOptions = settings.resolve;
 | |
| 						Object.assign(data.createData, {
 | |
| 							request: stringifyLoadersAndResource(
 | |
| 								allLoaders,
 | |
| 								resourceData.resource
 | |
| 							),
 | |
| 							userRequest,
 | |
| 							rawRequest: request,
 | |
| 							loaders: allLoaders,
 | |
| 							resource: resourceData.resource,
 | |
| 							matchResource: matchResourceData
 | |
| 								? matchResourceData.resource
 | |
| 								: undefined,
 | |
| 							resourceResolveData: resourceData.data,
 | |
| 							settings,
 | |
| 							type,
 | |
| 							parser: this.getParser(type, settings.parser),
 | |
| 							generator: this.getGenerator(type, settings.generator),
 | |
| 							resolveOptions
 | |
| 						});
 | |
| 						callback();
 | |
| 					});
 | |
| 					this.resolveRequestArray(
 | |
| 						contextInfo,
 | |
| 						this.context,
 | |
| 						useLoadersPost,
 | |
| 						loaderResolver,
 | |
| 						resolveContext,
 | |
| 						(err, result) => {
 | |
| 							postLoaders = result;
 | |
| 							continueCallback(err);
 | |
| 						}
 | |
| 					);
 | |
| 					this.resolveRequestArray(
 | |
| 						contextInfo,
 | |
| 						this.context,
 | |
| 						useLoaders,
 | |
| 						loaderResolver,
 | |
| 						resolveContext,
 | |
| 						(err, result) => {
 | |
| 							normalLoaders = result;
 | |
| 							continueCallback(err);
 | |
| 						}
 | |
| 					);
 | |
| 					this.resolveRequestArray(
 | |
| 						contextInfo,
 | |
| 						this.context,
 | |
| 						useLoadersPre,
 | |
| 						loaderResolver,
 | |
| 						resolveContext,
 | |
| 						(err, result) => {
 | |
| 							preLoaders = result;
 | |
| 							continueCallback(err);
 | |
| 						}
 | |
| 					);
 | |
| 				});
 | |
| 
 | |
| 				this.resolveRequestArray(
 | |
| 					contextInfo,
 | |
| 					context,
 | |
| 					elements,
 | |
| 					loaderResolver,
 | |
| 					resolveContext,
 | |
| 					(err, result) => {
 | |
| 						if (err) return continueCallback(err);
 | |
| 						loaders = result;
 | |
| 						continueCallback();
 | |
| 					}
 | |
| 				);
 | |
| 
 | |
| 				// resource with scheme
 | |
| 				if (scheme) {
 | |
| 					resourceData = {
 | |
| 						resource: unresolvedResource,
 | |
| 						data: {},
 | |
| 						path: undefined,
 | |
| 						query: undefined,
 | |
| 						fragment: undefined
 | |
| 					};
 | |
| 					this.hooks.resolveForScheme
 | |
| 						.for(scheme)
 | |
| 						.callAsync(resourceData, data, err => {
 | |
| 							if (err) return continueCallback(err);
 | |
| 							continueCallback();
 | |
| 						});
 | |
| 				}
 | |
| 
 | |
| 				// resource without scheme and without path
 | |
| 				else if (/^($|\?|#)/.test(unresolvedResource)) {
 | |
| 					resourceData = {
 | |
| 						resource: unresolvedResource,
 | |
| 						data: {},
 | |
| 						...cacheParseResource(unresolvedResource)
 | |
| 					};
 | |
| 					continueCallback();
 | |
| 				}
 | |
| 
 | |
| 				// resource without scheme and with path
 | |
| 				else {
 | |
| 					const normalResolver = this.getResolver(
 | |
| 						"normal",
 | |
| 						dependencyType
 | |
| 							? cachedSetProperty(
 | |
| 									resolveOptions || EMPTY_RESOLVE_OPTIONS,
 | |
| 									"dependencyType",
 | |
| 									dependencyType
 | |
| 							  )
 | |
| 							: resolveOptions
 | |
| 					);
 | |
| 					this.resolveResource(
 | |
| 						contextInfo,
 | |
| 						context,
 | |
| 						unresolvedResource,
 | |
| 						normalResolver,
 | |
| 						resolveContext,
 | |
| 						(err, resolvedResource, resolvedResourceResolveData) => {
 | |
| 							if (err) return continueCallback(err);
 | |
| 							if (resolvedResource !== false) {
 | |
| 								resourceData = {
 | |
| 									resource: resolvedResource,
 | |
| 									data: resolvedResourceResolveData,
 | |
| 									...cacheParseResource(resolvedResource)
 | |
| 								};
 | |
| 							}
 | |
| 							continueCallback();
 | |
| 						}
 | |
| 					);
 | |
| 				}
 | |
| 			}
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @param {ModuleFactoryCreateData} data data object
 | |
| 	 * @param {function(Error=, ModuleFactoryResult=): void} callback callback
 | |
| 	 * @returns {void}
 | |
| 	 */
 | |
| 	create(data, callback) {
 | |
| 		const dependencies = /** @type {ModuleDependency[]} */ (data.dependencies);
 | |
| 		if (this.unsafeCache) {
 | |
| 			const cacheEntry = dependencyCache.get(dependencies[0]);
 | |
| 			if (cacheEntry) return callback(null, cacheEntry);
 | |
| 		}
 | |
| 		const context = data.context || this.context;
 | |
| 		const resolveOptions = data.resolveOptions || EMPTY_RESOLVE_OPTIONS;
 | |
| 		const dependency = dependencies[0];
 | |
| 		const request = dependency.request;
 | |
| 		const contextInfo = data.contextInfo;
 | |
| 		const fileDependencies = new LazySet();
 | |
| 		const missingDependencies = new LazySet();
 | |
| 		const contextDependencies = new LazySet();
 | |
| 		/** @type {ResolveData} */
 | |
| 		const resolveData = {
 | |
| 			contextInfo,
 | |
| 			resolveOptions,
 | |
| 			context,
 | |
| 			request,
 | |
| 			dependencies,
 | |
| 			fileDependencies,
 | |
| 			missingDependencies,
 | |
| 			contextDependencies,
 | |
| 			createData: {},
 | |
| 			cacheable: true
 | |
| 		};
 | |
| 		this.hooks.beforeResolve.callAsync(resolveData, (err, result) => {
 | |
| 			if (err) {
 | |
| 				return callback(err, {
 | |
| 					fileDependencies,
 | |
| 					missingDependencies,
 | |
| 					contextDependencies
 | |
| 				});
 | |
| 			}
 | |
| 
 | |
| 			// Ignored
 | |
| 			if (result === false) {
 | |
| 				return callback(null, {
 | |
| 					fileDependencies,
 | |
| 					missingDependencies,
 | |
| 					contextDependencies
 | |
| 				});
 | |
| 			}
 | |
| 
 | |
| 			if (typeof result === "object")
 | |
| 				throw new Error(deprecationChangedHookMessage("beforeResolve"));
 | |
| 
 | |
| 			this.hooks.factorize.callAsync(resolveData, (err, module) => {
 | |
| 				if (err) {
 | |
| 					return callback(err, {
 | |
| 						fileDependencies,
 | |
| 						missingDependencies,
 | |
| 						contextDependencies
 | |
| 					});
 | |
| 				}
 | |
| 
 | |
| 				const factoryResult = {
 | |
| 					module,
 | |
| 					fileDependencies,
 | |
| 					missingDependencies,
 | |
| 					contextDependencies
 | |
| 				};
 | |
| 
 | |
| 				if (
 | |
| 					this.unsafeCache &&
 | |
| 					resolveData.cacheable &&
 | |
| 					module &&
 | |
| 					this.cachePredicate(module)
 | |
| 				) {
 | |
| 					for (const d of dependencies) {
 | |
| 						dependencyCache.set(d, factoryResult);
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				callback(null, factoryResult);
 | |
| 			});
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	resolveResource(
 | |
| 		contextInfo,
 | |
| 		context,
 | |
| 		unresolvedResource,
 | |
| 		resolver,
 | |
| 		resolveContext,
 | |
| 		callback
 | |
| 	) {
 | |
| 		resolver.resolve(
 | |
| 			contextInfo,
 | |
| 			context,
 | |
| 			unresolvedResource,
 | |
| 			resolveContext,
 | |
| 			(err, resolvedResource, resolvedResourceResolveData) => {
 | |
| 				if (err) {
 | |
| 					if (resolver.options.fullySpecified) {
 | |
| 						resolver
 | |
| 							.withOptions({
 | |
| 								fullySpecified: false
 | |
| 							})
 | |
| 							.resolve(
 | |
| 								contextInfo,
 | |
| 								context,
 | |
| 								unresolvedResource,
 | |
| 								resolveContext,
 | |
| 								(err2, resolvedResource) => {
 | |
| 									if (!err2 && resolvedResource) {
 | |
| 										const resource = parseResource(
 | |
| 											resolvedResource
 | |
| 										).path.replace(/^.*[\\/]/, "");
 | |
| 										err.message += `
 | |
| Did you mean '${resource}'?
 | |
| BREAKING CHANGE: The request '${unresolvedResource}' failed to resolve only because it was resolved as fully specified
 | |
| (probably because the origin is a '*.mjs' file or a '*.js' file where the package.json contains '"type": "module"').
 | |
| The extension in the request is mandatory for it to be fully specified.
 | |
| Add the extension to the request.`;
 | |
| 									}
 | |
| 									callback(err);
 | |
| 								}
 | |
| 							);
 | |
| 						return;
 | |
| 					}
 | |
| 				}
 | |
| 				callback(err, resolvedResource, resolvedResourceResolveData);
 | |
| 			}
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	resolveRequestArray(
 | |
| 		contextInfo,
 | |
| 		context,
 | |
| 		array,
 | |
| 		resolver,
 | |
| 		resolveContext,
 | |
| 		callback
 | |
| 	) {
 | |
| 		if (array.length === 0) return callback(null, array);
 | |
| 		asyncLib.map(
 | |
| 			array,
 | |
| 			(item, callback) => {
 | |
| 				resolver.resolve(
 | |
| 					contextInfo,
 | |
| 					context,
 | |
| 					item.loader,
 | |
| 					resolveContext,
 | |
| 					(err, result) => {
 | |
| 						if (
 | |
| 							err &&
 | |
| 							/^[^/]*$/.test(item.loader) &&
 | |
| 							!/-loader$/.test(item.loader)
 | |
| 						) {
 | |
| 							return resolver.resolve(
 | |
| 								contextInfo,
 | |
| 								context,
 | |
| 								item.loader + "-loader",
 | |
| 								resolveContext,
 | |
| 								err2 => {
 | |
| 									if (!err2) {
 | |
| 										err.message =
 | |
| 											err.message +
 | |
| 											"\n" +
 | |
| 											"BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffix when using loaders.\n" +
 | |
| 											`                 You need to specify '${item.loader}-loader' instead of '${item.loader}',\n` +
 | |
| 											"                 see https://webpack.js.org/migrate/3/#automatic-loader-module-name-extension-removed";
 | |
| 									}
 | |
| 									callback(err);
 | |
| 								}
 | |
| 							);
 | |
| 						}
 | |
| 						if (err) return callback(err);
 | |
| 
 | |
| 						const parsedResult = identToLoaderRequest(result);
 | |
| 						const resolved = {
 | |
| 							loader: parsedResult.loader,
 | |
| 							options:
 | |
| 								item.options === undefined
 | |
| 									? parsedResult.options
 | |
| 									: item.options,
 | |
| 							ident: item.options === undefined ? undefined : item.ident
 | |
| 						};
 | |
| 						return callback(null, resolved);
 | |
| 					}
 | |
| 				);
 | |
| 			},
 | |
| 			callback
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	getParser(type, parserOptions = EMPTY_RESOLVE_OPTIONS) {
 | |
| 		let cache = this.parserCache.get(type);
 | |
| 
 | |
| 		if (cache === undefined) {
 | |
| 			cache = new WeakMap();
 | |
| 			this.parserCache.set(type, cache);
 | |
| 		}
 | |
| 
 | |
| 		let parser = cache.get(parserOptions);
 | |
| 
 | |
| 		if (parser === undefined) {
 | |
| 			parser = this.createParser(type, parserOptions);
 | |
| 			cache.set(parserOptions, parser);
 | |
| 		}
 | |
| 
 | |
| 		return parser;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @param {string} type type
 | |
| 	 * @param {{[k: string]: any}} parserOptions parser options
 | |
| 	 * @returns {Parser} parser
 | |
| 	 */
 | |
| 	createParser(type, parserOptions = {}) {
 | |
| 		const parser = this.hooks.createParser.for(type).call(parserOptions);
 | |
| 		if (!parser) {
 | |
| 			throw new Error(`No parser registered for ${type}`);
 | |
| 		}
 | |
| 		this.hooks.parser.for(type).call(parser, parserOptions);
 | |
| 		return parser;
 | |
| 	}
 | |
| 
 | |
| 	getGenerator(type, generatorOptions = EMPTY_RESOLVE_OPTIONS) {
 | |
| 		let cache = this.generatorCache.get(type);
 | |
| 
 | |
| 		if (cache === undefined) {
 | |
| 			cache = new WeakMap();
 | |
| 			this.generatorCache.set(type, cache);
 | |
| 		}
 | |
| 
 | |
| 		let generator = cache.get(generatorOptions);
 | |
| 
 | |
| 		if (generator === undefined) {
 | |
| 			generator = this.createGenerator(type, generatorOptions);
 | |
| 			cache.set(generatorOptions, generator);
 | |
| 		}
 | |
| 
 | |
| 		return generator;
 | |
| 	}
 | |
| 
 | |
| 	createGenerator(type, generatorOptions = {}) {
 | |
| 		const generator = this.hooks.createGenerator
 | |
| 			.for(type)
 | |
| 			.call(generatorOptions);
 | |
| 		if (!generator) {
 | |
| 			throw new Error(`No generator registered for ${type}`);
 | |
| 		}
 | |
| 		this.hooks.generator.for(type).call(generator, generatorOptions);
 | |
| 		return generator;
 | |
| 	}
 | |
| 
 | |
| 	getResolver(type, resolveOptions) {
 | |
| 		return this.resolverFactory.get(type, resolveOptions);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| module.exports = NormalModuleFactory;
 |