glob-stream
A CommonJS utility that walks the filesystem for one or more glob patterns and exposes matches as a `streamx` readable stream. Each object contains an absolute normalized `cwd`, the non-glob `base`, and the matched `path`; it does not read file contents. Matching is provided by anymatch and picomatch-style options, while pause and resume behavior lets downstream object streams apply backpressure. It is mainly useful inside Gulp-style build pipelines that want file discovery to remain streaming.
A dependable adapter when file discovery must feed an existing Gulp or streamx object pipeline. For ordinary scripts that just need path names, a promise-returning glob library is easier to type, test, and clean up.
Use it if
- You are building a Gulp-style object pipeline and need matches to arrive incrementally instead of as one array
- You need each match labeled with cwd and base so a downstream step can preserve relative directory structure
- You want positive and negative globs, ignore patterns, dotfile control, and duplicate filtering in one file walker
- Your downstream code already uses streamx or accepts Node-compatible object streams
- You only need an array of paths: fast-glob or globby has a simpler promise API and avoids stream lifecycle code
- You expect file contents, stat objects, or Vinyl files: version 8 emits only plain objects with cwd, base, and path, so gulp.src or your own read step is still required
- You need TypeScript declarations or ESM exports: the package ships CommonJS index.js without bundled types or an exports map
- You need a watch mode: the walker performs one scan and ends; it does not report files added or removed after traversal starts
- You need deterministic ordering or heavily parallel traversal: the test suite treats multi-match output as unordered, and the source walks its filesystem action queue with concurrency 1
Setup reality
Install with `npm install glob-stream`; there are no peer dependencies, credentials, native addons, or configuration files. Version 8 supports Node 10.13 and later and is loaded with require(). The first surprise is the output: matches are plain object-mode records containing absolute, forward-slash-normalized cwd, base, and path values. You must add a read stream if a later transform needs bytes. At least one positive glob is mandatory, so an array containing only `!vendor/**` throws synchronously. A wildcard that matches nothing is allowed, but a literal path that does not exist emits an error by default because the package treats singular paths as promises that one item should exist. Set allowEmpty only when that absence is intentional. Dotfiles are excluded unless dot is true. cwd defaults to the process working directory, which makes CI behavior depend on where the command starts unless you set it explicitly. base defaults to the absolute portion before the first glob token; use base or cwdbase when downstream relative paths must be stable. Matches are deduplicated by path, and uniqueBy can be a property or function. The stream is implemented by streamx rather than node:stream Readable, though ordinary pipe and event consumption work. Its eight runtime dependencies handle walking, matching, normalization, queueing, and absolute-glob conversion. The walker follows directory symlinks only after resolving them and tries to avoid cycles, but large trees are still scanned from each positive glob parent. Errors from readdir or missing singular paths destroy the stream, so attach an error handler or use a pipeline that observes failures. There is no ordering guarantee, no watch behavior, and no content reading hidden behind the API.
Patterns
Stream matching file recordsstream-matches
const globStream = require('glob-stream')
const matches = globStream('src/**/*.js')
matches.on('data', ({ cwd, base, path }) => {
console.log({ cwd, base, path })
})
matches.on('error', console.error)The stream emits metadata records, not file contents. All three paths are absolute and normalized to forward slashes.
Combine includes and exclusionsmultiple-patterns
const matches = globStream([
'src/**/*.{js,ts}',
'!src/**/*.test.{js,ts}',
'!src/generated/**',
])At least one positive pattern is required. An all-negative pattern list throws `Missing positive glob` synchronously.
Resolve globs from a stable project directoryset-cwd
const path = require('node:path')
const matches = globStream('packages/*/src/**/*.ts', {
cwd: path.resolve(__dirname, '..'),
})cwd defaults to process.cwd(). Set it explicitly when scripts may be launched from different directories.
Permit an optional singular pathallow-missing-path
const optionalConfig = globStream('config/local.json', {
allowEmpty: true,
})Without allowEmpty, a missing non-glob path emits an error. Wildcard patterns are allowed to match zero files by default.
Include dotfiles and dot directoriesinclude-dotfiles
const allConfigs = globStream('**/*', {
cwd: './config',
dot: true,
})dot defaults to false, so entries such as .env and files below .config are otherwise skipped by matching.
Keep exclusions separate with ignoreignore-patterns
const sourceFiles = globStream('src/**/*.js', {
ignore: ['src/vendor/**', 'src/**/*.generated.js'],
})ignore accepts a string or array and is normalized relative to cwd and root like the positive globs.
Set the base used for relative destinationspreserve-relative-paths
const path = require('node:path')
const files = globStream('assets/**/*', {
cwd: __dirname,
base: path.join(__dirname, 'assets'),
})
files.on('data', (file) => {
console.log(path.relative(file.base, file.path))
})base must be an absolute or cwd-resolved string that matches the layout you want downstream. It does not change which files match.
Make cwd and base identicaluse-cwd-as-base
const files = globStream(['images/**', 'styles/**'], {
cwd: './public',
cwdbase: true,
})cwdbase overrides the inferred glob parent. This is useful when several positive globs should share one output root.
Deduplicate by a custom keycustom-deduplication
const path = require('node:path')
const files = globStream(['packages/*/assets/**', 'shared/assets/**'], {
uniqueBy: (file) => path.basename(file.path).toLowerCase(),
})The default uniqueBy is `path`. A broad custom key can intentionally discard different files that share the same name.
Match directories rather than their filesmatch-directories
const directories = globStream('packages/*/', {
cwd: process.cwd(),
})A trailing slash pattern can match directory records. The emitted object still has only cwd, base, and path, not a Dirent or stat.
Process records with a streamx writablepipe-object-records
const { Writable } = require('streamx')
const sink = new Writable({
objectMode: true,
write(file, callback) {
console.log(file.path)
callback(null)
},
})
globStream('src/**/*').pipe(sink)The readable side is object mode. A byte-oriented writable will reject the emitted plain objects.
Collect the stream into an arraycollect-results
function collect(globs, options) {
return new Promise((resolve, reject) => {
const items = []
globStream(globs, options)
.on('data', (item) => items.push(item))
.once('error', reject)
.once('end', () => resolve(items))
})
}
const files = await collect('src/**/*.ts')Result order is not guaranteed. Sort by path after collection when snapshots or reproducible builds require stable ordering.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| fast-glob | npm | You want fast promise, sync, or Node stream matching with path strings and a broad option set |
| globby | npm | You want a friendly ESM promise API with gitignore support and sensible multiple-pattern handling |
| glob | npm | You want the established node-glob API, PathScurry objects, async iteration, and traversal controls |
| tinyglobby | npm | You want a small modern matcher for array results and do not need a streaming object pipeline |