diff options
Diffstat (limited to 'std/fs')
43 files changed, 151 insertions, 3968 deletions
diff --git a/std/fs/copy.ts b/std/fs/copy.ts index 616fba975..f5e7f5078 100644 --- a/std/fs/copy.ts +++ b/std/fs/copy.ts @@ -1,5 +1,5 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as path from "./path/mod.ts"; +import * as path from "../path/mod.ts"; import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; import { isSubdir, getFileInfoType } from "./utils.ts"; diff --git a/std/fs/copy_test.ts b/std/fs/copy_test.ts index a11838c4b..46b3418ac 100644 --- a/std/fs/copy_test.ts +++ b/std/fs/copy_test.ts @@ -6,9 +6,9 @@ import { assertThrowsAsync, assert } from "../testing/asserts.ts"; +import * as path from "../path/mod.ts"; import { copy, copySync } from "./copy.ts"; import { exists, existsSync } from "./exists.ts"; -import * as path from "./path/mod.ts"; import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; import { ensureFile, ensureFileSync } from "./ensure_file.ts"; import { ensureSymlink, ensureSymlinkSync } from "./ensure_symlink.ts"; diff --git a/std/fs/empty_dir_test.ts b/std/fs/empty_dir_test.ts index b44e600d7..26e751c74 100644 --- a/std/fs/empty_dir_test.ts +++ b/std/fs/empty_dir_test.ts @@ -5,8 +5,8 @@ import { assertThrows, assertThrowsAsync } from "../testing/asserts.ts"; +import * as path from "../path/mod.ts"; import { emptyDir, emptyDirSync } from "./empty_dir.ts"; -import * as path from "./path/mod.ts"; const testdataDir = path.resolve("fs", "testdata"); diff --git a/std/fs/ensure_dir_test.ts b/std/fs/ensure_dir_test.ts index ad34336dc..068ec8693 100644 --- a/std/fs/ensure_dir_test.ts +++ b/std/fs/ensure_dir_test.ts @@ -1,8 +1,8 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import { test } from "../testing/mod.ts"; import { assertThrows, assertThrowsAsync } from "../testing/asserts.ts"; +import * as path from "../path/mod.ts"; import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; -import * as path from "./path/mod.ts"; import { ensureFile, ensureFileSync } from "./ensure_file.ts"; const testdataDir = path.resolve("fs", "testdata"); diff --git a/std/fs/ensure_file.ts b/std/fs/ensure_file.ts index d749b95e4..0d6d7fbc4 100644 --- a/std/fs/ensure_file.ts +++ b/std/fs/ensure_file.ts @@ -1,5 +1,5 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as path from "./path/mod.ts"; +import * as path from "../path/mod.ts"; import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; import { getFileInfoType } from "./utils.ts"; diff --git a/std/fs/ensure_file_test.ts b/std/fs/ensure_file_test.ts index fa27133ab..bb23084a8 100644 --- a/std/fs/ensure_file_test.ts +++ b/std/fs/ensure_file_test.ts @@ -1,8 +1,8 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import { test } from "../testing/mod.ts"; import { assertThrows, assertThrowsAsync } from "../testing/asserts.ts"; +import * as path from "../path/mod.ts"; import { ensureFile, ensureFileSync } from "./ensure_file.ts"; -import * as path from "./path/mod.ts"; const testdataDir = path.resolve("fs", "testdata"); diff --git a/std/fs/ensure_link.ts b/std/fs/ensure_link.ts index 707f2bee9..f2db5243c 100644 --- a/std/fs/ensure_link.ts +++ b/std/fs/ensure_link.ts @@ -1,5 +1,5 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as path from "./path/mod.ts"; +import * as path from "../path/mod.ts"; import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; import { exists, existsSync } from "./exists.ts"; import { getFileInfoType } from "./utils.ts"; diff --git a/std/fs/ensure_link_test.ts b/std/fs/ensure_link_test.ts index 593df5702..044f80f8d 100644 --- a/std/fs/ensure_link_test.ts +++ b/std/fs/ensure_link_test.ts @@ -6,8 +6,8 @@ import { assertThrows, assertThrowsAsync } from "../testing/asserts.ts"; +import * as path from "../path/mod.ts"; import { ensureLink, ensureLinkSync } from "./ensure_link.ts"; -import * as path from "./path/mod.ts"; const testdataDir = path.resolve("fs", "testdata"); diff --git a/std/fs/ensure_symlink.ts b/std/fs/ensure_symlink.ts index 9b7cc429c..4c771e5f9 100644 --- a/std/fs/ensure_symlink.ts +++ b/std/fs/ensure_symlink.ts @@ -1,5 +1,5 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as path from "./path/mod.ts"; +import * as path from "../path/mod.ts"; import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; import { exists, existsSync } from "./exists.ts"; import { getFileInfoType } from "./utils.ts"; diff --git a/std/fs/ensure_symlink_test.ts b/std/fs/ensure_symlink_test.ts index 4dee56788..0c2c53e5d 100644 --- a/std/fs/ensure_symlink_test.ts +++ b/std/fs/ensure_symlink_test.ts @@ -6,8 +6,8 @@ import { assertThrows, assertThrowsAsync } from "../testing/asserts.ts"; +import * as path from "../path/mod.ts"; import { ensureSymlink, ensureSymlinkSync } from "./ensure_symlink.ts"; -import * as path from "./path/mod.ts"; const testdataDir = path.resolve("fs", "testdata"); const isWindows = Deno.build.os === "win"; diff --git a/std/fs/exists_test.ts b/std/fs/exists_test.ts index 247bb7ed6..e65befa78 100644 --- a/std/fs/exists_test.ts +++ b/std/fs/exists_test.ts @@ -1,8 +1,8 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import { test } from "../testing/mod.ts"; import { assertEquals } from "../testing/asserts.ts"; +import * as path from "../path/mod.ts"; import { exists, existsSync } from "./exists.ts"; -import * as path from "./path/mod.ts"; const testdataDir = path.resolve("fs", "testdata"); diff --git a/std/fs/glob.ts b/std/fs/expand_glob.ts index 9be6197fc..c43e09d40 100644 --- a/std/fs/glob.ts +++ b/std/fs/expand_glob.ts @@ -1,127 +1,17 @@ -import { globrex } from "./globrex.ts"; -import { SEP, SEP_PATTERN, isWindows } from "./path/constants.ts"; -import { isAbsolute, join, normalize } from "./path/mod.ts"; +import { + GlobOptions, + SEP_PATTERN, + globToRegExp, + isAbsolute, + isGlob, + isWindows, + joinGlobs, + normalize +} from "../path/mod.ts"; import { WalkInfo, walk, walkSync } from "./walk.ts"; -const { DenoError, ErrorKind, cwd, stat, statSync } = Deno; +const { cwd, stat, statSync } = Deno; type FileInfo = Deno.FileInfo; -export interface GlobOptions { - extended?: boolean; - globstar?: boolean; -} - -export interface GlobToRegExpOptions extends GlobOptions { - flags?: string; -} - -/** - * Generate a regex based on glob pattern and options - * This was meant to be using the the `fs.walk` function - * but can be used anywhere else. - * Examples: - * - * Looking for all the `ts` files: - * walkSync(".", { - * match: [globToRegExp("*.ts")] - * }) - * - * Looking for all the `.json` files in any subfolder: - * walkSync(".", { - * match: [globToRegExp(join("a", "**", "*.json"),{ - * flags: "g", - * extended: true, - * globstar: true - * })] - * }) - * - * @param glob - Glob pattern to be used - * @param options - Specific options for the glob pattern - * @returns A RegExp for the glob pattern - */ -export function globToRegExp( - glob: string, - options: GlobToRegExpOptions = {} -): RegExp { - const result = globrex(glob, { ...options, strict: false, filepath: true }); - return result.path!.regex; -} - -/** Test whether the given string is a glob */ -export function isGlob(str: string): boolean { - const chars: Record<string, string> = { "{": "}", "(": ")", "[": "]" }; - /* eslint-disable-next-line max-len */ - 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 DenoError( - ErrorKind.InvalidPath, - `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 }); -} - export interface ExpandGlobOptions extends GlobOptions { root?: string; exclude?: string[]; diff --git a/std/fs/expand_glob_test.ts b/std/fs/expand_glob_test.ts new file mode 100644 index 000000000..12b0b3d1a --- /dev/null +++ b/std/fs/expand_glob_test.ts @@ -0,0 +1,120 @@ +const { cwd } = Deno; +import { test, runIfMain } from "../testing/mod.ts"; +import { assert, assertEquals } from "../testing/asserts.ts"; +import { + isWindows, + join, + joinGlobs, + normalize, + relative +} from "../path/mod.ts"; +import { + ExpandGlobOptions, + expandGlob, + expandGlobSync +} from "./expand_glob.ts"; + +async function expandGlobArray( + globString: string, + options: ExpandGlobOptions +): Promise<string[]> { + const paths: string[] = []; + for await (const { filename } of expandGlob(globString, options)) { + paths.push(filename); + } + paths.sort(); + const pathsSync = [...expandGlobSync(globString, options)].map( + ({ filename }): string => filename + ); + pathsSync.sort(); + assertEquals(paths, pathsSync); + const root = normalize(options.root || cwd()); + for (const path of paths) { + assert(path.startsWith(root)); + } + const relativePaths = paths.map( + (path: string): string => relative(root, path) || "." + ); + relativePaths.sort(); + return relativePaths; +} + +function urlToFilePath(url: URL): string { + // Since `new URL('file:///C:/a').pathname` is `/C:/a`, remove leading slash. + return url.pathname.slice(url.protocol == "file:" && isWindows ? 1 : 0); +} + +const EG_OPTIONS: ExpandGlobOptions = { + root: urlToFilePath(new URL(join("testdata", "glob"), import.meta.url)), + includeDirs: true, + extended: false, + globstar: false +}; + +test(async function expandGlobWildcard(): Promise<void> { + const options = EG_OPTIONS; + assertEquals(await expandGlobArray("*", options), [ + "abc", + "abcdef", + "abcdefghi", + "subdir" + ]); +}); + +test(async function expandGlobTrailingSeparator(): Promise<void> { + const options = EG_OPTIONS; + assertEquals(await expandGlobArray("*/", options), ["subdir"]); +}); + +test(async function expandGlobParent(): Promise<void> { + const options = EG_OPTIONS; + assertEquals(await expandGlobArray("subdir/../*", options), [ + "abc", + "abcdef", + "abcdefghi", + "subdir" + ]); +}); + +test(async function expandGlobExt(): Promise<void> { + const options = { ...EG_OPTIONS, extended: true }; + assertEquals(await expandGlobArray("abc?(def|ghi)", options), [ + "abc", + "abcdef" + ]); + assertEquals(await expandGlobArray("abc*(def|ghi)", options), [ + "abc", + "abcdef", + "abcdefghi" + ]); + assertEquals(await expandGlobArray("abc+(def|ghi)", options), [ + "abcdef", + "abcdefghi" + ]); + assertEquals(await expandGlobArray("abc@(def|ghi)", options), ["abcdef"]); + assertEquals(await expandGlobArray("abc{def,ghi}", options), ["abcdef"]); + assertEquals(await expandGlobArray("abc!(def|ghi)", options), ["abc"]); +}); + +test(async function expandGlobGlobstar(): Promise<void> { + const options = { ...EG_OPTIONS, globstar: true }; + assertEquals( + await expandGlobArray(joinGlobs(["**", "abc"], options), options), + ["abc", join("subdir", "abc")] + ); +}); + +test(async function expandGlobGlobstarParent(): Promise<void> { + const options = { ...EG_OPTIONS, globstar: true }; + assertEquals( + await expandGlobArray(joinGlobs(["subdir", "**", ".."], options), options), + ["."] + ); +}); + +test(async function expandGlobIncludeDirs(): Promise<void> { + const options = { ...EG_OPTIONS, includeDirs: false }; + assertEquals(await expandGlobArray("subdir", options), []); +}); + +runIfMain(import.meta); diff --git a/std/fs/glob_test.ts b/std/fs/glob_test.ts deleted file mode 100644 index df819d92c..000000000 --- a/std/fs/glob_test.ts +++ /dev/null @@ -1,374 +0,0 @@ -const { cwd, mkdir } = Deno; -import { test, runIfMain } from "../testing/mod.ts"; -import { assert, assertEquals } from "../testing/asserts.ts"; -import { SEP, isWindows } from "./path/constants.ts"; -import { - ExpandGlobOptions, - expandGlob, - expandGlobSync, - globToRegExp, - isGlob, - joinGlobs, - normalizeGlob -} from "./glob.ts"; -import { join, normalize, relative } from "./path.ts"; -import { testWalk } from "./walk_test.ts"; -import { touch, walkArray } from "./walk_test.ts"; - -test({ - name: "glob: glob to regex", - fn(): void { - assertEquals(globToRegExp("unicorn.*") instanceof RegExp, true); - assertEquals(globToRegExp("unicorn.*").test("poney.ts"), false); - assertEquals(globToRegExp("unicorn.*").test("unicorn.py"), true); - assertEquals(globToRegExp("*.ts").test("poney.ts"), true); - assertEquals(globToRegExp("*.ts").test("unicorn.js"), false); - assertEquals( - globToRegExp(join("unicorn", "**", "cathedral.ts")).test( - join("unicorn", "in", "the", "cathedral.ts") - ), - true - ); - assertEquals( - globToRegExp(join("unicorn", "**", "cathedral.ts")).test( - join("unicorn", "in", "the", "kitchen.ts") - ), - false - ); - assertEquals( - globToRegExp(join("unicorn", "**", "bathroom.*")).test( - join("unicorn", "sleeping", "in", "bathroom.py") - ), - true - ); - assertEquals( - globToRegExp(join("unicorn", "!(sleeping)", "bathroom.ts"), { - extended: true - }).test(join("unicorn", "flying", "bathroom.ts")), - true - ); - assertEquals( - globToRegExp(join("unicorn", "(!sleeping)", "bathroom.ts"), { - extended: true - }).test(join("unicorn", "sleeping", "bathroom.ts")), - false - ); - } -}); - -testWalk( - async (d: string): Promise<void> => { - await mkdir(d + "/a"); - await touch(d + "/a/x.ts"); - }, - async function globInWalk(): Promise<void> { - const arr = await walkArray(".", { match: [globToRegExp("*.ts")] }); - assertEquals(arr.length, 1); - assertEquals(arr[0], "a/x.ts"); - } -); - -testWalk( - async (d: string): Promise<void> => { - await mkdir(d + "/a"); - await mkdir(d + "/b"); - await touch(d + "/a/x.ts"); - await touch(d + "/b/z.ts"); - await touch(d + "/b/z.js"); - }, - async function globInWalkWildcardFiles(): Promise<void> { - const arr = await walkArray(".", { match: [globToRegExp("*.ts")] }); - assertEquals(arr.length, 2); - assertEquals(arr[0], "a/x.ts"); - assertEquals(arr[1], "b/z.ts"); - } -); - -testWalk( - async (d: string): Promise<void> => { - await mkdir(d + "/a"); - await mkdir(d + "/a/yo"); - await touch(d + "/a/yo/x.ts"); - }, - async function globInWalkFolderWildcard(): Promise<void> { - const arr = await walkArray(".", { - match: [ - globToRegExp(join("a", "**", "*.ts"), { - flags: "g", - globstar: true - }) - ] - }); - assertEquals(arr.length, 1); - assertEquals(arr[0], "a/yo/x.ts"); - } -); - -testWalk( - async (d: string): Promise<void> => { - await mkdir(d + "/a"); - await mkdir(d + "/a/unicorn"); - await mkdir(d + "/a/deno"); - await mkdir(d + "/a/raptor"); - await touch(d + "/a/raptor/x.ts"); - await touch(d + "/a/deno/x.ts"); - await touch(d + "/a/unicorn/x.ts"); - }, - async function globInWalkFolderExtended(): Promise<void> { - const arr = await walkArray(".", { - match: [ - globToRegExp(join("a", "+(raptor|deno)", "*.ts"), { - flags: "g", - extended: true - }) - ] - }); - assertEquals(arr.length, 2); - assertEquals(arr[0], "a/deno/x.ts"); - assertEquals(arr[1], "a/raptor/x.ts"); - } -); - -testWalk( - async (d: string): Promise<void> => { - await touch(d + "/x.ts"); - await touch(d + "/x.js"); - await touch(d + "/b.js"); - }, - async function globInWalkWildcardExtension(): Promise<void> { - const arr = await walkArray(".", { - match: [globToRegExp("x.*", { flags: "g", globstar: true })] - }); - assertEquals(arr.length, 2); - assertEquals(arr[0], "x.js"); - assertEquals(arr[1], "x.ts"); - } -); - -test({ - name: "isGlob: pattern to test", - 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")); - } -}); - -test(function normalizeGlobGlobstar(): void { - assertEquals(normalizeGlob(`**${SEP}..`, { globstar: true }), `**${SEP}..`); -}); - -test(function joinGlobsGlobstar(): void { - assertEquals(joinGlobs(["**", ".."], { globstar: true }), `**${SEP}..`); -}); - -async function expandGlobArray( - globString: string, - options: ExpandGlobOptions -): Promise<string[]> { - const paths: string[] = []; - for await (const { filename } of expandGlob(globString, options)) { - paths.push(filename); - } - paths.sort(); - const pathsSync = [...expandGlobSync(globString, options)].map( - ({ filename }): string => filename - ); - pathsSync.sort(); - assertEquals(paths, pathsSync); - const root = normalize(options.root || cwd()); - for (const path of paths) { - assert(path.startsWith(root)); - } - const relativePaths = paths.map( - (path: string): string => relative(root, path) || "." - ); - relativePaths.sort(); - return relativePaths; -} - -function urlToFilePath(url: URL): string { - // Since `new URL('file:///C:/a').pathname` is `/C:/a`, remove leading slash. - return url.pathname.slice(url.protocol == "file:" && isWindows ? 1 : 0); -} - -const EG_OPTIONS: ExpandGlobOptions = { - root: urlToFilePath(new URL(join("testdata", "glob"), import.meta.url)), - includeDirs: true, - extended: false, - globstar: false -}; - -test(async function expandGlobWildcard(): Promise<void> { - const options = EG_OPTIONS; - assertEquals(await expandGlobArray("*", options), [ - "abc", - "abcdef", - "abcdefghi", - "subdir" - ]); -}); - -test(async function expandGlobTrailingSeparator(): Promise<void> { - const options = EG_OPTIONS; - assertEquals(await expandGlobArray("*/", options), ["subdir"]); -}); - -test(async function expandGlobParent(): Promise<void> { - const options = EG_OPTIONS; - assertEquals(await expandGlobArray("subdir/../*", options), [ - "abc", - "abcdef", - "abcdefghi", - "subdir" - ]); -}); - -test(async function expandGlobExt(): Promise<void> { - const options = { ...EG_OPTIONS, extended: true }; - assertEquals(await expandGlobArray("abc?(def|ghi)", options), [ - "abc", - "abcdef" - ]); - assertEquals(await expandGlobArray("abc*(def|ghi)", options), [ - "abc", - "abcdef", - "abcdefghi" - ]); - assertEquals(await expandGlobArray("abc+(def|ghi)", options), [ - "abcdef", - "abcdefghi" - ]); - assertEquals(await expandGlobArray("abc@(def|ghi)", options), ["abcdef"]); - assertEquals(await expandGlobArray("abc{def,ghi}", options), ["abcdef"]); - assertEquals(await expandGlobArray("abc!(def|ghi)", options), ["abc"]); -}); - -test(async function expandGlobGlobstar(): Promise<void> { - const options = { ...EG_OPTIONS, globstar: true }; - assertEquals( - await expandGlobArray(joinGlobs(["**", "abc"], options), options), - ["abc", join("subdir", "abc")] - ); -}); - -test(async function expandGlobGlobstarParent(): Promise<void> { - const options = { ...EG_OPTIONS, globstar: true }; - assertEquals( - await expandGlobArray(joinGlobs(["subdir", "**", ".."], options), options), - ["."] - ); -}); - -test(async function expandGlobIncludeDirs(): Promise<void> { - const options = { ...EG_OPTIONS, includeDirs: false }; - assertEquals(await expandGlobArray("subdir", options), []); -}); - -runIfMain(import.meta); diff --git a/std/fs/globrex.ts b/std/fs/globrex.ts deleted file mode 100644 index 5bf0e16a0..000000000 --- a/std/fs/globrex.ts +++ /dev/null @@ -1,326 +0,0 @@ -// This file is ported from globrex@0.1.2 -// MIT License -// Copyright (c) 2018 Terkel Gjervig Nielsen - -const isWin = Deno.build.os === "win"; -const SEP = isWin ? `(\\\\+|\\/)` : `\\/`; -const SEP_ESC = isWin ? `\\\\` : `/`; -const SEP_RAW = isWin ? `\\` : `/`; -const GLOBSTAR = `((?:[^${SEP_ESC}/]*(?:${SEP_ESC}|\/|$))*)`; -const WILDCARD = `([^${SEP_ESC}/]*)`; -const GLOBSTAR_SEGMENT = `((?:[^${SEP_ESC}/]*(?:${SEP_ESC}|\/|$))*)`; -const WILDCARD_SEGMENT = `([^${SEP_ESC}/]*)`; - -export interface GlobrexOptions { - // Allow ExtGlob features - extended?: boolean; - // When globstar is true, '/foo/**' is equivelant - // to '/foo/*' when globstar is false. - // Having globstar set to true is the same usage as - // using wildcards in bash - globstar?: boolean; - // be laissez faire about mutiple slashes - strict?: boolean; - // Parse as filepath for extra path related features - filepath?: boolean; - // Flag to use in the generated RegExp - flags?: string; -} - -export interface GlobrexResult { - regex: RegExp; - path?: { - regex: RegExp; - segments: RegExp[]; - globstar?: RegExp; - }; -} - -/** - * Convert any glob pattern to a JavaScript Regexp object - * @param glob Glob pattern to convert - * @param opts Configuration object - * @param [opts.extended=false] Support advanced ext globbing - * @param [opts.globstar=false] Support globstar - * @param [opts.strict=true] be laissez faire about mutiple slashes - * @param [opts.filepath=""] Parse as filepath for extra path related features - * @param [opts.flags=""] RegExp globs - * @returns Converted object with string, segments and RegExp object - */ -export function globrex( - glob: string, - { - extended = false, - globstar = false, - strict = false, - filepath = false, - flags = "" - }: GlobrexOptions = {} -): GlobrexResult { - let regex = ""; - let segment = ""; - let pathRegexStr = ""; - const pathSegments = []; - - // If we are doing extended matching, this boolean is true when we are inside - // a group (eg {*.html,*.js}), and false otherwise. - let inGroup = false; - let inRange = false; - - // extglob stack. Keep track of scope - const ext = []; - - interface AddOptions { - split?: boolean; - last?: boolean; - only?: string; - } - - // Helper function to build string and segments - function add( - str: string, - options: AddOptions = { split: false, last: false, only: "" } - ): void { - const { split, last, only } = options; - if (only !== "path") regex += str; - if (filepath && only !== "regex") { - pathRegexStr += str.match(new RegExp(`^${SEP}$`)) ? SEP : str; - if (split) { - if (last) segment += str; - if (segment !== "") { - // change it 'includes' - if (!flags.includes("g")) segment = `^${segment}$`; - pathSegments.push(new RegExp(segment, flags)); - } - segment = ""; - } else { - segment += str; - } - } - } - - let c, n; - for (let i = 0; i < glob.length; i++) { - c = glob[i]; - n = glob[i + 1]; - - if (["\\", "$", "^", ".", "="].includes(c)) { - add(`\\${c}`); - continue; - } - - if (c === "/") { - add(`\\${c}`, { split: true }); - if (n === "/" && !strict) regex += "?"; - continue; - } - - if (c === "(") { - if (ext.length) { - add(c); - continue; - } - add(`\\${c}`); - continue; - } - - if (c === ")") { - if (ext.length) { - add(c); - const type: string | undefined = ext.pop(); - if (type === "@") { - add("{1}"); - } else if (type === "!") { - add("([^/]*)"); - } else { - add(type as string); - } - continue; - } - add(`\\${c}`); - continue; - } - - if (c === "|") { - if (ext.length) { - add(c); - continue; - } - add(`\\${c}`); - continue; - } - - if (c === "+") { - if (n === "(" && extended) { - ext.push(c); - continue; - } - add(`\\${c}`); - continue; - } - - if (c === "@" && extended) { - if (n === "(") { - ext.push(c); - continue; - } - } - - if (c === "!") { - if (extended) { - if (inRange) { - add("^"); - continue; - } - if (n === "(") { - ext.push(c); - add("(?!"); - i++; - continue; - } - add(`\\${c}`); - continue; - } - add(`\\${c}`); - continue; - } - - if (c === "?") { - if (extended) { - if (n === "(") { - ext.push(c); - } else { - add("."); - } - continue; - } - add(`\\${c}`); - continue; - } - - if (c === "[") { - if (inRange && n === ":") { - i++; // skip [ - let value = ""; - while (glob[++i] !== ":") value += glob[i]; - if (value === "alnum") add("(\\w|\\d)"); - else if (value === "space") add("\\s"); - else if (value === "digit") add("\\d"); - i++; // skip last ] - continue; - } - if (extended) { - inRange = true; - add(c); - continue; - } - add(`\\${c}`); - continue; - } - - if (c === "]") { - if (extended) { - inRange = false; - add(c); - continue; - } - add(`\\${c}`); - continue; - } - - if (c === "{") { - if (extended) { - inGroup = true; - add("("); - continue; - } - add(`\\${c}`); - continue; - } - - if (c === "}") { - if (extended) { - inGroup = false; - add(")"); - continue; - } - add(`\\${c}`); - continue; - } - - if (c === ",") { - if (inGroup) { - add("|"); - continue; - } - add(`\\${c}`); - continue; - } - - if (c === "*") { - if (n === "(" && extended) { - ext.push(c); - continue; - } - // Move over all consecutive "*"'s. - // Also store the previous and next characters - const prevChar = glob[i - 1]; - let starCount = 1; - while (glob[i + 1] === "*") { - starCount++; - i++; - } - const nextChar = glob[i + 1]; - if (!globstar) { - // globstar is disabled, so treat any number of "*" as one - add(".*"); - } else { - // globstar is enabled, so determine if this is a globstar segment - const isGlobstar = - starCount > 1 && // multiple "*"'s - // from the start of the segment - [SEP_RAW, "/", undefined].includes(prevChar) && - // to the end of the segment - [SEP_RAW, "/", undefined].includes(nextChar); - if (isGlobstar) { - // it's a globstar, so match zero or more path segments - add(GLOBSTAR, { only: "regex" }); - add(GLOBSTAR_SEGMENT, { only: "path", last: true, split: true }); - i++; // move over the "/" - } else { - // it's not a globstar, so only match one path segment - add(WILDCARD, { only: "regex" }); - add(WILDCARD_SEGMENT, { only: "path" }); - } - } - continue; - } - - add(c); - } - - // When regexp 'g' flag is specified don't - // constrain the regular expression with ^ & $ - if (!flags.includes("g")) { - regex = `^${regex}$`; - segment = `^${segment}$`; - if (filepath) pathRegexStr = `^${pathRegexStr}$`; - } - - const result: GlobrexResult = { regex: new RegExp(regex, flags) }; - - // Push the last segment - if (filepath) { - pathSegments.push(new RegExp(segment, flags)); - result.path = { - regex: new RegExp(pathRegexStr, flags), - segments: pathSegments, - globstar: new RegExp( - !flags.includes("g") ? `^${GLOBSTAR_SEGMENT}$` : GLOBSTAR_SEGMENT, - flags - ) - }; - } - - return result; -} diff --git a/std/fs/globrex_test.ts b/std/fs/globrex_test.ts deleted file mode 100644 index 31607216d..000000000 --- a/std/fs/globrex_test.ts +++ /dev/null @@ -1,823 +0,0 @@ -// This file is ported from globrex@0.1.2 -// MIT License -// Copyright (c) 2018 Terkel Gjervig Nielsen - -import { test } from "../testing/mod.ts"; -import { assertEquals } from "../testing/asserts.ts"; -import { globrex } from "./globrex.ts"; - -const isWin = Deno.build.os === "win"; -const t = { equal: assertEquals, is: assertEquals }; - -function match( - glob: string, - strUnix: string, - strWin?: string | object, - opts = {} -): boolean { - if (typeof strWin === "object") { - opts = strWin; - strWin = ""; - } - const res = globrex(glob, opts); - return res.regex.test(isWin && strWin ? strWin : strUnix); -} - -test({ - name: "globrex: standard", - fn(): void { - const res = globrex("*.js"); - t.equal(typeof globrex, "function", "constructor is a typeof function"); - t.equal(res instanceof Object, true, "returns object"); - t.equal(res.regex.toString(), "/^.*\\.js$/", "returns regex object"); - } -}); - -test({ - name: "globrex: Standard * matching", - fn(): void { - t.equal(match("*", "foo"), true, "match everything"); - t.equal(match("*", "foo", { flags: "g" }), true, "match everything"); - t.equal(match("f*", "foo"), true, "match the end"); - t.equal(match("f*", "foo", { flags: "g" }), true, "match the end"); - t.equal(match("*o", "foo"), true, "match the start"); - t.equal(match("*o", "foo", { flags: "g" }), true, "match the start"); - t.equal(match("u*orn", "unicorn"), true, "match the middle"); - t.equal( - match("u*orn", "unicorn", { flags: "g" }), - true, - "match the middle" - ); - t.equal(match("ico", "unicorn"), false, "do not match without g"); - t.equal( - match("ico", "unicorn", { flags: "g" }), - true, - 'match anywhere with RegExp "g"' - ); - t.equal(match("u*nicorn", "unicorn"), true, "match zero characters"); - t.equal( - match("u*nicorn", "unicorn", { flags: "g" }), - true, - "match zero characters" - ); - } -}); - -test({ - name: "globrex: advance * matching", - fn(): void { - t.equal( - match("*.min.js", "http://example.com/jquery.min.js", { - globstar: false - }), - true, - "complex match" - ); - t.equal( - match("*.min.*", "http://example.com/jquery.min.js", { globstar: false }), - true, - "complex match" - ); - t.equal( - match("*/js/*.js", "http://example.com/js/jquery.min.js", { - globstar: false - }), - true, - "complex match" - ); - t.equal( - match("*.min.*", "http://example.com/jquery.min.js", { flags: "g" }), - true, - "complex match global" - ); - t.equal( - match("*.min.js", "http://example.com/jquery.min.js", { flags: "g" }), - true, - "complex match global" - ); - t.equal( - match("*/js/*.js", "http://example.com/js/jquery.min.js", { flags: "g" }), - true, - "complex match global" - ); - - const str = "\\/$^+?.()=!|{},[].*"; - t.equal(match(str, str), true, "battle test complex string - strict"); - t.equal( - match(str, str, { flags: "g" }), - true, - "battle test complex string - strict" - ); - - t.equal( - match(".min.", "http://example.com/jquery.min.js"), - false, - 'matches without/with using RegExp "g"' - ); - t.equal( - match("*.min.*", "http://example.com/jquery.min.js"), - true, - 'matches without/with using RegExp "g"' - ); - t.equal( - match(".min.", "http://example.com/jquery.min.js", { flags: "g" }), - true, - 'matches without/with using RegExp "g"' - ); - t.equal( - match("http:", "http://example.com/jquery.min.js"), - false, - 'matches without/with using RegExp "g"' - ); - t.equal( - match("http:*", "http://example.com/jquery.min.js"), - true, - 'matches without/with using RegExp "g"' - ); - t.equal( - match("http:", "http://example.com/jquery.min.js", { flags: "g" }), - true, - 'matches without/with using RegExp "g"' - ); - t.equal( - match("min.js", "http://example.com/jquery.min.js"), - false, - 'matches without/with using RegExp "g"' - ); - t.equal( - match("*.min.js", "http://example.com/jquery.min.js"), - true, - 'matches without/with using RegExp "g"' - ); - t.equal( - match("min.js", "http://example.com/jquery.min.js", { flags: "g" }), - true, - 'matches without/with using RegExp "g"' - ); - t.equal( - match("min", "http://example.com/jquery.min.js", { flags: "g" }), - true, - 'match anywhere (globally) using RegExp "g"' - ); - t.equal( - match("/js/", "http://example.com/js/jquery.min.js", { flags: "g" }), - true, - 'match anywhere (globally) using RegExp "g"' - ); - t.equal(match("/js*jq*.js", "http://example.com/js/jquery.min.js"), false); - t.equal( - match("/js*jq*.js", "http://example.com/js/jquery.min.js", { - flags: "g" - }), - true - ); - } -}); - -test({ - name: "globrex: ? match one character, no more and no less", - fn(): void { - t.equal(match("f?o", "foo", { extended: true }), true); - t.equal(match("f?o", "fooo", { extended: true }), false); - t.equal(match("f?oo", "foo", { extended: true }), false); - - const tester = (globstar: boolean): void => { - t.equal( - match("f?o", "foo", { extended: true, globstar, flags: "g" }), - true - ); - t.equal( - match("f?o", "fooo", { extended: true, globstar, flags: "g" }), - true - ); - t.equal( - match("f?o?", "fooo", { extended: true, globstar, flags: "g" }), - true - ); - - t.equal( - match("?fo", "fooo", { extended: true, globstar, flags: "g" }), - false - ); - t.equal( - match("f?oo", "foo", { extended: true, globstar, flags: "g" }), - false - ); - t.equal( - match("foo?", "foo", { extended: true, globstar, flags: "g" }), - false - ); - }; - - tester(true); - tester(false); - } -}); - -test({ - name: "globrex: [] match a character range", - fn(): void { - t.equal(match("fo[oz]", "foo", { extended: true }), true); - t.equal(match("fo[oz]", "foz", { extended: true }), true); - t.equal(match("fo[oz]", "fog", { extended: true }), false); - t.equal(match("fo[a-z]", "fob", { extended: true }), true); - t.equal(match("fo[a-d]", "fot", { extended: true }), false); - t.equal(match("fo[!tz]", "fot", { extended: true }), false); - t.equal(match("fo[!tz]", "fob", { extended: true }), true); - - const tester = (globstar: boolean): void => { - t.equal( - match("fo[oz]", "foo", { extended: true, globstar, flags: "g" }), - true - ); - t.equal( - match("fo[oz]", "foz", { extended: true, globstar, flags: "g" }), - true - ); - t.equal( - match("fo[oz]", "fog", { extended: true, globstar, flags: "g" }), - false - ); - }; - - tester(true); - tester(false); - } -}); - -test({ - name: "globrex: [] extended character ranges", - fn(): void { - t.equal( - match("[[:alnum:]]/bar.txt", "a/bar.txt", { extended: true }), - true - ); - t.equal( - match("@([[:alnum:]abc]|11)/bar.txt", "11/bar.txt", { extended: true }), - true - ); - t.equal( - match("@([[:alnum:]abc]|11)/bar.txt", "a/bar.txt", { extended: true }), - true - ); - t.equal( - match("@([[:alnum:]abc]|11)/bar.txt", "b/bar.txt", { extended: true }), - true - ); - t.equal( - match("@([[:alnum:]abc]|11)/bar.txt", "c/bar.txt", { extended: true }), - true - ); - t.equal( - match("@([[:alnum:]abc]|11)/bar.txt", "abc/bar.txt", { extended: true }), - false - ); - t.equal( - match("@([[:alnum:]abc]|11)/bar.txt", "3/bar.txt", { extended: true }), - true - ); - t.equal( - match("[[:digit:]]/bar.txt", "1/bar.txt", { extended: true }), - true - ); - t.equal( - match("[[:digit:]b]/bar.txt", "b/bar.txt", { extended: true }), - true - ); - t.equal( - match("[![:digit:]b]/bar.txt", "a/bar.txt", { extended: true }), - true - ); - t.equal( - match("[[:alnum:]]/bar.txt", "!/bar.txt", { extended: true }), - false - ); - t.equal( - match("[[:digit:]]/bar.txt", "a/bar.txt", { extended: true }), - false - ); - t.equal( - match("[[:digit:]b]/bar.txt", "a/bar.txt", { extended: true }), - false - ); - } -}); - -test({ - name: "globrex: {} match a choice of different substrings", - fn(): void { - t.equal(match("foo{bar,baaz}", "foobaaz", { extended: true }), true); - t.equal(match("foo{bar,baaz}", "foobar", { extended: true }), true); - t.equal(match("foo{bar,baaz}", "foobuzz", { extended: true }), false); - t.equal(match("foo{bar,b*z}", "foobuzz", { extended: true }), true); - - const tester = (globstar: boolean): void => { - t.equal( - match("foo{bar,baaz}", "foobaaz", { - extended: true, - globstar, - flag: "g" - }), - true - ); - t.equal( - match("foo{bar,baaz}", "foobar", { - extended: true, - globstar, - flag: "g" - }), - true - ); - t.equal( - match("foo{bar,baaz}", "foobuzz", { - extended: true, - globstar, - flag: "g" - }), - false - ); - t.equal( - match("foo{bar,b*z}", "foobuzz", { - extended: true, - globstar, - flag: "g" - }), - true - ); - }; - - tester(true); - tester(false); - } -}); - -test({ - name: "globrex: complex extended matches", - fn(): void { - t.equal( - match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://foo.baaz.com/jquery.min.js", - { extended: true } - ), - true - ); - t.equal( - match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://moz.buzz.com/index.html", - { extended: true } - ), - true - ); - t.equal( - match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://moz.buzz.com/index.htm", - { extended: true } - ), - false - ); - t.equal( - match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://moz.bar.com/index.html", - { extended: true } - ), - false - ); - t.equal( - match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://flozz.buzz.com/index.html", - { extended: true } - ), - false - ); - - const tester = (globstar: boolean): void => { - t.equal( - match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://foo.baaz.com/jquery.min.js", - { extended: true, globstar, flags: "g" } - ), - true - ); - t.equal( - match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://moz.buzz.com/index.html", - { extended: true, globstar, flags: "g" } - ), - true - ); - t.equal( - match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://moz.buzz.com/index.htm", - { extended: true, globstar, flags: "g" } - ), - false - ); - t.equal( - match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://moz.bar.com/index.html", - { extended: true, globstar, flags: "g" } - ), - false - ); - t.equal( - match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://flozz.buzz.com/index.html", - { extended: true, globstar, flags: "g" } - ), - false - ); - }; - - tester(true); - tester(false); - } -}); - -test({ - name: "globrex: standard globstar", - fn(): void { - const tester = (globstar: boolean): void => { - t.equal( - match( - "http://foo.com/**/{*.js,*.html}", - "http://foo.com/bar/jquery.min.js", - { extended: true, globstar, flags: "g" } - ), - true - ); - t.equal( - match( - "http://foo.com/**/{*.js,*.html}", - "http://foo.com/bar/baz/jquery.min.js", - { extended: true, globstar, flags: "g" } - ), - true - ); - t.equal( - match("http://foo.com/**", "http://foo.com/bar/baz/jquery.min.js", { - extended: true, - globstar, - flags: "g" - }), - true - ); - }; - - tester(true); - tester(false); - } -}); - -test({ - name: "globrex: remaining chars should match themself", - fn(): void { - const tester = (globstar: boolean): void => { - const testExtStr = "\\/$^+.()=!|,.*"; - t.equal(match(testExtStr, testExtStr, { extended: true }), true); - t.equal( - match(testExtStr, testExtStr, { extended: true, globstar, flags: "g" }), - true - ); - }; - - tester(true); - tester(false); - } -}); - -test({ - name: "globrex: globstar advance testing", - fn(): void { - t.equal(match("/foo/*", "/foo/bar.txt", { globstar: true }), true); - t.equal(match("/foo/**", "/foo/bar.txt", { globstar: true }), true); - t.equal(match("/foo/**", "/foo/bar/baz.txt", { globstar: true }), true); - t.equal(match("/foo/**", "/foo/bar/baz.txt", { globstar: true }), true); - t.equal( - match("/foo/*/*.txt", "/foo/bar/baz.txt", { globstar: true }), - true - ); - t.equal( - match("/foo/**/*.txt", "/foo/bar/baz.txt", { globstar: true }), - true - ); - t.equal( - match("/foo/**/*.txt", "/foo/bar/baz/qux.txt", { globstar: true }), - true - ); - t.equal(match("/foo/**/bar.txt", "/foo/bar.txt", { globstar: true }), true); - t.equal( - match("/foo/**/**/bar.txt", "/foo/bar.txt", { globstar: true }), - true - ); - t.equal( - match("/foo/**/*/baz.txt", "/foo/bar/baz.txt", { globstar: true }), - true - ); - t.equal(match("/foo/**/*.txt", "/foo/bar.txt", { globstar: true }), true); - t.equal( - match("/foo/**/**/*.txt", "/foo/bar.txt", { globstar: true }), - true - ); - t.equal( - match("/foo/**/*/*.txt", "/foo/bar/baz.txt", { globstar: true }), - true - ); - t.equal( - match("**/*.txt", "/foo/bar/baz/qux.txt", { globstar: true }), - true - ); - t.equal(match("**/foo.txt", "foo.txt", { globstar: true }), true); - t.equal(match("**/*.txt", "foo.txt", { globstar: true }), true); - t.equal(match("/foo/*", "/foo/bar/baz.txt", { globstar: true }), false); - t.equal(match("/foo/*.txt", "/foo/bar/baz.txt", { globstar: true }), false); - t.equal( - match("/foo/*/*.txt", "/foo/bar/baz/qux.txt", { globstar: true }), - false - ); - t.equal(match("/foo/*/bar.txt", "/foo/bar.txt", { globstar: true }), false); - t.equal( - match("/foo/*/*/baz.txt", "/foo/bar/baz.txt", { globstar: true }), - false - ); - t.equal( - match("/foo/**.txt", "/foo/bar/baz/qux.txt", { globstar: true }), - false - ); - t.equal( - match("/foo/bar**/*.txt", "/foo/bar/baz/qux.txt", { globstar: true }), - false - ); - t.equal(match("/foo/bar**", "/foo/bar/baz.txt", { globstar: true }), false); - t.equal( - match("**/.txt", "/foo/bar/baz/qux.txt", { globstar: true }), - false - ); - t.equal( - match("*/*.txt", "/foo/bar/baz/qux.txt", { globstar: true }), - false - ); - t.equal(match("*/*.txt", "foo.txt", { globstar: true }), false); - t.equal( - match("http://foo.com/*", "http://foo.com/bar/baz/jquery.min.js", { - extended: true, - globstar: true - }), - false - ); - t.equal( - match("http://foo.com/*", "http://foo.com/bar/baz/jquery.min.js", { - globstar: true - }), - false - ); - t.equal( - match("http://foo.com/*", "http://foo.com/bar/baz/jquery.min.js", { - globstar: false - }), - true - ); - t.equal( - match("http://foo.com/**", "http://foo.com/bar/baz/jquery.min.js", { - globstar: true - }), - true - ); - t.equal( - match( - "http://foo.com/*/*/jquery.min.js", - "http://foo.com/bar/baz/jquery.min.js", - { globstar: true } - ), - true - ); - t.equal( - match( - "http://foo.com/**/jquery.min.js", - "http://foo.com/bar/baz/jquery.min.js", - { globstar: true } - ), - true - ); - t.equal( - match( - "http://foo.com/*/*/jquery.min.js", - "http://foo.com/bar/baz/jquery.min.js", - { globstar: false } - ), - true - ); - t.equal( - match( - "http://foo.com/*/jquery.min.js", - "http://foo.com/bar/baz/jquery.min.js", - { globstar: false } - ), - true - ); - t.equal( - match( - "http://foo.com/*/jquery.min.js", - "http://foo.com/bar/baz/jquery.min.js", - { globstar: true } - ), - false - ); - } -}); - -test({ - name: "globrex: extended extglob ?", - fn(): void { - t.equal(match("(foo).txt", "(foo).txt", { extended: true }), true); - t.equal(match("?(foo).txt", "foo.txt", { extended: true }), true); - t.equal(match("?(foo).txt", ".txt", { extended: true }), true); - t.equal(match("?(foo|bar)baz.txt", "foobaz.txt", { extended: true }), true); - t.equal( - match("?(ba[zr]|qux)baz.txt", "bazbaz.txt", { extended: true }), - true - ); - t.equal( - match("?(ba[zr]|qux)baz.txt", "barbaz.txt", { extended: true }), - true - ); - t.equal( - match("?(ba[zr]|qux)baz.txt", "quxbaz.txt", { extended: true }), - true - ); - t.equal( - match("?(ba[!zr]|qux)baz.txt", "batbaz.txt", { extended: true }), - true - ); - t.equal(match("?(ba*|qux)baz.txt", "batbaz.txt", { extended: true }), true); - t.equal( - match("?(ba*|qux)baz.txt", "batttbaz.txt", { extended: true }), - true - ); - t.equal(match("?(ba*|qux)baz.txt", "quxbaz.txt", { extended: true }), true); - t.equal( - match("?(ba?(z|r)|qux)baz.txt", "bazbaz.txt", { extended: true }), - true - ); - t.equal( - match("?(ba?(z|?(r))|qux)baz.txt", "bazbaz.txt", { extended: true }), - true - ); - t.equal(match("?(foo).txt", "foo.txt", { extended: false }), false); - t.equal( - match("?(foo|bar)baz.txt", "foobarbaz.txt", { extended: true }), - false - ); - t.equal( - match("?(ba[zr]|qux)baz.txt", "bazquxbaz.txt", { extended: true }), - false - ); - t.equal( - match("?(ba[!zr]|qux)baz.txt", "bazbaz.txt", { extended: true }), - false - ); - } -}); - -test({ - name: "globrex: extended extglob *", - fn(): void { - t.equal(match("*(foo).txt", "foo.txt", { extended: true }), true); - t.equal(match("*foo.txt", "bofoo.txt", { extended: true }), true); - t.equal(match("*(foo).txt", "foofoo.txt", { extended: true }), true); - t.equal(match("*(foo).txt", ".txt", { extended: true }), true); - t.equal(match("*(fooo).txt", ".txt", { extended: true }), true); - t.equal(match("*(fooo).txt", "foo.txt", { extended: true }), false); - t.equal(match("*(foo|bar).txt", "foobar.txt", { extended: true }), true); - t.equal(match("*(foo|bar).txt", "barbar.txt", { extended: true }), true); - t.equal(match("*(foo|bar).txt", "barfoobar.txt", { extended: true }), true); - t.equal(match("*(foo|bar).txt", ".txt", { extended: true }), true); - t.equal(match("*(foo|ba[rt]).txt", "bat.txt", { extended: true }), true); - t.equal(match("*(foo|b*[rt]).txt", "blat.txt", { extended: true }), true); - t.equal(match("*(foo|b*[rt]).txt", "tlat.txt", { extended: true }), false); - t.equal( - match("*(*).txt", "whatever.txt", { extended: true, globstar: true }), - true - ); - t.equal( - match("*(foo|bar)/**/*.txt", "foo/hello/world/bar.txt", { - extended: true, - globstar: true - }), - true - ); - t.equal( - match("*(foo|bar)/**/*.txt", "foo/world/bar.txt", { - extended: true, - globstar: true - }), - true - ); - } -}); - -test({ - name: "globrex: extended extglob +", - fn(): void { - t.equal(match("+(foo).txt", "foo.txt", { extended: true }), true); - t.equal(match("+foo.txt", "+foo.txt", { extended: true }), true); - t.equal(match("+(foo).txt", ".txt", { extended: true }), false); - t.equal(match("+(foo|bar).txt", "foobar.txt", { extended: true }), true); - } -}); - -test({ - name: "globrex: extended extglob @", - fn(): void { - t.equal(match("@(foo).txt", "foo.txt", { extended: true }), true); - t.equal(match("@foo.txt", "@foo.txt", { extended: true }), true); - t.equal(match("@(foo|baz)bar.txt", "foobar.txt", { extended: true }), true); - t.equal( - match("@(foo|baz)bar.txt", "foobazbar.txt", { extended: true }), - false - ); - t.equal( - match("@(foo|baz)bar.txt", "foofoobar.txt", { extended: true }), - false - ); - t.equal( - match("@(foo|baz)bar.txt", "toofoobar.txt", { extended: true }), - false - ); - } -}); - -test({ - name: "globrex: extended extglob !", - fn(): void { - t.equal(match("!(boo).txt", "foo.txt", { extended: true }), true); - t.equal(match("!(foo|baz)bar.txt", "buzbar.txt", { extended: true }), true); - t.equal(match("!bar.txt", "!bar.txt", { extended: true }), true); - t.equal( - match("!({foo,bar})baz.txt", "notbaz.txt", { extended: true }), - true - ); - t.equal( - match("!({foo,bar})baz.txt", "foobaz.txt", { extended: true }), - false - ); - } -}); - -test({ - name: "globrex: strict", - fn(): void { - t.equal(match("foo//bar.txt", "foo/bar.txt"), true); - t.equal(match("foo///bar.txt", "foo/bar.txt"), true); - t.equal(match("foo///bar.txt", "foo/bar.txt", { strict: true }), false); - } -}); - -test({ - name: "globrex: stress testing", - fn(): void { - t.equal( - match("**/*/?yfile.{md,js,txt}", "foo/bar/baz/myfile.md", { - extended: true - }), - true - ); - t.equal( - match("**/*/?yfile.{md,js,txt}", "foo/baz/myfile.md", { extended: true }), - true - ); - t.equal( - match("**/*/?yfile.{md,js,txt}", "foo/baz/tyfile.js", { extended: true }), - true - ); - t.equal( - match("[[:digit:]_.]/file.js", "1/file.js", { extended: true }), - true - ); - t.equal( - match("[[:digit:]_.]/file.js", "2/file.js", { extended: true }), - true - ); - t.equal( - match("[[:digit:]_.]/file.js", "_/file.js", { extended: true }), - true - ); - t.equal( - match("[[:digit:]_.]/file.js", "./file.js", { extended: true }), - true - ); - t.equal( - match("[[:digit:]_.]/file.js", "z/file.js", { extended: true }), - false - ); - } -}); diff --git a/std/fs/mod.ts b/std/fs/mod.ts index edbd7009f..684ad94bd 100644 --- a/std/fs/mod.ts +++ b/std/fs/mod.ts @@ -5,8 +5,7 @@ export * from "./ensure_file.ts"; export * from "./ensure_link.ts"; export * from "./ensure_symlink.ts"; export * from "./exists.ts"; -export * from "./glob.ts"; -export * from "./globrex.ts"; +export * from "./expand_glob.ts"; export * from "./move.ts"; export * from "./copy.ts"; export * from "./read_file_str.ts"; diff --git a/std/fs/move_test.ts b/std/fs/move_test.ts index fae951e1f..fc0141d8e 100644 --- a/std/fs/move_test.ts +++ b/std/fs/move_test.ts @@ -5,11 +5,11 @@ import { assertThrows, assertThrowsAsync } from "../testing/asserts.ts"; +import * as path from "../path/mod.ts"; import { move, moveSync } from "./move.ts"; import { ensureFile, ensureFileSync } from "./ensure_file.ts"; import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; import { exists, existsSync } from "./exists.ts"; -import * as path from "./path/mod.ts"; const testdataDir = path.resolve("fs", "testdata"); diff --git a/std/fs/path.ts b/std/fs/path.ts deleted file mode 100644 index 6ca0749c2..000000000 --- a/std/fs/path.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -export * from "./path/mod.ts"; -export * from "./path/interface.ts"; diff --git a/std/fs/path/README.md b/std/fs/path/README.md deleted file mode 100644 index 93d268aa7..000000000 --- a/std/fs/path/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Deno Path Manipulation Libraries - -Usage: - -```ts -import * as path from "https://deno.land/std/fs/path.ts"; -``` diff --git a/std/fs/path/basename_test.ts b/std/fs/path/basename_test.ts deleted file mode 100644 index f7770d1ca..000000000 --- a/std/fs/path/basename_test.ts +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ - -import { test } from "../../testing/mod.ts"; -import { assertEquals } from "../../testing/asserts.ts"; -import * as path from "./mod.ts"; - -test(function basename() { - 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 - ); -}); - -test(function basenameWin32() { - 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/fs/path/constants.ts b/std/fs/path/constants.ts deleted file mode 100644 index 1e1eeeb49..000000000 --- a/std/fs/path/constants.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ - -const { build } = Deno; - -// 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 */ - -export const isWindows = build.os === "win"; -export const EOL = isWindows ? "\r\n" : "\n"; -export const SEP = isWindows ? "\\" : "/"; -export const SEP_PATTERN = isWindows ? /[\\/]+/ : /\/+/; diff --git a/std/fs/path/dirname_test.ts b/std/fs/path/dirname_test.ts deleted file mode 100644 index 047d4859b..000000000 --- a/std/fs/path/dirname_test.ts +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ - -import { test } from "../../testing/mod.ts"; -import { assertEquals } from "../../testing/asserts.ts"; -import * as path from "./mod.ts"; - -test(function dirname() { - 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"), "."); -}); - -test(function dirnameWin32() { - 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/fs/path/extname_test.ts b/std/fs/path/extname_test.ts deleted file mode 100644 index 336d6b0b2..000000000 --- a/std/fs/path/extname_test.ts +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ - -import { test } from "../../testing/mod.ts"; -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.//", "."] -]; - -test(function extname() { - 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.\\\\"), ".\\\\"); -}); - -test(function extnameWin32() { - 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/fs/path/interface.ts b/std/fs/path/interface.ts deleted file mode 100644 index b31c89ea7..000000000 --- a/std/fs/path/interface.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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/fs/path/isabsolute_test.ts b/std/fs/path/isabsolute_test.ts deleted file mode 100644 index 87218a185..000000000 --- a/std/fs/path/isabsolute_test.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ - -import { test } from "../../testing/mod.ts"; -import { assertEquals } from "../../testing/asserts.ts"; -import * as path from "./mod.ts"; - -test(function isAbsolute() { - 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); -}); - -test(function isAbsoluteWin32() { - 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/fs/path/join_test.ts b/std/fs/path/join_test.ts deleted file mode 100644 index 2c0f35e48..000000000 --- a/std/fs/path/join_test.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { test } from "../../testing/mod.ts"; -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"] -]; - -test(function join() { - joinTests.forEach(function(p) { - const _p = p[0] as string[]; - const actual = path.posix.join.apply(null, _p); - assertEquals(actual, p[1]); - }); -}); - -test(function joinWin32() { - 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/fs/path/mod.ts b/std/fs/path/mod.ts deleted file mode 100644 index 660c061d6..000000000 --- a/std/fs/path/mod.ts +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ - -import * as _win32 from "./win32.ts"; -import * as _posix from "./posix.ts"; - -import { isWindows } from "./constants.ts"; - -const path = isWindows ? _win32 : _posix; - -export const win32 = _win32; -export const posix = _posix; -export const resolve = path.resolve; -export const normalize = path.normalize; -export const isAbsolute = path.isAbsolute; -export const join = path.join; -export const relative = path.relative; -export const toNamespacedPath = path.toNamespacedPath; -export const dirname = path.dirname; -export const basename = path.basename; -export const extname = path.extname; -export const format = path.format; -export const parse = path.parse; -export const sep = path.sep; -export const delimiter = path.delimiter; diff --git a/std/fs/path/parse_format_test.ts b/std/fs/path/parse_format_test.ts deleted file mode 100644 index db83c3354..000000000 --- a/std/fs/path/parse_format_test.ts +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -/* eslint-disable @typescript-eslint/no-explicit-any */ -// TODO(kt3k): fix any types in this file - -import { test } from "../../testing/mod.ts"; -import { assertEquals } from "../../testing/asserts.ts"; -import * as path from "./mod.ts"; - -const winPaths = [ - // [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 = [["/foo/bar", { root: "/" }]]; - -const winSpecialCaseFormatTests = [ - [{ 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 = [ - // [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 = [ - [{ 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: any, paths: any): void { - paths.forEach(function(p: Array<Record<string, unknown>>) { - const element = p[0]; - 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.rooroot, undefined); - assertEquals(output.dir, output.dir ? path.dirname(element) : ""); - assertEquals(output.base, path.basename(element)); - }); -} - -function checkSpecialCaseParseFormat(path: any, testCases: any): void { - testCases.forEach(function(testCase: Array<Record<string, unknown>>) { - const element = testCase[0]; - const expect = testCase[1]; - const output = path.parse(element); - Object.keys(expect).forEach(function(key) { - assertEquals(output[key], expect[key]); - }); - }); -} - -function checkFormat(path: any, testCases: unknown[][]): void { - testCases.forEach(function(testCase) { - assertEquals(path.format(testCase[0]), testCase[1]); - }); -} - -test(function parseWin32() { - checkParseFormat(path.win32, winPaths); - checkSpecialCaseParseFormat(path.win32, winSpecialCaseParseTests); -}); - -test(function parse() { - checkParseFormat(path.posix, unixPaths); -}); - -test(function formatWin32() { - checkFormat(path.win32, winSpecialCaseFormatTests); -}); - -test(function format() { - checkFormat(path.posix, unixSpecialCaseFormatTests); -}); - -// Test removal of trailing path separators -const windowsTrailingTests = [ - [".\\", { 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 = [ - ["./", { 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" } - ] -]; - -test(function parseTrailingWin32() { - windowsTrailingTests.forEach(function(p) { - const actual = path.win32.parse(p[0] as string); - const expected = p[1]; - assertEquals(actual, expected); - }); -}); - -test(function parseTrailing() { - posixTrailingTests.forEach(function(p) { - const actual = path.posix.parse(p[0] as string); - const expected = p[1]; - assertEquals(actual, expected); - }); -}); diff --git a/std/fs/path/posix.ts b/std/fs/path/posix.ts deleted file mode 100644 index 4377fd542..000000000 --- a/std/fs/path/posix.ts +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ - -const { cwd } = Deno; -import { FormatInputPathObject, ParsedPath } from "./interface.ts"; -import { CHAR_DOT, CHAR_FORWARD_SLASH } from "./constants.ts"; - -import { - assertPath, - normalizeString, - isPosixPathSeparator, - _format -} from "./utils.ts"; - -export const sep = "/"; -export const delimiter = ":"; - -// path.resolve([from ...], to) -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 path = 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 "."; -} - -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; -} - -export function isAbsolute(path: string): boolean { - assertPath(path); - return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH; -} - -export function join(...paths: string[]): string { - if (paths.length === 0) return "."; - let joined: string | undefined; - for (let i = 0, len = paths.length; i < len; ++i) { - const path = paths[i]; - assertPath(path); - if (path.length > 0) { - if (!joined) joined = path; - else joined += `/${path}`; - } - } - if (!joined) return "."; - return normalize(joined); -} - -export function relative(from: string, to: string): string { - assertPath(from); - assertPath(to); - - if (from === to) return ""; - - from = resolve(from); - to = resolve(to); - - if (from === to) return ""; - - // 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); - } -} - -export function toNamespacedPath(path: string): string { - // Non-op on posix systems - return path; -} - -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); -} - -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); - } -} - -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); -} - -export function format(pathObject: FormatInputPathObject): string { - /* eslint-disable max-len */ - if (pathObject === null || typeof pathObject !== "object") { - throw new TypeError( - `The "pathObject" argument must be of type Object. Received type ${typeof pathObject}` - ); - } - return _format("/", pathObject); -} - -export function parse(path: string): ParsedPath { - assertPath(path); - - const ret: ParsedPath = { root: "", dir: "", base: "", ext: "", name: "" }; - if (path.length === 0) return ret; - const isAbsolute = 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; -} diff --git a/std/fs/path/relative_test.ts b/std/fs/path/relative_test.ts deleted file mode 100644 index 0188b5368..000000000 --- a/std/fs/path/relative_test.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ - -import { test } from "../../testing/mod.ts"; -import { assertEquals } from "../../testing/asserts.ts"; -import * as path from "./mod.ts"; - -const relativeTests = { - win32: - // arguments result - [ - ["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"] - ], - posix: - // arguments result - [ - ["/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"] - ] -}; - -test(function relative() { - relativeTests.posix.forEach(function(p) { - const expected = p[2]; - const actual = path.posix.relative(p[0], p[1]); - assertEquals(actual, expected); - }); -}); - -test(function relativeWin32() { - 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/fs/path/resolve_test.ts b/std/fs/path/resolve_test.ts deleted file mode 100644 index 606570aad..000000000 --- a/std/fs/path/resolve_test.ts +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ - -const { cwd } = Deno; -import { test } from "../../testing/mod.ts"; -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/", "../../.."], cwd()], - [["."], cwd()], - [["/some/dir", ".", "/absolute/"], "/absolute"], - [["/foo/tmp.3/", "../tmp.3/cycles/root.js"], "/foo/tmp.3/cycles/root.js"] - ]; - -test(function resolve() { - posixTests.forEach(function(p) { - const _p = p[0] as string[]; - const actual = path.posix.resolve.apply(null, _p); - assertEquals(actual, p[1]); - }); -}); - -test(function resolveWin32() { - 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/fs/path/test.ts b/std/fs/path/test.ts deleted file mode 100644 index 3664ae5f1..000000000 --- a/std/fs/path/test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import "./basename_test.ts"; -import "./dirname_test.ts"; -import "./extname_test.ts"; -import "./isabsolute_test.ts"; -import "./join_test.ts"; -import "./parse_format_test.ts"; -import "./relative_test.ts"; -import "./resolve_test.ts"; -import "./zero_length_strings_test.ts"; diff --git a/std/fs/path/utils.ts b/std/fs/path/utils.ts deleted file mode 100644 index 7a4cd7299..000000000 --- a/std/fs/path/utils.ts +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ - -import { FormatInputPathObject } from "./interface.ts"; -import { - CHAR_UPPERCASE_A, - CHAR_LOWERCASE_A, - CHAR_UPPERCASE_Z, - CHAR_LOWERCASE_Z, - CHAR_DOT, - CHAR_FORWARD_SLASH, - CHAR_BACKWARD_SLASH -} 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; - 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/fs/path/win32.ts b/std/fs/path/win32.ts deleted file mode 100644 index 79e04ea6e..000000000 --- a/std/fs/path/win32.ts +++ /dev/null @@ -1,896 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ - -const { cwd, env } = Deno; -import { FormatInputPathObject, ParsedPath } from "./interface.ts"; -import { - CHAR_DOT, - CHAR_BACKWARD_SLASH, - CHAR_COLON, - CHAR_QUESTION_MARK -} from "./constants.ts"; - -import { - assertPath, - isPathSeparator, - isWindowsDeviceRoot, - normalizeString, - _format -} from "./utils.ts"; - -export const sep = "\\"; -export const delimiter = ";"; - -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) { - path = cwd(); - } else { - // 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 = env()[`=${resolvedDevice}`] || 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 || "."; -} - -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; - } -} - -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; -} - -export function join(...paths: string[]): string { - const pathsCount = paths.length; - if (pathsCount === 0) return "."; - - let joined: string | undefined; - let firstPart: string; - 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; - firstPart = firstPart!; - 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' -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); - } -} - -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; -} - -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); -} - -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); - } -} - -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); -} - -export function format(pathObject: FormatInputPathObject): string { - /* eslint-disable max-len */ - if (pathObject === null || typeof pathObject !== "object") { - throw new TypeError( - `The "pathObject" argument must be of type Object. Received type ${typeof pathObject}` - ); - } - return _format("\\", pathObject); -} - -export function parse(path: string): ParsedPath { - assertPath(path); - - const ret: ParsedPath = { root: "", dir: "", base: "", ext: "", name: "" }; - - 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; -} diff --git a/std/fs/path/zero_length_strings_test.ts b/std/fs/path/zero_length_strings_test.ts deleted file mode 100644 index 744e97735..000000000 --- a/std/fs/path/zero_length_strings_test.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ - -const { cwd } = Deno; -import { test } from "../../testing/mod.ts"; -import { assertEquals } from "../../testing/asserts.ts"; -import * as path from "./mod.ts"; - -const pwd = cwd(); - -test(function joinZeroLength() { - // 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); -}); - -test(function normalizeZeroLength() { - // 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); -}); - -test(function isAbsoluteZeroLength() { - // 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); -}); - -test(function resolveZeroLength() { - // resolve, internally ignores all the zero-length strings and returns the - // current working directory - assertEquals(path.resolve(""), pwd); - assertEquals(path.resolve("", ""), pwd); -}); - -test(function relativeZeroLength() { - // relative, internally calls resolve. So, '' is actually the current - // directory - assertEquals(path.relative("", pwd), ""); - assertEquals(path.relative(pwd, ""), ""); - assertEquals(path.relative(pwd, pwd), ""); -}); diff --git a/std/fs/read_file_str_test.ts b/std/fs/read_file_str_test.ts index d7d67d8d4..58c649322 100644 --- a/std/fs/read_file_str_test.ts +++ b/std/fs/read_file_str_test.ts @@ -1,7 +1,7 @@ import { test } from "../testing/mod.ts"; import { assert } from "../testing/asserts.ts"; +import * as path from "../path/mod.ts"; import { readFileStrSync, readFileStr } from "./read_file_str.ts"; -import * as path from "./path/mod.ts"; const testdataDir = path.resolve("fs", "testdata"); diff --git a/std/fs/read_json_test.ts b/std/fs/read_json_test.ts index c8aa6dcf1..a9362b0ac 100644 --- a/std/fs/read_json_test.ts +++ b/std/fs/read_json_test.ts @@ -5,8 +5,8 @@ import { assertThrowsAsync, assertThrows } from "../testing/asserts.ts"; +import * as path from "../path/mod.ts"; import { readJson, readJsonSync } from "./read_json.ts"; -import * as path from "./path/mod.ts"; const testdataDir = path.resolve("fs", "testdata"); diff --git a/std/fs/utils.ts b/std/fs/utils.ts index f644f4869..ff11fbf13 100644 --- a/std/fs/utils.ts +++ b/std/fs/utils.ts @@ -1,4 +1,4 @@ -import * as path from "./path/mod.ts"; +import * as path from "../path/mod.ts"; /** * Test whether or not `dest` is a sub-directory of `src` diff --git a/std/fs/utils_test.ts b/std/fs/utils_test.ts index 5b33842ad..751180177 100644 --- a/std/fs/utils_test.ts +++ b/std/fs/utils_test.ts @@ -2,8 +2,8 @@ import { test } from "../testing/mod.ts"; import { assertEquals } from "../testing/asserts.ts"; +import * as path from "../path/mod.ts"; import { isSubdir, getFileInfoType, PathType } from "./utils.ts"; -import * as path from "./path/mod.ts"; import { ensureFileSync } from "./ensure_file.ts"; import { ensureDirSync } from "./ensure_dir.ts"; diff --git a/std/fs/walk.ts b/std/fs/walk.ts index 16ccc357e..efc54bc23 100644 --- a/std/fs/walk.ts +++ b/std/fs/walk.ts @@ -2,7 +2,7 @@ // https://golang.org/pkg/path/filepath/#Walk // Copyright 2009 The Go Authors. All rights reserved. BSD license. import { unimplemented } from "../testing/asserts.ts"; -import { join } from "./path/mod.ts"; +import { join } from "../path/mod.ts"; const { readDir, readDirSync, stat, statSync } = Deno; type FileInfo = Deno.FileInfo; diff --git a/std/fs/write_file_str_test.ts b/std/fs/write_file_str_test.ts index 77b1e734e..f9cf760fe 100644 --- a/std/fs/write_file_str_test.ts +++ b/std/fs/write_file_str_test.ts @@ -1,7 +1,7 @@ import { test } from "../testing/mod.ts"; import { assertEquals } from "../testing/asserts.ts"; +import * as path from "../path/mod.ts"; import { writeFileStr, writeFileStrSync } from "./write_file_str.ts"; -import * as path from "./path/mod.ts"; const testdataDir = path.resolve("fs", "testdata"); diff --git a/std/fs/write_json_test.ts b/std/fs/write_json_test.ts index a282d399f..38bc3ed4b 100644 --- a/std/fs/write_json_test.ts +++ b/std/fs/write_json_test.ts @@ -5,8 +5,8 @@ import { assertThrowsAsync, assertThrows } from "../testing/asserts.ts"; +import * as path from "../path/mod.ts"; import { writeJson, writeJsonSync } from "./write_json.ts"; -import * as path from "./path/mod.ts"; const testdataDir = path.resolve("fs", "testdata"); |