From f51dcc12d7a75a677529d63eb53d7a577d5b9289 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Wed, 16 Oct 2019 19:39:33 +0100 Subject: [PATCH] std: Move fs/path to the top-level (#3100) --- std/archive/tar_test.ts | 2 +- std/encoding/toml_test.ts | 2 +- std/fs/copy.ts | 2 +- std/fs/copy_test.ts | 2 +- std/fs/empty_dir_test.ts | 2 +- std/fs/ensure_dir_test.ts | 2 +- std/fs/ensure_file.ts | 2 +- std/fs/ensure_file_test.ts | 2 +- std/fs/ensure_link.ts | 2 +- std/fs/ensure_link_test.ts | 2 +- std/fs/ensure_symlink.ts | 2 +- std/fs/ensure_symlink_test.ts | 2 +- std/fs/exists_test.ts | 2 +- std/fs/{glob.ts => expand_glob.ts} | 132 ++---------------- std/fs/expand_glob_test.ts | 120 ++++++++++++++++ std/fs/mod.ts | 3 +- std/fs/move_test.ts | 2 +- std/fs/path.ts | 3 - std/fs/path/README.md | 7 - std/fs/read_file_str_test.ts | 2 +- std/fs/read_json_test.ts | 2 +- std/fs/utils.ts | 2 +- std/fs/utils_test.ts | 2 +- std/fs/walk.ts | 2 +- std/fs/write_file_str_test.ts | 2 +- std/fs/write_json_test.ts | 2 +- std/http/file_server.ts | 4 +- std/installer/mod.ts | 2 +- std/io/util.ts | 3 +- std/io/util_test.ts | 2 +- std/media_types/deps.ts | 2 +- std/mime/multipart.ts | 4 +- std/mime/multipart_test.ts | 2 +- std/path/README.md | 7 + std/{fs => }/path/basename_test.ts | 4 +- std/{fs => }/path/constants.ts | 0 std/{fs => }/path/dirname_test.ts | 4 +- std/{fs => }/path/extname_test.ts | 4 +- std/path/glob.ts | 121 ++++++++++++++++ std/{fs => path}/glob_test.ts | 121 +--------------- std/{fs => path}/globrex.ts | 0 std/{fs => path}/globrex_test.ts | 0 std/{fs => }/path/interface.ts | 0 std/{fs => }/path/isabsolute_test.ts | 4 +- std/{fs => }/path/join_test.ts | 4 +- std/{fs => }/path/mod.ts | 5 + std/{fs => }/path/parse_format_test.ts | 4 +- std/{fs => }/path/posix.ts | 0 std/{fs => }/path/relative_test.ts | 4 +- std/{fs => }/path/resolve_test.ts | 4 +- std/{fs => }/path/test.ts | 0 std/{fs => }/path/utils.ts | 0 std/{fs => }/path/win32.ts | 0 std/{fs => }/path/zero_length_strings_test.ts | 4 +- std/prettier/main_test.ts | 5 +- std/testing/runner.ts | 3 +- std/testing/runner_test.ts | 4 +- 57 files changed, 323 insertions(+), 305 deletions(-) rename std/fs/{glob.ts => expand_glob.ts} (68%) create mode 100644 std/fs/expand_glob_test.ts delete mode 100644 std/fs/path.ts delete mode 100644 std/fs/path/README.md create mode 100644 std/path/README.md rename std/{fs => }/path/basename_test.ts (97%) rename std/{fs => }/path/constants.ts (100%) rename std/{fs => }/path/dirname_test.ts (96%) rename std/{fs => }/path/extname_test.ts (96%) create mode 100644 std/path/glob.ts rename std/{fs => path}/glob_test.ts (69%) rename std/{fs => path}/globrex.ts (100%) rename std/{fs => path}/globrex_test.ts (100%) rename std/{fs => }/path/interface.ts (100%) rename std/{fs => }/path/isabsolute_test.ts (93%) rename std/{fs => }/path/join_test.ts (97%) rename std/{fs => }/path/mod.ts (84%) rename std/{fs => }/path/parse_format_test.ts (98%) rename std/{fs => }/path/posix.ts (100%) rename std/{fs => }/path/relative_test.ts (96%) rename std/{fs => }/path/resolve_test.ts (94%) rename std/{fs => }/path/test.ts (100%) rename std/{fs => }/path/utils.ts (100%) rename std/{fs => }/path/win32.ts (100%) rename std/{fs => }/path/zero_length_strings_test.ts (94%) diff --git a/std/archive/tar_test.ts b/std/archive/tar_test.ts index 474e1e2d32..d317a89dd7 100644 --- a/std/archive/tar_test.ts +++ b/std/archive/tar_test.ts @@ -11,8 +11,8 @@ import { test, runIfMain } from "../testing/mod.ts"; import { assertEquals } from "../testing/asserts.ts"; +import { resolve } from "../path/mod.ts"; import { Tar, Untar } from "./tar.ts"; -import { resolve } from "../fs/path/mod.ts"; const filePath = resolve("archive", "testdata", "example.txt"); diff --git a/std/encoding/toml_test.ts b/std/encoding/toml_test.ts index 22ecfa68a9..633ccd1dbc 100644 --- a/std/encoding/toml_test.ts +++ b/std/encoding/toml_test.ts @@ -3,8 +3,8 @@ import { runIfMain, test } from "../testing/mod.ts"; import { assertEquals } from "../testing/asserts.ts"; import { existsSync } from "../fs/exists.ts"; import { readFileStrSync } from "../fs/read_file_str.ts"; +import * as path from "../path/mod.ts"; import { parse, stringify } from "./toml.ts"; -import * as path from "../fs/path/mod.ts"; const testFilesDir = path.resolve("encoding", "testdata"); diff --git a/std/fs/copy.ts b/std/fs/copy.ts index 616fba9751..f5e7f5078d 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 a11838c4bd..46b3418acf 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 b44e600d74..26e751c746 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 ad34336dca..068ec86938 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 d749b95e43..0d6d7fbc4e 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 fa27133ab4..bb23084a8b 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 707f2bee99..f2db5243c4 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 593df5702b..044f80f8dd 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 9b7cc429c7..4c771e5f9a 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 4dee56788f..0c2c53e5dc 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 247bb7ed65..e65befa784 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 similarity index 68% rename from std/fs/glob.ts rename to std/fs/expand_glob.ts index 9be6197fc2..c43e09d40f 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 = { "{": "}", "(": ")", "[": "]" }; - /* 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 0000000000..12b0b3d1a1 --- /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 { + 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 { + const options = EG_OPTIONS; + assertEquals(await expandGlobArray("*", options), [ + "abc", + "abcdef", + "abcdefghi", + "subdir" + ]); +}); + +test(async function expandGlobTrailingSeparator(): Promise { + const options = EG_OPTIONS; + assertEquals(await expandGlobArray("*/", options), ["subdir"]); +}); + +test(async function expandGlobParent(): Promise { + const options = EG_OPTIONS; + assertEquals(await expandGlobArray("subdir/../*", options), [ + "abc", + "abcdef", + "abcdefghi", + "subdir" + ]); +}); + +test(async function expandGlobExt(): Promise { + 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 { + const options = { ...EG_OPTIONS, globstar: true }; + assertEquals( + await expandGlobArray(joinGlobs(["**", "abc"], options), options), + ["abc", join("subdir", "abc")] + ); +}); + +test(async function expandGlobGlobstarParent(): Promise { + const options = { ...EG_OPTIONS, globstar: true }; + assertEquals( + await expandGlobArray(joinGlobs(["subdir", "**", ".."], options), options), + ["."] + ); +}); + +test(async function expandGlobIncludeDirs(): Promise { + const options = { ...EG_OPTIONS, includeDirs: false }; + assertEquals(await expandGlobArray("subdir", options), []); +}); + +runIfMain(import.meta); diff --git a/std/fs/mod.ts b/std/fs/mod.ts index edbd7009f3..684ad94bd3 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 fae951e1fa..fc0141d8e1 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 6ca0749c23..0000000000 --- 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 93d268aa70..0000000000 --- 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/read_file_str_test.ts b/std/fs/read_file_str_test.ts index d7d67d8d46..58c649322e 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 c8aa6dcf1d..a9362b0ac5 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 f644f4869e..ff11fbf130 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 5b33842ad8..7511801775 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 16ccc357e6..efc54bc235 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 77b1e734e4..f9cf760fe0 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 a282d399f4..38bc3ed4ba 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"); diff --git a/std/http/file_server.ts b/std/http/file_server.ts index fd6c19e9d8..a2de5eecd0 100755 --- a/std/http/file_server.ts +++ b/std/http/file_server.ts @@ -7,14 +7,14 @@ // https://github.com/indexzero/http-server/blob/master/test/http-server-test.js const { ErrorKind, cwd, args, stat, readDir, open } = Deno; +import { contentType } from "../media_types/mod.ts"; +import { extname, posix } from "../path/mod.ts"; import { listenAndServe, ServerRequest, setContentLength, Response } from "./server.ts"; -import { extname, posix } from "../fs/path.ts"; -import { contentType } from "../media_types/mod.ts"; const dirViewerTemplate = ` diff --git a/std/installer/mod.ts b/std/installer/mod.ts index ef94e2e4e6..17752517c3 100644 --- a/std/installer/mod.ts +++ b/std/installer/mod.ts @@ -2,9 +2,9 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. const { env, stdin, args, exit, writeFile, chmod, run } = Deno; import { parse } from "../flags/mod.ts"; -import * as path from "../fs/path.ts"; import { exists } from "../fs/exists.ts"; import { ensureDir } from "../fs/ensure_dir.ts"; +import * as path from "../path/mod.ts"; const encoder = new TextEncoder(); const decoder = new TextDecoder("utf-8"); diff --git a/std/io/util.ts b/std/io/util.ts index 96ff10b0e2..7ecf0e48d1 100644 --- a/std/io/util.ts +++ b/std/io/util.ts @@ -2,8 +2,9 @@ const { Buffer, mkdir, open } = Deno; type File = Deno.File; type Reader = Deno.Reader; +import * as path from "../path/mod.ts"; import { encode } from "../strings/mod.ts"; -import * as path from "../fs/path.ts"; + // `off` is the offset into `dst` where it will at which to begin writing values // from `src`. // Returns the number of bytes copied. diff --git a/std/io/util_test.ts b/std/io/util_test.ts index c616a4bba3..4f97bf5efa 100644 --- a/std/io/util_test.ts +++ b/std/io/util_test.ts @@ -2,8 +2,8 @@ const { remove } = Deno; import { test } from "../testing/mod.ts"; import { assert, assertEquals } from "../testing/asserts.ts"; +import * as path from "../path/mod.ts"; import { copyBytes, tempFile } from "./util.ts"; -import * as path from "../fs/path.ts"; test(function testCopyBytes(): void { const dst = new Uint8Array(4); diff --git a/std/media_types/deps.ts b/std/media_types/deps.ts index fb87a8617d..ee095f50c5 100644 --- a/std/media_types/deps.ts +++ b/std/media_types/deps.ts @@ -1,6 +1,6 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -export { extname } from "../fs/path.ts"; +export { extname } from "../path/mod.ts"; interface DB { [mediaType: string]: { diff --git a/std/mime/multipart.ts b/std/mime/multipart.ts index 93375242ee..8eb27b52ac 100644 --- a/std/mime/multipart.ts +++ b/std/mime/multipart.ts @@ -5,11 +5,11 @@ const { min, max } = Math; type Closer = Deno.Closer; type Reader = Deno.Reader; type Writer = Deno.Writer; -import { FormFile } from "../multipart/formfile.ts"; import { equal, findIndex, findLastIndex, hasPrefix } from "../bytes/mod.ts"; -import { extname } from "../fs/path.ts"; import { copyN } from "../io/ioutil.ts"; import { MultiReader } from "../io/readers.ts"; +import { FormFile } from "../multipart/formfile.ts"; +import { extname } from "../path/mod.ts"; import { tempFile } from "../io/util.ts"; import { BufReader, BufWriter, UnexpectedEOFError } from "../io/bufio.ts"; import { encoder } from "../strings/mod.ts"; diff --git a/std/mime/multipart_test.ts b/std/mime/multipart_test.ts index b73cd529a0..4382ec6709 100644 --- a/std/mime/multipart_test.ts +++ b/std/mime/multipart_test.ts @@ -8,13 +8,13 @@ import { assertThrowsAsync } from "../testing/asserts.ts"; import { test, runIfMain } from "../testing/mod.ts"; +import * as path from "../path/mod.ts"; import { matchAfterPrefix, MultipartReader, MultipartWriter, scanUntilBoundary } from "./multipart.ts"; -import * as path from "../fs/path.ts"; import { FormFile, isFormFile } from "../multipart/formfile.ts"; import { StringWriter } from "../io/writers.ts"; diff --git a/std/path/README.md b/std/path/README.md new file mode 100644 index 0000000000..a1f3f425d1 --- /dev/null +++ b/std/path/README.md @@ -0,0 +1,7 @@ +# Deno Path Manipulation Libraries + +Usage: + +```ts +import * as path from "https://deno.land/std/path/mod.ts"; +``` diff --git a/std/fs/path/basename_test.ts b/std/path/basename_test.ts similarity index 97% rename from std/fs/path/basename_test.ts rename to std/path/basename_test.ts index f7770d1caf..c4e294501a 100644 --- a/std/fs/path/basename_test.ts +++ b/std/path/basename_test.ts @@ -1,8 +1,8 @@ // 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 { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; import * as path from "./mod.ts"; test(function basename() { diff --git a/std/fs/path/constants.ts b/std/path/constants.ts similarity index 100% rename from std/fs/path/constants.ts rename to std/path/constants.ts diff --git a/std/fs/path/dirname_test.ts b/std/path/dirname_test.ts similarity index 96% rename from std/fs/path/dirname_test.ts rename to std/path/dirname_test.ts index 047d4859b6..ffcfa99931 100644 --- a/std/fs/path/dirname_test.ts +++ b/std/path/dirname_test.ts @@ -1,8 +1,8 @@ // 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 { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; import * as path from "./mod.ts"; test(function dirname() { diff --git a/std/fs/path/extname_test.ts b/std/path/extname_test.ts similarity index 96% rename from std/fs/path/extname_test.ts rename to std/path/extname_test.ts index 336d6b0b2c..e272226556 100644 --- a/std/fs/path/extname_test.ts +++ b/std/path/extname_test.ts @@ -1,8 +1,8 @@ // 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 { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; import * as path from "./mod.ts"; const slashRE = /\//g; diff --git a/std/path/glob.ts b/std/path/glob.ts new file mode 100644 index 0000000000..faccb8d77a --- /dev/null +++ b/std/path/glob.ts @@ -0,0 +1,121 @@ +import { SEP, SEP_PATTERN } from "./constants.ts"; +import { globrex } from "./globrex.ts"; +import { join, normalize } from "./mod.ts"; +const { DenoError, ErrorKind } = Deno; + +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 = { "{": "}", "(": ")", "[": "]" }; + /* 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 }); +} diff --git a/std/fs/glob_test.ts b/std/path/glob_test.ts similarity index 69% rename from std/fs/glob_test.ts rename to std/path/glob_test.ts index df819d92c6..6cacd2eebd 100644 --- a/std/fs/glob_test.ts +++ b/std/path/glob_test.ts @@ -1,19 +1,9 @@ -const { cwd, mkdir } = Deno; +const { 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"; +import { testWalk, touch, walkArray } from "../fs/walk_test.ts"; +import { globToRegExp, isGlob, joinGlobs, normalizeGlob } from "./glob.ts"; +import { SEP, join } from "./mod.ts"; test({ name: "glob: glob to regex", @@ -268,107 +258,4 @@ test(function joinGlobsGlobstar(): void { assertEquals(joinGlobs(["**", ".."], { globstar: true }), `**${SEP}..`); }); -async function expandGlobArray( - globString: string, - options: ExpandGlobOptions -): Promise { - 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 { - const options = EG_OPTIONS; - assertEquals(await expandGlobArray("*", options), [ - "abc", - "abcdef", - "abcdefghi", - "subdir" - ]); -}); - -test(async function expandGlobTrailingSeparator(): Promise { - const options = EG_OPTIONS; - assertEquals(await expandGlobArray("*/", options), ["subdir"]); -}); - -test(async function expandGlobParent(): Promise { - const options = EG_OPTIONS; - assertEquals(await expandGlobArray("subdir/../*", options), [ - "abc", - "abcdef", - "abcdefghi", - "subdir" - ]); -}); - -test(async function expandGlobExt(): Promise { - 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 { - const options = { ...EG_OPTIONS, globstar: true }; - assertEquals( - await expandGlobArray(joinGlobs(["**", "abc"], options), options), - ["abc", join("subdir", "abc")] - ); -}); - -test(async function expandGlobGlobstarParent(): Promise { - const options = { ...EG_OPTIONS, globstar: true }; - assertEquals( - await expandGlobArray(joinGlobs(["subdir", "**", ".."], options), options), - ["."] - ); -}); - -test(async function expandGlobIncludeDirs(): Promise { - const options = { ...EG_OPTIONS, includeDirs: false }; - assertEquals(await expandGlobArray("subdir", options), []); -}); - runIfMain(import.meta); diff --git a/std/fs/globrex.ts b/std/path/globrex.ts similarity index 100% rename from std/fs/globrex.ts rename to std/path/globrex.ts diff --git a/std/fs/globrex_test.ts b/std/path/globrex_test.ts similarity index 100% rename from std/fs/globrex_test.ts rename to std/path/globrex_test.ts diff --git a/std/fs/path/interface.ts b/std/path/interface.ts similarity index 100% rename from std/fs/path/interface.ts rename to std/path/interface.ts diff --git a/std/fs/path/isabsolute_test.ts b/std/path/isabsolute_test.ts similarity index 93% rename from std/fs/path/isabsolute_test.ts rename to std/path/isabsolute_test.ts index 87218a1858..9a472e18cc 100644 --- a/std/fs/path/isabsolute_test.ts +++ b/std/path/isabsolute_test.ts @@ -1,8 +1,8 @@ // 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 { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; import * as path from "./mod.ts"; test(function isAbsolute() { diff --git a/std/fs/path/join_test.ts b/std/path/join_test.ts similarity index 97% rename from std/fs/path/join_test.ts rename to std/path/join_test.ts index 2c0f35e482..878e896551 100644 --- a/std/fs/path/join_test.ts +++ b/std/path/join_test.ts @@ -1,5 +1,5 @@ -import { test } from "../../testing/mod.ts"; -import { assertEquals } from "../../testing/asserts.ts"; +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; import * as path from "./mod.ts"; const backslashRE = /\\/g; diff --git a/std/fs/path/mod.ts b/std/path/mod.ts similarity index 84% rename from std/fs/path/mod.ts rename to std/path/mod.ts index 660c061d64..dd79441f59 100644 --- a/std/fs/path/mod.ts +++ b/std/path/mod.ts @@ -23,3 +23,8 @@ export const format = path.format; export const parse = path.parse; export const sep = path.sep; export const delimiter = path.delimiter; + +export { EOL, SEP, SEP_PATTERN, isWindows } from "./constants.ts"; +export * from "./interface.ts"; +export * from "./glob.ts"; +export * from "./globrex.ts"; diff --git a/std/fs/path/parse_format_test.ts b/std/path/parse_format_test.ts similarity index 98% rename from std/fs/path/parse_format_test.ts rename to std/path/parse_format_test.ts index db83c33540..6ecce73043 100644 --- a/std/fs/path/parse_format_test.ts +++ b/std/path/parse_format_test.ts @@ -3,8 +3,8 @@ /* 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 { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; import * as path from "./mod.ts"; const winPaths = [ diff --git a/std/fs/path/posix.ts b/std/path/posix.ts similarity index 100% rename from std/fs/path/posix.ts rename to std/path/posix.ts diff --git a/std/fs/path/relative_test.ts b/std/path/relative_test.ts similarity index 96% rename from std/fs/path/relative_test.ts rename to std/path/relative_test.ts index 0188b53680..8a130fbff8 100644 --- a/std/fs/path/relative_test.ts +++ b/std/path/relative_test.ts @@ -1,8 +1,8 @@ // 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 { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; import * as path from "./mod.ts"; const relativeTests = { diff --git a/std/fs/path/resolve_test.ts b/std/path/resolve_test.ts similarity index 94% rename from std/fs/path/resolve_test.ts rename to std/path/resolve_test.ts index 606570aadb..65a8a2d050 100644 --- a/std/fs/path/resolve_test.ts +++ b/std/path/resolve_test.ts @@ -2,8 +2,8 @@ // Ported from https://github.com/browserify/path-browserify/ const { cwd } = Deno; -import { test } from "../../testing/mod.ts"; -import { assertEquals } from "../../testing/asserts.ts"; +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; import * as path from "./mod.ts"; const windowsTests = diff --git a/std/fs/path/test.ts b/std/path/test.ts similarity index 100% rename from std/fs/path/test.ts rename to std/path/test.ts diff --git a/std/fs/path/utils.ts b/std/path/utils.ts similarity index 100% rename from std/fs/path/utils.ts rename to std/path/utils.ts diff --git a/std/fs/path/win32.ts b/std/path/win32.ts similarity index 100% rename from std/fs/path/win32.ts rename to std/path/win32.ts diff --git a/std/fs/path/zero_length_strings_test.ts b/std/path/zero_length_strings_test.ts similarity index 94% rename from std/fs/path/zero_length_strings_test.ts rename to std/path/zero_length_strings_test.ts index 744e97735d..5649f6999b 100644 --- a/std/fs/path/zero_length_strings_test.ts +++ b/std/path/zero_length_strings_test.ts @@ -2,8 +2,8 @@ // Ported from https://github.com/browserify/path-browserify/ const { cwd } = Deno; -import { test } from "../../testing/mod.ts"; -import { assertEquals } from "../../testing/asserts.ts"; +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; import * as path from "./mod.ts"; const pwd = cwd(); diff --git a/std/prettier/main_test.ts b/std/prettier/main_test.ts index 65a963c02a..5214db8ef3 100644 --- a/std/prettier/main_test.ts +++ b/std/prettier/main_test.ts @@ -1,10 +1,9 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { join } from "../fs/path.ts"; -import { EOL } from "../fs/path/constants.ts"; import { assertEquals } from "../testing/asserts.ts"; import { test, runIfMain } from "../testing/mod.ts"; -import { xrun } from "./util.ts"; import { copy, emptyDir } from "../fs/mod.ts"; +import { EOL, join } from "../path/mod.ts"; +import { xrun } from "./util.ts"; const { readAll, execPath } = Deno; const decoder = new TextDecoder(); diff --git a/std/testing/runner.ts b/std/testing/runner.ts index 11c8f39443..48bc4c9c3a 100755 --- a/std/testing/runner.ts +++ b/std/testing/runner.ts @@ -2,8 +2,7 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import { parse } from "../flags/mod.ts"; import { ExpandGlobOptions, expandGlob } from "../fs/mod.ts"; -import { isWindows } from "../fs/path/constants.ts"; -import { join } from "../fs/path/mod.ts"; +import { isWindows, join } from "../path/mod.ts"; import { RunTestsOptions, runTests } from "./mod.ts"; const { DenoError, ErrorKind, args, cwd, exit } = Deno; diff --git a/std/testing/runner_test.ts b/std/testing/runner_test.ts index e2617b1552..40159db3d4 100644 --- a/std/testing/runner_test.ts +++ b/std/testing/runner_test.ts @@ -1,8 +1,8 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { assertEquals } from "../testing/asserts.ts"; +import { isWindows } from "../path/mod.ts"; import { test } from "./mod.ts"; import { findTestModules } from "./runner.ts"; -import { isWindows } from "../fs/path/constants.ts"; -import { assertEquals } from "../testing/asserts.ts"; const { cwd } = Deno; function urlToFilePath(url: URL): string {