import type { FormatInputPathObject, ParsedPath } from "./_interface.ts";import { CHAR_DOT } from "./_constants.ts";
import { _format, assertPath, encodeWhitespace, isPosixPathSeparator, lastPathSegment, normalizeString, stripSuffix, stripTrailingSeparators,} from "./_util.ts";
export const sep = "/";export const delimiter = ":";
export function resolve(...pathSegments: string[]): string { let resolvedPath = ""; let resolvedAbsolute = false;
for (let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--) { let path: string;
if (i >= 0) path = pathSegments[i]; else { const { Deno } = globalThis as any; if (typeof Deno?.cwd !== "function") { throw new TypeError("Resolved a relative path without a CWD."); } path = Deno.cwd(); }
assertPath(path);
if (path.length === 0) { continue; }
resolvedPath = `${path}/${resolvedPath}`; resolvedAbsolute = isPosixPathSeparator(path.charCodeAt(0)); }
resolvedPath = normalizeString( resolvedPath, !resolvedAbsolute, "/", isPosixPathSeparator, );
if (resolvedAbsolute) { if (resolvedPath.length > 0) return `/${resolvedPath}`; else return "/"; } else if (resolvedPath.length > 0) return resolvedPath; else return ".";}
export function normalize(path: string): string { assertPath(path);
if (path.length === 0) return ".";
const isAbsolute = isPosixPathSeparator(path.charCodeAt(0)); const trailingSeparator = isPosixPathSeparator( path.charCodeAt(path.length - 1), );
path = normalizeString(path, !isAbsolute, "/", isPosixPathSeparator);
if (path.length === 0 && !isAbsolute) path = "."; if (path.length > 0 && trailingSeparator) path += "/";
if (isAbsolute) return `/${path}`; return path;}
export function isAbsolute(path: string): boolean { assertPath(path); return path.length > 0 && isPosixPathSeparator(path.charCodeAt(0));}
export function join(...paths: string[]): string { if (paths.length === 0) return "."; let joined: string | undefined; for (let i = 0, len = paths.length; i < len; ++i) { const path = paths[i]; assertPath(path); if (path.length > 0) { if (!joined) joined = path; else joined += `/${path}`; } } if (!joined) return "."; return normalize(joined);}
export function relative(from: string, to: string): string { assertPath(from); assertPath(to);
if (from === to) return "";
from = resolve(from); to = resolve(to);
if (from === to) return "";
let fromStart = 1; const fromEnd = from.length; for (; fromStart < fromEnd; ++fromStart) { if (!isPosixPathSeparator(from.charCodeAt(fromStart))) break; } const fromLen = fromEnd - fromStart;
let toStart = 1; const toEnd = to.length; for (; toStart < toEnd; ++toStart) { if (!isPosixPathSeparator(to.charCodeAt(toStart))) break; } const toLen = toEnd - toStart;
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))) { return to.slice(toStart + i + 1); } else if (i === 0) { return to.slice(toStart + i); } } else if (fromLen > length) { if (isPosixPathSeparator(from.charCodeAt(fromStart + i))) { lastCommonSep = i; } else if (i === 0) { 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 = ""; for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { if (i === fromEnd || isPosixPathSeparator(from.charCodeAt(i))) { if (out.length === 0) out += ".."; else out += "/.."; } }
if (out.length > 0) return out + to.slice(toStart + lastCommonSep); else { toStart += lastCommonSep; if (isPosixPathSeparator(to.charCodeAt(toStart))) ++toStart; return to.slice(toStart); }}
export function toNamespacedPath(path: string): string { return path;}
export function dirname(path: string): string { if (path.length === 0) return ".";
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; } }
if (end === -1) { return isPosixPathSeparator(path.charCodeAt(0)) ? "/" : "."; }
return stripTrailingSeparators( path.slice(0, end), isPosixPathSeparator, );}
export function basename(path: string, suffix = ""): string { assertPath(path);
if (path.length === 0) return path;
if (typeof suffix !== "string") { throw new TypeError( `Suffix must be a string. Received ${JSON.stringify(suffix)}`, ); }
const lastSegment = lastPathSegment(path, isPosixPathSeparator); const strippedSegment = stripTrailingSeparators( lastSegment, isPosixPathSeparator, ); return suffix ? stripSuffix(strippedSegment, suffix) : strippedSegment;}
export function extname(path: string): string { assertPath(path); let startDot = -1; let startPart = 0; let end = -1; let matchedSlash = true; let preDotState = 0; for (let i = path.length - 1; i >= 0; --i) { const code = path.charCodeAt(i); if (isPosixPathSeparator(code)) { if (!matchedSlash) { startPart = i + 1; break; } continue; } if (end === -1) { matchedSlash = false; end = i + 1; } if (code === CHAR_DOT) { if (startDot === -1) startDot = i; else if (preDotState !== 1) preDotState = 1; } else if (startDot !== -1) { preDotState = -1; } }
if ( startDot === -1 || end === -1 || preDotState === 0 || (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) ) { return ""; } return path.slice(startDot, end);}
export function format(pathObject: FormatInputPathObject): string { if (pathObject === null || typeof pathObject !== "object") { throw new TypeError( `The "pathObject" argument must be of type Object. Received type ${typeof pathObject}`, ); } return _format("/", pathObject);}
export function parse(path: string): ParsedPath { assertPath(path);
const ret: ParsedPath = { root: "", dir: "", base: "", ext: "", name: "" }; if (path.length === 0) return ret; const isAbsolute = isPosixPathSeparator(path.charCodeAt(0)); let start: number; 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;
let preDotState = 0;
for (; i >= start; --i) { const code = path.charCodeAt(i); if (isPosixPathSeparator(code)) { if (!matchedSlash) { startPart = i + 1; break; } continue; } if (end === -1) { matchedSlash = false; end = i + 1; } if (code === CHAR_DOT) { if (startDot === -1) startDot = i; else if (preDotState !== 1) preDotState = 1; } else if (startDot !== -1) { preDotState = -1; } }
if ( startDot === -1 || end === -1 || preDotState === 0 || (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); } } 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;}
export function fromFileUrl(url: string | URL): string { url = url instanceof URL ? url : new URL(url); if (url.protocol != "file:") { throw new TypeError("Must be a file URL."); } return decodeURIComponent( url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25"), );}
export function toFileUrl(path: string): URL { if (!isAbsolute(path)) { throw new TypeError("Must be an absolute path."); } const url = new URL("file:///"); url.pathname = encodeWhitespace( path.replace(/%/g, "%25").replace(/\\/g, "%5C"), ); return url;}