UNPKG

@eslint/config-inspector

Version:

A visual tool for inspecting and understanding your ESLint flat configs

1,515 lines (1,470 loc) 274 kB
import { existsSync } from 'node:fs'; import fs, { stat, readFile } from 'node:fs/promises'; import { relative as relative$2, resolve as resolve$3, dirname as dirname$2, basename as basename$2, join as join$2 } from 'node:path'; import process$1 from 'node:process'; import cac from 'cac'; import fg from 'fast-glob'; import { getPort } from 'get-port-please'; import open from 'open'; import c from 'picocolors'; import fswalk from '@nodelib/fs.walk'; import { bundleRequire } from 'bundle-require'; import { findUp } from 'find-up'; import { resolve as resolve$2 } from 'mlly'; import createDebug from 'debug'; import { fileURLToPath } from 'node:url'; import { createServer } from 'node:http'; import { createApp, eventHandler, serveStatic, toNodeListener } from 'h3'; import { lookup } from 'mrmime'; import chokidar from 'chokidar'; import { WebSocketServer } from 'ws'; /** * Searches a directory looking for matching files. This uses the config * array's logic to determine if a directory or file should be ignored. * * Derived from {@link https://github.com/eslint/eslint/blob/d2d06f7a70d9b96b125ecf2de8951bea549db4da/lib/eslint/eslint-helpers.js#L217-L382|ESLint globSearch()} * * @param {Object} options The options for this function. * @param {string} options.basePath The directory to search. * @param {import('@eslint/config-array').ConfigArray} options.configs The config array to use for determining what to ignore. * @param {import('@nodelib/fs.walk').DeepFilterFunction} [options.deepFilter] Optional function that indicates whether the directory will be read deep or not. * @param {import('@nodelib/fs.walk').EntryFilterFunction} [options.entryFilter] Optional function that indicates whether the entry will be included to results or not. * @returns {Promise<Array<string>>} An array of matching file paths or an empty array if there are no matches. */ async function configArrayFindFiles (options) { const { basePath, configs, deepFilter, entryFilter, } = options; /** @type {import('@nodelib/fs.walk').Entry[]} */ const filePaths = (await new Promise((resolve, reject) => { let promiseRejected = false; /** * Wraps a boolean-returning filter function. The wrapped function will reject the promise if an error occurs. * * @param {import('@nodelib/fs.walk').DeepFilterFunction | import('@nodelib/fs.walk').EntryFilterFunction} filter A filter function to wrap. * @returns {import('@nodelib/fs.walk').DeepFilterFunction | import('@nodelib/fs.walk').EntryFilterFunction} A function similar to the wrapped filter that rejects the promise if an error occurs. */ function wrapFilter (filter) { /** @type {import('@nodelib/fs.walk').DeepFilterFunction | import('@nodelib/fs.walk').EntryFilterFunction} */ const result = (...args) => { // No need to run the filter if an error has been thrown. if (!promiseRejected) { try { return filter(...args); } catch (err) { promiseRejected = true; reject(err); } } return false; }; return result; } fswalk.walk( basePath, { deepFilter: wrapFilter(entry => { if (deepFilter && !deepFilter(entry)) { return false; } return !configs.isDirectoryIgnored(entry.path); }), entryFilter: wrapFilter(entry => { // entries may be directories or files so filter out directories if (entry.dirent.isDirectory()) { return false; } if (entryFilter && !entryFilter(entry)) { return false; } return configs.getConfig(entry.path) !== undefined; }), }, (error, entries) => { // If the promise is already rejected, calling `resolve` or `reject` will do nothing. if (error) { reject(error); } else { resolve(entries); } } ); })); return filePaths.map(entry => entry.path); } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright the Browserify authors. MIT License. function assertPath$1(path) { if (typeof path !== "string") { throw new TypeError(`Path must be a string, received "${JSON.stringify(path)}"`); } } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. function stripSuffix$1(name, suffix) { if (suffix.length >= name.length) { return name; } const lenDiff = name.length - suffix.length; for(let i = suffix.length - 1; i >= 0; --i){ if (name.charCodeAt(lenDiff + i) !== suffix.charCodeAt(i)) { return name; } } return name.slice(0, -suffix.length); } function lastPathSegment$1(path, isSep, start = 0) { let matchedNonSeparator = false; let end = path.length; for(let i = path.length - 1; i >= start; --i){ if (isSep(path.charCodeAt(i))) { if (matchedNonSeparator) { start = i + 1; break; } } else if (!matchedNonSeparator) { matchedNonSeparator = true; end = i + 1; } } return path.slice(start, end); } function assertArgs$1$1(path, suffix) { assertPath$1(path); if (path.length === 0) return path; if (typeof suffix !== "string") { throw new TypeError(`Suffix must be a string, received "${JSON.stringify(suffix)}"`); } } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ // This module is browser compatible. function stripTrailingSeparators$1(segment, isSep) { if (segment.length <= 1) { return segment; } let end = segment.length; for(let i = segment.length - 1; i > 0; i--){ if (isSep(segment.charCodeAt(i))) { end = i; } else { break; } } return segment.slice(0, end); } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ // This module is browser compatible. // Alphabet chars. // Non-alphabetic chars. const CHAR_DOT$1 = 46; /* . */ const CHAR_FORWARD_SLASH$1 = 47; /* / */ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ // This module is browser compatible. function isPosixPathSeparator$1(code) { return code === CHAR_FORWARD_SLASH$1; } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Return the last portion of a `path`. * Trailing directory separators are ignored, and optional suffix is removed. * * @example Usage * ```ts * import { basename } from "@std/path/posix/basename"; * import { assertEquals } from "@std/assert"; * * assertEquals(basename("/home/user/Documents/"), "Documents"); * assertEquals(basename("/home/user/Documents/image.png"), "image.png"); * assertEquals(basename("/home/user/Documents/image.png", ".png"), "image"); * ``` * * @example Working with URLs * * Note: This function doesn't automatically strip hash and query parts from * URLs. If your URL contains a hash or query, remove them before passing the * URL to the function. This can be done by passing the URL to `new URL(url)`, * and setting the `hash` and `search` properties to empty strings. * * ```ts * import { basename } from "@std/path/posix/basename"; * import { assertEquals } from "@std/assert"; * * assertEquals(basename("https://deno.land/std/path/mod.ts"), "mod.ts"); * assertEquals(basename("https://deno.land/std/path/mod.ts", ".ts"), "mod"); * assertEquals(basename("https://deno.land/std/path/mod.ts?a=b"), "mod.ts?a=b"); * assertEquals(basename("https://deno.land/std/path/mod.ts#header"), "mod.ts#header"); * ``` * * Note: If you are working with file URLs, * use the new version of `basename` from `@std/path/posix/unstable-basename`. * * @param path The path to extract the name from. * @param suffix The suffix to remove from extracted name. * @returns The extracted name. */ function basename$1(path, suffix = "") { assertArgs$1$1(path, suffix); const lastSegment = lastPathSegment$1(path, isPosixPathSeparator$1); const strippedSegment = stripTrailingSeparators$1(lastSegment, isPosixPathSeparator$1); return suffix ? stripSuffix$1(strippedSegment, suffix) : strippedSegment; } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * The character used to separate entries in the PATH environment variable. */ const DELIMITER$1 = ":"; /** * The character used to separate components of a file path. */ const SEPARATOR$1 = "/"; /** * A regular expression that matches one or more path separators. */ const SEPARATOR_PATTERN$1 = /\/+/; // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. function assertArg$3$1(path) { assertPath$1(path); if (path.length === 0) return "."; } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Return the directory path of a `path`. * * @example Usage * ```ts * import { dirname } from "@std/path/posix/dirname"; * import { assertEquals } from "@std/assert"; * * assertEquals(dirname("/home/user/Documents/"), "/home/user"); * assertEquals(dirname("/home/user/Documents/image.png"), "/home/user/Documents"); * assertEquals(dirname("https://deno.land/std/path/mod.ts"), "https://deno.land/std/path"); * ``` * * @example Working with URLs * * ```ts * import { dirname } from "@std/path/posix/dirname"; * import { assertEquals } from "@std/assert"; * * assertEquals(dirname("https://deno.land/std/path/mod.ts"), "https://deno.land/std/path"); * assertEquals(dirname("https://deno.land/std/path/mod.ts?a=b"), "https://deno.land/std/path"); * assertEquals(dirname("https://deno.land/std/path/mod.ts#header"), "https://deno.land/std/path"); * ``` * * Note: If you are working with file URLs, * use the new version of `dirname` from `@std/path/posix/unstable-dirname`. * * @param path The path to get the directory from. * @returns The directory path. */ function dirname$1(path) { assertArg$3$1(path); let end = -1; let matchedNonSeparator = false; for(let i = path.length - 1; i >= 1; --i){ if (isPosixPathSeparator$1(path.charCodeAt(i))) { if (matchedNonSeparator) { end = i; break; } } else { matchedNonSeparator = true; } } // No matches. Fallback based on provided path: // // - leading slashes paths // "/foo" => "/" // "///foo" => "/" // - no slash path // "foo" => "." if (end === -1) { return isPosixPathSeparator$1(path.charCodeAt(0)) ? "/" : "."; } return stripTrailingSeparators$1(path.slice(0, end), isPosixPathSeparator$1); } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Return the extension of the `path` with leading period. * * @example Usage * ```ts * import { extname } from "@std/path/posix/extname"; * import { assertEquals } from "@std/assert"; * * assertEquals(extname("/home/user/Documents/file.ts"), ".ts"); * assertEquals(extname("/home/user/Documents/"), ""); * assertEquals(extname("/home/user/Documents/image.png"), ".png"); * ``` * * @example Working with URLs * * Note: This function doesn't automatically strip hash and query parts from * URLs. If your URL contains a hash or query, remove them before passing the * URL to the function. This can be done by passing the URL to `new URL(url)`, * and setting the `hash` and `search` properties to empty strings. * * ```ts * import { extname } from "@std/path/posix/extname"; * import { assertEquals } from "@std/assert"; * * assertEquals(extname("https://deno.land/std/path/mod.ts"), ".ts"); * assertEquals(extname("https://deno.land/std/path/mod.ts?a=b"), ".ts?a=b"); * assertEquals(extname("https://deno.land/std/path/mod.ts#header"), ".ts#header"); * ``` * * Note: If you are working with file URLs, * use the new version of `extname` from `@std/path/posix/unstable-extname`. * * @param path The path to get the extension from. * @returns The extension (ex. for `file.ts` returns `.ts`). */ function extname$1(path) { assertPath$1(path); let startDot = -1; let startPart = 0; let end = -1; let matchedSlash = true; // Track the state of characters (if any) we see before our first dot and // after any path separator we find let preDotState = 0; for(let i = path.length - 1; i >= 0; --i){ const code = path.charCodeAt(i); if (isPosixPathSeparator$1(code)) { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now if (!matchedSlash) { startPart = i + 1; break; } continue; } if (end === -1) { // We saw the first non-path separator, mark this as the end of our // extension matchedSlash = false; end = i + 1; } if (code === CHAR_DOT$1) { // If this is our first dot, mark it as the start of our extension if (startDot === -1) startDot = i; else if (preDotState !== 1) preDotState = 1; } else if (startDot !== -1) { // We saw a non-dot and non-path separator before our dot, so we should // have a good chance at having a non-empty extension preDotState = -1; } } if (startDot === -1 || end === -1 || // We saw a non-dot character immediately before the dot preDotState === 0 || // The (right-most) trimmed path component is exactly '..' preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { return ""; } return path.slice(startDot, end); } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. function _format$1(sep, pathObject) { const dir = pathObject.dir || pathObject.root; const base = pathObject.base || (pathObject.name ?? "") + (pathObject.ext ?? ""); if (!dir) return base; if (base === sep) return dir; if (dir === pathObject.root) return dir + base; return dir + sep + base; } function assertArg$2$1(pathObject) { if (pathObject === null || typeof pathObject !== "object") { throw new TypeError(`The "pathObject" argument must be of type Object, received type "${typeof pathObject}"`); } } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Generate a path from `ParsedPath` object. * * @example Usage * ```ts * import { format } from "@std/path/posix/format"; * import { assertEquals } from "@std/assert"; * * const path = format({ * root: "/", * dir: "/path/dir", * base: "file.txt", * ext: ".txt", * name: "file" * }); * assertEquals(path, "/path/dir/file.txt"); * ``` * * @param pathObject The path object to format. * @returns The formatted path. */ function format$1(pathObject) { assertArg$2$1(pathObject); return _format$1("/", pathObject); } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. function assertArg$1$1(url) { url = url instanceof URL ? url : new URL(url); if (url.protocol !== "file:") { throw new TypeError(`URL must be a file URL: received "${url.protocol}"`); } return url; } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Converts a file URL to a path string. * * @example Usage * ```ts * import { fromFileUrl } from "@std/path/posix/from-file-url"; * import { assertEquals } from "@std/assert"; * * assertEquals(fromFileUrl(new URL("file:///home/foo")), "/home/foo"); * ``` * * @param url The file URL to convert. * @returns The path string. */ function fromFileUrl$1(url) { url = assertArg$1$1(url); return decodeURIComponent(url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25")); } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Verifies whether provided path is absolute. * * @example Usage * ```ts * import { isAbsolute } from "@std/path/posix/is-absolute"; * import { assert, assertFalse } from "@std/assert"; * * assert(isAbsolute("/home/user/Documents/")); * assertFalse(isAbsolute("home/user/Documents/")); * ``` * * @param path The path to verify. * @returns Whether the path is absolute. */ function isAbsolute$1(path) { assertPath$1(path); return path.length > 0 && isPosixPathSeparator$1(path.charCodeAt(0)); } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. function assertArg$4(path) { assertPath$1(path); if (path.length === 0) return "."; } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ // This module is browser compatible. // Resolves . and .. elements in a path with directory names function normalizeString$1(path, allowAboveRoot, separator, isPathSeparator) { let res = ""; let lastSegmentLength = 0; let lastSlash = -1; let dots = 0; let code; for(let i = 0; i <= path.length; ++i){ if (i < path.length) code = path.charCodeAt(i); else if (isPathSeparator(code)) break; else code = CHAR_FORWARD_SLASH$1; if (isPathSeparator(code)) { if (lastSlash === i - 1 || dots === 1) ; else if (lastSlash !== i - 1 && dots === 2) { if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== CHAR_DOT$1 || res.charCodeAt(res.length - 2) !== CHAR_DOT$1) { if (res.length > 2) { const lastSlashIndex = res.lastIndexOf(separator); if (lastSlashIndex === -1) { res = ""; lastSegmentLength = 0; } else { res = res.slice(0, lastSlashIndex); lastSegmentLength = res.length - 1 - res.lastIndexOf(separator); } lastSlash = i; dots = 0; continue; } else if (res.length === 2 || res.length === 1) { res = ""; lastSegmentLength = 0; lastSlash = i; dots = 0; continue; } } if (allowAboveRoot) { if (res.length > 0) res += `${separator}..`; else res = ".."; lastSegmentLength = 2; } } else { if (res.length > 0) res += separator + path.slice(lastSlash + 1, i); else res = path.slice(lastSlash + 1, i); lastSegmentLength = i - lastSlash - 1; } lastSlash = i; dots = 0; } else if (code === CHAR_DOT$1 && dots !== -1) { ++dots; } else { dots = -1; } } return res; } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Normalize the `path`, resolving `'..'` and `'.'` segments. * Note that resolving these segments does not necessarily mean that all will be eliminated. * A `'..'` at the top-level will be preserved, and an empty path is canonically `'.'`. * * @example Usage * ```ts * import { normalize } from "@std/path/posix/normalize"; * import { assertEquals } from "@std/assert"; * * const path = normalize("/foo/bar//baz/asdf/quux/.."); * assertEquals(path, "/foo/bar/baz/asdf"); * ``` * * @example Working with URLs * * Note: This function will remove the double slashes from a URL's scheme. * Hence, do not pass a full URL to this function. Instead, pass the pathname of * the URL. * * ```ts * import { normalize } from "@std/path/posix/normalize"; * import { assertEquals } from "@std/assert"; * * const url = new URL("https://deno.land"); * url.pathname = normalize("//std//assert//.//mod.ts"); * assertEquals(url.href, "https://deno.land/std/assert/mod.ts"); * * url.pathname = normalize("std/assert/../async/retry.ts"); * assertEquals(url.href, "https://deno.land/std/async/retry.ts"); * ``` * * Note: If you are working with file URLs, * use the new version of `normalize` from `@std/path/posix/unstable-normalize`. * * @param path The path to normalize. * @returns The normalized path. */ function normalize$2(path) { assertArg$4(path); const isAbsolute = isPosixPathSeparator$1(path.charCodeAt(0)); const trailingSeparator = isPosixPathSeparator$1(path.charCodeAt(path.length - 1)); // Normalize the path path = normalizeString$1(path, !isAbsolute, "/", isPosixPathSeparator$1); if (path.length === 0 && !isAbsolute) path = "."; if (path.length > 0 && trailingSeparator) path += "/"; if (isAbsolute) return `/${path}`; return path; } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Join all given a sequence of `paths`,then normalizes the resulting path. * * @example Usage * ```ts * import { join } from "@std/path/posix/join"; * import { assertEquals } from "@std/assert"; * * const path = join("/foo", "bar", "baz/asdf", "quux", ".."); * assertEquals(path, "/foo/bar/baz/asdf"); * ``` * * @example Working with URLs * ```ts * import { join } from "@std/path/posix/join"; * import { assertEquals } from "@std/assert"; * * const url = new URL("https://deno.land"); * url.pathname = join("std", "path", "mod.ts"); * assertEquals(url.href, "https://deno.land/std/path/mod.ts"); * * url.pathname = join("//std", "path/", "/mod.ts"); * assertEquals(url.href, "https://deno.land/std/path/mod.ts"); * ``` * * Note: If you are working with file URLs, * use the new version of `join` from `@std/path/posix/unstable-join`. * * @param paths The paths to join. * @returns The joined path. */ function join$1(...paths) { if (paths.length === 0) return "."; paths.forEach((path)=>assertPath$1(path)); const joined = paths.filter((path)=>path.length > 0).join("/"); return joined === "" ? "." : normalize$2(joined); } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Return a `ParsedPath` object of the `path`. * * @example Usage * ```ts * import { parse } from "@std/path/posix/parse"; * import { assertEquals } from "@std/assert"; * * const path = parse("/home/user/file.txt"); * assertEquals(path, { * root: "/", * dir: "/home/user", * base: "file.txt", * ext: ".txt", * name: "file" * }); * ``` * * @param path The path to parse. * @returns The parsed path object. */ function parse$1(path) { assertPath$1(path); const ret = { root: "", dir: "", base: "", ext: "", name: "" }; if (path.length === 0) return ret; const isAbsolute = isPosixPathSeparator$1(path.charCodeAt(0)); let start; if (isAbsolute) { ret.root = "/"; start = 1; } else { start = 0; } let startDot = -1; let startPart = 0; let end = -1; let matchedSlash = true; let i = path.length - 1; // Track the state of characters (if any) we see before our first dot and // after any path separator we find let preDotState = 0; // Get non-dir info for(; i >= start; --i){ const code = path.charCodeAt(i); if (isPosixPathSeparator$1(code)) { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now if (!matchedSlash) { startPart = i + 1; break; } continue; } if (end === -1) { // We saw the first non-path separator, mark this as the end of our // extension matchedSlash = false; end = i + 1; } if (code === CHAR_DOT$1) { // If this is our first dot, mark it as the start of our extension if (startDot === -1) startDot = i; else if (preDotState !== 1) preDotState = 1; } else if (startDot !== -1) { // We saw a non-dot and non-path separator before our dot, so we should // have a good chance at having a non-empty extension preDotState = -1; } } if (startDot === -1 || end === -1 || // We saw a non-dot character immediately before the dot preDotState === 0 || // The (right-most) trimmed path component is exactly '..' preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { if (end !== -1) { if (startPart === 0 && isAbsolute) { ret.base = ret.name = path.slice(1, end); } else { ret.base = ret.name = path.slice(startPart, end); } } // Fallback to '/' in case there is no basename ret.base = ret.base || "/"; } else { if (startPart === 0 && isAbsolute) { ret.name = path.slice(1, startDot); ret.base = path.slice(1, end); } else { ret.name = path.slice(startPart, startDot); ret.base = path.slice(startPart, end); } ret.ext = path.slice(startDot, end); } if (startPart > 0) { ret.dir = stripTrailingSeparators$1(path.slice(0, startPart - 1), isPosixPathSeparator$1); } else if (isAbsolute) ret.dir = "/"; return ret; } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Resolves path segments into a `path`. * * @example Usage * ```ts * import { resolve } from "@std/path/posix/resolve"; * import { assertEquals } from "@std/assert"; * * const path = resolve("/foo", "bar", "baz/asdf", "quux", ".."); * assertEquals(path, "/foo/bar/baz/asdf"); * ``` * * @param pathSegments The path segments to resolve. * @returns The resolved path. */ function resolve$1(...pathSegments) { let resolvedPath = ""; let resolvedAbsolute = false; for(let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--){ let path; if (i >= 0) path = pathSegments[i]; else { // deno-lint-ignore no-explicit-any const { Deno } = globalThis; if (typeof Deno?.cwd !== "function") { throw new TypeError("Resolved a relative path without a current working directory (CWD)"); } path = Deno.cwd(); } assertPath$1(path); // Skip empty entries if (path.length === 0) { continue; } resolvedPath = `${path}/${resolvedPath}`; resolvedAbsolute = isPosixPathSeparator$1(path.charCodeAt(0)); } // At this point the path should be resolved to a full absolute path, but // handle relative paths to be safe (might happen when Deno.cwd() fails) // Normalize the path resolvedPath = normalizeString$1(resolvedPath, !resolvedAbsolute, "/", isPosixPathSeparator$1); if (resolvedAbsolute) { if (resolvedPath.length > 0) return `/${resolvedPath}`; else return "/"; } else if (resolvedPath.length > 0) return resolvedPath; else return "."; } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. function assertArgs$2(from, to) { assertPath$1(from); assertPath$1(to); if (from === to) return ""; } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Return the relative path from `from` to `to` based on current working directory. * * If `from` and `to` are the same, return an empty string. * * @example Usage * ```ts * import { relative } from "@std/path/posix/relative"; * import { assertEquals } from "@std/assert"; * * const path = relative("/data/orandea/test/aaa", "/data/orandea/impl/bbb"); * assertEquals(path, "../../impl/bbb"); * ``` * * @param from The path to start from. * @param to The path to reach. * @returns The relative path. */ function relative$1(from, to) { assertArgs$2(from, to); from = resolve$1(from); to = resolve$1(to); if (from === to) return ""; // Trim any leading backslashes let fromStart = 1; const fromEnd = from.length; for(; fromStart < fromEnd; ++fromStart){ if (!isPosixPathSeparator$1(from.charCodeAt(fromStart))) break; } const fromLen = fromEnd - fromStart; // Trim any leading backslashes let toStart = 1; const toEnd = to.length; for(; toStart < toEnd; ++toStart){ if (!isPosixPathSeparator$1(to.charCodeAt(toStart))) break; } const toLen = toEnd - toStart; // Compare paths to find the longest common path from root const length = fromLen < toLen ? fromLen : toLen; let lastCommonSep = -1; let i = 0; for(; i <= length; ++i){ if (i === length) { if (toLen > length) { if (isPosixPathSeparator$1(to.charCodeAt(toStart + i))) { // We get here if `from` is the exact base path for `to`. // For example: from='/foo/bar'; to='/foo/bar/baz' return to.slice(toStart + i + 1); } else if (i === 0) { // We get here if `from` is the root // For example: from='/'; to='/foo' return to.slice(toStart + i); } } else if (fromLen > length) { if (isPosixPathSeparator$1(from.charCodeAt(fromStart + i))) { // We get here if `to` is the exact base path for `from`. // For example: from='/foo/bar/baz'; to='/foo/bar' lastCommonSep = i; } else if (i === 0) { // We get here if `to` is the root. // For example: from='/foo'; to='/' lastCommonSep = 0; } } break; } const fromCode = from.charCodeAt(fromStart + i); const toCode = to.charCodeAt(toStart + i); if (fromCode !== toCode) break; else if (isPosixPathSeparator$1(fromCode)) lastCommonSep = i; } let out = ""; // Generate the relative path based on the path difference between `to` // and `from` for(i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i){ if (i === fromEnd || isPosixPathSeparator$1(from.charCodeAt(i))) { if (out.length === 0) out += ".."; else out += "/.."; } } // Lastly, append the rest of the destination (`to`) path that comes after // the common path parts if (out.length > 0) return out + to.slice(toStart + lastCommonSep); else { toStart += lastCommonSep; if (isPosixPathSeparator$1(to.charCodeAt(toStart))) ++toStart; return to.slice(toStart); } } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. const WHITESPACE_ENCODINGS$1 = { "\u0009": "%09", "\u000A": "%0A", "\u000B": "%0B", "\u000C": "%0C", "\u000D": "%0D", "\u0020": "%20" }; function encodeWhitespace$1(string) { return string.replaceAll(/[\s]/g, (c)=>{ return WHITESPACE_ENCODINGS$1[c] ?? c; }); } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Converts a path string to a file URL. * * @example Usage * ```ts * import { toFileUrl } from "@std/path/posix/to-file-url"; * import { assertEquals } from "@std/assert"; * * assertEquals(toFileUrl("/home/foo"), new URL("file:///home/foo")); * assertEquals(toFileUrl("/home/foo bar"), new URL("file:///home/foo%20bar")); * ``` * * @param path The path to convert. * @returns The file URL. */ function toFileUrl$1(path) { if (!isAbsolute$1(path)) { throw new TypeError(`Path must be absolute: received "${path}"`); } const url = new URL("file:///"); url.pathname = encodeWhitespace$1(path.replace(/%/g, "%25").replace(/\\/g, "%5C")); return url; } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Converts a path to a namespaced path. This function returns the path as is on posix. * * @example Usage * ```ts * import { toNamespacedPath } from "@std/path/posix/to-namespaced-path"; * import { assertEquals } from "@std/assert"; * * assertEquals(toNamespacedPath("/home/foo"), "/home/foo"); * ``` * * @param path The path. * @returns The namespaced path. */ function toNamespacedPath$1(path) { // Non-op on posix systems return path; } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. function common$1$1(paths, sep) { const [first = "", ...remaining] = paths; const parts = first.split(sep); let endOfPrefix = parts.length; let append = ""; for (const path of remaining){ const compare = path.split(sep); if (compare.length <= endOfPrefix) { endOfPrefix = compare.length; append = ""; } for(let i = 0; i < endOfPrefix; i++){ if (compare[i] !== parts[i]) { endOfPrefix = i; append = i === 0 ? "" : sep; break; } } } return parts.slice(0, endOfPrefix).join(sep) + append; } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** Determines the common path from a set of paths for POSIX systems. * * @example Usage * ```ts * import { common } from "@std/path/posix/common"; * import { assertEquals } from "@std/assert"; * * const path = common([ * "./deno/std/path/mod.ts", * "./deno/std/fs/mod.ts", * ]); * assertEquals(path, "./deno/std/"); * ``` * * @param paths The paths to compare. * @returns The common path. */ function common$2(paths) { return common$1$1(paths, SEPARATOR$1); } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Options for {@linkcode globToRegExp}, {@linkcode joinGlobs}, * {@linkcode normalizeGlob} and {@linkcode expandGlob}. */ const REG_EXP_ESCAPE_CHARS$1 = [ "!", "$", "(", ")", "*", "+", ".", "=", "?", "[", "\\", "^", "{", "|" ]; const RANGE_ESCAPE_CHARS$1 = [ "-", "\\", "]" ]; function _globToRegExp$1(c, glob, { extended = true, globstar: globstarOption = true, // os = osType, caseInsensitive = false } = {}) { if (glob === "") { return /(?!)/; } // Remove trailing separators. let newLength = glob.length; for(; newLength > 1 && c.seps.includes(glob[newLength - 1]); newLength--); glob = glob.slice(0, newLength); let regExpString = ""; // Terminates correctly. Trust that `j` is incremented every iteration. for(let j = 0; j < glob.length;){ let segment = ""; const groupStack = []; let inRange = false; let inEscape = false; let endsWithSep = false; let i = j; // Terminates with `i` at the non-inclusive end of the current segment. for(; i < glob.length && !c.seps.includes(glob[i]); i++){ if (inEscape) { inEscape = false; const escapeChars = inRange ? RANGE_ESCAPE_CHARS$1 : REG_EXP_ESCAPE_CHARS$1; segment += escapeChars.includes(glob[i]) ? `\\${glob[i]}` : glob[i]; continue; } if (glob[i] === c.escapePrefix) { inEscape = true; continue; } if (glob[i] === "[") { if (!inRange) { inRange = true; segment += "["; if (glob[i + 1] === "!") { i++; segment += "^"; } else if (glob[i + 1] === "^") { i++; segment += "\\^"; } continue; } else if (glob[i + 1] === ":") { let k = i + 1; let value = ""; while(glob[k + 1] !== undefined && glob[k + 1] !== ":"){ value += glob[k + 1]; k++; } if (glob[k + 1] === ":" && glob[k + 2] === "]") { i = k + 2; if (value === "alnum") segment += "\\dA-Za-z"; else if (value === "alpha") segment += "A-Za-z"; else if (value === "ascii") segment += "\x00-\x7F"; else if (value === "blank") segment += "\t "; else if (value === "cntrl") segment += "\x00-\x1F\x7F"; else if (value === "digit") segment += "\\d"; else if (value === "graph") segment += "\x21-\x7E"; else if (value === "lower") segment += "a-z"; else if (value === "print") segment += "\x20-\x7E"; else if (value === "punct") { segment += "!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_‘{|}~"; } else if (value === "space") segment += "\\s\v"; else if (value === "upper") segment += "A-Z"; else if (value === "word") segment += "\\w"; else if (value === "xdigit") segment += "\\dA-Fa-f"; continue; } } } if (glob[i] === "]" && inRange) { inRange = false; segment += "]"; continue; } if (inRange) { segment += glob[i]; continue; } if (glob[i] === ")" && groupStack.length > 0 && groupStack[groupStack.length - 1] !== "BRACE") { segment += ")"; const type = groupStack.pop(); if (type === "!") { segment += c.wildcard; } else if (type !== "@") { segment += type; } continue; } if (glob[i] === "|" && groupStack.length > 0 && groupStack[groupStack.length - 1] !== "BRACE") { segment += "|"; continue; } if (glob[i] === "+" && extended && glob[i + 1] === "(") { i++; groupStack.push("+"); segment += "(?:"; continue; } if (glob[i] === "@" && extended && glob[i + 1] === "(") { i++; groupStack.push("@"); segment += "(?:"; continue; } if (glob[i] === "?") { if (extended && glob[i + 1] === "(") { i++; groupStack.push("?"); segment += "(?:"; } else { segment += "."; } continue; } if (glob[i] === "!" && extended && glob[i + 1] === "(") { i++; groupStack.push("!"); segment += "(?!"; continue; } if (glob[i] === "{") { groupStack.push("BRACE"); segment += "(?:"; continue; } if (glob[i] === "}" && groupStack[groupStack.length - 1] === "BRACE") { groupStack.pop(); segment += ")"; continue; } if (glob[i] === "," && groupStack[groupStack.length - 1] === "BRACE") { segment += "|"; continue; } if (glob[i] === "*") { if (extended && glob[i + 1] === "(") { i++; groupStack.push("*"); segment += "(?:"; } else { const prevChar = glob[i - 1]; let numStars = 1; while(glob[i + 1] === "*"){ i++; numStars++; } const nextChar = glob[i + 1]; if (globstarOption && numStars === 2 && [ ...c.seps, undefined ].includes(prevChar) && [ ...c.seps, undefined ].includes(nextChar)) { segment += c.globstar; endsWithSep = true; } else { segment += c.wildcard; } } continue; } segment += REG_EXP_ESCAPE_CHARS$1.includes(glob[i]) ? `\\${glob[i]}` : glob[i]; } // Check for unclosed groups or a dangling backslash. if (groupStack.length > 0 || inRange || inEscape) { // Parse failure. Take all characters from this segment literally. segment = ""; for (const c of glob.slice(j, i)){ segment += REG_EXP_ESCAPE_CHARS$1.includes(c) ? `\\${c}` : c; endsWithSep = false; } } regExpString += segment; if (!endsWithSep) { regExpString += i < glob.length ? c.sep : c.sepMaybe; endsWithSep = true; } // Terminates with `i` at the start of the next segment. while(c.seps.includes(glob[i]))i++; j = i; } regExpString = `^${regExpString}$`; return new RegExp(regExpString, caseInsensitive ? "i" : ""); } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. const constants$1 = { sep: "/+", sepMaybe: "/*", seps: [ "/" ], globstar: "(?:[^/]*(?:/|$)+)*", wildcard: "[^/]*", escapePrefix: "\\" }; /** Convert a glob string to a regular expression. * * Tries to match bash glob expansion as closely as possible. * * Basic glob syntax: * - `*` - Matches everything without leaving the path segment. * - `?` - Matches any single character. * - `{foo,bar}` - Matches `foo` or `bar`. * - `[abcd]` - Matches `a`, `b`, `c` or `d`. * - `[a-d]` - Matches `a`, `b`, `c` or `d`. * - `[!abcd]` - Matches any single character besides `a`, `b`, `c` or `d`. * - `[[:<class>:]]` - Matches any character belonging to `<class>`. * - `[[:alnum:]]` - Matches any digit or letter. * - `[[:digit:]abc]` - Matches any digit, `a`, `b` or `c`. * - See https://facelessuser.github.io/wcmatch/glob/#posix-character-classes * for a complete list of supported character classes. * - `\` - Escapes the next character for an `os` other than `"windows"`. * - \` - Escapes the next character for `os` set to `"windows"`. * - `/` - Path separator. * - `\` - Additional path separator only for `os` set to `"windows"`. * * Extended syntax: * - Requires `{ extended: true }`. * - `?(foo|bar)` - Matches 0 or 1 instance of `{foo,bar}`. * - `@(foo|bar)` - Matches 1 instance of `{foo,bar}`. They behave the same. * - `*(foo|bar)` - Matches _n_ instances of `{foo,bar}`. * - `+(foo|bar)` - Matches _n > 0_ instances of `{foo,bar}`. * - `!(foo|bar)` - Matches anything other than `{foo,bar}`. * - See https://www.linuxjournal.com/content/bash-extended-globbing. * * Globstar syntax: * - Requires `{ globstar: true }`. * - `**` - Matches any number of any path segments. * - Must comprise its entire path segment in the provided glob. * - See https://www.linuxjournal.com/content/globstar-new-bash-globbing-option. * * Note the following properties: * - The generated `RegExp` is anchored at both start and end. * - Repeating and trailing separators are tolerated. Trailing separators in the * provided glob have no meaning and are discarded. * - Absolute globs will only match absolute paths, etc. * - Empty globs will match nothing. * - Any special glob syntax must be contained to one path segment. For example, * `?(foo|bar/baz)` is invalid. The separator will take precedence and the * first segment ends with an unclosed group. * - If a path segment ends with unclosed groups or a dangling escape prefix, a * parse error has occurred. Every character for that segment is taken * literally in this event. * * Limitations: * - A negative group like `!(foo|bar)` will wrongly be converted to a negative * look-ahead followed by a wildcard. This means that `!(foo).js` will wrongly * fail to match `foobar.js`, even though `foobar` is not `foo`. Effectively, * `!(foo|bar)` is treated like `!(@(foo|bar)*)`. This will work correctly if * the group occurs not nested at the end of the segment. * * @example Usage * ```ts * import { globToRegExp } from "@std/path/posix/glob-to-regexp"; * import { assertEquals } from "@std/assert"; * * assertEquals(globToRegExp("*.js"), /^[^/]*\.js\/*$/); * ``` * * @param glob Glob string to convert. * @param options Conversion options. * @returns The regular expression equivalent to the glob. */ function globToRegExp$1(glob, options = {}) { return _globToRegExp$1(constants$1, glob, options); } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Test whether the given string is a glob. * * @example Usage * ```ts * import { isGlob } from "@std/path/is-glob"; * import { assert } from "@std/assert"; * * assert(!isGlob("foo/bar/../baz")); * assert(isGlob("foo/*ar/../baz")); * ``` * * @param str String to test. * @returns `true` if the given string is a glob, otherwise `false` */ function isGlob$1(str) { const chars = { "{": "}", "(": ")", "[": "]" }; const regex = /\\(.)|(^!|\*|\?|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; if (str === "") { return false; } let match; while(match = regex.exec(str)){ if (match[2]) return true; let idx = match.index + match[0].length; // if an open bracket/brace/paren is escaped, // set the index to the next closing character const open = match[1]; const close = open ? chars[open] : null; if (open && close) { const n = str.indexOf(close, idx); if (n !== -1) { idx = n + 1; } } str = str.slice(idx); } return false; } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Like normalize(), but doesn't collapse "**\/.." when `globstar` is true. * * @example Usage * ```ts * import { normalizeGlob } from "@std/path/posix/normalize-glob"; * import { assertEquals } from "@std/assert"; * * const path = normalizeGlob("foo/bar/../*", { globstar: true }); * assertEquals(path, "foo/*"); * ``` * * @param glob The glob to normalize. * @param options The options to use. * @returns The normalized path. */ function normalizeGlob$1(glob, options = {}) { const { globstar = false } = options; if (glob.match(/\0/g)) { throw new Error(`Glob contains invalid characters: "${glob}"`); } if (!globstar) { return normalize$2(glob); } const s = SEPARATOR_PATTERN$1.source; const badParentPattern = new RegExp(`(?<=(${s}|^)\\*\\*${s})\\.\\.(?=${s}|$)`, "g"); return normalize$2(glob.replace(badParentPattern, "\0")).replace(/\0/g, ".."); } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. /** * Like join(), but doesn't collapse "**\/.." when `globstar` is true. * * @example Usage * ```ts * import { joinGlobs } from "@std/path/posix/join-globs"; * import { assertEquals } from "@std/assert"; * * const path = joinGlobs(["foo", "bar", "**"], { globstar: true }); * assertEquals(path, "foo/bar/**"); * ``` * * @param globs The globs to join. * @param options The options to use. * @returns The joined path. */ function joinGlobs$1(globs, options = {}) { const { globstar = false } = options; if (!globstar || globs.length === 0) { return join$1(...globs); } let joined; for (const glob of globs){ const path = glob; if (path.length > 0) { if (!joined) joined = path; else joined += `${SEPARATOR$1}${path}`; } } if (!joined) return "."; return normalizeGlob$1(joined, { globstar }); } const posixPath = { __proto__: null, DELIMITER: DELIMITER$1, SEPARATOR: SEPARATOR$1, SEPARATOR_PATTERN: SEPARATOR_PATTERN$1, basename: basename$1, common: common$2, dirname: dirname$1, extname: extname$1, format: format$1, fromFileUrl: fromFileUrl$1, globToRegExp: globToRegExp$1, isAbsolute: isAbsolute$1, isGlob: isGlob$1, join: join$1, joinGlobs: joinGlobs$1, normalize: normalize$2, normalizeGlob: normalizeGlob$1, parse: parse$1, relative: relative$1, resolve: resolve$1, toFileUrl: toFileUrl$1, toNamespacedPath: toNamespacedPath$1 }; // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright the Browserify authors. MIT License. function assertPath(path) { if (typeof path !== "string") { throw new TypeError(`Path must be a string, received "${JSON.stringify(path)}"`); } } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. function stripSuffix(name, suffix) { if (suffix.length >= name.length) { return name; } const lenDiff = name.length - suffix.length; for(let i = suffix.length - 1; i >= 0; --i){ if (name.charCodeAt(lenDiff + i) !== suffix.charCodeAt(i)) { return name; } } return name.slice(0, -suffix.length); } function lastPathSegment(path, isSep, start = 0) { let matchedNonSeparator = false; let end = path.length; for(let i = path.length - 1; i >= start; --i){ if (isSep(path.charCodeAt(i))) { if (matchedNonSeparator) { start = i + 1; break; } } else if (!matchedNonSeparator) { matchedNonSeparator = true; end = i + 1; } } return path.slice(start, end); } function assertArgs$1(path, suffix) { assertPath(path); if (path.length === 0) return path; if (typeof suffix !== "string") { throw new TypeError(`Suffix must be a string, received "${JSON.stringify(suffix)}"`); } } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ // This module is browser compatible. // Alphabet chars. const CHAR_UPPERCASE_A = 65; /* A */ const CHAR_LOWERCASE_A = 97; /* a */ const CHAR_UPPERCASE_Z = 90; /* Z */ const CHAR_LOWERCASE_Z = 122; /* z */ // Non-alphabetic chars. const CHAR_DOT = 46; /* . */ const CHAR_FORWARD_SLASH = 47; /* / */ const CHAR_BACKWARD_SLASH = 92; /* \ */ const CHAR_COLON = 58; /* : */ const CHAR_QUESTION_MARK = 63; /* ? */ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ // This module is browser compatible. function stripTrailingSeparators(segment, isSep) { if (segment.length <= 1) { return segment; } let end = segment.length; for(let i = segment.length - 1; i > 0; i--){ if (isSep(segment.charCodeAt(i))) { end = i; } else { break; } } return segment.slice(0, end); } // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ // This modul