mirror of
https://github.com/denoland/deno.git
synced 2024-10-29 08:58:01 -04:00
walk() should not use deprecated FileInfo.path (denoland/deno_std#398)
The walk() interface is change to return a WalkInfo object which
contains both the resolved filename and FileInfo object from stat.
The followSymlinks feature is disabled for now.
Original: 07ca4f9629
This commit is contained in:
parent
e3e9269c76
commit
1b359f4106
5 changed files with 120 additions and 136 deletions
|
@ -1,32 +1,11 @@
|
|||
const { mkdir, open } = Deno;
|
||||
const { mkdir } = Deno;
|
||||
type FileInfo = Deno.FileInfo;
|
||||
import { test } from "../testing/mod.ts";
|
||||
import { test, runIfMain } from "../testing/mod.ts";
|
||||
import { assertEquals } from "../testing/asserts.ts";
|
||||
import { glob } from "./glob.ts";
|
||||
import { join } from "./path.ts";
|
||||
import { testWalk } from "./walk_test.ts";
|
||||
import { walk, walkSync, WalkOptions } from "./walk.ts";
|
||||
|
||||
async function touch(path: string): Promise<void> {
|
||||
await open(path, "w");
|
||||
}
|
||||
|
||||
async function walkArray(
|
||||
dirname: string = ".",
|
||||
options: WalkOptions = {}
|
||||
): Promise<string[]> {
|
||||
const arr: string[] = [];
|
||||
for await (const f of walk(dirname, { ...options })) {
|
||||
arr.push(f.path.replace(/\\/g, "/"));
|
||||
}
|
||||
arr.sort();
|
||||
const arrSync = Array.from(
|
||||
walkSync(dirname, options),
|
||||
(f: FileInfo): string => f.path.replace(/\\/g, "/")
|
||||
).sort();
|
||||
assertEquals(arr, arrSync);
|
||||
return arr;
|
||||
}
|
||||
import { touch, walkArray } from "./walk_test.ts";
|
||||
|
||||
test({
|
||||
name: "glob: glob to regex",
|
||||
|
@ -77,7 +56,7 @@ testWalk(
|
|||
async function globInWalk(): Promise<void> {
|
||||
const arr = await walkArray(".", { match: [glob("*.ts")] });
|
||||
assertEquals(arr.length, 1);
|
||||
assertEquals(arr[0], "./a/x.ts");
|
||||
assertEquals(arr[0], "a/x.ts");
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -92,8 +71,8 @@ testWalk(
|
|||
async function globInWalkWildcardFiles(): Promise<void> {
|
||||
const arr = await walkArray(".", { match: [glob("*.ts")] });
|
||||
assertEquals(arr.length, 2);
|
||||
assertEquals(arr[0], "./a/x.ts");
|
||||
assertEquals(arr[1], "./b/z.ts");
|
||||
assertEquals(arr[0], "a/x.ts");
|
||||
assertEquals(arr[1], "b/z.ts");
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -113,7 +92,7 @@ testWalk(
|
|||
]
|
||||
});
|
||||
assertEquals(arr.length, 1);
|
||||
assertEquals(arr[0], "./a/yo/x.ts");
|
||||
assertEquals(arr[0], "a/yo/x.ts");
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -137,8 +116,8 @@ testWalk(
|
|||
]
|
||||
});
|
||||
assertEquals(arr.length, 2);
|
||||
assertEquals(arr[0], "./a/deno/x.ts");
|
||||
assertEquals(arr[1], "./a/raptor/x.ts");
|
||||
assertEquals(arr[0], "a/deno/x.ts");
|
||||
assertEquals(arr[1], "a/raptor/x.ts");
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -154,7 +133,9 @@ testWalk(
|
|||
});
|
||||
console.log(arr);
|
||||
assertEquals(arr.length, 2);
|
||||
assertEquals(arr[0], "./x.js");
|
||||
assertEquals(arr[1], "./x.ts");
|
||||
assertEquals(arr[0], "x.js");
|
||||
assertEquals(arr[1], "x.ts");
|
||||
}
|
||||
);
|
||||
|
||||
runIfMain(import.meta);
|
||||
|
|
114
fs/walk.ts
114
fs/walk.ts
|
@ -1,5 +1,10 @@
|
|||
const { readDir, readDirSync, readlink, readlinkSync, stat, statSync } = Deno;
|
||||
// Documentation and interface for walk were adapted from Go
|
||||
// https://golang.org/pkg/path/filepath/#Walk
|
||||
// Copyright 2009 The Go Authors. All rights reserved. BSD license.
|
||||
const { readDir, readDirSync } = Deno;
|
||||
type FileInfo = Deno.FileInfo;
|
||||
import { unimplemented } from "../testing/asserts.ts";
|
||||
import { join } from "./path/mod.ts";
|
||||
|
||||
export interface WalkOptions {
|
||||
maxDepth?: number;
|
||||
|
@ -23,121 +28,114 @@ function patternTest(patterns: RegExp[], path: string): boolean {
|
|||
);
|
||||
}
|
||||
|
||||
function include(f: FileInfo, options: WalkOptions): boolean {
|
||||
function include(filename: string, options: WalkOptions): boolean {
|
||||
if (
|
||||
options.exts &&
|
||||
!options.exts.some((ext): boolean => f.path.endsWith(ext))
|
||||
!options.exts.some((ext): boolean => filename.endsWith(ext))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (options.match && !patternTest(options.match, f.path)) {
|
||||
if (options.match && !patternTest(options.match, filename)) {
|
||||
return false;
|
||||
}
|
||||
if (options.skip && patternTest(options.skip, f.path)) {
|
||||
if (options.skip && patternTest(options.skip, filename)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function resolve(f: FileInfo): Promise<FileInfo> {
|
||||
// This is the full path, unfortunately if we were to make it relative
|
||||
// it could resolve to a symlink and cause an infinite loop.
|
||||
const fpath = await readlink(f.path);
|
||||
f = await stat(fpath);
|
||||
// workaround path not being returned by stat
|
||||
f.path = fpath;
|
||||
return f;
|
||||
export interface WalkInfo {
|
||||
filename: string;
|
||||
info: FileInfo;
|
||||
}
|
||||
|
||||
function resolveSync(f: FileInfo): FileInfo {
|
||||
// This is the full path, unfortunately if we were to make it relative
|
||||
// it could resolve to a symlink and cause an infinite loop.
|
||||
const fpath = readlinkSync(f.path);
|
||||
f = statSync(fpath);
|
||||
// workaround path not being returned by stat
|
||||
f.path = fpath;
|
||||
return f;
|
||||
}
|
||||
|
||||
/** Generate all files in a directory recursively.
|
||||
/** Walks the file tree rooted at root, calling walkFn for each file or
|
||||
* directory in the tree, including root. The files are walked in lexical
|
||||
* order, which makes the output deterministic but means that for very large
|
||||
* directories walk() can be inefficient.
|
||||
*
|
||||
* for await (const fileInfo of walk()) {
|
||||
* console.log(fileInfo.path);
|
||||
* assert(fileInfo.isFile());
|
||||
* Options:
|
||||
* - maxDepth?: number;
|
||||
* - exts?: string[];
|
||||
* - match?: RegExp[];
|
||||
* - skip?: RegExp[];
|
||||
* - onError?: (err: Error) => void;
|
||||
* - followSymlinks?: boolean;
|
||||
*
|
||||
* for await (const { filename, info } of walk(".")) {
|
||||
* console.log(filename);
|
||||
* assert(info.isFile());
|
||||
* };
|
||||
*/
|
||||
export async function* walk(
|
||||
dir: string = ".",
|
||||
root: string,
|
||||
options: WalkOptions = {}
|
||||
): AsyncIterableIterator<FileInfo> {
|
||||
): AsyncIterableIterator<WalkInfo> {
|
||||
options.maxDepth -= 1;
|
||||
let ls: FileInfo[] = [];
|
||||
try {
|
||||
ls = await readDir(dir);
|
||||
ls = await readDir(root);
|
||||
} catch (err) {
|
||||
if (options.onError) {
|
||||
options.onError(err);
|
||||
}
|
||||
}
|
||||
const length = ls.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
let f = ls[i];
|
||||
if (f.isSymlink()) {
|
||||
for (let info of ls) {
|
||||
if (info.isSymlink()) {
|
||||
if (options.followSymlinks) {
|
||||
f = await resolve(f);
|
||||
// TODO(ry) Re-enable followSymlinks.
|
||||
unimplemented();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (f.isFile()) {
|
||||
if (include(f, options)) {
|
||||
yield f;
|
||||
|
||||
const filename = join(root, info.name);
|
||||
|
||||
if (info.isFile()) {
|
||||
if (include(filename, options)) {
|
||||
yield { filename, info };
|
||||
}
|
||||
} else {
|
||||
if (!(options.maxDepth < 0)) {
|
||||
yield* walk(f.path, options);
|
||||
yield* walk(filename, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Generate all files in a directory recursively.
|
||||
*
|
||||
* for (const fileInfo of walkSync()) {
|
||||
* console.log(fileInfo.path);
|
||||
* assert(fileInfo.isFile());
|
||||
* };
|
||||
*/
|
||||
/** Same as walk() but uses synchronous ops */
|
||||
export function* walkSync(
|
||||
dir: string = ".",
|
||||
root: string = ".",
|
||||
options: WalkOptions = {}
|
||||
): IterableIterator<FileInfo> {
|
||||
): IterableIterator<WalkInfo> {
|
||||
options.maxDepth -= 1;
|
||||
let ls: FileInfo[] = [];
|
||||
try {
|
||||
ls = readDirSync(dir);
|
||||
ls = readDirSync(root);
|
||||
} catch (err) {
|
||||
if (options.onError) {
|
||||
options.onError(err);
|
||||
}
|
||||
}
|
||||
const length = ls.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
let f = ls[i];
|
||||
if (f.isSymlink()) {
|
||||
for (let info of ls) {
|
||||
if (info.isSymlink()) {
|
||||
if (options.followSymlinks) {
|
||||
f = resolveSync(f);
|
||||
unimplemented();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (f.isFile()) {
|
||||
if (include(f, options)) {
|
||||
yield f;
|
||||
|
||||
const filename = join(root, info.name);
|
||||
|
||||
if (info.isFile()) {
|
||||
if (include(filename, options)) {
|
||||
yield { filename, info };
|
||||
}
|
||||
} else {
|
||||
if (!(options.maxDepth < 0)) {
|
||||
yield* walkSync(f.path, options);
|
||||
yield* walkSync(filename, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
const { cwd, chdir, makeTempDir, mkdir, open, build, remove, symlink } = Deno;
|
||||
const { cwd, chdir, makeTempDir, mkdir, open, remove } = Deno;
|
||||
type FileInfo = Deno.FileInfo;
|
||||
import { walk, walkSync, WalkOptions } from "./walk.ts";
|
||||
import { test, TestFunction } from "../testing/mod.ts";
|
||||
import { assert, assertEquals } from "../testing/asserts.ts";
|
||||
|
||||
const isWindows = build.os === "win";
|
||||
import { walk, walkSync, WalkOptions, WalkInfo } from "./walk.ts";
|
||||
import { test, TestFunction, runIfMain } from "../testing/mod.ts";
|
||||
import { assertEquals } from "../testing/asserts.ts";
|
||||
|
||||
export async function testWalk(
|
||||
setup: (string) => void | Promise<void>,
|
||||
|
@ -26,28 +24,32 @@ export async function testWalk(
|
|||
test({ name, fn });
|
||||
}
|
||||
|
||||
async function walkArray(
|
||||
dirname: string = ".",
|
||||
function normalize({ filename }: WalkInfo): string {
|
||||
return filename.replace(/\\/g, "/");
|
||||
}
|
||||
|
||||
export async function walkArray(
|
||||
root: string = ".",
|
||||
options: WalkOptions = {}
|
||||
): Promise<string[]> {
|
||||
const arr: string[] = [];
|
||||
for await (const f of walk(dirname, { ...options })) {
|
||||
arr.push(f.path.replace(/\\/g, "/"));
|
||||
for await (const w of walk(root, { ...options })) {
|
||||
arr.push(normalize(w));
|
||||
}
|
||||
arr.sort();
|
||||
const arrSync = Array.from(
|
||||
walkSync(dirname, options),
|
||||
(f: FileInfo): string => f.path.replace(/\\/g, "/")
|
||||
).sort();
|
||||
arr.sort(); // TODO(ry) Remove sort. The order should be deterministic.
|
||||
const arrSync = Array.from(walkSync(root, options), normalize);
|
||||
arrSync.sort(); // TODO(ry) Remove sort. The order should be deterministic.
|
||||
assertEquals(arr, arrSync);
|
||||
return arr;
|
||||
}
|
||||
|
||||
async function touch(path: string): Promise<void> {
|
||||
export async function touch(path: string): Promise<void> {
|
||||
await open(path, "w");
|
||||
}
|
||||
|
||||
function assertReady(expectedLength: number): void {
|
||||
const arr = Array.from(walkSync(), (f: FileInfo): string => f.path);
|
||||
const arr = Array.from(walkSync(), normalize);
|
||||
|
||||
assertEquals(arr.length, expectedLength);
|
||||
}
|
||||
|
||||
|
@ -68,7 +70,7 @@ testWalk(
|
|||
async function singleFile(): Promise<void> {
|
||||
const arr = await walkArray();
|
||||
assertEquals(arr.length, 1);
|
||||
assertEquals(arr[0], "./x");
|
||||
assertEquals(arr[0], "x");
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -78,11 +80,11 @@ testWalk(
|
|||
},
|
||||
async function iteratable(): Promise<void> {
|
||||
let count = 0;
|
||||
for (const _ of walkSync()) {
|
||||
for (const _ of walkSync(".")) {
|
||||
count += 1;
|
||||
}
|
||||
assertEquals(count, 1);
|
||||
for await (const _ of walk()) {
|
||||
for await (const _ of walk(".")) {
|
||||
count += 1;
|
||||
}
|
||||
assertEquals(count, 2);
|
||||
|
@ -97,7 +99,7 @@ testWalk(
|
|||
async function nestedSingleFile(): Promise<void> {
|
||||
const arr = await walkArray();
|
||||
assertEquals(arr.length, 1);
|
||||
assertEquals(arr[0], "./a/x");
|
||||
assertEquals(arr[0], "a/x");
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -112,7 +114,7 @@ testWalk(
|
|||
assertEquals(arr3.length, 0);
|
||||
const arr5 = await walkArray(".", { maxDepth: 5 });
|
||||
assertEquals(arr5.length, 1);
|
||||
assertEquals(arr5[0], "./a/b/c/d/x");
|
||||
assertEquals(arr5[0], "a/b/c/d/x");
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -125,7 +127,7 @@ testWalk(
|
|||
assertReady(2);
|
||||
const arr = await walkArray(".", { exts: [".ts"] });
|
||||
assertEquals(arr.length, 1);
|
||||
assertEquals(arr[0], "./x.ts");
|
||||
assertEquals(arr[0], "x.ts");
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -139,8 +141,8 @@ testWalk(
|
|||
assertReady(3);
|
||||
const arr = await walkArray(".", { exts: [".rs", ".ts"] });
|
||||
assertEquals(arr.length, 2);
|
||||
assertEquals(arr[0], "./x.ts");
|
||||
assertEquals(arr[1], "./y.rs");
|
||||
assertEquals(arr[0], "x.ts");
|
||||
assertEquals(arr[1], "y.rs");
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -153,7 +155,7 @@ testWalk(
|
|||
assertReady(2);
|
||||
const arr = await walkArray(".", { match: [/x/] });
|
||||
assertEquals(arr.length, 1);
|
||||
assertEquals(arr[0], "./x");
|
||||
assertEquals(arr[0], "x");
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -167,8 +169,8 @@ testWalk(
|
|||
assertReady(3);
|
||||
const arr = await walkArray(".", { match: [/x/, /y/] });
|
||||
assertEquals(arr.length, 2);
|
||||
assertEquals(arr[0], "./x");
|
||||
assertEquals(arr[1], "./y");
|
||||
assertEquals(arr[0], "x");
|
||||
assertEquals(arr[1], "y");
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -181,7 +183,7 @@ testWalk(
|
|||
assertReady(2);
|
||||
const arr = await walkArray(".", { skip: [/x/] });
|
||||
assertEquals(arr.length, 1);
|
||||
assertEquals(arr[0], "./y");
|
||||
assertEquals(arr[0], "y");
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -195,7 +197,7 @@ testWalk(
|
|||
assertReady(3);
|
||||
const arr = await walkArray(".", { skip: [/x/, /y/] });
|
||||
assertEquals(arr.length, 1);
|
||||
assertEquals(arr[0], "./z");
|
||||
assertEquals(arr[0], "z");
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -228,6 +230,7 @@ testWalk(
|
|||
}
|
||||
);
|
||||
|
||||
/* TODO(ry) Re-enable followSymlinks
|
||||
testWalk(
|
||||
async (d: string): Promise<void> => {
|
||||
await mkdir(d + "/a");
|
||||
|
@ -258,3 +261,6 @@ testWalk(
|
|||
assert(arr.some((f): boolean => f.endsWith("/b/z")));
|
||||
}
|
||||
);
|
||||
*/
|
||||
|
||||
runIfMain(import.meta);
|
||||
|
|
|
@ -12,9 +12,8 @@
|
|||
// This script formats the given source files. If the files are omitted, it
|
||||
// formats the all files in the repository.
|
||||
const { args, exit, readFile, writeFile } = Deno;
|
||||
type FileInfo = Deno.FileInfo;
|
||||
import { glob } from "../fs/glob.ts";
|
||||
import { walk } from "../fs/walk.ts";
|
||||
import { walk, WalkInfo } from "../fs/walk.ts";
|
||||
import { parse } from "../flags/mod.ts";
|
||||
import { prettier, prettierPlugins } from "./prettier.ts";
|
||||
|
||||
|
@ -174,15 +173,15 @@ function selectParser(path: string): ParserLabel | null {
|
|||
* If paths are empty, then checks all the files.
|
||||
*/
|
||||
async function checkSourceFiles(
|
||||
files: AsyncIterableIterator<FileInfo>,
|
||||
files: AsyncIterableIterator<WalkInfo>,
|
||||
prettierOpts: PrettierOptions
|
||||
): Promise<void> {
|
||||
const checks: Array<Promise<boolean>> = [];
|
||||
|
||||
for await (const file of files) {
|
||||
const parser = selectParser(file.path);
|
||||
for await (const { filename } of files) {
|
||||
const parser = selectParser(filename);
|
||||
if (parser) {
|
||||
checks.push(checkFile(file.path, parser, prettierOpts));
|
||||
checks.push(checkFile(filename, parser, prettierOpts));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -202,15 +201,15 @@ async function checkSourceFiles(
|
|||
* If paths are empty, then formats all the files.
|
||||
*/
|
||||
async function formatSourceFiles(
|
||||
files: AsyncIterableIterator<FileInfo>,
|
||||
files: AsyncIterableIterator<WalkInfo>,
|
||||
prettierOpts: PrettierOptions
|
||||
): Promise<void> {
|
||||
const formats: Array<Promise<void>> = [];
|
||||
|
||||
for await (const file of files) {
|
||||
const parser = selectParser(file.path);
|
||||
for await (const { filename } of files) {
|
||||
const parser = selectParser(filename);
|
||||
if (parser) {
|
||||
formats.push(formatFile(file.path, parser, prettierOpts));
|
||||
formats.push(formatFile(filename, parser, prettierOpts));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,8 +63,8 @@ test(async function testPrettierCheckAndFormatFiles(): Promise<void> {
|
|||
assertEquals(code, 0);
|
||||
assertEquals(
|
||||
normalizeOutput(stdout),
|
||||
`Formatting ./prettier/testdata/0.ts
|
||||
Formatting ./prettier/testdata/1.js`
|
||||
`Formatting prettier/testdata/0.ts
|
||||
Formatting prettier/testdata/1.js`
|
||||
);
|
||||
|
||||
var { code, stdout } = await run([...cmd, "--check", ...files]);
|
||||
|
@ -87,10 +87,10 @@ test(async function testPrettierCheckAndFormatDirs(): Promise<void> {
|
|||
assertEquals(code, 0);
|
||||
assertEquals(
|
||||
normalizeOutput(stdout),
|
||||
`Formatting ./prettier/testdata/bar/0.ts
|
||||
Formatting ./prettier/testdata/bar/1.js
|
||||
Formatting ./prettier/testdata/foo/0.ts
|
||||
Formatting ./prettier/testdata/foo/1.js`
|
||||
`Formatting prettier/testdata/bar/0.ts
|
||||
Formatting prettier/testdata/bar/1.js
|
||||
Formatting prettier/testdata/foo/0.ts
|
||||
Formatting prettier/testdata/foo/1.js`
|
||||
);
|
||||
|
||||
var { code, stdout } = await run([...cmd, "--check", ...dirs]);
|
||||
|
|
Loading…
Reference in a new issue