| // @ts-nocheck |
| 'use strict'; |
| |
| // Copyright 2018-2025 the Deno authors. 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-2025 the Deno authors. 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-2025 the Deno authors. MIT license. |
| // This module is browser compatible. |
| function assertArg$3(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-2025 the Deno authors. 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(url) { |
| url = assertArg$3(url); |
| return decodeURIComponent(url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25")); |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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-2025 the Deno authors. 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 = 46; /* . */ |
| const CHAR_FORWARD_SLASH = 47; /* / */ |
| |
| // Copyright 2018-2025 the Deno authors. MIT license. |
| // Copyright the Browserify authors. MIT License. |
| // Ported from https://github.com/browserify/path-browserify/ |
| // This module is browser compatible. |
| function isPosixPathSeparator(code) { |
| return code === CHAR_FORWARD_SLASH; |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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"); |
| * assertEquals(basename(new URL("file:///home/user/Documents/image.png")), "image.png"); |
| * assertEquals(basename(new URL("file:///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"); |
| * ``` |
| * |
| * @param path The path to extract the name from. |
| * @param suffix The suffix to remove from extracted name. |
| * @returns The extracted name. |
| */ function basename(path, suffix = "") { |
| if (path instanceof URL) { |
| path = fromFileUrl(path); |
| } |
| assertArgs$1(path, suffix); |
| const lastSegment = lastPathSegment(path, isPosixPathSeparator); |
| const strippedSegment = stripTrailingSeparators(lastSegment, isPosixPathSeparator); |
| return suffix ? stripSuffix(strippedSegment, suffix) : strippedSegment; |
| } |
| |
| // Copyright 2018-2025 the Deno authors. MIT license. |
| // This module is browser compatible. |
| /** |
| * The character used to separate entries in the PATH environment variable. |
| */ const DELIMITER = ":"; |
| /** |
| * The character used to separate components of a file path. |
| */ const SEPARATOR = "/"; |
| /** |
| * A regular expression that matches one or more path separators. |
| */ const SEPARATOR_PATTERN = /\/+/; |
| |
| // Copyright 2018-2025 the Deno authors. MIT license. |
| // This module is browser compatible. |
| function assertArg$2(path) { |
| assertPath(path); |
| if (path.length === 0) return "."; |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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"); |
| * assertEquals(dirname(new URL("file:///home/user/Documents/image.png")), "/home/user/Documents"); |
| * ``` |
| * |
| * @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"); |
| * ``` |
| * |
| * @param path The path to get the directory from. |
| * @returns The directory path. |
| */ function dirname(path) { |
| if (path instanceof URL) { |
| path = fromFileUrl(path); |
| } |
| assertArg$2(path); |
| let end = -1; |
| let matchedNonSeparator = false; |
| for(let i = path.length - 1; i >= 1; --i){ |
| if (isPosixPathSeparator(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(path.charCodeAt(0)) ? "/" : "."; |
| } |
| return stripTrailingSeparators(path.slice(0, end), isPosixPathSeparator); |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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"); |
| * assertEquals(extname(new URL("file:///home/user/Documents/file.ts")), ".ts"); |
| * assertEquals(extname(new URL("file:///home/user/Documents/file.ts?a=b")), ".ts"); |
| * assertEquals(extname(new URL("file:///home/user/Documents/file.ts#header")), ".ts"); |
| * ``` |
| * |
| * @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"); |
| * ``` |
| * |
| * @param path The path to get the extension from. |
| * @returns The extension (ex. for `file.ts` returns `.ts`). |
| */ function extname(path) { |
| if (path instanceof URL) { |
| path = fromFileUrl(path); |
| } |
| assertPath(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(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) { |
| // 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-2025 the Deno authors. MIT license. |
| // This module is browser compatible. |
| function _format(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$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-2025 the Deno authors. 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(pathObject) { |
| assertArg$1(pathObject); |
| return _format("/", pathObject); |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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(path) { |
| assertPath(path); |
| return path.length > 0 && isPosixPathSeparator(path.charCodeAt(0)); |
| } |
| |
| // Copyright 2018-2025 the Deno authors. MIT license. |
| // This module is browser compatible. |
| function assertArg(path) { |
| assertPath(path); |
| if (path.length === 0) return "."; |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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(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; |
| 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 || res.charCodeAt(res.length - 2) !== CHAR_DOT) { |
| 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 && dots !== -1) { |
| ++dots; |
| } else { |
| dots = -1; |
| } |
| } |
| return res; |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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"; |
| * |
| * assertEquals(normalize("/foo/bar//baz/asdf/quux/.."), "/foo/bar/baz/asdf"); |
| * assertEquals(normalize(new URL("file:///foo/bar//baz/asdf/quux/..")), "/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"); |
| * ``` |
| * |
| * @param path The path to normalize. |
| * @returns The normalized path. |
| */ function normalize(path) { |
| if (path instanceof URL) { |
| path = fromFileUrl(path); |
| } |
| assertArg(path); |
| const isAbsolute = isPosixPathSeparator(path.charCodeAt(0)); |
| const trailingSeparator = isPosixPathSeparator(path.charCodeAt(path.length - 1)); |
| // Normalize the path |
| path = normalizeString(path, !isAbsolute, "/", isPosixPathSeparator); |
| if (path.length === 0 && !isAbsolute) path = "."; |
| if (path.length > 0 && trailingSeparator) path += "/"; |
| if (isAbsolute) return `/${path}`; |
| return path; |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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"; |
| * |
| * assertEquals(join("/foo", "bar", "baz/asdf", "quux", ".."), "/foo/bar/baz/asdf"); |
| * assertEquals(join(new URL("file:///foo"), "bar", "baz/asdf", "quux", ".."), "/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"); |
| * ``` |
| * |
| * @param path The path to join. This can be string or file URL. |
| * @param paths The paths to join. |
| * @returns The joined path. |
| */ function join(path, ...paths) { |
| if (path === undefined) return "."; |
| if (path instanceof URL) { |
| path = fromFileUrl(path); |
| } |
| paths = path ? [ |
| path, |
| ...paths |
| ] : paths; |
| paths.forEach((path)=>assertPath(path)); |
| const joined = paths.filter((path)=>path.length > 0).join("/"); |
| return joined === "" ? "." : normalize(joined); |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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(path) { |
| assertPath(path); |
| const ret = { |
| root: "", |
| dir: "", |
| base: "", |
| ext: "", |
| name: "" |
| }; |
| if (path.length === 0) return ret; |
| const isAbsolute = isPosixPathSeparator(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(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) { |
| // 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(path.slice(0, startPart - 1), isPosixPathSeparator); |
| } else if (isAbsolute) ret.dir = "/"; |
| return ret; |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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(...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(path); |
| // Skip empty entries |
| if (path.length === 0) { |
| continue; |
| } |
| resolvedPath = `${path}/${resolvedPath}`; |
| resolvedAbsolute = isPosixPathSeparator(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(resolvedPath, !resolvedAbsolute, "/", isPosixPathSeparator); |
| if (resolvedAbsolute) { |
| if (resolvedPath.length > 0) return `/${resolvedPath}`; |
| else return "/"; |
| } else if (resolvedPath.length > 0) return resolvedPath; |
| else return "."; |
| } |
| |
| // Copyright 2018-2025 the Deno authors. MIT license. |
| // This module is browser compatible. |
| function assertArgs(from, to) { |
| assertPath(from); |
| assertPath(to); |
| if (from === to) return ""; |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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(from, to) { |
| assertArgs(from, to); |
| from = resolve(from); |
| to = resolve(to); |
| if (from === to) return ""; |
| // Trim any leading backslashes |
| let fromStart = 1; |
| const fromEnd = from.length; |
| for(; fromStart < fromEnd; ++fromStart){ |
| if (!isPosixPathSeparator(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(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(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(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(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(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(to.charCodeAt(toStart))) ++toStart; |
| return to.slice(toStart); |
| } |
| } |
| |
| // Copyright 2018-2025 the Deno authors. MIT license. |
| // This module is browser compatible. |
| const WHITESPACE_ENCODINGS = { |
| "\u0009": "%09", |
| "\u000A": "%0A", |
| "\u000B": "%0B", |
| "\u000C": "%0C", |
| "\u000D": "%0D", |
| "\u0020": "%20" |
| }; |
| function encodeWhitespace(string) { |
| return string.replaceAll(/[\s]/g, (c)=>{ |
| return WHITESPACE_ENCODINGS[c] ?? c; |
| }); |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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(path) { |
| if (!isAbsolute(path)) { |
| throw new TypeError(`Path must be absolute: received "${path}"`); |
| } |
| const url = new URL("file:///"); |
| url.pathname = encodeWhitespace(path.replace(/%/g, "%25").replace(/\\/g, "%5C")); |
| return url; |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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(path) { |
| // Non-op on posix systems |
| return path; |
| } |
| |
| // Copyright 2018-2025 the Deno authors. MIT license. |
| // This module is browser compatible. |
| function common$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-2025 the Deno authors. 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(paths) { |
| return common$1(paths, SEPARATOR); |
| } |
| |
| // Copyright 2018-2025 the Deno authors. MIT license. |
| // This module is browser compatible. |
| /** |
| * Options for {@linkcode globToRegExp}, {@linkcode joinGlobs}, |
| * {@linkcode normalizeGlob} and {@linkcode expandGlob}. |
| */ const REG_EXP_ESCAPE_CHARS = [ |
| "!", |
| "$", |
| "(", |
| ")", |
| "*", |
| "+", |
| ".", |
| "=", |
| "?", |
| "[", |
| "\\", |
| "^", |
| "{", |
| "|" |
| ]; |
| const RANGE_ESCAPE_CHARS = [ |
| "-", |
| "\\", |
| "]" |
| ]; |
| function _globToRegExp(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 : REG_EXP_ESCAPE_CHARS; |
| 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.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.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-2025 the Deno authors. MIT license. |
| // This module is browser compatible. |
| const constants = { |
| 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(glob, options = {}) { |
| return _globToRegExp(constants, glob, options); |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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(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-2025 the Deno authors. 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(glob, options = {}) { |
| const { globstar = false } = options; |
| if (glob.match(/\0/g)) { |
| throw new Error(`Glob contains invalid characters: "${glob}"`); |
| } |
| if (!globstar) { |
| return normalize(glob); |
| } |
| const s = SEPARATOR_PATTERN.source; |
| const badParentPattern = new RegExp(`(?<=(${s}|^)\\*\\*${s})\\.\\.(?=${s}|$)`, "g"); |
| return normalize(glob.replace(badParentPattern, "\0")).replace(/\0/g, ".."); |
| } |
| |
| // Copyright 2018-2025 the Deno authors. 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(globs, options = {}) { |
| const { globstar = false } = options; |
| if (!globstar || globs.length === 0) { |
| return join(...globs); |
| } |
| let joined; |
| for (const glob of globs){ |
| const path = glob; |
| if (path.length > 0) { |
| if (!joined) joined = path; |
| else joined += `${SEPARATOR}${path}`; |
| } |
| } |
| if (!joined) return "."; |
| return normalizeGlob(joined, { |
| globstar |
| }); |
| } |
| |
| exports.DELIMITER = DELIMITER; |
| exports.SEPARATOR = SEPARATOR; |
| exports.SEPARATOR_PATTERN = SEPARATOR_PATTERN; |
| exports.basename = basename; |
| exports.common = common; |
| exports.dirname = dirname; |
| exports.extname = extname; |
| exports.format = format; |
| exports.fromFileUrl = fromFileUrl; |
| exports.globToRegExp = globToRegExp; |
| exports.isAbsolute = isAbsolute; |
| exports.isGlob = isGlob; |
| exports.join = join; |
| exports.joinGlobs = joinGlobs; |
| exports.normalize = normalize; |
| exports.normalizeGlob = normalizeGlob; |
| exports.parse = parse; |
| exports.relative = relative; |
| exports.resolve = resolve; |
| exports.toFileUrl = toFileUrl; |
| exports.toNamespacedPath = toNamespacedPath; |