| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";const versions = require("process").versions;const Resolver = require("./Resolver");const { getType, PathType } = require("./util/path");const SyncAsyncFileSystemDecorator = require("./SyncAsyncFileSystemDecorator");const AliasFieldPlugin = require("./AliasFieldPlugin");const AliasPlugin = require("./AliasPlugin");const AppendPlugin = require("./AppendPlugin");const ConditionalPlugin = require("./ConditionalPlugin");const DescriptionFilePlugin = require("./DescriptionFilePlugin");const DirectoryExistsPlugin = require("./DirectoryExistsPlugin");const ExportsFieldPlugin = require("./ExportsFieldPlugin");const ExtensionAliasPlugin = require("./ExtensionAliasPlugin");const FileExistsPlugin = require("./FileExistsPlugin");const ImportsFieldPlugin = require("./ImportsFieldPlugin");const JoinRequestPartPlugin = require("./JoinRequestPartPlugin");const JoinRequestPlugin = require("./JoinRequestPlugin");const MainFieldPlugin = require("./MainFieldPlugin");const ModulesInHierarchicalDirectoriesPlugin = require("./ModulesInHierarchicalDirectoriesPlugin");const ModulesInRootPlugin = require("./ModulesInRootPlugin");const NextPlugin = require("./NextPlugin");const ParsePlugin = require("./ParsePlugin");const PnpPlugin = require("./PnpPlugin");const RestrictionsPlugin = require("./RestrictionsPlugin");const ResultPlugin = require("./ResultPlugin");const RootsPlugin = require("./RootsPlugin");const SelfReferencePlugin = require("./SelfReferencePlugin");const SymlinkPlugin = require("./SymlinkPlugin");const TryNextPlugin = require("./TryNextPlugin");const UnsafeCachePlugin = require("./UnsafeCachePlugin");const UseFilePlugin = require("./UseFilePlugin");/** @typedef {import("./AliasPlugin").AliasOption} AliasOptionEntry *//** @typedef {import("./ExtensionAliasPlugin").ExtensionAliasOption} ExtensionAliasOption *//** @typedef {import("./PnpPlugin").PnpApiImpl} PnpApi *//** @typedef {import("./Resolver").FileSystem} FileSystem *//** @typedef {import("./Resolver").ResolveRequest} ResolveRequest *//** @typedef {import("./Resolver").SyncFileSystem} SyncFileSystem *//** @typedef {string|string[]|false} AliasOptionNewRequest *//** @typedef {{[k: string]: AliasOptionNewRequest}} AliasOptions *//** @typedef {{[k: string]: string|string[] }} ExtensionAliasOptions *//** @typedef {{apply: function(Resolver): void} | function(this: Resolver, Resolver): void} Plugin *//** * @typedef {Object} UserResolveOptions * @property {(AliasOptions | AliasOptionEntry[])=} alias A list of module alias configurations or an object which maps key to value * @property {(AliasOptions | AliasOptionEntry[])=} fallback A list of module alias configurations or an object which maps key to value, applied only after modules option * @property {ExtensionAliasOptions=} extensionAlias An object which maps extension to extension aliases * @property {(string | string[])[]=} aliasFields A list of alias fields in description files * @property {(function(ResolveRequest): boolean)=} cachePredicate A function which decides whether a request should be cached or not. An object is passed with at least `path` and `request` properties. * @property {boolean=} cacheWithContext Whether or not the unsafeCache should include request context as part of the cache key. * @property {string[]=} descriptionFiles A list of description files to read from * @property {string[]=} conditionNames A list of exports field condition names. * @property {boolean=} enforceExtension Enforce that a extension from extensions must be used * @property {(string | string[])[]=} exportsFields A list of exports fields in description files * @property {(string | string[])[]=} importsFields A list of imports fields in description files * @property {string[]=} extensions A list of extensions which should be tried for files * @property {FileSystem} fileSystem The file system which should be used * @property {(object | boolean)=} unsafeCache Use this cache object to unsafely cache the successful requests * @property {boolean=} symlinks Resolve symlinks to their symlinked location * @property {Resolver=} resolver A prepared Resolver to which the plugins are attached * @property {string[] | string=} modules A list of directories to resolve modules from, can be absolute path or folder name * @property {(string | string[] | {name: string | string[], forceRelative: boolean})[]=} mainFields A list of main fields in description files * @property {string[]=} mainFiles  A list of main files in directories * @property {Plugin[]=} plugins A list of additional resolve plugins which should be applied * @property {PnpApi | null=} pnpApi A PnP API that should be used - null is "never", undefined is "auto" * @property {string[]=} roots A list of root paths * @property {boolean=} fullySpecified The request is already fully specified and no extensions or directories are resolved for it * @property {boolean=} resolveToContext Resolve to a context instead of a file * @property {(string|RegExp)[]=} restrictions A list of resolve restrictions * @property {boolean=} useSyncFileSystemCalls Use only the sync constraints of the file system calls * @property {boolean=} preferRelative Prefer to resolve module requests as relative requests before falling back to modules * @property {boolean=} preferAbsolute Prefer to resolve server-relative urls as absolute paths before falling back to resolve in roots *//** * @typedef {Object} ResolveOptions * @property {AliasOptionEntry[]} alias * @property {AliasOptionEntry[]} fallback * @property {Set<string | string[]>} aliasFields * @property {ExtensionAliasOption[]} extensionAlias * @property {(function(ResolveRequest): boolean)} cachePredicate * @property {boolean} cacheWithContext * @property {Set<string>} conditionNames A list of exports field condition names. * @property {string[]} descriptionFiles * @property {boolean} enforceExtension * @property {Set<string | string[]>} exportsFields * @property {Set<string | string[]>} importsFields * @property {Set<string>} extensions * @property {FileSystem} fileSystem * @property {object | false} unsafeCache * @property {boolean} symlinks * @property {Resolver=} resolver * @property {Array<string | string[]>} modules * @property {{name: string[], forceRelative: boolean}[]} mainFields * @property {Set<string>} mainFiles * @property {Plugin[]} plugins * @property {PnpApi | null} pnpApi * @property {Set<string>} roots * @property {boolean} fullySpecified * @property {boolean} resolveToContext * @property {Set<string|RegExp>} restrictions * @property {boolean} preferRelative * @property {boolean} preferAbsolute *//** * @param {PnpApi | null=} option option * @returns {PnpApi | null} processed option */function processPnpApiOption(option) {	if (		option === undefined &&		/** @type {NodeJS.ProcessVersions & {pnp: string}} */ versions.pnp	) {		// @ts-ignore		return require("pnpapi"); // eslint-disable-line node/no-missing-require	}	return option || null;}/** * @param {AliasOptions | AliasOptionEntry[] | undefined} alias alias * @returns {AliasOptionEntry[]} normalized aliases */function normalizeAlias(alias) {	return typeof alias === "object" && !Array.isArray(alias) && alias !== null		? Object.keys(alias).map(key => {				/** @type {AliasOptionEntry} */				const obj = { name: key, onlyModule: false, alias: alias[key] };				if (/\$$/.test(key)) {					obj.onlyModule = true;					obj.name = key.substr(0, key.length - 1);				}				return obj;		  })		: /** @type {Array<AliasOptionEntry>} */ (alias) || [];}/** * @param {UserResolveOptions} options input options * @returns {ResolveOptions} output options */function createOptions(options) {	const mainFieldsSet = new Set(options.mainFields || ["main"]);	const mainFields = [];	for (const item of mainFieldsSet) {		if (typeof item === "string") {			mainFields.push({				name: [item],				forceRelative: true			});		} else if (Array.isArray(item)) {			mainFields.push({				name: item,				forceRelative: true			});		} else {			mainFields.push({				name: Array.isArray(item.name) ? item.name : [item.name],				forceRelative: item.forceRelative			});		}	}	return {		alias: normalizeAlias(options.alias),		fallback: normalizeAlias(options.fallback),		aliasFields: new Set(options.aliasFields),		cachePredicate:			options.cachePredicate ||			function () {				return true;			},		cacheWithContext:			typeof options.cacheWithContext !== "undefined"				? options.cacheWithContext				: true,		exportsFields: new Set(options.exportsFields || ["exports"]),		importsFields: new Set(options.importsFields || ["imports"]),		conditionNames: new Set(options.conditionNames),		descriptionFiles: Array.from(			new Set(options.descriptionFiles || ["package.json"])		),		enforceExtension:			options.enforceExtension === undefined				? options.extensions && options.extensions.includes("")					? true					: false				: options.enforceExtension,		extensions: new Set(options.extensions || [".js", ".json", ".node"]),		extensionAlias: options.extensionAlias			? Object.keys(options.extensionAlias).map(k => ({					extension: k,					alias: /** @type {ExtensionAliasOptions} */ (options.extensionAlias)[						k					]			  }))			: [],		fileSystem: options.useSyncFileSystemCalls			? new SyncAsyncFileSystemDecorator(					/** @type {SyncFileSystem} */ (						/** @type {unknown} */ (options.fileSystem)					)			  )			: options.fileSystem,		unsafeCache:			options.unsafeCache && typeof options.unsafeCache !== "object"				? {}				: options.unsafeCache || false,		symlinks: typeof options.symlinks !== "undefined" ? options.symlinks : true,		resolver: options.resolver,		modules: mergeFilteredToArray(			Array.isArray(options.modules)				? options.modules				: options.modules				? [options.modules]				: ["node_modules"],			item => {				const type = getType(item);				return type === PathType.Normal || type === PathType.Relative;			}		),		mainFields,		mainFiles: new Set(options.mainFiles || ["index"]),		plugins: options.plugins || [],		pnpApi: processPnpApiOption(options.pnpApi),		roots: new Set(options.roots || undefined),		fullySpecified: options.fullySpecified || false,		resolveToContext: options.resolveToContext || false,		preferRelative: options.preferRelative || false,		preferAbsolute: options.preferAbsolute || false,		restrictions: new Set(options.restrictions)	};}/** * @param {UserResolveOptions} options resolve options * @returns {Resolver} created resolver */exports.createResolver = function (options) {	const normalizedOptions = createOptions(options);	const {		alias,		fallback,		aliasFields,		cachePredicate,		cacheWithContext,		conditionNames,		descriptionFiles,		enforceExtension,		exportsFields,		extensionAlias,		importsFields,		extensions,		fileSystem,		fullySpecified,		mainFields,		mainFiles,		modules,		plugins: userPlugins,		pnpApi,		resolveToContext,		preferRelative,		preferAbsolute,		symlinks,		unsafeCache,		resolver: customResolver,		restrictions,		roots	} = normalizedOptions;	const plugins = userPlugins.slice();	const resolver = customResolver		? customResolver		: new Resolver(fileSystem, normalizedOptions);	//// pipeline ////	resolver.ensureHook("resolve");	resolver.ensureHook("internalResolve");	resolver.ensureHook("newInternalResolve");	resolver.ensureHook("parsedResolve");	resolver.ensureHook("describedResolve");	resolver.ensureHook("rawResolve");	resolver.ensureHook("normalResolve");	resolver.ensureHook("internal");	resolver.ensureHook("rawModule");	resolver.ensureHook("module");	resolver.ensureHook("resolveAsModule");	resolver.ensureHook("undescribedResolveInPackage");	resolver.ensureHook("resolveInPackage");	resolver.ensureHook("resolveInExistingDirectory");	resolver.ensureHook("relative");	resolver.ensureHook("describedRelative");	resolver.ensureHook("directory");	resolver.ensureHook("undescribedExistingDirectory");	resolver.ensureHook("existingDirectory");	resolver.ensureHook("undescribedRawFile");	resolver.ensureHook("rawFile");	resolver.ensureHook("file");	resolver.ensureHook("finalFile");	resolver.ensureHook("existingFile");	resolver.ensureHook("resolved");	// TODO remove in next major	// cspell:word Interal	// Backward-compat	resolver.hooks.newInteralResolve = resolver.hooks.newInternalResolve;	// resolve	for (const { source, resolveOptions } of [		{ source: "resolve", resolveOptions: { fullySpecified } },		{ source: "internal-resolve", resolveOptions: { fullySpecified: false } }	]) {		if (unsafeCache) {			plugins.push(				new UnsafeCachePlugin(					source,					cachePredicate,					unsafeCache,					cacheWithContext,					`new-${source}`				)			);			plugins.push(				new ParsePlugin(`new-${source}`, resolveOptions, "parsed-resolve")			);		} else {			plugins.push(new ParsePlugin(source, resolveOptions, "parsed-resolve"));		}	}	// parsed-resolve	plugins.push(		new DescriptionFilePlugin(			"parsed-resolve",			descriptionFiles,			false,			"described-resolve"		)	);	plugins.push(new NextPlugin("after-parsed-resolve", "described-resolve"));	// described-resolve	plugins.push(new NextPlugin("described-resolve", "raw-resolve"));	if (fallback.length > 0) {		plugins.push(			new AliasPlugin("described-resolve", fallback, "internal-resolve")		);	}	// raw-resolve	if (alias.length > 0) {		plugins.push(new AliasPlugin("raw-resolve", alias, "internal-resolve"));	}	aliasFields.forEach(item => {		plugins.push(new AliasFieldPlugin("raw-resolve", item, "internal-resolve"));	});	extensionAlias.forEach(item =>		plugins.push(			new ExtensionAliasPlugin("raw-resolve", item, "normal-resolve")		)	);	plugins.push(new NextPlugin("raw-resolve", "normal-resolve"));	// normal-resolve	if (preferRelative) {		plugins.push(new JoinRequestPlugin("after-normal-resolve", "relative"));	}	plugins.push(		new ConditionalPlugin(			"after-normal-resolve",			{ module: true },			"resolve as module",			false,			"raw-module"		)	);	plugins.push(		new ConditionalPlugin(			"after-normal-resolve",			{ internal: true },			"resolve as internal import",			false,			"internal"		)	);	if (preferAbsolute) {		plugins.push(new JoinRequestPlugin("after-normal-resolve", "relative"));	}	if (roots.size > 0) {		plugins.push(new RootsPlugin("after-normal-resolve", roots, "relative"));	}	if (!preferRelative && !preferAbsolute) {		plugins.push(new JoinRequestPlugin("after-normal-resolve", "relative"));	}	// internal	importsFields.forEach(importsField => {		plugins.push(			new ImportsFieldPlugin(				"internal",				conditionNames,				importsField,				"relative",				"internal-resolve"			)		);	});	// raw-module	exportsFields.forEach(exportsField => {		plugins.push(			new SelfReferencePlugin("raw-module", exportsField, "resolve-as-module")		);	});	modules.forEach(item => {		if (Array.isArray(item)) {			if (item.includes("node_modules") && pnpApi) {				plugins.push(					new ModulesInHierarchicalDirectoriesPlugin(						"raw-module",						item.filter(i => i !== "node_modules"),						"module"					)				);				plugins.push(					new PnpPlugin("raw-module", pnpApi, "undescribed-resolve-in-package")				);			} else {				plugins.push(					new ModulesInHierarchicalDirectoriesPlugin(						"raw-module",						item,						"module"					)				);			}		} else {			plugins.push(new ModulesInRootPlugin("raw-module", item, "module"));		}	});	// module	plugins.push(new JoinRequestPartPlugin("module", "resolve-as-module"));	// resolve-as-module	if (!resolveToContext) {		plugins.push(			new ConditionalPlugin(				"resolve-as-module",				{ directory: false, request: "." },				"single file module",				true,				"undescribed-raw-file"			)		);	}	plugins.push(		new DirectoryExistsPlugin(			"resolve-as-module",			"undescribed-resolve-in-package"		)	);	// undescribed-resolve-in-package	plugins.push(		new DescriptionFilePlugin(			"undescribed-resolve-in-package",			descriptionFiles,			false,			"resolve-in-package"		)	);	plugins.push(		new NextPlugin("after-undescribed-resolve-in-package", "resolve-in-package")	);	// resolve-in-package	exportsFields.forEach(exportsField => {		plugins.push(			new ExportsFieldPlugin(				"resolve-in-package",				conditionNames,				exportsField,				"relative"			)		);	});	plugins.push(		new NextPlugin("resolve-in-package", "resolve-in-existing-directory")	);	// resolve-in-existing-directory	plugins.push(		new JoinRequestPlugin("resolve-in-existing-directory", "relative")	);	// relative	plugins.push(		new DescriptionFilePlugin(			"relative",			descriptionFiles,			true,			"described-relative"		)	);	plugins.push(new NextPlugin("after-relative", "described-relative"));	// described-relative	if (resolveToContext) {		plugins.push(new NextPlugin("described-relative", "directory"));	} else {		plugins.push(			new ConditionalPlugin(				"described-relative",				{ directory: false },				null,				true,				"raw-file"			)		);		plugins.push(			new ConditionalPlugin(				"described-relative",				{ fullySpecified: false },				"as directory",				true,				"directory"			)		);	}	// directory	plugins.push(		new DirectoryExistsPlugin("directory", "undescribed-existing-directory")	);	if (resolveToContext) {		// undescribed-existing-directory		plugins.push(new NextPlugin("undescribed-existing-directory", "resolved"));	} else {		// undescribed-existing-directory		plugins.push(			new DescriptionFilePlugin(				"undescribed-existing-directory",				descriptionFiles,				false,				"existing-directory"			)		);		mainFiles.forEach(item => {			plugins.push(				new UseFilePlugin(					"undescribed-existing-directory",					item,					"undescribed-raw-file"				)			);		});		// described-existing-directory		mainFields.forEach(item => {			plugins.push(				new MainFieldPlugin(					"existing-directory",					item,					"resolve-in-existing-directory"				)			);		});		mainFiles.forEach(item => {			plugins.push(				new UseFilePlugin("existing-directory", item, "undescribed-raw-file")			);		});		// undescribed-raw-file		plugins.push(			new DescriptionFilePlugin(				"undescribed-raw-file",				descriptionFiles,				true,				"raw-file"			)		);		plugins.push(new NextPlugin("after-undescribed-raw-file", "raw-file"));		// raw-file		plugins.push(			new ConditionalPlugin(				"raw-file",				{ fullySpecified: true },				null,				false,				"file"			)		);		if (!enforceExtension) {			plugins.push(new TryNextPlugin("raw-file", "no extension", "file"));		}		extensions.forEach(item => {			plugins.push(new AppendPlugin("raw-file", item, "file"));		});		// file		if (alias.length > 0)			plugins.push(new AliasPlugin("file", alias, "internal-resolve"));		aliasFields.forEach(item => {			plugins.push(new AliasFieldPlugin("file", item, "internal-resolve"));		});		plugins.push(new NextPlugin("file", "final-file"));		// final-file		plugins.push(new FileExistsPlugin("final-file", "existing-file"));		// existing-file		if (symlinks)			plugins.push(new SymlinkPlugin("existing-file", "existing-file"));		plugins.push(new NextPlugin("existing-file", "resolved"));	}	// resolved	if (restrictions.size > 0) {		plugins.push(new RestrictionsPlugin(resolver.hooks.resolved, restrictions));	}	plugins.push(new ResultPlugin(resolver.hooks.resolved));	//// RESOLVER ////	for (const plugin of plugins) {		if (typeof plugin === "function") {			plugin.call(resolver, resolver);		} else {			plugin.apply(resolver);		}	}	return resolver;};/** * Merging filtered elements * @param {string[]} array source array * @param {function(string): boolean} filter predicate * @returns {Array<string | string[]>} merge result */function mergeFilteredToArray(array, filter) {	/** @type {Array<string | string[]>} */	const result = [];	const set = new Set(array);	for (const item of set) {		if (filter(item)) {			const lastElement =				result.length > 0 ? result[result.length - 1] : undefined;			if (Array.isArray(lastElement)) {				lastElement.push(item);			} else {				result.push([item]);			}		} else {			result.push(item);		}	}	return result;}
 |