diff options
Diffstat (limited to 'node_modules/enhanced-resolve/lib/ResolverFactory.js')
-rw-r--r-- | node_modules/enhanced-resolve/lib/ResolverFactory.js | 652 |
1 files changed, 652 insertions, 0 deletions
diff --git a/node_modules/enhanced-resolve/lib/ResolverFactory.js b/node_modules/enhanced-resolve/lib/ResolverFactory.js new file mode 100644 index 0000000..21ac9b0 --- /dev/null +++ b/node_modules/enhanced-resolve/lib/ResolverFactory.js @@ -0,0 +1,652 @@ +/* + 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 FileExistsPlugin = require("./FileExistsPlugin"); +const ImportsFieldPlugin = require("./ImportsFieldPlugin"); +const JoinRequestPartPlugin = require("./JoinRequestPartPlugin"); +const JoinRequestPlugin = require("./JoinRequestPlugin"); +const MainFieldPlugin = require("./MainFieldPlugin"); +const ModulesInHierachicDirectoriesPlugin = require("./ModulesInHierachicDirectoriesPlugin"); +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("./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 {{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 {(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 constiants 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 {(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"]), + 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, + 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("newInteralResolve"); + resolver.ensureHook("parsedResolve"); + resolver.ensureHook("describedResolve"); + 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"); + + // 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", "normal-resolve")); + if (fallback.length > 0) { + plugins.push( + new AliasPlugin("described-resolve", fallback, "internal-resolve") + ); + } + + // normal-resolve + if (alias.length > 0) + plugins.push(new AliasPlugin("normal-resolve", alias, "internal-resolve")); + aliasFields.forEach(item => { + plugins.push( + new AliasFieldPlugin("normal-resolve", item, "internal-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 ModulesInHierachicDirectoriesPlugin( + "raw-module", + item.filter(i => i !== "node_modules"), + "module" + ) + ); + plugins.push( + new PnpPlugin("raw-module", pnpApi, "undescribed-resolve-in-package") + ); + } else { + plugins.push( + new ModulesInHierachicDirectoriesPlugin("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; +} |