Skip to content

Commit

Permalink
std: Move fs/path to the top-level (denoland/deno#3100)
Browse files Browse the repository at this point in the history
  • Loading branch information
nayeemrmn authored and denobot committed Feb 1, 2021
1 parent d8bee29 commit 5b2aec7
Show file tree
Hide file tree
Showing 57 changed files with 323 additions and 305 deletions.
2 changes: 1 addition & 1 deletion archive/tar_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down
2 changes: 1 addition & 1 deletion encoding/toml_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down
2 changes: 1 addition & 1 deletion fs/copy.ts
Original file line number Diff line number Diff line change
@@ -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";

Expand Down
2 changes: 1 addition & 1 deletion fs/copy_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
2 changes: 1 addition & 1 deletion fs/empty_dir_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down
2 changes: 1 addition & 1 deletion fs/ensure_dir_test.ts
Original file line number Diff line number Diff line change
@@ -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");
Expand Down
2 changes: 1 addition & 1 deletion fs/ensure_file.ts
Original file line number Diff line number Diff line change
@@ -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";

Expand Down
2 changes: 1 addition & 1 deletion fs/ensure_file_test.ts
Original file line number Diff line number Diff line change
@@ -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");

Expand Down
2 changes: 1 addition & 1 deletion fs/ensure_link.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down
2 changes: 1 addition & 1 deletion fs/ensure_link_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down
2 changes: 1 addition & 1 deletion fs/ensure_symlink.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down
2 changes: 1 addition & 1 deletion fs/ensure_symlink_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
2 changes: 1 addition & 1 deletion fs/exists_test.ts
Original file line number Diff line number Diff line change
@@ -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");

Expand Down
132 changes: 11 additions & 121 deletions fs/glob.ts → fs/expand_glob.ts
Original file line number Diff line number Diff line change
@@ -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[];
Expand Down
120 changes: 120 additions & 0 deletions fs/expand_glob_test.ts
Original file line number Diff line number Diff line change
@@ -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:https:///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);
3 changes: 1 addition & 2 deletions fs/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Loading

0 comments on commit 5b2aec7

Please sign in to comment.