diff options
author | Minteck <nekostarfan@gmail.com> | 2021-08-24 14:41:48 +0200 |
---|---|---|
committer | Minteck <nekostarfan@gmail.com> | 2021-08-24 14:41:48 +0200 |
commit | d25e11bee6ca5ca523884da132d18e1400e077b9 (patch) | |
tree | 8af39fde19f7ed640a60fb397c7edd647dff1c4c /node_modules/enhanced-resolve/lib/Resolver.js | |
download | kartik-iridium-d25e11bee6ca5ca523884da132d18e1400e077b9.tar.gz kartik-iridium-d25e11bee6ca5ca523884da132d18e1400e077b9.tar.bz2 kartik-iridium-d25e11bee6ca5ca523884da132d18e1400e077b9.zip |
Initial commit
Diffstat (limited to 'node_modules/enhanced-resolve/lib/Resolver.js')
-rw-r--r-- | node_modules/enhanced-resolve/lib/Resolver.js | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/node_modules/enhanced-resolve/lib/Resolver.js b/node_modules/enhanced-resolve/lib/Resolver.js new file mode 100644 index 0000000..a312008 --- /dev/null +++ b/node_modules/enhanced-resolve/lib/Resolver.js @@ -0,0 +1,475 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +const { AsyncSeriesBailHook, AsyncSeriesHook, SyncHook } = require("tapable"); +const createInnerContext = require("./createInnerContext"); +const { parseIdentifier } = require("./util/identifier"); +const { + normalize, + cachedJoin: join, + getType, + PathType +} = require("./util/path"); + +/** @typedef {import("./ResolverFactory").ResolveOptions} ResolveOptions */ + +/** + * @typedef {Object} FileSystemStats + * @property {function(): boolean} isDirectory + * @property {function(): boolean} isFile + */ + +/** + * @typedef {Object} FileSystemDirent + * @property {Buffer | string} name + * @property {function(): boolean} isDirectory + * @property {function(): boolean} isFile + */ + +/** + * @typedef {Object} PossibleFileSystemError + * @property {string=} code + * @property {number=} errno + * @property {string=} path + * @property {string=} syscall + */ + +/** + * @template T + * @callback FileSystemCallback + * @param {PossibleFileSystemError & Error | null | undefined} err + * @param {T=} result + */ + +/** + * @typedef {Object} FileSystem + * @property {(function(string, FileSystemCallback<Buffer | string>): void) & function(string, object, FileSystemCallback<Buffer | string>): void} readFile + * @property {(function(string, FileSystemCallback<(Buffer | string)[] | FileSystemDirent[]>): void) & function(string, object, FileSystemCallback<(Buffer | string)[] | FileSystemDirent[]>): void} readdir + * @property {((function(string, FileSystemCallback<object>): void) & function(string, object, FileSystemCallback<object>): void)=} readJson + * @property {(function(string, FileSystemCallback<Buffer | string>): void) & function(string, object, FileSystemCallback<Buffer | string>): void} readlink + * @property {(function(string, FileSystemCallback<FileSystemStats>): void) & function(string, object, FileSystemCallback<Buffer | string>): void=} lstat + * @property {(function(string, FileSystemCallback<FileSystemStats>): void) & function(string, object, FileSystemCallback<Buffer | string>): void} stat + */ + +/** + * @typedef {Object} SyncFileSystem + * @property {function(string, object=): Buffer | string} readFileSync + * @property {function(string, object=): (Buffer | string)[] | FileSystemDirent[]} readdirSync + * @property {(function(string, object=): object)=} readJsonSync + * @property {function(string, object=): Buffer | string} readlinkSync + * @property {function(string, object=): FileSystemStats=} lstatSync + * @property {function(string, object=): FileSystemStats} statSync + */ + +/** + * @typedef {Object} ParsedIdentifier + * @property {string} request + * @property {string} query + * @property {string} fragment + * @property {boolean} directory + * @property {boolean} module + * @property {boolean} file + * @property {boolean} internal + */ + +/** + * @typedef {Object} BaseResolveRequest + * @property {string | false} path + * @property {string=} descriptionFilePath + * @property {string=} descriptionFileRoot + * @property {object=} descriptionFileData + * @property {string=} relativePath + * @property {boolean=} ignoreSymlinks + * @property {boolean=} fullySpecified + */ + +/** @typedef {BaseResolveRequest & Partial<ParsedIdentifier>} ResolveRequest */ + +/** + * String with special formatting + * @typedef {string} StackEntry + */ + +/** @template T @typedef {{ add: (T) => void }} WriteOnlySet */ + +/** + * Resolve context + * @typedef {Object} ResolveContext + * @property {WriteOnlySet<string>=} contextDependencies + * @property {WriteOnlySet<string>=} fileDependencies files that was found on file system + * @property {WriteOnlySet<string>=} missingDependencies dependencies that was not found on file system + * @property {Set<StackEntry>=} stack set of hooks' calls. For instance, `resolve → parsedResolve → describedResolve`, + * @property {(function(string): void)=} log log function + */ + +/** @typedef {AsyncSeriesBailHook<[ResolveRequest, ResolveContext], ResolveRequest | null>} ResolveStepHook */ + +/** + * @param {string} str input string + * @returns {string} in camel case + */ +function toCamelCase(str) { + return str.replace(/-([a-z])/g, str => str.substr(1).toUpperCase()); +} + +class Resolver { + /** + * @param {ResolveStepHook} hook hook + * @param {ResolveRequest} request request + * @returns {StackEntry} stack entry + */ + static createStackEntry(hook, request) { + return ( + hook.name + + ": (" + + request.path + + ") " + + (request.request || "") + + (request.query || "") + + (request.fragment || "") + + (request.directory ? " directory" : "") + + (request.module ? " module" : "") + ); + } + + /** + * @param {FileSystem} fileSystem a filesystem + * @param {ResolveOptions} options options + */ + constructor(fileSystem, options) { + this.fileSystem = fileSystem; + this.options = options; + this.hooks = { + /** @type {SyncHook<[ResolveStepHook, ResolveRequest], void>} */ + resolveStep: new SyncHook(["hook", "request"], "resolveStep"), + /** @type {SyncHook<[ResolveRequest, Error]>} */ + noResolve: new SyncHook(["request", "error"], "noResolve"), + /** @type {ResolveStepHook} */ + resolve: new AsyncSeriesBailHook( + ["request", "resolveContext"], + "resolve" + ), + /** @type {AsyncSeriesHook<[ResolveRequest, ResolveContext]>} */ + result: new AsyncSeriesHook(["result", "resolveContext"], "result") + }; + } + + /** + * @param {string | ResolveStepHook} name hook name or hook itself + * @returns {ResolveStepHook} the hook + */ + ensureHook(name) { + if (typeof name !== "string") { + return name; + } + name = toCamelCase(name); + if (/^before/.test(name)) { + return /** @type {ResolveStepHook} */ (this.ensureHook( + name[6].toLowerCase() + name.substr(7) + ).withOptions({ + stage: -10 + })); + } + if (/^after/.test(name)) { + return /** @type {ResolveStepHook} */ (this.ensureHook( + name[5].toLowerCase() + name.substr(6) + ).withOptions({ + stage: 10 + })); + } + const hook = this.hooks[name]; + if (!hook) { + return (this.hooks[name] = new AsyncSeriesBailHook( + ["request", "resolveContext"], + name + )); + } + return hook; + } + + /** + * @param {string | ResolveStepHook} name hook name or hook itself + * @returns {ResolveStepHook} the hook + */ + getHook(name) { + if (typeof name !== "string") { + return name; + } + name = toCamelCase(name); + if (/^before/.test(name)) { + return /** @type {ResolveStepHook} */ (this.getHook( + name[6].toLowerCase() + name.substr(7) + ).withOptions({ + stage: -10 + })); + } + if (/^after/.test(name)) { + return /** @type {ResolveStepHook} */ (this.getHook( + name[5].toLowerCase() + name.substr(6) + ).withOptions({ + stage: 10 + })); + } + const hook = this.hooks[name]; + if (!hook) { + throw new Error(`Hook ${name} doesn't exist`); + } + return hook; + } + + /** + * @param {object} context context information object + * @param {string} path context path + * @param {string} request request string + * @returns {string | false} result + */ + resolveSync(context, path, request) { + /** @type {Error | null | undefined} */ + let err = undefined; + /** @type {string | false | undefined} */ + let result = undefined; + let sync = false; + this.resolve(context, path, request, {}, (e, r) => { + err = e; + result = r; + sync = true; + }); + if (!sync) { + throw new Error( + "Cannot 'resolveSync' because the fileSystem is not sync. Use 'resolve'!" + ); + } + if (err) throw err; + if (result === undefined) throw new Error("No result"); + return result; + } + + /** + * @param {object} context context information object + * @param {string} path context path + * @param {string} request request string + * @param {ResolveContext} resolveContext resolve context + * @param {function(Error | null, (string|false)=, ResolveRequest=): void} callback callback function + * @returns {void} + */ + resolve(context, path, request, resolveContext, callback) { + if (!context || typeof context !== "object") + return callback(new Error("context argument is not an object")); + if (typeof path !== "string") + return callback(new Error("path argument is not a string")); + if (typeof request !== "string") + return callback(new Error("path argument is not a string")); + if (!resolveContext) + return callback(new Error("resolveContext argument is not set")); + + const obj = { + context: context, + path: path, + request: request + }; + + const message = `resolve '${request}' in '${path}'`; + + const finishResolved = result => { + return callback( + null, + result.path === false + ? false + : `${result.path.replace(/#/g, "\0#")}${ + result.query ? result.query.replace(/#/g, "\0#") : "" + }${result.fragment || ""}`, + result + ); + }; + + const finishWithoutResolve = log => { + /** + * @type {Error & {details?: string}} + */ + const error = new Error("Can't " + message); + error.details = log.join("\n"); + this.hooks.noResolve.call(obj, error); + return callback(error); + }; + + if (resolveContext.log) { + // We need log anyway to capture it in case of an error + const parentLog = resolveContext.log; + const log = []; + return this.doResolve( + this.hooks.resolve, + obj, + message, + { + log: msg => { + parentLog(msg); + log.push(msg); + }, + fileDependencies: resolveContext.fileDependencies, + contextDependencies: resolveContext.contextDependencies, + missingDependencies: resolveContext.missingDependencies, + stack: resolveContext.stack + }, + (err, result) => { + if (err) return callback(err); + + if (result) return finishResolved(result); + + return finishWithoutResolve(log); + } + ); + } else { + // Try to resolve assuming there is no error + // We don't log stuff in this case + return this.doResolve( + this.hooks.resolve, + obj, + message, + { + log: undefined, + fileDependencies: resolveContext.fileDependencies, + contextDependencies: resolveContext.contextDependencies, + missingDependencies: resolveContext.missingDependencies, + stack: resolveContext.stack + }, + (err, result) => { + if (err) return callback(err); + + if (result) return finishResolved(result); + + // log is missing for the error details + // so we redo the resolving for the log info + // this is more expensive to the success case + // is assumed by default + + const log = []; + + return this.doResolve( + this.hooks.resolve, + obj, + message, + { + log: msg => log.push(msg), + stack: resolveContext.stack + }, + (err, result) => { + if (err) return callback(err); + + return finishWithoutResolve(log); + } + ); + } + ); + } + } + + doResolve(hook, request, message, resolveContext, callback) { + const stackEntry = Resolver.createStackEntry(hook, request); + + let newStack; + if (resolveContext.stack) { + newStack = new Set(resolveContext.stack); + if (resolveContext.stack.has(stackEntry)) { + /** + * Prevent recursion + * @type {Error & {recursion?: boolean}} + */ + const recursionError = new Error( + "Recursion in resolving\nStack:\n " + + Array.from(newStack).join("\n ") + ); + recursionError.recursion = true; + if (resolveContext.log) + resolveContext.log("abort resolving because of recursion"); + return callback(recursionError); + } + newStack.add(stackEntry); + } else { + newStack = new Set([stackEntry]); + } + this.hooks.resolveStep.call(hook, request); + + if (hook.isUsed()) { + const innerContext = createInnerContext( + { + log: resolveContext.log, + fileDependencies: resolveContext.fileDependencies, + contextDependencies: resolveContext.contextDependencies, + missingDependencies: resolveContext.missingDependencies, + stack: newStack + }, + message + ); + return hook.callAsync(request, innerContext, (err, result) => { + if (err) return callback(err); + if (result) return callback(null, result); + callback(); + }); + } else { + callback(); + } + } + + /** + * @param {string} identifier identifier + * @returns {ParsedIdentifier} parsed identifier + */ + parse(identifier) { + const part = { + request: "", + query: "", + fragment: "", + module: false, + directory: false, + file: false, + internal: false + }; + + const parsedIdentifier = parseIdentifier(identifier); + + if (!parsedIdentifier) return part; + + [part.request, part.query, part.fragment] = parsedIdentifier; + + if (part.request.length > 0) { + part.internal = this.isPrivate(identifier); + part.module = this.isModule(part.request); + part.directory = this.isDirectory(part.request); + if (part.directory) { + part.request = part.request.substr(0, part.request.length - 1); + } + } + + return part; + } + + isModule(path) { + return getType(path) === PathType.Normal; + } + + isPrivate(path) { + return getType(path) === PathType.Internal; + } + + /** + * @param {string} path a path + * @returns {boolean} true, if the path is a directory path + */ + isDirectory(path) { + return path.endsWith("/"); + } + + join(path, request) { + return join(path, request); + } + + normalize(path) { + return normalize(path); + } +} + +module.exports = Resolver; |