diff options
Diffstat (limited to 'std/path')
-rw-r--r-- | std/path/README.md | 22 | ||||
-rw-r--r-- | std/path/_constants.ts | 48 | ||||
-rw-r--r-- | std/path/_interface.ts | 30 | ||||
-rw-r--r-- | std/path/_util.ts | 117 | ||||
-rw-r--r-- | std/path/basename_test.ts | 74 | ||||
-rw-r--r-- | std/path/common.ts | 39 | ||||
-rw-r--r-- | std/path/common_test.ts | 44 | ||||
-rw-r--r-- | std/path/dirname_test.ts | 60 | ||||
-rw-r--r-- | std/path/extname_test.ts | 88 | ||||
-rw-r--r-- | std/path/from_file_url_test.ts | 51 | ||||
-rw-r--r-- | std/path/glob.ts | 387 | ||||
-rw-r--r-- | std/path/glob_test.ts | 638 | ||||
-rw-r--r-- | std/path/isabsolute_test.ts | 32 | ||||
-rw-r--r-- | std/path/join_test.ts | 128 | ||||
-rw-r--r-- | std/path/mod.ts | 34 | ||||
-rw-r--r-- | std/path/parse_format_test.ts | 186 | ||||
-rw-r--r-- | std/path/posix.ts | 504 | ||||
-rw-r--r-- | std/path/relative_test.ts | 65 | ||||
-rw-r--r-- | std/path/resolve_test.ts | 49 | ||||
-rw-r--r-- | std/path/separator.ts | 7 | ||||
-rw-r--r-- | std/path/test.ts | 2 | ||||
-rw-r--r-- | std/path/to_file_url_test.ts | 49 | ||||
-rw-r--r-- | std/path/win32.ts | 1001 | ||||
-rw-r--r-- | std/path/zero_length_strings_test.ts | 46 |
24 files changed, 0 insertions, 3701 deletions
diff --git a/std/path/README.md b/std/path/README.md deleted file mode 100644 index c1debfc31..000000000 --- a/std/path/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Deno Path Manipulation Libraries - -Usage: - -```ts -import * as path from "https://deno.land/std@$STD_VERSION/path/mod.ts"; -``` - -### globToRegExp - -Generate a regex based on glob pattern and options This was meant to be using -the `fs.walk` function but can be used anywhere else. - -```ts -import { globToRegExp } from "https://deno.land/std@$STD_VERSION/path/glob.ts"; - -globToRegExp("foo/**/*.json", { - flags: "g", - extended: true, - globstar: true, -}); // returns the regex to find all .json files in the folder foo. -``` diff --git a/std/path/_constants.ts b/std/path/_constants.ts deleted file mode 100644 index 88374ae05..000000000 --- a/std/path/_constants.ts +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -// This module is browser compatible. - -// Alphabet chars. -export const CHAR_UPPERCASE_A = 65; /* A */ -export const CHAR_LOWERCASE_A = 97; /* a */ -export const CHAR_UPPERCASE_Z = 90; /* Z */ -export const CHAR_LOWERCASE_Z = 122; /* z */ - -// Non-alphabetic chars. -export const CHAR_DOT = 46; /* . */ -export const CHAR_FORWARD_SLASH = 47; /* / */ -export const CHAR_BACKWARD_SLASH = 92; /* \ */ -export const CHAR_VERTICAL_LINE = 124; /* | */ -export const CHAR_COLON = 58; /* : */ -export const CHAR_QUESTION_MARK = 63; /* ? */ -export const CHAR_UNDERSCORE = 95; /* _ */ -export const CHAR_LINE_FEED = 10; /* \n */ -export const CHAR_CARRIAGE_RETURN = 13; /* \r */ -export const CHAR_TAB = 9; /* \t */ -export const CHAR_FORM_FEED = 12; /* \f */ -export const CHAR_EXCLAMATION_MARK = 33; /* ! */ -export const CHAR_HASH = 35; /* # */ -export const CHAR_SPACE = 32; /* */ -export const CHAR_NO_BREAK_SPACE = 160; /* \u00A0 */ -export const CHAR_ZERO_WIDTH_NOBREAK_SPACE = 65279; /* \uFEFF */ -export const CHAR_LEFT_SQUARE_BRACKET = 91; /* [ */ -export const CHAR_RIGHT_SQUARE_BRACKET = 93; /* ] */ -export const CHAR_LEFT_ANGLE_BRACKET = 60; /* < */ -export const CHAR_RIGHT_ANGLE_BRACKET = 62; /* > */ -export const CHAR_LEFT_CURLY_BRACKET = 123; /* { */ -export const CHAR_RIGHT_CURLY_BRACKET = 125; /* } */ -export const CHAR_HYPHEN_MINUS = 45; /* - */ -export const CHAR_PLUS = 43; /* + */ -export const CHAR_DOUBLE_QUOTE = 34; /* " */ -export const CHAR_SINGLE_QUOTE = 39; /* ' */ -export const CHAR_PERCENT = 37; /* % */ -export const CHAR_SEMICOLON = 59; /* ; */ -export const CHAR_CIRCUMFLEX_ACCENT = 94; /* ^ */ -export const CHAR_GRAVE_ACCENT = 96; /* ` */ -export const CHAR_AT = 64; /* @ */ -export const CHAR_AMPERSAND = 38; /* & */ -export const CHAR_EQUAL = 61; /* = */ - -// Digits -export const CHAR_0 = 48; /* 0 */ -export const CHAR_9 = 57; /* 9 */ diff --git a/std/path/_interface.ts b/std/path/_interface.ts deleted file mode 100644 index d5015cbeb..000000000 --- a/std/path/_interface.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. - -/** - * A parsed path object generated by path.parse() or consumed by path.format(). - */ -export interface ParsedPath { - /** - * The root of the path such as '/' or 'c:\' - */ - root: string; - /** - * The full directory path such as '/home/user/dir' or 'c:\path\dir' - */ - dir: string; - /** - * The file name including extension (if any) such as 'index.html' - */ - base: string; - /** - * The file extension (if any) such as '.html' - */ - ext: string; - /** - * The file name without extension (if any) such as 'index' - */ - name: string; -} - -export type FormatInputPathObject = Partial<ParsedPath>; diff --git a/std/path/_util.ts b/std/path/_util.ts deleted file mode 100644 index 046c44337..000000000 --- a/std/path/_util.ts +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -// This module is browser compatible. - -import type { FormatInputPathObject } from "./_interface.ts"; -import { - CHAR_BACKWARD_SLASH, - CHAR_DOT, - CHAR_FORWARD_SLASH, - CHAR_LOWERCASE_A, - CHAR_LOWERCASE_Z, - CHAR_UPPERCASE_A, - CHAR_UPPERCASE_Z, -} from "./_constants.ts"; - -export function assertPath(path: string): void { - if (typeof path !== "string") { - throw new TypeError( - `Path must be a string. Received ${JSON.stringify(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; -} diff --git a/std/path/basename_test.ts b/std/path/basename_test.ts deleted file mode 100644 index 9e2265db9..000000000 --- a/std/path/basename_test.ts +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -Deno.test("basename", function () { - assertEquals(path.basename(".js", ".js"), ""); - assertEquals(path.basename(""), ""); - assertEquals(path.basename("/dir/basename.ext"), "basename.ext"); - assertEquals(path.basename("/basename.ext"), "basename.ext"); - assertEquals(path.basename("basename.ext"), "basename.ext"); - assertEquals(path.basename("basename.ext/"), "basename.ext"); - assertEquals(path.basename("basename.ext//"), "basename.ext"); - assertEquals(path.basename("aaa/bbb", "/bbb"), "bbb"); - assertEquals(path.basename("aaa/bbb", "a/bbb"), "bbb"); - assertEquals(path.basename("aaa/bbb", "bbb"), "bbb"); - assertEquals(path.basename("aaa/bbb//", "bbb"), "bbb"); - assertEquals(path.basename("aaa/bbb", "bb"), "b"); - assertEquals(path.basename("aaa/bbb", "b"), "bb"); - assertEquals(path.basename("/aaa/bbb", "/bbb"), "bbb"); - assertEquals(path.basename("/aaa/bbb", "a/bbb"), "bbb"); - assertEquals(path.basename("/aaa/bbb", "bbb"), "bbb"); - assertEquals(path.basename("/aaa/bbb//", "bbb"), "bbb"); - assertEquals(path.basename("/aaa/bbb", "bb"), "b"); - assertEquals(path.basename("/aaa/bbb", "b"), "bb"); - assertEquals(path.basename("/aaa/bbb"), "bbb"); - assertEquals(path.basename("/aaa/"), "aaa"); - assertEquals(path.basename("/aaa/b"), "b"); - assertEquals(path.basename("/a/b"), "b"); - assertEquals(path.basename("//a"), "a"); - - // On unix a backslash is just treated as any other character. - assertEquals( - path.posix.basename("\\dir\\basename.ext"), - "\\dir\\basename.ext", - ); - assertEquals(path.posix.basename("\\basename.ext"), "\\basename.ext"); - assertEquals(path.posix.basename("basename.ext"), "basename.ext"); - assertEquals(path.posix.basename("basename.ext\\"), "basename.ext\\"); - assertEquals(path.posix.basename("basename.ext\\\\"), "basename.ext\\\\"); - assertEquals(path.posix.basename("foo"), "foo"); - - // POSIX filenames may include control characters - const controlCharFilename = "Icon" + String.fromCharCode(13); - assertEquals( - path.posix.basename("/a/b/" + controlCharFilename), - controlCharFilename, - ); -}); - -Deno.test("basenameWin32", function () { - assertEquals(path.win32.basename("\\dir\\basename.ext"), "basename.ext"); - assertEquals(path.win32.basename("\\basename.ext"), "basename.ext"); - assertEquals(path.win32.basename("basename.ext"), "basename.ext"); - assertEquals(path.win32.basename("basename.ext\\"), "basename.ext"); - assertEquals(path.win32.basename("basename.ext\\\\"), "basename.ext"); - assertEquals(path.win32.basename("foo"), "foo"); - assertEquals(path.win32.basename("aaa\\bbb", "\\bbb"), "bbb"); - assertEquals(path.win32.basename("aaa\\bbb", "a\\bbb"), "bbb"); - assertEquals(path.win32.basename("aaa\\bbb", "bbb"), "bbb"); - assertEquals(path.win32.basename("aaa\\bbb\\\\\\\\", "bbb"), "bbb"); - assertEquals(path.win32.basename("aaa\\bbb", "bb"), "b"); - assertEquals(path.win32.basename("aaa\\bbb", "b"), "bb"); - assertEquals(path.win32.basename("C:"), ""); - assertEquals(path.win32.basename("C:."), "."); - assertEquals(path.win32.basename("C:\\"), ""); - assertEquals(path.win32.basename("C:\\dir\\base.ext"), "base.ext"); - assertEquals(path.win32.basename("C:\\basename.ext"), "basename.ext"); - assertEquals(path.win32.basename("C:basename.ext"), "basename.ext"); - assertEquals(path.win32.basename("C:basename.ext\\"), "basename.ext"); - assertEquals(path.win32.basename("C:basename.ext\\\\"), "basename.ext"); - assertEquals(path.win32.basename("C:foo"), "foo"); - assertEquals(path.win32.basename("file:stream"), "file:stream"); -}); diff --git a/std/path/common.ts b/std/path/common.ts deleted file mode 100644 index 390c04f1d..000000000 --- a/std/path/common.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. - -import { SEP } from "./separator.ts"; - -/** Determines the common path from a set of paths, using an optional separator, - * which defaults to the OS default separator. - * - * import { common } from "https://deno.land/std/path/mod.ts"; - * const p = common([ - * "./deno/std/path/mod.ts", - * "./deno/std/fs/mod.ts", - * ]); - * console.log(p); // "./deno/std/" - * - */ -export function common(paths: string[], sep = SEP): string { - const [first = "", ...remaining] = paths; - if (first === "" || remaining.length === 0) { - return first.substring(0, first.lastIndexOf(sep) + 1); - } - const parts = first.split(sep); - - let endOfPrefix = parts.length; - for (const path of remaining) { - const compare = path.split(sep); - for (let i = 0; i < endOfPrefix; i++) { - if (compare[i] !== parts[i]) { - endOfPrefix = i; - } - } - - if (endOfPrefix === 0) { - return ""; - } - } - const prefix = parts.slice(0, endOfPrefix).join(sep); - return prefix.endsWith(sep) ? prefix : `${prefix}${sep}`; -} diff --git a/std/path/common_test.ts b/std/path/common_test.ts deleted file mode 100644 index b34b54741..000000000 --- a/std/path/common_test.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { common } from "./mod.ts"; - -Deno.test({ - name: "path - common - basic usage", - fn() { - const actual = common( - [ - "file://deno/cli/js/deno.ts", - "file://deno/std/path/mod.ts", - "file://deno/cli/js/main.ts", - ], - "/", - ); - assertEquals(actual, "file://deno/"); - }, -}); - -Deno.test({ - name: "path - common - no shared", - fn() { - const actual = common( - ["file://deno/cli/js/deno.ts", "https://deno.land/std/path/mod.ts"], - "/", - ); - assertEquals(actual, ""); - }, -}); - -Deno.test({ - name: "path - common - windows sep", - fn() { - const actual = common( - [ - "c:\\deno\\cli\\js\\deno.ts", - "c:\\deno\\std\\path\\mod.ts", - "c:\\deno\\cli\\js\\main.ts", - ], - "\\", - ); - assertEquals(actual, "c:\\deno\\"); - }, -}); diff --git a/std/path/dirname_test.ts b/std/path/dirname_test.ts deleted file mode 100644 index 210d3a9ec..000000000 --- a/std/path/dirname_test.ts +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -Deno.test("dirname", function () { - assertEquals(path.posix.dirname("/a/b/"), "/a"); - assertEquals(path.posix.dirname("/a/b"), "/a"); - assertEquals(path.posix.dirname("/a"), "/"); - assertEquals(path.posix.dirname(""), "."); - assertEquals(path.posix.dirname("/"), "/"); - assertEquals(path.posix.dirname("////"), "/"); - assertEquals(path.posix.dirname("//a"), "//"); - assertEquals(path.posix.dirname("foo"), "."); -}); - -Deno.test("dirnameWin32", function () { - assertEquals(path.win32.dirname("c:\\"), "c:\\"); - assertEquals(path.win32.dirname("c:\\foo"), "c:\\"); - assertEquals(path.win32.dirname("c:\\foo\\"), "c:\\"); - assertEquals(path.win32.dirname("c:\\foo\\bar"), "c:\\foo"); - assertEquals(path.win32.dirname("c:\\foo\\bar\\"), "c:\\foo"); - assertEquals(path.win32.dirname("c:\\foo\\bar\\baz"), "c:\\foo\\bar"); - assertEquals(path.win32.dirname("\\"), "\\"); - assertEquals(path.win32.dirname("\\foo"), "\\"); - assertEquals(path.win32.dirname("\\foo\\"), "\\"); - assertEquals(path.win32.dirname("\\foo\\bar"), "\\foo"); - assertEquals(path.win32.dirname("\\foo\\bar\\"), "\\foo"); - assertEquals(path.win32.dirname("\\foo\\bar\\baz"), "\\foo\\bar"); - assertEquals(path.win32.dirname("c:"), "c:"); - assertEquals(path.win32.dirname("c:foo"), "c:"); - assertEquals(path.win32.dirname("c:foo\\"), "c:"); - assertEquals(path.win32.dirname("c:foo\\bar"), "c:foo"); - assertEquals(path.win32.dirname("c:foo\\bar\\"), "c:foo"); - assertEquals(path.win32.dirname("c:foo\\bar\\baz"), "c:foo\\bar"); - assertEquals(path.win32.dirname("file:stream"), "."); - assertEquals(path.win32.dirname("dir\\file:stream"), "dir"); - assertEquals(path.win32.dirname("\\\\unc\\share"), "\\\\unc\\share"); - assertEquals(path.win32.dirname("\\\\unc\\share\\foo"), "\\\\unc\\share\\"); - assertEquals(path.win32.dirname("\\\\unc\\share\\foo\\"), "\\\\unc\\share\\"); - assertEquals( - path.win32.dirname("\\\\unc\\share\\foo\\bar"), - "\\\\unc\\share\\foo", - ); - assertEquals( - path.win32.dirname("\\\\unc\\share\\foo\\bar\\"), - "\\\\unc\\share\\foo", - ); - assertEquals( - path.win32.dirname("\\\\unc\\share\\foo\\bar\\baz"), - "\\\\unc\\share\\foo\\bar", - ); - assertEquals(path.win32.dirname("/a/b/"), "/a"); - assertEquals(path.win32.dirname("/a/b"), "/a"); - assertEquals(path.win32.dirname("/a"), "/"); - assertEquals(path.win32.dirname(""), "."); - assertEquals(path.win32.dirname("/"), "/"); - assertEquals(path.win32.dirname("////"), "/"); - assertEquals(path.win32.dirname("foo"), "."); -}); diff --git a/std/path/extname_test.ts b/std/path/extname_test.ts deleted file mode 100644 index 7e1abf4bc..000000000 --- a/std/path/extname_test.ts +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -const slashRE = /\//g; - -const pairs = [ - ["", ""], - ["/path/to/file", ""], - ["/path/to/file.ext", ".ext"], - ["/path.to/file.ext", ".ext"], - ["/path.to/file", ""], - ["/path.to/.file", ""], - ["/path.to/.file.ext", ".ext"], - ["/path/to/f.ext", ".ext"], - ["/path/to/..ext", ".ext"], - ["/path/to/..", ""], - ["file", ""], - ["file.ext", ".ext"], - [".file", ""], - [".file.ext", ".ext"], - ["/file", ""], - ["/file.ext", ".ext"], - ["/.file", ""], - ["/.file.ext", ".ext"], - [".path/file.ext", ".ext"], - ["file.ext.ext", ".ext"], - ["file.", "."], - [".", ""], - ["./", ""], - [".file.ext", ".ext"], - [".file", ""], - [".file.", "."], - [".file..", "."], - ["..", ""], - ["../", ""], - ["..file.ext", ".ext"], - ["..file", ".file"], - ["..file.", "."], - ["..file..", "."], - ["...", "."], - ["...ext", ".ext"], - ["....", "."], - ["file.ext/", ".ext"], - ["file.ext//", ".ext"], - ["file/", ""], - ["file//", ""], - ["file./", "."], - ["file.//", "."], -]; - -Deno.test("extname", function () { - pairs.forEach(function (p) { - const input = p[0]; - const expected = p[1]; - assertEquals(expected, path.posix.extname(input)); - }); - - // On *nix, backslash is a valid name component like any other character. - assertEquals(path.posix.extname(".\\"), ""); - assertEquals(path.posix.extname("..\\"), ".\\"); - assertEquals(path.posix.extname("file.ext\\"), ".ext\\"); - assertEquals(path.posix.extname("file.ext\\\\"), ".ext\\\\"); - assertEquals(path.posix.extname("file\\"), ""); - assertEquals(path.posix.extname("file\\\\"), ""); - assertEquals(path.posix.extname("file.\\"), ".\\"); - assertEquals(path.posix.extname("file.\\\\"), ".\\\\"); -}); - -Deno.test("extnameWin32", function () { - pairs.forEach(function (p) { - const input = p[0].replace(slashRE, "\\"); - const expected = p[1]; - assertEquals(expected, path.win32.extname(input)); - assertEquals(expected, path.win32.extname("C:" + input)); - }); - - // On Windows, backslash is a path separator. - assertEquals(path.win32.extname(".\\"), ""); - assertEquals(path.win32.extname("..\\"), ""); - assertEquals(path.win32.extname("file.ext\\"), ".ext"); - assertEquals(path.win32.extname("file.ext\\\\"), ".ext"); - assertEquals(path.win32.extname("file\\"), ""); - assertEquals(path.win32.extname("file\\\\"), ""); - assertEquals(path.win32.extname("file.\\"), "."); - assertEquals(path.win32.extname("file.\\\\"), "."); -}); diff --git a/std/path/from_file_url_test.ts b/std/path/from_file_url_test.ts deleted file mode 100644 index 61a45a89b..000000000 --- a/std/path/from_file_url_test.ts +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { posix, win32 } from "./mod.ts"; -import { assertEquals, assertThrows } from "../testing/asserts.ts"; - -Deno.test("[path] fromFileUrl", function () { - assertEquals(posix.fromFileUrl(new URL("file:///home/foo")), "/home/foo"); - assertEquals(posix.fromFileUrl("file:///"), "/"); - assertEquals(posix.fromFileUrl("file:///home/foo"), "/home/foo"); - assertEquals(posix.fromFileUrl("file:///home/foo%20bar"), "/home/foo bar"); - assertEquals(posix.fromFileUrl("file:///%"), "/%"); - assertEquals(posix.fromFileUrl("file://localhost/foo"), "/foo"); - assertEquals(posix.fromFileUrl("file:///C:"), "/C:"); - assertEquals(posix.fromFileUrl("file:///C:/"), "/C:/"); - assertEquals(posix.fromFileUrl("file:///C:/Users/"), "/C:/Users/"); - assertEquals(posix.fromFileUrl("file:///C:foo/bar"), "/C:foo/bar"); - assertThrows( - () => posix.fromFileUrl("http://localhost/foo"), - TypeError, - "Must be a file URL.", - ); - assertThrows( - () => posix.fromFileUrl("abcd://localhost/foo"), - TypeError, - "Must be a file URL.", - ); -}); - -Deno.test("[path] fromFileUrl (win32)", function () { - assertEquals(win32.fromFileUrl(new URL("file:///home/foo")), "\\home\\foo"); - assertEquals(win32.fromFileUrl("file:///"), "\\"); - assertEquals(win32.fromFileUrl("file:///home/foo"), "\\home\\foo"); - assertEquals(win32.fromFileUrl("file:///home/foo%20bar"), "\\home\\foo bar"); - assertEquals(win32.fromFileUrl("file:///%"), "\\%"); - assertEquals(win32.fromFileUrl("file://localhost/foo"), "\\\\localhost\\foo"); - assertEquals(win32.fromFileUrl("file:///C:"), "C:\\"); - assertEquals(win32.fromFileUrl("file:///C:/"), "C:\\"); - // Drop the hostname if a drive letter is parsed. - assertEquals(win32.fromFileUrl("file://localhost/C:/"), "C:\\"); - assertEquals(win32.fromFileUrl("file:///C:/Users/"), "C:\\Users\\"); - assertEquals(win32.fromFileUrl("file:///C:foo/bar"), "\\C:foo\\bar"); - assertThrows( - () => win32.fromFileUrl("http://localhost/foo"), - TypeError, - "Must be a file URL.", - ); - assertThrows( - () => win32.fromFileUrl("abcd://localhost/foo"), - TypeError, - "Must be a file URL.", - ); -}); diff --git a/std/path/glob.ts b/std/path/glob.ts deleted file mode 100644 index d599c1b88..000000000 --- a/std/path/glob.ts +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. - -import { osType } from "../_util/os.ts"; -import { join, normalize } from "./mod.ts"; -import { SEP, SEP_PATTERN } from "./separator.ts"; - -export interface GlobOptions { - /** Extended glob syntax. - * See https://www.linuxjournal.com/content/bash-extended-globbing. Defaults - * to true. */ - extended?: boolean; - /** Globstar syntax. - * See https://www.linuxjournal.com/content/globstar-new-bash-globbing-option. - * If false, `**` is treated like `*`. Defaults to true. */ - globstar?: boolean; - /** Operating system. Defaults to the native OS. */ - os?: typeof Deno.build.os; -} - -export type GlobToRegExpOptions = GlobOptions; - -// deno-fmt-ignore -const regExpEscapeChars = ["!", "$", "(", ")", "*", "+", ".", "=", "?", "[", "\\", "^", "{", "|"]; -const rangeEscapeChars = ["-", "\\", "]"]; - -/** 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. - * - `{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 precendence 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 occured. 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. */ -export function globToRegExp( - glob: string, - { extended = true, globstar: globstarOption = true, os = osType }: - GlobToRegExpOptions = {}, -): RegExp { - if (glob == "") { - return /(?!)/; - } - - const sep = os == "windows" ? "(?:\\\\|/)+" : "/+"; - const sepMaybe = os == "windows" ? "(?:\\\\|/)*" : "/*"; - const seps = os == "windows" ? ["\\", "/"] : ["/"]; - const globstar = os == "windows" - ? "(?:[^\\\\/]*(?:\\\\|/|$)+)*" - : "(?:[^/]*(?:/|$)+)*"; - const wildcard = os == "windows" ? "[^\\\\/]*" : "[^/]*"; - const escapePrefix = os == "windows" ? "`" : "\\"; - - // Remove trailing separators. - let newLength = glob.length; - for (; newLength > 1 && 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 && !seps.includes(glob[i]); i++) { - if (inEscape) { - inEscape = false; - const escapeChars = inRange ? rangeEscapeChars : regExpEscapeChars; - segment += escapeChars.includes(glob[i]) ? `\\${glob[i]}` : glob[i]; - continue; - } - - if (glob[i] == 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] != null && 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) { - if (glob[i] == "\\") { - segment += `\\\\`; - } else { - segment += glob[i]; - } - continue; - } - - if ( - glob[i] == ")" && groupStack.length > 0 && - groupStack[groupStack.length - 1] != "BRACE" - ) { - segment += ")"; - const type = groupStack.pop()!; - if (type == "!") { - segment += 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 && - [...seps, undefined].includes(prevChar) && - [...seps, undefined].includes(nextChar) - ) { - segment += globstar; - endsWithSep = true; - } else { - segment += wildcard; - } - } - continue; - } - - segment += regExpEscapeChars.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 += regExpEscapeChars.includes(c) ? `\\${c}` : c; - endsWithSep = false; - } - } - - regExpString += segment; - if (!endsWithSep) { - regExpString += i < glob.length ? sep : sepMaybe; - endsWithSep = true; - } - - // Terminates with `i` at the start of the next segment. - while (seps.includes(glob[i])) i++; - - // Check that the next value of `j` is indeed higher than the current value. - if (!(i > j)) { - throw new Error("Assertion failure: i > j (potential infinite loop)"); - } - j = i; - } - - regExpString = `^${regExpString}$`; - return new RegExp(regExpString); -} - -/** Test whether the given string is a glob */ -export function isGlob(str: string): boolean { - const chars: Record<string, string> = { "{": "}", "(": ")", "[": "]" }; - const regex = - /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; - - if (str === "") { - return false; - } - - let match: RegExpExecArray | null; - - 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; -} - -/** Like normalize(), but doesn't collapse "**\/.." when `globstar` is true. */ -export function normalizeGlob( - glob: string, - { globstar = false }: GlobOptions = {}, -): string { - if (glob.match(/\0/g)) { - throw new Error(`Glob contains invalid characters: "${glob}"`); - } - if (!globstar) { - return normalize(glob); - } - const s = SEP_PATTERN.source; - const badParentPattern = new RegExp( - `(?<=(${s}|^)\\*\\*${s})\\.\\.(?=${s}|$)`, - "g", - ); - return normalize(glob.replace(badParentPattern, "\0")).replace(/\0/g, ".."); -} - -/** Like join(), but doesn't collapse "**\/.." when `globstar` is true. */ -export function joinGlobs( - globs: string[], - { extended = false, globstar = false }: GlobOptions = {}, -): string { - if (!globstar || globs.length == 0) { - return join(...globs); - } - if (globs.length === 0) return "."; - let joined: string | undefined; - for (const glob of globs) { - const path = glob; - if (path.length > 0) { - if (!joined) joined = path; - else joined += `${SEP}${path}`; - } - } - if (!joined) return "."; - return normalizeGlob(joined, { extended, globstar }); -} diff --git a/std/path/glob_test.ts b/std/path/glob_test.ts deleted file mode 100644 index cf46a3c5c..000000000 --- a/std/path/glob_test.ts +++ /dev/null @@ -1,638 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals } from "../testing/asserts.ts"; -import { - globToRegExp, - GlobToRegExpOptions, - isGlob, - joinGlobs, - normalizeGlob, -} from "./glob.ts"; -import { SEP } from "./mod.ts"; - -function match( - glob: string, - path: string, - opts: GlobToRegExpOptions = {}, -): boolean { - if (opts.os == null) { - const matchDarwin = path.match( - globToRegExp(glob, { ...opts, os: "darwin" }), - ); - if (matchDarwin) { - assertEquals(matchDarwin.length, 1); - } - const matchLinux = path.match(globToRegExp(glob, { ...opts, os: "linux" })); - if (matchLinux) { - assertEquals(matchLinux.length, 1); - } - const matchWindows = path.match( - globToRegExp(glob, { ...opts, os: "windows" }), - ); - if (matchWindows) { - assertEquals(matchWindows.length, 1); - } - return !!matchDarwin && !!matchLinux && !!matchWindows; - } else { - const match = path.match(globToRegExp(glob, opts)); - if (match) { - assertEquals(match.length, 1); - } - return !!match; - } -} - -Deno.test({ - name: "[path] globToRegExp() Basic RegExp", - fn(): void { - assertEquals(globToRegExp("*.js", { os: "linux" }), /^[^/]*\.js\/*$/); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Empty glob", - fn(): void { - assertEquals(globToRegExp(""), /(?!)/); - assertEquals(globToRegExp("*.js", { os: "linux" }), /^[^/]*\.js\/*$/); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() * (wildcard)", - fn(): void { - assert(match("*", "foo", { extended: false, globstar: false })); - assert(match("*", "foo", { extended: false, globstar: false })); - assert(match("f*", "foo", { extended: false, globstar: false })); - assert(match("f*", "foo", { extended: false, globstar: false })); - assert(match("*o", "foo", { extended: false, globstar: false })); - assert(match("*o", "foo", { extended: false, globstar: false })); - assert(match("u*orn", "unicorn", { extended: false, globstar: false })); - assert(match("u*orn", "unicorn", { extended: false, globstar: false })); - assert(!match("ico", "unicorn", { extended: false, globstar: false })); - assert(match("u*nicorn", "unicorn", { extended: false, globstar: false })); - assert(match("u*nicorn", "unicorn", { extended: false, globstar: false })); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() ? (match one character)", - fn(): void { - assert(match("f?o", "foo", { extended: false, globstar: false })); - assert(match("f?o?", "fooo", { extended: false, globstar: false })); - assert(!match("f?oo", "foo", { extended: false, globstar: false })); - assert(!match("?fo", "fooo", { extended: false, globstar: false })); - assert(!match("f?oo", "foo", { extended: false, globstar: false })); - assert(!match("foo?", "foo", { extended: false, globstar: false })); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() [seq] (character range)", - fn(): void { - assert(match("fo[oz]", "foo", { extended: false, globstar: false })); - assert(match("fo[oz]", "foz", { extended: false, globstar: false })); - assert(!match("fo[oz]", "fog", { extended: false, globstar: false })); - assert(match("fo[a-z]", "fob", { extended: false, globstar: false })); - assert(!match("fo[a-d]", "fot", { extended: false, globstar: false })); - assert(!match("fo[!tz]", "fot", { extended: false, globstar: false })); - assert(match("fo[!tz]", "fob", { extended: false, globstar: false })); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() [[:alnum:]] (character class in range)", - fn(): void { - assert( - match( - "[[:alnum:]]/bar.txt", - "a/bar.txt", - { extended: false, globstar: false }, - ), - ); - assert( - match( - "[[:alnum:]abc]/bar.txt", - "1/bar.txt", - { extended: false, globstar: false }, - ), - ); - assert( - !match( - "[[:alnum:]]/bar.txt", - "!/bar.txt", - { extended: false, globstar: false }, - ), - ); - for (const c of "09AGZagz") { - assert(match("[[:alnum:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "AGZagz") { - assert(match("[[:alpha:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "\x00\x20\x7F") { - assert(match("[[:ascii:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "\t ") { - assert(match("[[:blank:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "\x00\x1F\x7F") { - assert(match("[[:cntrl:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "09") { - assert(match("[[:digit:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "\x21\x7E") { - assert(match("[[:graph:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "az") { - assert(match("[[:lower:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "\x20\x7E") { - assert(match("[[:print:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "!\"#$%&'()*+,-./:;<=>?@[\\]^_‘{|}~") { - assert(match("[[:punct:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "\t\n\v\f\r ") { - assert(match("[[:space:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "AZ") { - assert(match("[[:upper:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "09AZaz_") { - assert(match("[[:word:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "09AFaf") { - assert(match("[[:xdigit:]]", c, { extended: false, globstar: false }), c); - } - }, -}); - -Deno.test({ - name: "[path] globToRegExp() {} (brace expansion)", - fn(): void { - assert( - match("foo{bar,baaz}", "foobaaz", { extended: false, globstar: false }), - ); - assert( - match("foo{bar,baaz}", "foobar", { extended: false, globstar: false }), - ); - assert( - !match("foo{bar,baaz}", "foobuzz", { extended: false, globstar: false }), - ); - assert( - match("foo{bar,b*z}", "foobuzz", { extended: false, globstar: false }), - ); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Complex matches", - fn(): void { - assert( - match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://foo.baaz.com/jquery.min.js", - { extended: false, globstar: false }, - ), - ); - assert( - match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://moz.buzz.com/index.html", - { extended: false, globstar: false }, - ), - ); - assert( - !match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://moz.buzz.com/index.htm", - { extended: false, globstar: false }, - ), - ); - assert( - !match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://moz.bar.com/index.html", - { extended: false, globstar: false }, - ), - ); - assert( - !match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://flozz.buzz.com/index.html", - { extended: false, globstar: false }, - ), - ); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() ** (globstar)", - fn(): void { - assert(match("/foo/**", "/foo/bar.txt")); - assert(match("/foo/**", "/foo/bar/baz.txt")); - assert(!match("/foo/**", "/foo/bar/baz.txt", { globstar: false })); - assert(match("/foo/**", "/foo/bar", { globstar: false })); - assert(match("/foo/**/*.txt", "/foo/bar/baz.txt")); - assert(match("/foo/**/*.txt", "/foo/bar/baz/qux.txt")); - assert(match("/foo/**/bar.txt", "/foo/bar.txt")); - assert(match("/foo/**/**/bar.txt", "/foo/bar.txt")); - assert(match("/foo/**/*/baz.txt", "/foo/bar/baz.txt")); - assert(match("/foo/**/*.txt", "/foo/bar.txt")); - assert(match("/foo/**/**/*.txt", "/foo/bar.txt")); - assert(match("/foo/**/*/*.txt", "/foo/bar/baz.txt")); - assert(match("**/*.txt", "/foo/bar/baz/qux.txt")); - assert(match("**/foo.txt", "foo.txt")); - assert(match("**/*.txt", "foo.txt")); - assert(!match("/foo/**.txt", "/foo/bar/baz/qux.txt")); - assert( - !match("/foo/bar**/*.txt", "/foo/bar/baz/qux.txt"), - ); - assert(!match("/foo/bar**", "/foo/bar/baz.txt")); - assert(!match("**/.txt", "/foo/bar/baz/qux.txt")); - assert( - !match( - "http://foo.com/*", - "http://foo.com/bar/baz/jquery.min.js", - ), - ); - assert( - !match("http://foo.com/*", "http://foo.com/bar/baz/jquery.min.js"), - ); - assert( - match("http://foo.com/**", "http://foo.com/bar/baz/jquery.min.js"), - ); - assert( - match( - "http://foo.com/**/jquery.min.js", - "http://foo.com/bar/baz/jquery.min.js", - ), - ); - assert( - !match( - "http://foo.com/*/jquery.min.js", - "http://foo.com/bar/baz/jquery.min.js", - ), - ); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() ?(pattern-list) (extended: match zero or one)", - fn(): void { - assert(match("?(foo).txt", "foo.txt")); - assert(!match("?(foo).txt", "foo.txt", { extended: false })); - assert(match("?(foo).txt", "a(foo).txt", { extended: false })); - assert(match("?(foo).txt", ".txt")); - assert(match("?(foo|bar)baz.txt", "foobaz.txt")); - assert(match("?(ba[zr]|qux)baz.txt", "bazbaz.txt")); - assert(match("?(ba[zr]|qux)baz.txt", "barbaz.txt")); - assert(match("?(ba[zr]|qux)baz.txt", "quxbaz.txt")); - assert(match("?(ba[!zr]|qux)baz.txt", "batbaz.txt")); - assert(match("?(ba*|qux)baz.txt", "batbaz.txt")); - assert(match("?(ba*|qux)baz.txt", "batttbaz.txt")); - assert(match("?(ba*|qux)baz.txt", "quxbaz.txt")); - assert(match("?(ba?(z|r)|qux)baz.txt", "bazbaz.txt")); - assert(match("?(ba?(z|?(r))|qux)baz.txt", "bazbaz.txt")); - assert(!match("?(foo|bar)baz.txt", "foobarbaz.txt")); - assert(!match("?(ba[zr]|qux)baz.txt", "bazquxbaz.txt")); - assert(!match("?(ba[!zr]|qux)baz.txt", "bazbaz.txt")); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() *(pattern-list) (extended: match zero or more)", - fn(): void { - assert(match("*(foo).txt", "foo.txt")); - assert(!match("*(foo).txt", "foo.txt", { extended: false })); - assert(match("*(foo).txt", "bar(foo).txt", { extended: false })); - assert(match("*(foo).txt", "foofoo.txt")); - assert(match("*(foo).txt", ".txt")); - assert(match("*(fooo).txt", ".txt")); - assert(!match("*(fooo).txt", "foo.txt")); - assert(match("*(foo|bar).txt", "foobar.txt")); - assert(match("*(foo|bar).txt", "barbar.txt")); - assert(match("*(foo|bar).txt", "barfoobar.txt")); - assert(match("*(foo|bar).txt", ".txt")); - assert(match("*(foo|ba[rt]).txt", "bat.txt")); - assert(match("*(foo|b*[rt]).txt", "blat.txt")); - assert(!match("*(foo|b*[rt]).txt", "tlat.txt")); - assert(match("*(*).txt", "whatever.txt")); - assert(match("*(foo|bar)/**/*.txt", "foo/hello/world/bar.txt")); - assert(match("*(foo|bar)/**/*.txt", "foo/world/bar.txt")); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() +(pattern-list) (extended: match 1 or more)", - fn(): void { - assert(match("+(foo).txt", "foo.txt")); - assert(!match("+(foo).txt", "foo.txt", { extended: false })); - assert(match("+(foo).txt", "+(foo).txt", { extended: false })); - assert(!match("+(foo).txt", ".txt")); - assert(match("+(foo|bar).txt", "foobar.txt")); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() @(pattern-list) (extended: match one)", - fn(): void { - assert(match("@(foo).txt", "foo.txt")); - assert(!match("@(foo).txt", "foo.txt", { extended: false })); - assert(match("@(foo).txt", "@(foo).txt", { extended: false })); - assert(match("@(foo|baz)bar.txt", "foobar.txt")); - assert(!match("@(foo|baz)bar.txt", "foobazbar.txt")); - assert(!match("@(foo|baz)bar.txt", "foofoobar.txt")); - assert(!match("@(foo|baz)bar.txt", "toofoobar.txt")); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() !(pattern-list) (extended: match any except)", - fn(): void { - assert(match("!(boo).txt", "foo.txt")); - assert(!match("!(boo).txt", "foo.txt", { extended: false })); - assert(match("!(boo).txt", "!(boo).txt", { extended: false })); - assert(match("!(foo|baz)bar.txt", "buzbar.txt")); - assert(match("!({foo,bar})baz.txt", "notbaz.txt")); - assert(!match("!({foo,bar})baz.txt", "foobaz.txt")); - }, -}); - -Deno.test({ - name: - "[path] globToRegExp() Special extended characters should match themselves", - fn(): void { - const glob = "\\/$^+.()=!|,.*"; - assert(match(glob, glob)); - assert(match(glob, glob, { extended: false })); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Special extended characters in range", - fn(): void { - assertEquals(globToRegExp("[?*+@!|]", { os: "linux" }), /^[?*+@!|]\/*$/); - assertEquals(globToRegExp("[!?*+@!|]", { os: "linux" }), /^[^?*+@!|]\/*$/); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Special RegExp characters in range", - fn(): void { - // Excluding characters checked in the previous test. - assertEquals(globToRegExp("[\\\\$^.=]", { os: "linux" }), /^[\\$^.=]\/*$/); - assertEquals( - globToRegExp("[!\\\\$^.=]", { os: "linux" }), - /^[^\\$^.=]\/*$/, - ); - assertEquals(globToRegExp("[^^]", { os: "linux" }), /^[\^^]\/*$/); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Repeating separators", - fn() { - assert(match("foo/bar", "foo//bar")); - assert(match("foo//bar", "foo/bar")); - assert(match("foo//bar", "foo//bar")); - assert(match("**/bar", "foo//bar")); - assert(match("**//bar", "foo/bar")); - assert(match("**//bar", "foo//bar")); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Trailing separators", - fn() { - assert(match("foo", "foo/")); - assert(match("foo/", "foo")); - assert(match("foo/", "foo/")); - assert(match("**", "foo/")); - assert(match("**/", "foo")); - assert(match("**/", "foo/")); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Backslashes on Windows", - fn() { - assert(match("foo/bar", "foo\\bar", { os: "windows" })); - assert(match("foo\\bar", "foo/bar", { os: "windows" })); - assert(match("foo\\bar", "foo\\bar", { os: "windows" })); - assert(match("**/bar", "foo\\bar", { os: "windows" })); - assert(match("**\\bar", "foo/bar", { os: "windows" })); - assert(match("**\\bar", "foo\\bar", { os: "windows" })); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Unclosed groups", - fn() { - assert(match("{foo,bar}/[ab", "foo/[ab")); - assert(match("{foo,bar}/{foo,bar", "foo/{foo,bar")); - assert(match("{foo,bar}/?(foo|bar", "foo/?(foo|bar")); - assert(match("{foo,bar}/@(foo|bar", "foo/@(foo|bar")); - assert(match("{foo,bar}/*(foo|bar", "foo/*(foo|bar")); - assert(match("{foo,bar}/+(foo|bar", "foo/+(foo|bar")); - assert(match("{foo,bar}/!(foo|bar", "foo/!(foo|bar")); - assert(match("{foo,bar}/?({)}", "foo/?({)}")); - assert(match("{foo,bar}/{?(})", "foo/{?(})")); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Escape glob characters", - fn() { - assert(match("\\[ab]", "[ab]", { os: "linux" })); - assert(match("`[ab]", "[ab]", { os: "windows" })); - assert(match("\\{foo,bar}", "{foo,bar}", { os: "linux" })); - assert(match("`{foo,bar}", "{foo,bar}", { os: "windows" })); - assert(match("\\?(foo|bar)", "?(foo|bar)", { os: "linux" })); - assert(match("`?(foo|bar)", "?(foo|bar)", { os: "windows" })); - assert(match("\\@(foo|bar)", "@(foo|bar)", { os: "linux" })); - assert(match("`@(foo|bar)", "@(foo|bar)", { os: "windows" })); - assert(match("\\*(foo|bar)", "*(foo|bar)", { os: "linux" })); - assert(match("`*(foo|bar)", "*(foo|bar)", { os: "windows" })); - assert(match("\\+(foo|bar)", "+(foo|bar)", { os: "linux" })); - assert(match("`+(foo|bar)", "+(foo|bar)", { os: "windows" })); - assert(match("\\!(foo|bar)", "!(foo|bar)", { os: "linux" })); - assert(match("`!(foo|bar)", "!(foo|bar)", { os: "windows" })); - assert(match("@\\(foo|bar)", "@(foo|bar)", { os: "linux" })); - assert(match("@`(foo|bar)", "@(foo|bar)", { os: "windows" })); - assert(match("{foo,bar}/[ab]\\", "foo/[ab]\\", { os: "linux" })); - assert(match("{foo,bar}/[ab]`", "foo/[ab]`", { os: "windows" })); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Dangling escape prefix", - fn() { - assert(match("{foo,bar}/[ab]\\", "foo/[ab]\\", { os: "linux" })); - assert(match("{foo,bar}/[ab]`", "foo/[ab]`", { os: "windows" })); - }, -}); - -Deno.test({ - name: "[path] GlobToRegExpOptions::extended", - fn() { - const pattern1 = globToRegExp("?(foo|bar)"); - assertEquals("foo".match(pattern1)?.[0], "foo"); - assertEquals("bar".match(pattern1)?.[0], "bar"); - - const pattern2 = globToRegExp("?(foo|bar)", { extended: false }); - assertEquals("foo".match(pattern2)?.[0], undefined); - assertEquals("bar".match(pattern2)?.[0], undefined); - assertEquals("?(foo|bar)".match(pattern2)?.[0], "?(foo|bar)"); - }, -}); - -Deno.test({ - name: "[path] GlobToRegExpOptions::globstar", - fn() { - const pattern1 = globToRegExp("**/foo"); - assertEquals("foo".match(pattern1)?.[0], "foo"); - assertEquals("path/to/foo".match(pattern1)?.[0], "path/to/foo"); - - const pattern2 = globToRegExp("**/foo", { globstar: false }); - assertEquals("foo".match(pattern2)?.[0], undefined); - assertEquals("path/to/foo".match(pattern2)?.[0], undefined); - assertEquals("path-to/foo".match(pattern2)?.[0], "path-to/foo"); - }, -}); - -Deno.test({ - name: "[path] GlobToRegExpOptions::os", - fn() { - const pattern1 = globToRegExp("foo/bar", { os: "linux" }); - assertEquals("foo/bar".match(pattern1)?.[0], "foo/bar"); - assertEquals("foo\\bar".match(pattern1)?.[0], undefined); - - const pattern2 = globToRegExp("foo/bar", { os: "windows" }); - assertEquals("foo/bar".match(pattern2)?.[0], "foo/bar"); - assertEquals("foo\\bar".match(pattern2)?.[0], "foo\\bar"); - }, -}); - -Deno.test({ - name: "[path] isGlob()", - fn(): void { - // should be true if valid glob pattern - assert(isGlob("!foo.js")); - assert(isGlob("*.js")); - assert(isGlob("!*.js")); - assert(isGlob("!foo")); - assert(isGlob("!foo.js")); - assert(isGlob("**/abc.js")); - assert(isGlob("abc/*.js")); - assert(isGlob("@.(?:abc)")); - assert(isGlob("@.(?!abc)")); - - // should be false if invalid glob pattern - assert(!isGlob("")); - assert(!isGlob("~/abc")); - assert(!isGlob("~/abc")); - assert(!isGlob("~/(abc)")); - assert(!isGlob("+~(abc)")); - assert(!isGlob(".")); - assert(!isGlob("@.(abc)")); - assert(!isGlob("aa")); - assert(!isGlob("who?")); - assert(!isGlob("why!?")); - assert(!isGlob("where???")); - assert(!isGlob("abc!/def/!ghi.js")); - assert(!isGlob("abc.js")); - assert(!isGlob("abc/def/!ghi.js")); - assert(!isGlob("abc/def/ghi.js")); - - // Should be true if path has regex capture group - assert(isGlob("abc/(?!foo).js")); - assert(isGlob("abc/(?:foo).js")); - assert(isGlob("abc/(?=foo).js")); - assert(isGlob("abc/(a|b).js")); - assert(isGlob("abc/(a|b|c).js")); - assert(isGlob("abc/(foo bar)/*.js")); - - // Should be false if the path has parens but is not a valid capture group - assert(!isGlob("abc/(?foo).js")); - assert(!isGlob("abc/(a b c).js")); - assert(!isGlob("abc/(ab).js")); - assert(!isGlob("abc/(abc).js")); - assert(!isGlob("abc/(foo bar).js")); - - // should be false if the capture group is imbalanced - assert(!isGlob("abc/(?ab.js")); - assert(!isGlob("abc/(ab.js")); - assert(!isGlob("abc/(a|b.js")); - assert(!isGlob("abc/(a|b|c.js")); - - // should be true if the path has a regex character class - assert(isGlob("abc/[abc].js")); - assert(isGlob("abc/[^abc].js")); - assert(isGlob("abc/[1-3].js")); - - // should be false if the character class is not balanced - assert(!isGlob("abc/[abc.js")); - assert(!isGlob("abc/[^abc.js")); - assert(!isGlob("abc/[1-3.js")); - - // should be false if the character class is escaped - assert(!isGlob("abc/\\[abc].js")); - assert(!isGlob("abc/\\[^abc].js")); - assert(!isGlob("abc/\\[1-3].js")); - - // should be true if the path has brace characters - assert(isGlob("abc/{a,b}.js")); - assert(isGlob("abc/{a..z}.js")); - assert(isGlob("abc/{a..z..2}.js")); - - // should be false if (basic) braces are not balanced - assert(!isGlob("abc/\\{a,b}.js")); - assert(!isGlob("abc/\\{a..z}.js")); - assert(!isGlob("abc/\\{a..z..2}.js")); - - // should be true if the path has regex characters - assert(isGlob("!&(abc)")); - assert(isGlob("!*.js")); - assert(isGlob("!foo")); - assert(isGlob("!foo.js")); - assert(isGlob("**/abc.js")); - assert(isGlob("*.js")); - assert(isGlob("*z(abc)")); - assert(isGlob("[1-10].js")); - assert(isGlob("[^abc].js")); - assert(isGlob("[a-j]*[^c]b/c")); - assert(isGlob("[abc].js")); - assert(isGlob("a/b/c/[a-z].js")); - assert(isGlob("abc/(aaa|bbb).js")); - assert(isGlob("abc/*.js")); - assert(isGlob("abc/{a,b}.js")); - assert(isGlob("abc/{a..z..2}.js")); - assert(isGlob("abc/{a..z}.js")); - - assert(!isGlob("$(abc)")); - assert(!isGlob("&(abc)")); - assert(!isGlob("Who?.js")); - assert(!isGlob("? (abc)")); - assert(!isGlob("?.js")); - assert(!isGlob("abc/?.js")); - - // should be false if regex characters are escaped - assert(!isGlob("\\?.js")); - assert(!isGlob("\\[1-10\\].js")); - assert(!isGlob("\\[^abc\\].js")); - assert(!isGlob("\\[a-j\\]\\*\\[^c\\]b/c")); - assert(!isGlob("\\[abc\\].js")); - assert(!isGlob("\\a/b/c/\\[a-z\\].js")); - assert(!isGlob("abc/\\(aaa|bbb).js")); - assert(!isGlob("abc/\\?.js")); - }, -}); - -Deno.test("[path] normalizeGlob() Globstar", function (): void { - assertEquals(normalizeGlob(`**${SEP}..`, { globstar: true }), `**${SEP}..`); -}); - -Deno.test("[path] joinGlobs() Globstar", function (): void { - assertEquals(joinGlobs(["**", ".."], { globstar: true }), `**${SEP}..`); -}); diff --git a/std/path/isabsolute_test.ts b/std/path/isabsolute_test.ts deleted file mode 100644 index 88ed54417..000000000 --- a/std/path/isabsolute_test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -Deno.test("isAbsolute", function () { - assertEquals(path.posix.isAbsolute("/home/foo"), true); - assertEquals(path.posix.isAbsolute("/home/foo/.."), true); - assertEquals(path.posix.isAbsolute("bar/"), false); - assertEquals(path.posix.isAbsolute("./baz"), false); -}); - -Deno.test("isAbsoluteWin32", function () { - assertEquals(path.win32.isAbsolute("/"), true); - assertEquals(path.win32.isAbsolute("//"), true); - assertEquals(path.win32.isAbsolute("//server"), true); - assertEquals(path.win32.isAbsolute("//server/file"), true); - assertEquals(path.win32.isAbsolute("\\\\server\\file"), true); - assertEquals(path.win32.isAbsolute("\\\\server"), true); - assertEquals(path.win32.isAbsolute("\\\\"), true); - assertEquals(path.win32.isAbsolute("c"), false); - assertEquals(path.win32.isAbsolute("c:"), false); - assertEquals(path.win32.isAbsolute("c:\\"), true); - assertEquals(path.win32.isAbsolute("c:/"), true); - assertEquals(path.win32.isAbsolute("c://"), true); - assertEquals(path.win32.isAbsolute("C:/Users/"), true); - assertEquals(path.win32.isAbsolute("C:\\Users\\"), true); - assertEquals(path.win32.isAbsolute("C:cwd/another"), false); - assertEquals(path.win32.isAbsolute("C:cwd\\another"), false); - assertEquals(path.win32.isAbsolute("directory/directory"), false); - assertEquals(path.win32.isAbsolute("directory\\directory"), false); -}); diff --git a/std/path/join_test.ts b/std/path/join_test.ts deleted file mode 100644 index 122376be1..000000000 --- a/std/path/join_test.ts +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -const backslashRE = /\\/g; - -const joinTests = - // arguments result - [ - [[".", "x/b", "..", "/b/c.js"], "x/b/c.js"], - [[], "."], - [["/.", "x/b", "..", "/b/c.js"], "/x/b/c.js"], - [["/foo", "../../../bar"], "/bar"], - [["foo", "../../../bar"], "../../bar"], - [["foo/", "../../../bar"], "../../bar"], - [["foo/x", "../../../bar"], "../bar"], - [["foo/x", "./bar"], "foo/x/bar"], - [["foo/x/", "./bar"], "foo/x/bar"], - [["foo/x/", ".", "bar"], "foo/x/bar"], - [["./"], "./"], - [[".", "./"], "./"], - [[".", ".", "."], "."], - [[".", "./", "."], "."], - [[".", "/./", "."], "."], - [[".", "/////./", "."], "."], - [["."], "."], - [["", "."], "."], - [["", "foo"], "foo"], - [["foo", "/bar"], "foo/bar"], - [["", "/foo"], "/foo"], - [["", "", "/foo"], "/foo"], - [["", "", "foo"], "foo"], - [["foo", ""], "foo"], - [["foo/", ""], "foo/"], - [["foo", "", "/bar"], "foo/bar"], - [["./", "..", "/foo"], "../foo"], - [["./", "..", "..", "/foo"], "../../foo"], - [[".", "..", "..", "/foo"], "../../foo"], - [["", "..", "..", "/foo"], "../../foo"], - [["/"], "/"], - [["/", "."], "/"], - [["/", ".."], "/"], - [["/", "..", ".."], "/"], - [[""], "."], - [["", ""], "."], - [[" /foo"], " /foo"], - [[" ", "foo"], " /foo"], - [[" ", "."], " "], - [[" ", "/"], " /"], - [[" ", ""], " "], - [["/", "foo"], "/foo"], - [["/", "/foo"], "/foo"], - [["/", "//foo"], "/foo"], - [["/", "", "/foo"], "/foo"], - [["", "/", "foo"], "/foo"], - [["", "/", "/foo"], "/foo"], - ]; - -// Windows-specific join tests -const windowsJoinTests = [ - // arguments result - // UNC path expected - [["//foo/bar"], "\\\\foo\\bar\\"], - [["\\/foo/bar"], "\\\\foo\\bar\\"], - [["\\\\foo/bar"], "\\\\foo\\bar\\"], - // UNC path expected - server and share separate - [["//foo", "bar"], "\\\\foo\\bar\\"], - [["//foo/", "bar"], "\\\\foo\\bar\\"], - [["//foo", "/bar"], "\\\\foo\\bar\\"], - // UNC path expected - questionable - [["//foo", "", "bar"], "\\\\foo\\bar\\"], - [["//foo/", "", "bar"], "\\\\foo\\bar\\"], - [["//foo/", "", "/bar"], "\\\\foo\\bar\\"], - // UNC path expected - even more questionable - [["", "//foo", "bar"], "\\\\foo\\bar\\"], - [["", "//foo/", "bar"], "\\\\foo\\bar\\"], - [["", "//foo/", "/bar"], "\\\\foo\\bar\\"], - // No UNC path expected (no double slash in first component) - [["\\", "foo/bar"], "\\foo\\bar"], - [["\\", "/foo/bar"], "\\foo\\bar"], - [["", "/", "/foo/bar"], "\\foo\\bar"], - // No UNC path expected (no non-slashes in first component - - // questionable) - [["//", "foo/bar"], "\\foo\\bar"], - [["//", "/foo/bar"], "\\foo\\bar"], - [["\\\\", "/", "/foo/bar"], "\\foo\\bar"], - [["//"], "\\"], - // No UNC path expected (share name missing - questionable). - [["//foo"], "\\foo"], - [["//foo/"], "\\foo\\"], - [["//foo", "/"], "\\foo\\"], - [["//foo", "", "/"], "\\foo\\"], - // No UNC path expected (too many leading slashes - questionable) - [["///foo/bar"], "\\foo\\bar"], - [["////foo", "bar"], "\\foo\\bar"], - [["\\\\\\/foo/bar"], "\\foo\\bar"], - // Drive-relative vs drive-absolute paths. This merely describes the - // status quo, rather than being obviously right - [["c:"], "c:."], - [["c:."], "c:."], - [["c:", ""], "c:."], - [["", "c:"], "c:."], - [["c:.", "/"], "c:.\\"], - [["c:.", "file"], "c:file"], - [["c:", "/"], "c:\\"], - [["c:", "file"], "c:\\file"], -]; - -Deno.test("join", function () { - joinTests.forEach(function (p) { - const _p = p[0] as string[]; - const actual = path.posix.join.apply(null, _p); - assertEquals(actual, p[1]); - }); -}); - -Deno.test("joinWin32", function () { - joinTests.forEach(function (p) { - const _p = p[0] as string[]; - const actual = path.win32.join.apply(null, _p).replace(backslashRE, "/"); - assertEquals(actual, p[1]); - }); - windowsJoinTests.forEach(function (p) { - const _p = p[0] as string[]; - const actual = path.win32.join.apply(null, _p); - assertEquals(actual, p[1]); - }); -}); diff --git a/std/path/mod.ts b/std/path/mod.ts deleted file mode 100644 index 5fd793c75..000000000 --- a/std/path/mod.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported mostly from https://github.com/browserify/path-browserify/ -// This module is browser compatible. - -import { isWindows } from "../_util/os.ts"; -import * as _win32 from "./win32.ts"; -import * as _posix from "./posix.ts"; - -const path = isWindows ? _win32 : _posix; - -export const win32 = _win32; -export const posix = _posix; -export const { - basename, - delimiter, - dirname, - extname, - format, - fromFileUrl, - isAbsolute, - join, - normalize, - parse, - relative, - resolve, - sep, - toFileUrl, - toNamespacedPath, -} = path; - -export * from "./common.ts"; -export { SEP, SEP_PATTERN } from "./separator.ts"; -export * from "./_interface.ts"; -export * from "./glob.ts"; diff --git a/std/path/parse_format_test.ts b/std/path/parse_format_test.ts deleted file mode 100644 index 785588359..000000000 --- a/std/path/parse_format_test.ts +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import type { FormatInputPathObject, ParsedPath } from "./mod.ts"; - -import { assertEquals } from "../testing/asserts.ts"; -import { posix, win32 } from "./mod.ts"; - -type FormatTestCase = [FormatInputPathObject, string]; -type ParseTestCase = [string, ParsedPath]; - -const winPaths: Array<[string, string]> = [ - // [path, root] - ["C:\\path\\dir\\index.html", "C:\\"], - ["C:\\another_path\\DIR\\1\\2\\33\\\\index", "C:\\"], - ["another_path\\DIR with spaces\\1\\2\\33\\index", ""], - ["\\", "\\"], - ["\\foo\\C:", "\\"], - ["file", ""], - ["file:stream", ""], - [".\\file", ""], - ["C:", "C:"], - ["C:.", "C:"], - ["C:..", "C:"], - ["C:abc", "C:"], - ["C:\\", "C:\\"], - ["C:\\abc", "C:\\"], - ["", ""], - // unc - ["\\\\server\\share\\file_path", "\\\\server\\share\\"], - [ - "\\\\server two\\shared folder\\file path.zip", - "\\\\server two\\shared folder\\", - ], - ["\\\\teela\\admin$\\system32", "\\\\teela\\admin$\\"], - ["\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\"], -]; - -const winSpecialCaseParseTests: ParseTestCase[] = [ - ["/foo/bar", { root: "/", dir: "/foo", base: "bar", ext: "", name: "bar" }], -]; - -const winSpecialCaseFormatTests: FormatTestCase[] = [ - [{ dir: "some\\dir" }, "some\\dir\\"], - [{ base: "index.html" }, "index.html"], - [{ root: "C:\\" }, "C:\\"], - [{ name: "index", ext: ".html" }, "index.html"], - [{ dir: "some\\dir", name: "index", ext: ".html" }, "some\\dir\\index.html"], - [{ root: "C:\\", name: "index", ext: ".html" }, "C:\\index.html"], - [{}, ""], -]; - -const unixPaths: Array<[string, string]> = [ - // [path, root] - ["/home/user/dir/file.txt", "/"], - ["/home/user/a dir/another File.zip", "/"], - ["/home/user/a dir//another&File.", "/"], - ["/home/user/a$$$dir//another File.zip", "/"], - ["user/dir/another File.zip", ""], - ["file", ""], - [".\\file", ""], - ["./file", ""], - ["C:\\foo", ""], - ["/", "/"], - ["", ""], - [".", ""], - ["..", ""], - ["/foo", "/"], - ["/foo.", "/"], - ["/foo.bar", "/"], - ["/.", "/"], - ["/.foo", "/"], - ["/.foo.bar", "/"], - ["/foo/bar.baz", "/"], -]; - -const unixSpecialCaseFormatTests: FormatTestCase[] = [ - [{ dir: "some/dir" }, "some/dir/"], - [{ base: "index.html" }, "index.html"], - [{ root: "/" }, "/"], - [{ name: "index", ext: ".html" }, "index.html"], - [{ dir: "some/dir", name: "index", ext: ".html" }, "some/dir/index.html"], - [{ root: "/", name: "index", ext: ".html" }, "/index.html"], - [{}, ""], -]; - -function checkParseFormat( - path: typeof win32 | typeof posix, - testCases: Array<[string, string]>, -): void { - testCases.forEach(([element, root]) => { - const output = path.parse(element); - assertEquals(typeof output.root, "string"); - assertEquals(typeof output.dir, "string"); - assertEquals(typeof output.base, "string"); - assertEquals(typeof output.ext, "string"); - assertEquals(typeof output.name, "string"); - assertEquals(path.format(output), element); - assertEquals(output.root, root); - assertEquals(output.dir, output.dir ? path.dirname(element) : ""); - assertEquals(output.base, path.basename(element)); - assertEquals(output.ext, path.extname(element)); - }); -} - -function checkSpecialCaseParseFormat( - path: typeof win32 | typeof posix, - testCases: ParseTestCase[], -): void { - testCases.forEach(([element, expect]) => { - assertEquals(path.parse(element), expect); - }); -} - -function checkFormat( - path: typeof win32 | typeof posix, - testCases: FormatTestCase[], -): void { - testCases.forEach((testCase) => { - assertEquals(path.format(testCase[0]), testCase[1]); - }); -} - -Deno.test("parseWin32", function () { - checkParseFormat(win32, winPaths); - checkSpecialCaseParseFormat(win32, winSpecialCaseParseTests); -}); - -Deno.test("parse", function () { - checkParseFormat(posix, unixPaths); -}); - -Deno.test("formatWin32", function () { - checkFormat(win32, winSpecialCaseFormatTests); -}); - -Deno.test("format", function () { - checkFormat(posix, unixSpecialCaseFormatTests); -}); - -// Test removal of trailing path separators -const windowsTrailingTests: ParseTestCase[] = [ - [".\\", { root: "", dir: "", base: ".", ext: "", name: "." }], - ["\\\\", { root: "\\", dir: "\\", base: "", ext: "", name: "" }], - ["\\\\", { root: "\\", dir: "\\", base: "", ext: "", name: "" }], - [ - "c:\\foo\\\\\\", - { root: "c:\\", dir: "c:\\", base: "foo", ext: "", name: "foo" }, - ], - [ - "D:\\foo\\\\\\bar.baz", - { - root: "D:\\", - dir: "D:\\foo\\\\", - base: "bar.baz", - ext: ".baz", - name: "bar", - }, - ], -]; - -const posixTrailingTests: ParseTestCase[] = [ - ["./", { root: "", dir: "", base: ".", ext: "", name: "." }], - ["//", { root: "/", dir: "/", base: "", ext: "", name: "" }], - ["///", { root: "/", dir: "/", base: "", ext: "", name: "" }], - ["/foo///", { root: "/", dir: "/", base: "foo", ext: "", name: "foo" }], - [ - "/foo///bar.baz", - { root: "/", dir: "/foo//", base: "bar.baz", ext: ".baz", name: "bar" }, - ], -]; - -Deno.test("parseTrailingWin32", function () { - windowsTrailingTests.forEach(function (p) { - const actual = win32.parse(p[0]); - const expected = p[1]; - assertEquals(actual, expected); - }); -}); - -Deno.test("parseTrailing", function () { - posixTrailingTests.forEach(function (p) { - const actual = posix.parse(p[0]); - const expected = p[1]; - assertEquals(actual, expected); - }); -}); diff --git a/std/path/posix.ts b/std/path/posix.ts deleted file mode 100644 index ed3649bd3..000000000 --- a/std/path/posix.ts +++ /dev/null @@ -1,504 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -// This module is browser compatible. - -import type { FormatInputPathObject, ParsedPath } from "./_interface.ts"; -import { CHAR_DOT, CHAR_FORWARD_SLASH } from "./_constants.ts"; - -import { - _format, - assertPath, - isPosixPathSeparator, - normalizeString, -} from "./_util.ts"; - -export const sep = "/"; -export const delimiter = ":"; - -// path.resolve([from ...], to) -/** - * Resolves `pathSegments` into an absolute path. - * @param pathSegments an array of path segments - */ -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 { - if (globalThis.Deno == null) { - throw new TypeError("Resolved a relative path without a CWD."); - } - path = Deno.cwd(); - } - - assertPath(path); - - // Skip empty entries - if (path.length === 0) { - continue; - } - - resolvedPath = `${path}/${resolvedPath}`; - resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; - } - - // At this point the path should be resolved to a full absolute path, but - // handle relative paths to be safe (might happen when process.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 "."; -} - -/** - * Normalize the `path`, resolving `'..'` and `'.'` segments. - * @param path to be normalized - */ -export function normalize(path: string): string { - assertPath(path); - - if (path.length === 0) return "."; - - const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; - const trailingSeparator = - path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH; - - // 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; -} - -/** - * Verifies whether provided path is absolute - * @param path to be verified as absolute - */ -export function isAbsolute(path: string): boolean { - assertPath(path); - return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH; -} - -/** - * Join all given a sequence of `paths`,then normalizes the resulting path. - * @param paths to be joined and normalized - */ -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); -} - -/** - * Return the relative path from `from` to `to` based on current working directory. - * @param from path in current working directory - * @param to path in current working directory - */ -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 ""; - - // Trim any leading backslashes - let fromStart = 1; - const fromEnd = from.length; - for (; fromStart < fromEnd; ++fromStart) { - if (from.charCodeAt(fromStart) !== CHAR_FORWARD_SLASH) break; - } - const fromLen = fromEnd - fromStart; - - // Trim any leading backslashes - let toStart = 1; - const toEnd = to.length; - for (; toStart < toEnd; ++toStart) { - if (to.charCodeAt(toStart) !== CHAR_FORWARD_SLASH) 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 (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) { - // 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 (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { - // 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 (fromCode === CHAR_FORWARD_SLASH) 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 || from.charCodeAt(i) === CHAR_FORWARD_SLASH) { - 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 (to.charCodeAt(toStart) === CHAR_FORWARD_SLASH) ++toStart; - return to.slice(toStart); - } -} - -/** - * Resolves path to a namespace path - * @param path to resolve to namespace - */ -export function toNamespacedPath(path: string): string { - // Non-op on posix systems - return path; -} - -/** - * Return the directory name of a `path`. - * @param path to determine name for - */ -export function dirname(path: string): string { - assertPath(path); - if (path.length === 0) return "."; - const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH; - let end = -1; - let matchedSlash = true; - for (let i = path.length - 1; i >= 1; --i) { - if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { - if (!matchedSlash) { - end = i; - break; - } - } else { - // We saw the first non-path separator - matchedSlash = false; - } - } - - if (end === -1) return hasRoot ? "/" : "."; - if (hasRoot && end === 1) return "//"; - return path.slice(0, end); -} - -/** - * Return the last portion of a `path`. Trailing directory separators are ignored. - * @param path to process - * @param ext of path directory - */ -export function basename(path: string, ext = ""): string { - if (ext !== undefined && typeof ext !== "string") { - throw new TypeError('"ext" argument must be a string'); - } - assertPath(path); - - let start = 0; - let end = -1; - let matchedSlash = true; - let i: number; - - if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { - if (ext.length === path.length && ext === path) return ""; - let extIdx = ext.length - 1; - let firstNonSlashEnd = -1; - for (i = path.length - 1; i >= 0; --i) { - const code = path.charCodeAt(i); - if (code === CHAR_FORWARD_SLASH) { - // 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) { - start = i + 1; - break; - } - } else { - if (firstNonSlashEnd === -1) { - // We saw the first non-path separator, remember this index in case - // we need it if the extension ends up not matching - matchedSlash = false; - firstNonSlashEnd = i + 1; - } - if (extIdx >= 0) { - // Try to match the explicit extension - if (code === ext.charCodeAt(extIdx)) { - if (--extIdx === -1) { - // We matched the extension, so mark this as the end of our path - // component - end = i; - } - } else { - // Extension does not match, so our result is the entire path - // component - extIdx = -1; - end = firstNonSlashEnd; - } - } - } - } - - if (start === end) end = firstNonSlashEnd; - else if (end === -1) end = path.length; - return path.slice(start, end); - } else { - for (i = path.length - 1; i >= 0; --i) { - if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { - // 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) { - start = i + 1; - break; - } - } else if (end === -1) { - // We saw the first non-path separator, mark this as the end of our - // path component - matchedSlash = false; - end = i + 1; - } - } - - if (end === -1) return ""; - return path.slice(start, end); - } -} - -/** - * Return the extension of the `path`. - * @param path with extension - */ -export function extname(path: string): string { - 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 (code === CHAR_FORWARD_SLASH) { - // 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); -} - -/** - * Generate a path from `FormatInputPathObject` object. - * @param pathObject with path - */ -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); -} - -/** - * Return a `ParsedPath` object of the `path`. - * @param path to process - */ -export function parse(path: string): ParsedPath { - assertPath(path); - - const ret: ParsedPath = { root: "", dir: "", base: "", ext: "", name: "" }; - if (path.length === 0) return ret; - const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; - 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; - - // 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 (code === CHAR_FORWARD_SLASH) { - // 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); - } - } - } 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 = path.slice(0, startPart - 1); - else if (isAbsolute) ret.dir = "/"; - - return ret; -} - -/** - * Converts a file URL to a path string. - * - * fromFileUrl("file:///home/foo"); // "/home/foo" - * @param url of a file URL - */ -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"), - ); -} - -/** - * Converts a path string to a file URL. - * - * toFileUrl("/home/foo"); // new URL("file:///home/foo") - * @param path to convert to file URL - */ -export function toFileUrl(path: string): URL { - if (!isAbsolute(path)) { - throw new TypeError("Must be an absolute path."); - } - const url = new URL("file:///"); - url.pathname = path.replace(/%/g, "%25").replace(/\\/g, "%5C"); - return url; -} diff --git a/std/path/relative_test.ts b/std/path/relative_test.ts deleted file mode 100644 index e00e16d73..000000000 --- a/std/path/relative_test.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -const relativeTests = { - // arguments result - win32: [ - ["c:/blah\\blah", "d:/games", "d:\\games"], - ["c:/aaaa/bbbb", "c:/aaaa", ".."], - ["c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"], - ["c:/aaaa/bbbb", "c:/aaaa/bbbb", ""], - ["c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"], - ["c:/aaaa/", "c:/aaaa/cccc", "cccc"], - ["c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"], - ["c:/aaaa/bbbb", "d:\\", "d:\\"], - ["c:/AaAa/bbbb", "c:/aaaa/bbbb", ""], - ["c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"], - ["C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."], - ["C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"], - ["C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"], - ["C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"], - ["\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"], - ["\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."], - ["\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"], - ["\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"], - ["C:\\baz-quux", "C:\\baz", "..\\baz"], - ["C:\\baz", "C:\\baz-quux", "..\\baz-quux"], - ["\\\\foo\\baz-quux", "\\\\foo\\baz", "..\\baz"], - ["\\\\foo\\baz", "\\\\foo\\baz-quux", "..\\baz-quux"], - ["C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"], - ["\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"], - ], - // arguments result - posix: [ - ["/var/lib", "/var", ".."], - ["/var/lib", "/bin", "../../bin"], - ["/var/lib", "/var/lib", ""], - ["/var/lib", "/var/apache", "../apache"], - ["/var/", "/var/lib", "lib"], - ["/", "/var/lib", "var/lib"], - ["/foo/test", "/foo/test/bar/package.json", "bar/package.json"], - ["/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."], - ["/foo/bar/baz-quux", "/foo/bar/baz", "../baz"], - ["/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"], - ["/baz-quux", "/baz", "../baz"], - ["/baz", "/baz-quux", "../baz-quux"], - ], -}; - -Deno.test("relative", function () { - relativeTests.posix.forEach(function (p) { - const expected = p[2]; - const actual = path.posix.relative(p[0], p[1]); - assertEquals(actual, expected); - }); -}); - -Deno.test("relativeWin32", function () { - relativeTests.win32.forEach(function (p) { - const expected = p[2]; - const actual = path.win32.relative(p[0], p[1]); - assertEquals(actual, expected); - }); -}); diff --git a/std/path/resolve_test.ts b/std/path/resolve_test.ts deleted file mode 100644 index dec032f47..000000000 --- a/std/path/resolve_test.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -const windowsTests = - // arguments result - [ - [["c:/blah\\blah", "d:/games", "c:../a"], "c:\\blah\\a"], - [["c:/ignore", "d:\\a/b\\c/d", "\\e.exe"], "d:\\e.exe"], - [["c:/ignore", "c:/some/file"], "c:\\some\\file"], - [["d:/ignore", "d:some/dir//"], "d:\\ignore\\some\\dir"], - [["//server/share", "..", "relative\\"], "\\\\server\\share\\relative"], - [["c:/", "//"], "c:\\"], - [["c:/", "//dir"], "c:\\dir"], - [["c:/", "//server/share"], "\\\\server\\share\\"], - [["c:/", "//server//share"], "\\\\server\\share\\"], - [["c:/", "///some//dir"], "c:\\some\\dir"], - [ - ["C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js"], - "C:\\foo\\tmp.3\\cycles\\root.js", - ], - ]; -const posixTests = - // arguments result - [ - [["/var/lib", "../", "file/"], "/var/file"], - [["/var/lib", "/../", "file/"], "/file"], - [["a/b/c/", "../../.."], Deno.cwd()], - [["."], Deno.cwd()], - [["/some/dir", ".", "/absolute/"], "/absolute"], - [["/foo/tmp.3/", "../tmp.3/cycles/root.js"], "/foo/tmp.3/cycles/root.js"], - ]; - -Deno.test("resolve", function () { - posixTests.forEach(function (p) { - const _p = p[0] as string[]; - const actual = path.posix.resolve.apply(null, _p); - assertEquals(actual, p[1]); - }); -}); - -Deno.test("resolveWin32", function () { - windowsTests.forEach(function (p) { - const _p = p[0] as string[]; - const actual = path.win32.resolve.apply(null, _p); - assertEquals(actual, p[1]); - }); -}); diff --git a/std/path/separator.ts b/std/path/separator.ts deleted file mode 100644 index d897e07e6..000000000 --- a/std/path/separator.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. - -import { isWindows } from "../_util/os.ts"; - -export const SEP = isWindows ? "\\" : "/"; -export const SEP_PATTERN = isWindows ? /[\\/]+/ : /\/+/; diff --git a/std/path/test.ts b/std/path/test.ts deleted file mode 100644 index 590417055..000000000 --- a/std/path/test.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import "./mod.ts"; diff --git a/std/path/to_file_url_test.ts b/std/path/to_file_url_test.ts deleted file mode 100644 index c4ee1a236..000000000 --- a/std/path/to_file_url_test.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { posix, win32 } from "./mod.ts"; -import { assertEquals, assertThrows } from "../testing/asserts.ts"; - -Deno.test("[path] toFileUrl", function () { - assertEquals(posix.toFileUrl("/home/foo").href, "file:///home/foo"); - assertEquals(posix.toFileUrl("/home/ ").href, "file:///home/%20"); - assertEquals(posix.toFileUrl("/home/%20").href, "file:///home/%2520"); - assertEquals(posix.toFileUrl("/home\\foo").href, "file:///home%5Cfoo"); - assertThrows( - () => posix.toFileUrl("foo").href, - TypeError, - "Must be an absolute path.", - ); - assertThrows( - () => posix.toFileUrl("C:/"), - TypeError, - "Must be an absolute path.", - ); - assertEquals( - posix.toFileUrl("//localhost/home/foo").href, - "file:////localhost/home/foo", - ); - assertEquals(posix.toFileUrl("//localhost/").href, "file:////localhost/"); - assertEquals(posix.toFileUrl("//:/home/foo").href, "file:////:/home/foo"); -}); - -Deno.test("[path] toFileUrl (win32)", function () { - assertEquals(win32.toFileUrl("/home/foo").href, "file:///home/foo"); - assertEquals(win32.toFileUrl("/home/ ").href, "file:///home/%20"); - assertEquals(win32.toFileUrl("/home/%20").href, "file:///home/%2520"); - assertEquals(win32.toFileUrl("/home\\foo").href, "file:///home/foo"); - assertThrows( - () => win32.toFileUrl("foo").href, - TypeError, - "Must be an absolute path.", - ); - assertEquals(win32.toFileUrl("C:/").href, "file:///C:/"); - assertEquals( - win32.toFileUrl("//localhost/home/foo").href, - "file://localhost/home/foo", - ); - assertEquals(win32.toFileUrl("//localhost/").href, "file:////localhost/"); - assertThrows( - () => win32.toFileUrl("//:/home/foo").href, - TypeError, - "Invalid hostname.", - ); -}); diff --git a/std/path/win32.ts b/std/path/win32.ts deleted file mode 100644 index d456c68e4..000000000 --- a/std/path/win32.ts +++ /dev/null @@ -1,1001 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -// This module is browser compatible. - -import type { FormatInputPathObject, ParsedPath } from "./_interface.ts"; -import { - CHAR_BACKWARD_SLASH, - CHAR_COLON, - CHAR_DOT, - CHAR_QUESTION_MARK, -} from "./_constants.ts"; - -import { - _format, - assertPath, - isPathSeparator, - isWindowsDeviceRoot, - normalizeString, -} from "./_util.ts"; -import { assert } from "../_util/assert.ts"; - -export const sep = "\\"; -export const delimiter = ";"; - -/** - * Resolves path segments into a `path` - * @param pathSegments to process to path - */ -export function resolve(...pathSegments: string[]): string { - let resolvedDevice = ""; - let resolvedTail = ""; - let resolvedAbsolute = false; - - for (let i = pathSegments.length - 1; i >= -1; i--) { - let path: string; - if (i >= 0) { - path = pathSegments[i]; - } else if (!resolvedDevice) { - if (globalThis.Deno == null) { - throw new TypeError("Resolved a drive-letter-less path without a CWD."); - } - path = Deno.cwd(); - } else { - if (globalThis.Deno == null) { - throw new TypeError("Resolved a relative path without a CWD."); - } - // Windows has the concept of drive-specific current working - // directories. If we've resolved a drive letter but not yet an - // absolute path, get cwd for that drive, or the process cwd if - // the drive cwd is not available. We're sure the device is not - // a UNC path at this points, because UNC paths are always absolute. - path = Deno.env.get(`=${resolvedDevice}`) || Deno.cwd(); - - // Verify that a cwd was found and that it actually points - // to our drive. If not, default to the drive's root. - if ( - path === undefined || - path.slice(0, 3).toLowerCase() !== `${resolvedDevice.toLowerCase()}\\` - ) { - path = `${resolvedDevice}\\`; - } - } - - assertPath(path); - - const len = path.length; - - // Skip empty entries - if (len === 0) continue; - - let rootEnd = 0; - let device = ""; - let isAbsolute = false; - const code = path.charCodeAt(0); - - // Try to match a root - if (len > 1) { - if (isPathSeparator(code)) { - // Possible UNC root - - // If we started with a separator, we know we at least have an - // absolute path of some kind (UNC or otherwise) - isAbsolute = true; - - if (isPathSeparator(path.charCodeAt(1))) { - // Matched double path separator at beginning - let j = 2; - let last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - const firstPart = path.slice(last, j); - // Matched! - last = j; - // Match 1 or more path separators - for (; j < len; ++j) { - if (!isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j === len) { - // We matched a UNC root only - device = `\\\\${firstPart}\\${path.slice(last)}`; - rootEnd = j; - } else if (j !== last) { - // We matched a UNC root with leftovers - - device = `\\\\${firstPart}\\${path.slice(last, j)}`; - rootEnd = j; - } - } - } - } else { - rootEnd = 1; - } - } else if (isWindowsDeviceRoot(code)) { - // Possible device root - - if (path.charCodeAt(1) === CHAR_COLON) { - device = path.slice(0, 2); - rootEnd = 2; - if (len > 2) { - if (isPathSeparator(path.charCodeAt(2))) { - // Treat separator following drive name as an absolute path - // indicator - isAbsolute = true; - rootEnd = 3; - } - } - } - } - } else if (isPathSeparator(code)) { - // `path` contains just a path separator - rootEnd = 1; - isAbsolute = true; - } - - if ( - device.length > 0 && - resolvedDevice.length > 0 && - device.toLowerCase() !== resolvedDevice.toLowerCase() - ) { - // This path points to another device so it is not applicable - continue; - } - - if (resolvedDevice.length === 0 && device.length > 0) { - resolvedDevice = device; - } - if (!resolvedAbsolute) { - resolvedTail = `${path.slice(rootEnd)}\\${resolvedTail}`; - resolvedAbsolute = isAbsolute; - } - - if (resolvedAbsolute && resolvedDevice.length > 0) break; - } - - // At this point the path should be resolved to a full absolute path, - // but handle relative paths to be safe (might happen when process.cwd() - // fails) - - // Normalize the tail path - resolvedTail = normalizeString( - resolvedTail, - !resolvedAbsolute, - "\\", - isPathSeparator, - ); - - return resolvedDevice + (resolvedAbsolute ? "\\" : "") + resolvedTail || "."; -} - -/** - * Normalizes a `path` - * @param path to normalize - */ -export function normalize(path: string): string { - assertPath(path); - const len = path.length; - if (len === 0) return "."; - let rootEnd = 0; - let device: string | undefined; - let isAbsolute = false; - const code = path.charCodeAt(0); - - // Try to match a root - if (len > 1) { - if (isPathSeparator(code)) { - // Possible UNC root - - // If we started with a separator, we know we at least have an absolute - // path of some kind (UNC or otherwise) - isAbsolute = true; - - if (isPathSeparator(path.charCodeAt(1))) { - // Matched double path separator at beginning - let j = 2; - let last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - const firstPart = path.slice(last, j); - // Matched! - last = j; - // Match 1 or more path separators - for (; j < len; ++j) { - if (!isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j === len) { - // We matched a UNC root only - // Return the normalized version of the UNC root since there - // is nothing left to process - - return `\\\\${firstPart}\\${path.slice(last)}\\`; - } else if (j !== last) { - // We matched a UNC root with leftovers - - device = `\\\\${firstPart}\\${path.slice(last, j)}`; - rootEnd = j; - } - } - } - } else { - rootEnd = 1; - } - } else if (isWindowsDeviceRoot(code)) { - // Possible device root - - if (path.charCodeAt(1) === CHAR_COLON) { - device = path.slice(0, 2); - rootEnd = 2; - if (len > 2) { - if (isPathSeparator(path.charCodeAt(2))) { - // Treat separator following drive name as an absolute path - // indicator - isAbsolute = true; - rootEnd = 3; - } - } - } - } - } else if (isPathSeparator(code)) { - // `path` contains just a path separator, exit early to avoid unnecessary - // work - return "\\"; - } - - let tail: string; - if (rootEnd < len) { - tail = normalizeString( - path.slice(rootEnd), - !isAbsolute, - "\\", - isPathSeparator, - ); - } else { - tail = ""; - } - if (tail.length === 0 && !isAbsolute) tail = "."; - if (tail.length > 0 && isPathSeparator(path.charCodeAt(len - 1))) { - tail += "\\"; - } - if (device === undefined) { - if (isAbsolute) { - if (tail.length > 0) return `\\${tail}`; - else return "\\"; - } else if (tail.length > 0) { - return tail; - } else { - return ""; - } - } else if (isAbsolute) { - if (tail.length > 0) return `${device}\\${tail}`; - else return `${device}\\`; - } else if (tail.length > 0) { - return device + tail; - } else { - return device; - } -} - -/** - * Verifies whether path is absolute - * @param path to verify - */ -export function isAbsolute(path: string): boolean { - assertPath(path); - const len = path.length; - if (len === 0) return false; - - const code = path.charCodeAt(0); - if (isPathSeparator(code)) { - return true; - } else if (isWindowsDeviceRoot(code)) { - // Possible device root - - if (len > 2 && path.charCodeAt(1) === CHAR_COLON) { - if (isPathSeparator(path.charCodeAt(2))) return true; - } - } - return false; -} - -/** - * Join all given a sequence of `paths`,then normalizes the resulting path. - * @param paths to be joined and normalized - */ -export function join(...paths: string[]): string { - const pathsCount = paths.length; - if (pathsCount === 0) return "."; - - let joined: string | undefined; - let firstPart: string | null = null; - for (let i = 0; i < pathsCount; ++i) { - const path = paths[i]; - assertPath(path); - if (path.length > 0) { - if (joined === undefined) joined = firstPart = path; - else joined += `\\${path}`; - } - } - - if (joined === undefined) return "."; - - // Make sure that the joined path doesn't start with two slashes, because - // normalize() will mistake it for an UNC path then. - // - // This step is skipped when it is very clear that the user actually - // intended to point at an UNC path. This is assumed when the first - // non-empty string arguments starts with exactly two slashes followed by - // at least one more non-slash character. - // - // Note that for normalize() to treat a path as an UNC path it needs to - // have at least 2 components, so we don't filter for that here. - // This means that the user can use join to construct UNC paths from - // a server name and a share name; for example: - // path.join('//server', 'share') -> '\\\\server\\share\\') - let needsReplace = true; - let slashCount = 0; - assert(firstPart != null); - if (isPathSeparator(firstPart.charCodeAt(0))) { - ++slashCount; - const firstLen = firstPart.length; - if (firstLen > 1) { - if (isPathSeparator(firstPart.charCodeAt(1))) { - ++slashCount; - if (firstLen > 2) { - if (isPathSeparator(firstPart.charCodeAt(2))) ++slashCount; - else { - // We matched a UNC path in the first part - needsReplace = false; - } - } - } - } - } - if (needsReplace) { - // Find any more consecutive slashes we need to replace - for (; slashCount < joined.length; ++slashCount) { - if (!isPathSeparator(joined.charCodeAt(slashCount))) break; - } - - // Replace the slashes if needed - if (slashCount >= 2) joined = `\\${joined.slice(slashCount)}`; - } - - return normalize(joined); -} - -/** - * It will solve the relative path from `from` to `to`, for instance: - * from = 'C:\\orandea\\test\\aaa' - * to = 'C:\\orandea\\impl\\bbb' - * The output of the function should be: '..\\..\\impl\\bbb' - * @param from relative path - * @param to relative path - */ -export function relative(from: string, to: string): string { - assertPath(from); - assertPath(to); - - if (from === to) return ""; - - const fromOrig = resolve(from); - const toOrig = resolve(to); - - if (fromOrig === toOrig) return ""; - - from = fromOrig.toLowerCase(); - to = toOrig.toLowerCase(); - - if (from === to) return ""; - - // Trim any leading backslashes - let fromStart = 0; - let fromEnd = from.length; - for (; fromStart < fromEnd; ++fromStart) { - if (from.charCodeAt(fromStart) !== CHAR_BACKWARD_SLASH) break; - } - // Trim trailing backslashes (applicable to UNC paths only) - for (; fromEnd - 1 > fromStart; --fromEnd) { - if (from.charCodeAt(fromEnd - 1) !== CHAR_BACKWARD_SLASH) break; - } - const fromLen = fromEnd - fromStart; - - // Trim any leading backslashes - let toStart = 0; - let toEnd = to.length; - for (; toStart < toEnd; ++toStart) { - if (to.charCodeAt(toStart) !== CHAR_BACKWARD_SLASH) break; - } - // Trim trailing backslashes (applicable to UNC paths only) - for (; toEnd - 1 > toStart; --toEnd) { - if (to.charCodeAt(toEnd - 1) !== CHAR_BACKWARD_SLASH) 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 (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) { - // We get here if `from` is the exact base path for `to`. - // For example: from='C:\\foo\\bar'; to='C:\\foo\\bar\\baz' - return toOrig.slice(toStart + i + 1); - } else if (i === 2) { - // We get here if `from` is the device root. - // For example: from='C:\\'; to='C:\\foo' - return toOrig.slice(toStart + i); - } - } - if (fromLen > length) { - if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) { - // We get here if `to` is the exact base path for `from`. - // For example: from='C:\\foo\\bar'; to='C:\\foo' - lastCommonSep = i; - } else if (i === 2) { - // We get here if `to` is the device root. - // For example: from='C:\\foo\\bar'; to='C:\\' - lastCommonSep = 3; - } - } - break; - } - const fromCode = from.charCodeAt(fromStart + i); - const toCode = to.charCodeAt(toStart + i); - if (fromCode !== toCode) break; - else if (fromCode === CHAR_BACKWARD_SLASH) lastCommonSep = i; - } - - // We found a mismatch before the first common path separator was seen, so - // return the original `to`. - if (i !== length && lastCommonSep === -1) { - return toOrig; - } - - let out = ""; - if (lastCommonSep === -1) lastCommonSep = 0; - // Generate the relative path based on the path difference between `to` and - // `from` - for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { - if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) { - 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 + toOrig.slice(toStart + lastCommonSep, toEnd); - } else { - toStart += lastCommonSep; - if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) ++toStart; - return toOrig.slice(toStart, toEnd); - } -} - -/** - * Resolves path to a namespace path - * @param path to resolve to namespace - */ -export function toNamespacedPath(path: string): string { - // Note: this will *probably* throw somewhere. - if (typeof path !== "string") return path; - if (path.length === 0) return ""; - - const resolvedPath = resolve(path); - - if (resolvedPath.length >= 3) { - if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) { - // Possible UNC root - - if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) { - const code = resolvedPath.charCodeAt(2); - if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) { - // Matched non-long UNC root, convert the path to a long UNC path - return `\\\\?\\UNC\\${resolvedPath.slice(2)}`; - } - } - } else if (isWindowsDeviceRoot(resolvedPath.charCodeAt(0))) { - // Possible device root - - if ( - resolvedPath.charCodeAt(1) === CHAR_COLON && - resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH - ) { - // Matched device root, convert the path to a long UNC path - return `\\\\?\\${resolvedPath}`; - } - } - } - - return path; -} - -/** - * Return the directory name of a `path`. - * @param path to determine name for - */ -export function dirname(path: string): string { - assertPath(path); - const len = path.length; - if (len === 0) return "."; - let rootEnd = -1; - let end = -1; - let matchedSlash = true; - let offset = 0; - const code = path.charCodeAt(0); - - // Try to match a root - if (len > 1) { - if (isPathSeparator(code)) { - // Possible UNC root - - rootEnd = offset = 1; - - if (isPathSeparator(path.charCodeAt(1))) { - // Matched double path separator at beginning - let j = 2; - let last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more path separators - for (; j < len; ++j) { - if (!isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j === len) { - // We matched a UNC root only - return path; - } - if (j !== last) { - // We matched a UNC root with leftovers - - // Offset by 1 to include the separator after the UNC root to - // treat it as a "normal root" on top of a (UNC) root - rootEnd = offset = j + 1; - } - } - } - } - } else if (isWindowsDeviceRoot(code)) { - // Possible device root - - if (path.charCodeAt(1) === CHAR_COLON) { - rootEnd = offset = 2; - if (len > 2) { - if (isPathSeparator(path.charCodeAt(2))) rootEnd = offset = 3; - } - } - } - } else if (isPathSeparator(code)) { - // `path` contains just a path separator, exit early to avoid - // unnecessary work - return path; - } - - for (let i = len - 1; i >= offset; --i) { - if (isPathSeparator(path.charCodeAt(i))) { - if (!matchedSlash) { - end = i; - break; - } - } else { - // We saw the first non-path separator - matchedSlash = false; - } - } - - if (end === -1) { - if (rootEnd === -1) return "."; - else end = rootEnd; - } - return path.slice(0, end); -} - -/** - * Return the last portion of a `path`. Trailing directory separators are ignored. - * @param path to process - * @param ext of path directory - */ -export function basename(path: string, ext = ""): string { - if (ext !== undefined && typeof ext !== "string") { - throw new TypeError('"ext" argument must be a string'); - } - - assertPath(path); - - let start = 0; - let end = -1; - let matchedSlash = true; - let i: number; - - // Check for a drive letter prefix so as not to mistake the following - // path separator as an extra separator at the end of the path that can be - // disregarded - if (path.length >= 2) { - const drive = path.charCodeAt(0); - if (isWindowsDeviceRoot(drive)) { - if (path.charCodeAt(1) === CHAR_COLON) start = 2; - } - } - - if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { - if (ext.length === path.length && ext === path) return ""; - let extIdx = ext.length - 1; - let firstNonSlashEnd = -1; - for (i = path.length - 1; i >= start; --i) { - const code = path.charCodeAt(i); - if (isPathSeparator(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) { - start = i + 1; - break; - } - } else { - if (firstNonSlashEnd === -1) { - // We saw the first non-path separator, remember this index in case - // we need it if the extension ends up not matching - matchedSlash = false; - firstNonSlashEnd = i + 1; - } - if (extIdx >= 0) { - // Try to match the explicit extension - if (code === ext.charCodeAt(extIdx)) { - if (--extIdx === -1) { - // We matched the extension, so mark this as the end of our path - // component - end = i; - } - } else { - // Extension does not match, so our result is the entire path - // component - extIdx = -1; - end = firstNonSlashEnd; - } - } - } - } - - if (start === end) end = firstNonSlashEnd; - else if (end === -1) end = path.length; - return path.slice(start, end); - } else { - for (i = path.length - 1; i >= start; --i) { - if (isPathSeparator(path.charCodeAt(i))) { - // 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) { - start = i + 1; - break; - } - } else if (end === -1) { - // We saw the first non-path separator, mark this as the end of our - // path component - matchedSlash = false; - end = i + 1; - } - } - - if (end === -1) return ""; - return path.slice(start, end); - } -} - -/** - * Return the extension of the `path`. - * @param path with extension - */ -export function extname(path: string): string { - assertPath(path); - let start = 0; - 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; - - // Check for a drive letter prefix so as not to mistake the following - // path separator as an extra separator at the end of the path that can be - // disregarded - - if ( - path.length >= 2 && - path.charCodeAt(1) === CHAR_COLON && - isWindowsDeviceRoot(path.charCodeAt(0)) - ) { - start = startPart = 2; - } - - for (let i = path.length - 1; i >= start; --i) { - const code = path.charCodeAt(i); - if (isPathSeparator(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); -} - -/** - * Generate a path from `FormatInputPathObject` object. - * @param pathObject with path - */ -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); -} - -/** - * Return a `ParsedPath` object of the `path`. - * @param path to process - */ -export function parse(path: string): ParsedPath { - assertPath(path); - - const ret: ParsedPath = { root: "", dir: "", base: "", ext: "", name: "" }; - - const len = path.length; - if (len === 0) return ret; - - let rootEnd = 0; - let code = path.charCodeAt(0); - - // Try to match a root - if (len > 1) { - if (isPathSeparator(code)) { - // Possible UNC root - - rootEnd = 1; - if (isPathSeparator(path.charCodeAt(1))) { - // Matched double path separator at beginning - let j = 2; - let last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more path separators - for (; j < len; ++j) { - if (!isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j === len) { - // We matched a UNC root only - - rootEnd = j; - } else if (j !== last) { - // We matched a UNC root with leftovers - - rootEnd = j + 1; - } - } - } - } - } else if (isWindowsDeviceRoot(code)) { - // Possible device root - - if (path.charCodeAt(1) === CHAR_COLON) { - rootEnd = 2; - if (len > 2) { - if (isPathSeparator(path.charCodeAt(2))) { - if (len === 3) { - // `path` contains just a drive root, exit early to avoid - // unnecessary work - ret.root = ret.dir = path; - return ret; - } - rootEnd = 3; - } - } else { - // `path` contains just a drive root, exit early to avoid - // unnecessary work - ret.root = ret.dir = path; - return ret; - } - } - } - } else if (isPathSeparator(code)) { - // `path` contains just a path separator, exit early to avoid - // unnecessary work - ret.root = ret.dir = path; - return ret; - } - - if (rootEnd > 0) ret.root = path.slice(0, rootEnd); - - let startDot = -1; - let startPart = rootEnd; - 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 >= rootEnd; --i) { - code = path.charCodeAt(i); - if (isPathSeparator(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) { - ret.base = ret.name = path.slice(startPart, end); - } - } else { - ret.name = path.slice(startPart, startDot); - ret.base = path.slice(startPart, end); - ret.ext = path.slice(startDot, end); - } - - // If the directory is the root, use the entire root as the `dir` including - // the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the - // trailing slash (`C:\abc\def` -> `C:\abc`). - if (startPart > 0 && startPart !== rootEnd) { - ret.dir = path.slice(0, startPart - 1); - } else ret.dir = ret.root; - - return ret; -} - -/** - * Converts a file URL to a path string. - * - * fromFileUrl("file:///home/foo"); // "\\home\\foo" - * fromFileUrl("file:///C:/Users/foo"); // "C:\\Users\\foo" - * fromFileUrl("file://localhost/home/foo"); // "\\\\localhost\\home\\foo" - * @param url of a file URL - */ -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."); - } - let path = decodeURIComponent( - url.pathname.replace(/\//g, "\\").replace(/%(?![0-9A-Fa-f]{2})/g, "%25"), - ).replace(/^\\*([A-Za-z]:)(\\|$)/, "$1\\"); - if (url.hostname != "") { - // Note: The `URL` implementation guarantees that the drive letter and - // hostname are mutually exclusive. Otherwise it would not have been valid - // to append the hostname and path like this. - path = `\\\\${url.hostname}${path}`; - } - return path; -} - -/** - * Converts a path string to a file URL. - * - * toFileUrl("\\home\\foo"); // new URL("file:///home/foo") - * toFileUrl("C:\\Users\\foo"); // new URL("file:///C:/Users/foo") - * toFileUrl("\\\\localhost\\home\\foo"); // new URL("file://localhost/home/foo") - * @param path to convert to file URL - */ -export function toFileUrl(path: string): URL { - if (!isAbsolute(path)) { - throw new TypeError("Must be an absolute path."); - } - const [, hostname, pathname] = path.match( - /^(?:[/\\]{2}([^/\\]+)(?=[/\\][^/\\]))?(.*)/, - )!; - const url = new URL("file:///"); - url.pathname = pathname.replace(/%/g, "%25"); - if (hostname != null) { - url.hostname = hostname; - if (!url.hostname) { - throw new TypeError("Invalid hostname."); - } - } - return url; -} diff --git a/std/path/zero_length_strings_test.ts b/std/path/zero_length_strings_test.ts deleted file mode 100644 index e2ec466a5..000000000 --- a/std/path/zero_length_strings_test.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -const pwd = Deno.cwd(); - -Deno.test("joinZeroLength", function () { - // join will internally ignore all the zero-length strings and it will return - // '.' if the joined string is a zero-length string. - assertEquals(path.posix.join(""), "."); - assertEquals(path.posix.join("", ""), "."); - if (path.win32) assertEquals(path.win32.join(""), "."); - if (path.win32) assertEquals(path.win32.join("", ""), "."); - assertEquals(path.join(pwd), pwd); - assertEquals(path.join(pwd, ""), pwd); -}); - -Deno.test("normalizeZeroLength", function () { - // normalize will return '.' if the input is a zero-length string - assertEquals(path.posix.normalize(""), "."); - if (path.win32) assertEquals(path.win32.normalize(""), "."); - assertEquals(path.normalize(pwd), pwd); -}); - -Deno.test("isAbsoluteZeroLength", function () { - // Since '' is not a valid path in any of the common environments, - // return false - assertEquals(path.posix.isAbsolute(""), false); - if (path.win32) assertEquals(path.win32.isAbsolute(""), false); -}); - -Deno.test("resolveZeroLength", function () { - // resolve, internally ignores all the zero-length strings and returns the - // current working directory - assertEquals(path.resolve(""), pwd); - assertEquals(path.resolve("", ""), pwd); -}); - -Deno.test("relativeZeroLength", function () { - // relative, internally calls resolve. So, '' is actually the current - // directory - assertEquals(path.relative("", pwd), ""); - assertEquals(path.relative(pwd, ""), ""); - assertEquals(path.relative(pwd, pwd), ""); -}); |