Globbing and File Matching

For years, matching src/**/*.js meant installing a package. Node added fs.glob in 22.0.0 and marked it stable in 24.0.0, so a dependency-free build script can now find its own inputs.

Globbing with the built-in fs.globJavaScript
import { glob } from 'node:fs/promises';
import { globSync } from 'node:fs';
import path from 'node:path';
for await (const hit of glob('src/**/*.js')) console.log('glob    :', hit);
console.log('exclude :', globSync('**/*.js', { exclude: ['src/routes/**'] }));
const dirents = globSync('*', { cwd: 'public', withFileTypes: true });
console.log('types   :', dirents.map((d) => `${d.name}:${d.isFile()}`).join(' '));
console.log('matches :', path.matchesGlob('src/routes/users.js', 'src/**/*.js'));
Output
glob    : src\app.js
glob    : src\routes\posts.js
glob    : src\routes\users.js
glob    : src\models\User.js
exclude : [ 'src\\app.js', 'src\\models\\User.js' ]
types   : img:false index.html:true
matches : true

Patterns are always written with forward slashes, but results come back in native form — backslashes on Windows, as above. The async form is an async iterator, not a promise for an array, so a huge tree costs one entry of memory at a time. exclude takes a predicate or an array of patterns (patterns since Node 23.7.0), cwd sets the base, and withFileTypes yields Dirents. Node 26 added followSymlinks (26.1.0) and maxDepth (26.9.0). path.matchesGlob, stable since Node 24.8.0, tests a string against a pattern with no disk access — right for filtering a list you already hold, such as ignore rules applied to watcher events.

Built-in globbing compared with the popular npm 2,036 alternatives (all MIT licensed)
Package Version Notes
node:fs glob built in stable, streams results, no dependencies
fast-glob 2,827 3.3.3 fastest on large trees, many options
tinyglobby 0.2.17 small fast-glob replacement, few deps
globby 2,649 16.2.4 fast-glob plus .gitignore support
picomatch 4.0.7 matching only, the engine under the others

Reach for a package when you need what the built-in lacks. globby (github.com/sindresorhus/globby (https://github.com/sindresorhus/globby 2,649 ), npm i globby) honors .gitignore files, which is what a linter or formatter wants. fast-glob (github.com/mrmlnc/fast-glob (https://github.com/mrmlnc/fast-glob 2,827 )) still wins on very large trees: it walks directories concurrently and prunes branches that cannot match. All of them, Node's included, compile the pattern to a regular expression first; picomatch is that compiler, published on its own.