Skip to content
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
10 changes: 8 additions & 2 deletions fs/walk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,10 @@ export async function* walk(
if (exts) {
exts = exts.map((ext) => ext.startsWith(".") ? ext : `.${ext}`);
}
if (includeDirs && include(root, exts, match, skip)) {
// Directories don't have file extensions, so the `exts` filter must not be
// applied when deciding whether to yield a directory entry. The `match`
// and `skip` regex filters still apply. See #6736.
if (includeDirs && include(root, undefined, match, skip)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The same class of bug still exists for symlinks-to-directories in the !followSymlinks branch below (fs/walk.ts:488 and :920): include(path, exts, match, skip) still applies exts to the symlink entry. A symlink pointing at a directory has no extension, so includeSymlinks: true + exts will silently drop it — exactly the issue you're fixing here.

The PR description's justification ("a symlink's name still has an extension just like the target") isn't accurate for symlinks-to-directories. Either handle it consistently here, or — if you'd rather defer it — soften that line in the description so it doesn't read as verified-correct.

yield await createWalkEntry(root);
}
if (maxDepth < 1 || !include(root, undefined, undefined, skip)) {
Expand Down Expand Up @@ -898,7 +901,10 @@ export function* walkSync(
if (maxDepth < 0) {
return;
}
if (includeDirs && include(root, exts, match, skip)) {
// Directories don't have file extensions, so the `exts` filter must not
// be applied when deciding whether to yield a directory entry. The
// `match` and `skip` regex filters still apply. See #6736.
if (includeDirs && include(root, undefined, match, skip)) {
yield createWalkEntrySync(root);
}
if (maxDepth < 1 || !include(root, undefined, undefined, skip)) {
Expand Down
25 changes: 22 additions & 3 deletions fs/walk_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,23 +113,42 @@ Deno.test("walkSync() accepts includeFiles option set to false", () =>
includeFiles: false,
}));

// `includeDirs` defaults to true, so the root directory entry is also
// expected. `exts` no longer filters directories (see #6736).
Deno.test("walk() accepts ext option as strings", async () =>
await assertWalkPaths(testdataDir, "ext", ["y.rs", "x.ts"], {
await assertWalkPaths(testdataDir, "ext", [".", "y.rs", "x.ts"], {
exts: [".rs", ".ts"],
}));

Deno.test("walk() accepts ext option as strings (excluding period prefix)", async () =>
await assertWalkPaths(testdataDir, "ext", ["y.rs", "x.ts"], {
await assertWalkPaths(testdataDir, "ext", [".", "y.rs", "x.ts"], {
exts: ["rs", "ts"],
}));

Deno.test("walkSync() accepts ext option as strings", () =>
assertWalkSyncPaths(testdataDir, "ext", ["y.rs", "x.ts"], {
assertWalkSyncPaths(testdataDir, "ext", [".", "y.rs", "x.ts"], {
exts: [".rs", ".ts"],
}));

Deno.test("walkSync() accepts ext option as strings (excluding period prefix)", () =>
assertWalkSyncPaths(testdataDir, "ext", [".", "y.rs", "x.ts"], {
exts: [".rs", ".ts"],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Pre-existing copy-paste error (optional fix-along): this (excluding period prefix) test uses exts: [".rs", ".ts"] (with leading periods), which doesn't match its own name. Its async sibling above correctly uses ["rs", "ts"]. Since you're already editing this line, cheap to fix.

}));

// https://github.com/denoland/std/issues/6736 — explicit includeDirs:false
// still drops the directory when an `exts` filter is set. Before the fix,
// includeDirs:true also dropped it (because the dir name had no extension);
// after the fix, the dir is yielded as expected (verified by the strings
// tests above).
Deno.test("walk() with exts and includeDirs:false yields only files", async () =>
await assertWalkPaths(testdataDir, "ext", ["y.rs", "x.ts"], {
includeDirs: false,
exts: [".rs", ".ts"],
}));

Deno.test("walkSync() with exts and includeDirs:false yields only files", () =>
assertWalkSyncPaths(testdataDir, "ext", ["y.rs", "x.ts"], {
includeDirs: false,
exts: [".rs", ".ts"],
}));

Expand Down
Loading