Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(fs/unstable): add readDirSync #6381

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 46 additions & 6 deletions fs/unstable_read_dir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@
import { toDirEntry } from "./_to_dir_entry.ts";
import type { DirEntry } from "./unstable_types.ts";

/** Reads the directory given by `path` and returns an async iterable of
/**
* Reads the directory given by `path` and returns an async iterable of
* {@linkcode DirEntry}. The order of entries is not guaranteed.
*
* Throws Error if `path` is not a directory.
*
* Requires `allow-read` permission.
*
* @example Usage
* ```ts
* import { readDir } from "@std/fs/unstable-read-dir";
Expand All @@ -17,12 +22,10 @@
* }
* ```
*
* Throws error if `path` is not a directory.
*
* Requires `allow-read` permission.
*
* @tags allow-read
* @category File System
*
* @param path The path to the directory.
* @returns An async iterable of `DirEntry` elements.
*/
export async function* readDir(path: string | URL): AsyncIterable<DirEntry> {
if (isDeno) {
Expand All @@ -38,3 +41,40 @@
}
}
}

Copy link
Contributor

@petamoriken petamoriken Feb 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about defining the type of IteratorObject here?

Suggested change
interface IteratorObject<T> extends Iterable<T> {}

In Deno 1 (TypeScript 5.3 or lower), it should be treated as just a new type definition, and in Deno 2 (TypeScript 5.4 or higher), where IteratorObject type exists, it should be treated as a merge of interface types and nothing should happen.

/**
* Synchronously reads the directory given by `path` and returns an iterable
* of {@linkcode Deno.DirEntry}. The order of entries is not guaranteed.
*
* Throws Error if `path` is not a directory.
*
* Requires `allow-read` permission.
*
* @example Usage
* ```ts
* import { readDirSync } from "@std/fs/unstable-read-dir";
*
* for (const dirEntry of readDirSync("/")) {
* console.log(dirEntry.name);
* }
* ```
*
* @tags allow-read
*
* @param path The path to the directory.
* @returns An iterator object of `DirEntry` elements.
*/
export function* readDirSync(path: string | URL): IteratorObject<DirEntry> {
if (isDeno) {
return yield* Deno.readDirSync(path);
} else {
try {
const dir = getNodeFs().readdirSync(path, { withFileTypes: true });
for (const entry of dir) {
yield toDirEntry(entry);
}
} catch (error) {
throw mapError(error);
}
}

Check warning on line 79 in fs/unstable_read_dir.ts

View check run for this annotation

Codecov / codecov/patch

fs/unstable_read_dir.ts#L71-L79

Added lines #L71 - L79 were not covered by tests
}
43 changes: 35 additions & 8 deletions fs/unstable_read_dir_test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright 2018-2025 the Deno authors. MIT license.

import { assert, assertEquals, assertRejects } from "@std/assert";
import { assert, assertEquals, assertRejects, assertThrows } from "@std/assert";
import { fromFileUrl, join, resolve } from "@std/path";
import { readDir } from "./unstable_read_dir.ts";
import { readDir, readDirSync } from "./unstable_read_dir.ts";
import { NotFound } from "./unstable_errors.js";

const testdataDir = resolve(fromFileUrl(import.meta.url), "../testdata");
Expand Down Expand Up @@ -32,10 +32,37 @@ Deno.test("readDir() rejects when the path is not a directory", async () => {
});

Deno.test("readDir() rejects when the directory does not exist", async () => {
await assertRejects(
async () => {
await readDir("non_existent_dir")[Symbol.asyncIterator]().next();
},
NotFound,
);
await assertRejects(async () => {
await readDir("non_existent_dir")[Symbol.asyncIterator]().next();
}, NotFound);
});

Deno.test("readDirSync() reads from the directory and its subdirectories", () => {
const files = [];
for (const e of readDirSync(testdataDir)) {
files.push(e);
}

let counter = 0;
for (const f of files) {
if (f.name === "walk") {
assert(f.isDirectory);
counter++;
}
}

assertEquals(counter, 1);
});

Deno.test("readDirSync() throws with Error when the path is not a directory", () => {
assertThrows(() => {
const testFile = join(testdataDir, "0.ts");
readDirSync(testFile)[Symbol.iterator]().next();
}, Error);
});

Deno.test("readDirSync() throws with NotFound when a directory does not exist", () => {
assertThrows(() => {
readDirSync("non_existent_dir")[Symbol.iterator]().next();
}, NotFound);
});
Loading