diff options
Diffstat (limited to 'ext/node/polyfills/path/_util.ts')
-rw-r--r-- | ext/node/polyfills/path/_util.ts | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/ext/node/polyfills/path/_util.ts b/ext/node/polyfills/path/_util.ts new file mode 100644 index 000000000..ccc12abc9 --- /dev/null +++ b/ext/node/polyfills/path/_util.ts @@ -0,0 +1,131 @@ +// Copyright the Browserify authors. MIT License. +// Ported from https://github.com/browserify/path-browserify/ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import type { FormatInputPathObject } from "internal:deno_node/polyfills/path/_interface.ts"; +import { + CHAR_BACKWARD_SLASH, + CHAR_DOT, + CHAR_FORWARD_SLASH, + CHAR_LOWERCASE_A, + CHAR_LOWERCASE_Z, + CHAR_UPPERCASE_A, + CHAR_UPPERCASE_Z, +} from "internal:deno_node/polyfills/path/_constants.ts"; +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; + +export function assertPath(path: string) { + if (typeof path !== "string") { + throw new ERR_INVALID_ARG_TYPE("path", ["string"], path); + } +} + +export function isPosixPathSeparator(code: number): boolean { + return code === CHAR_FORWARD_SLASH; +} + +export function isPathSeparator(code: number): boolean { + return isPosixPathSeparator(code) || code === CHAR_BACKWARD_SLASH; +} + +export function isWindowsDeviceRoot(code: number): boolean { + return ( + (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z) || + (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) + ); +} + +// Resolves . and .. elements in a path with directory names +export function normalizeString( + path: string, + allowAboveRoot: boolean, + separator: string, + isPathSeparator: (code: number) => boolean, +): string { + let res = ""; + let lastSegmentLength = 0; + let lastSlash = -1; + let dots = 0; + let code: number | undefined; + for (let i = 0, len = path.length; i <= len; ++i) { + if (i < len) code = path.charCodeAt(i); + else if (isPathSeparator(code!)) break; + else code = CHAR_FORWARD_SLASH; + + if (isPathSeparator(code!)) { + if (lastSlash === i - 1 || dots === 1) { + // NOOP + } 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; +} + +export function _format( + sep: string, + pathObject: FormatInputPathObject, +): string { + const dir: string | undefined = pathObject.dir || pathObject.root; + const base: string = pathObject.base || + (pathObject.name || "") + (pathObject.ext || ""); + if (!dir) return base; + if (dir === pathObject.root) return dir + base; + return dir + sep + base; +} + +const WHITESPACE_ENCODINGS: Record<string, string> = { + "\u0009": "%09", + "\u000A": "%0A", + "\u000B": "%0B", + "\u000C": "%0C", + "\u000D": "%0D", + "\u0020": "%20", +}; + +export function encodeWhitespace(string: string): string { + return string.replaceAll(/[\s]/g, (c) => { + return WHITESPACE_ENCODINGS[c] ?? c; + }); +} |