node
node copied to clipboard
fs.glob add option to only include files (not directories) in the results entries
What is the problem this feature will solve?
Since Node.js v22, with this PR: https://github.com/nodejs/node/pull/51912, it is possible to use fs.promises.glob
for matching file paths based on specified patterns.
However, the results of entries also includes directories, but other famous userland library (e.g: globby) only returns files (not directories).
Example
With a file structure like the following:
$ mkdir -p foo/bar && touch foo/bar.md
$ tree foo
foo
├── bar
└── bar.md
2 directories, 1 file
And the following code:
import fs from "node:fs"
import { globby } from "globby"
console.log("fs.glob", await Array.fromAsync(fs.promises.glob("foo/**")))
console.log("globby", await globby("foo/**"))
It prints:
fs.glob [ 'foo', 'foo/bar.md', 'foo/bar' ]
globby [ 'foo/bar.md' ]
What is the feature you are proposing to solve the problem?
Add 2 options to fs.glob
:
-
onlyDirectories
, boolean, default tofalse
. -
onlyFiles
, boolean, default tofalse
.
Both default to false
, to keep same behavior, so no breaking changes is introduced.
Options based on the fast-glob library which globby
uses under the hood.
What alternatives have you considered?
export const globby = async (patterns) => {
const files = []
for await (const entry of fs.promises.glob(patterns)) {
const stats = await fs.promises.stat(entry)
if (stats.isFile()) {
files.push(entry)
}
}
return files
}
@MoLow
Maybe just expose the dirent insstead of its name to exclude which would enable:
fs.promises.glob("foo/**", { exclude(entry) { return entry.isFile(); })
Maybe just expose the dirent insstead of its name to exclude which would enable:
fs.promises.glob("foo/**", { exclude(entry) { return entry.isFile(); })
You mean exclude
take as argument (entry
) the await fs.promises.stat(entry)
?
That would work, fs.promises.glob("foo/**", { exclude(entry) { return entry.isDirectory(); })
to only get files.
However, that would be a BREAKING CHANGE, probably fine, as it's still experimental, but still worth to point out.
But then we don't have information about filename/path.
Maybe the argument in exclude
should be an object with multiple information about the file/directory (name, relativePath, absolutePath, stats, etc.).
we should probably just add a withFileTypes
option like fs.readDir
has
If I want to return strings (because they're relative to the cwd) and ensure they are all files, then is the code
for (const dirent of fs.globSync('**', { cwd: path, withFileTypes: true })) {
if (dirent.isFile()) {
const name = path.relative(this.path, path.resolve(dirent.parentPath, dirent.name));
dict[name] = {};
}
}
a bit too complicated?