Skip to content

Commit 9d3609e

Browse files
committed
add maxDepth option
Fix: #296
1 parent ef3f0fb commit 9d3609e

File tree

6 files changed

+219
-34
lines changed

6 files changed

+219
-34
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@ share the previously loaded cache.
310310
systems, or `false` on case-insensitive file systems, then the
311311
walk may return more or less results than expected.
312312

313+
- `maxDepth` Specify a number to limit the depth of the directory
314+
traversal to this many levels below the `cwd`.
315+
313316
- `matchBase` Perform a basename-only match if the pattern does
314317
not contain any slash characters. That is, `*.js` would be
315318
treated as equivalent to `**/*.js`, matching all js files in

src/glob.ts

+27
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,14 @@ export interface GlobOptions {
126126
*/
127127
matchBase?: boolean
128128

129+
/**
130+
* Limit the directory traversal to a given depth below the cwd.
131+
* Note that this does NOT prevent traversal to sibling folders,
132+
* root patterns, and so on. It only limits the maximum folder depth
133+
* that the walk will descend, relative to the cwd.
134+
*/
135+
maxDepth?: number
136+
129137
/**
130138
* Do not expand `{a,b}` and `{1..3}` brace sets.
131139
*/
@@ -293,6 +301,7 @@ export class Glob<Opts extends GlobOptions> implements GlobOptions {
293301
magicalBraces: boolean
294302
mark?: boolean
295303
matchBase: boolean
304+
maxDepth: number
296305
nobrace: boolean
297306
nocase: boolean
298307
nodir: boolean
@@ -351,6 +360,8 @@ export class Glob<Opts extends GlobOptions> implements GlobOptions {
351360

352361
this.noglobstar = !!opts.noglobstar
353362
this.matchBase = !!opts.matchBase
363+
this.maxDepth =
364+
typeof opts.maxDepth === 'number' ? opts.maxDepth : Infinity
354365

355366
if (this.withFileTypes && this.absolute !== undefined) {
356367
throw new Error('cannot set absolute and withFileTypes:true')
@@ -445,6 +456,10 @@ export class Glob<Opts extends GlobOptions> implements GlobOptions {
445456
return [
446457
...(await new GlobWalker(this.patterns, this.scurry.cwd, {
447458
...this.opts,
459+
maxDepth:
460+
this.maxDepth !== Infinity
461+
? this.maxDepth + this.scurry.cwd.depth()
462+
: Infinity,
448463
platform: this.platform,
449464
nocase: this.nocase,
450465
}).walk()),
@@ -459,6 +474,10 @@ export class Glob<Opts extends GlobOptions> implements GlobOptions {
459474
return [
460475
...new GlobWalker(this.patterns, this.scurry.cwd, {
461476
...this.opts,
477+
maxDepth:
478+
this.maxDepth !== Infinity
479+
? this.maxDepth + this.scurry.cwd.depth()
480+
: Infinity,
462481
platform: this.platform,
463482
nocase: this.nocase,
464483
}).walkSync(),
@@ -472,6 +491,10 @@ export class Glob<Opts extends GlobOptions> implements GlobOptions {
472491
stream(): Minipass<string | Path, string | Path> {
473492
return new GlobStream(this.patterns, this.scurry.cwd, {
474493
...this.opts,
494+
maxDepth:
495+
this.maxDepth !== Infinity
496+
? this.maxDepth + this.scurry.cwd.depth()
497+
: Infinity,
475498
platform: this.platform,
476499
nocase: this.nocase,
477500
}).stream()
@@ -484,6 +507,10 @@ export class Glob<Opts extends GlobOptions> implements GlobOptions {
484507
streamSync(): Minipass<string | Path, string | Path> {
485508
return new GlobStream(this.patterns, this.scurry.cwd, {
486509
...this.opts,
510+
maxDepth:
511+
this.maxDepth !== Infinity
512+
? this.maxDepth + this.scurry.cwd.depth()
513+
: Infinity,
487514
platform: this.platform,
488515
nocase: this.nocase,
489516
}).streamSync()

src/index.ts

+26-34
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
11
import { escape, unescape } from 'minimatch'
2+
import Minipass from 'minipass'
3+
import { Path } from 'path-scurry'
24
import type {
35
GlobOptions,
46
GlobOptionsWithFileTypesFalse,
57
GlobOptionsWithFileTypesTrue,
68
GlobOptionsWithFileTypesUnset,
7-
Results,
89
} from './glob.js'
910
import { Glob } from './glob.js'
1011
import { hasMagic } from './has-magic.js'
11-
import type {
12-
GWOFileTypesFalse,
13-
GWOFileTypesTrue,
14-
GWOFileTypesUnset,
15-
MatchStream,
16-
Result,
17-
} from './walker.js'
1812

1913
/**
2014
* Syncronous form of {@link globStream}. Will read all the matches as fast as
@@ -24,19 +18,19 @@ import type {
2418
export function globStreamSync(
2519
pattern: string | string[],
2620
options: GlobOptionsWithFileTypesTrue
27-
): MatchStream<GWOFileTypesTrue>
21+
): Minipass<Path, Path>
2822
export function globStreamSync(
2923
pattern: string | string[],
3024
options: GlobOptionsWithFileTypesFalse
31-
): MatchStream<GWOFileTypesFalse>
25+
): Minipass<string, string>
3226
export function globStreamSync(
3327
pattern: string | string[],
3428
options: GlobOptionsWithFileTypesUnset
35-
): MatchStream<GWOFileTypesUnset>
29+
): Minipass<string, string>
3630
export function globStreamSync(
3731
pattern: string | string[],
3832
options: GlobOptions
39-
): MatchStream<GlobOptions>
33+
): Minipass<Path, Path> | Minipass<string, string>
4034
export function globStreamSync(
4135
pattern: string | string[],
4236
options: GlobOptions = {}
@@ -51,19 +45,19 @@ export function globStreamSync(
5145
export function globStream(
5246
pattern: string | string[],
5347
options: GlobOptionsWithFileTypesFalse
54-
): MatchStream<GWOFileTypesFalse>
48+
): Minipass<string, string>
5549
export function globStream(
5650
pattern: string | string[],
5751
options: GlobOptionsWithFileTypesTrue
58-
): MatchStream<GWOFileTypesTrue>
52+
): Minipass<Path, Path>
5953
export function globStream(
6054
pattern: string | string[],
6155
options?: GlobOptionsWithFileTypesUnset | undefined
62-
): MatchStream<GWOFileTypesUnset>
56+
): Minipass<string, string>
6357
export function globStream(
6458
pattern: string | string[],
6559
options: GlobOptions
66-
): MatchStream<GlobOptions>
60+
): Minipass<Path, Path> | Minipass<string, string>
6761
export function globStream(
6862
pattern: string | string[],
6963
options: GlobOptions = {}
@@ -77,19 +71,19 @@ export function globStream(
7771
export function globSync(
7872
pattern: string | string[],
7973
options: GlobOptionsWithFileTypesFalse
80-
): Results<GWOFileTypesFalse>
74+
): string[]
8175
export function globSync(
8276
pattern: string | string[],
8377
options: GlobOptionsWithFileTypesTrue
84-
): Results<GWOFileTypesTrue>
78+
): Path[]
8579
export function globSync(
8680
pattern: string | string[],
8781
options?: GlobOptionsWithFileTypesUnset | undefined
88-
): Results<GWOFileTypesUnset>
82+
): string[]
8983
export function globSync(
9084
pattern: string | string[],
9185
options: GlobOptions
92-
): Results<GlobOptions>
86+
): Path[] | string[]
9387
export function globSync(
9488
pattern: string | string[],
9589
options: GlobOptions = {}
@@ -106,19 +100,19 @@ export function globSync(
106100
export async function glob(
107101
pattern: string | string[],
108102
options?: GlobOptionsWithFileTypesUnset | undefined
109-
): Promise<Results<GWOFileTypesUnset>>
103+
): Promise<string[]>
110104
export async function glob(
111105
pattern: string | string[],
112106
options: GlobOptionsWithFileTypesTrue
113-
): Promise<Results<GWOFileTypesTrue>>
107+
): Promise<Path[]>
114108
export async function glob(
115109
pattern: string | string[],
116110
options: GlobOptionsWithFileTypesFalse
117-
): Promise<Results<GWOFileTypesFalse>>
111+
): Promise<string[]>
118112
export async function glob(
119113
pattern: string | string[],
120114
options: GlobOptions
121-
): Promise<Results<GlobOptions>>
115+
): Promise<Path[] | string[]>
122116
export async function glob(
123117
pattern: string | string[],
124118
options: GlobOptions = {}
@@ -132,19 +126,19 @@ export async function glob(
132126
export function globIterate(
133127
pattern: string | string[],
134128
options?: GlobOptionsWithFileTypesUnset | undefined
135-
): AsyncGenerator<Result<GWOFileTypesUnset>, void, void>
129+
): AsyncGenerator<string, void, void>
136130
export function globIterate(
137131
pattern: string | string[],
138132
options: GlobOptionsWithFileTypesTrue
139-
): AsyncGenerator<Result<GWOFileTypesTrue>, void, void>
133+
): AsyncGenerator<Path, void, void>
140134
export function globIterate(
141135
pattern: string | string[],
142136
options: GlobOptionsWithFileTypesFalse
143-
): AsyncGenerator<Result<GWOFileTypesFalse>, void, void>
137+
): AsyncGenerator<string, void, void>
144138
export function globIterate(
145139
pattern: string | string[],
146140
options: GlobOptions
147-
): AsyncGenerator<Result<GlobOptions>, void, void>
141+
): AsyncGenerator<Path, void, void> | AsyncGenerator<string, void, void>
148142
export function globIterate(
149143
pattern: string | string[],
150144
options: GlobOptions = {}
@@ -158,19 +152,19 @@ export function globIterate(
158152
export function globIterateSync(
159153
pattern: string | string[],
160154
options?: GlobOptionsWithFileTypesUnset | undefined
161-
): Generator<Result<GWOFileTypesUnset>, void, void>
155+
): Generator<string, void, void>
162156
export function globIterateSync(
163157
pattern: string | string[],
164158
options: GlobOptionsWithFileTypesTrue
165-
): Generator<Result<GWOFileTypesTrue>, void, void>
159+
): Generator<Path, void, void>
166160
export function globIterateSync(
167161
pattern: string | string[],
168162
options: GlobOptionsWithFileTypesFalse
169-
): Generator<Result<GWOFileTypesFalse>, void, void>
163+
): Generator<string, void, void>
170164
export function globIterateSync(
171165
pattern: string | string[],
172166
options: GlobOptions
173-
): Generator<Result<GlobOptions>, void, void>
167+
): Generator<Path, void, void> | Generator<string, void, void>
174168
export function globIterateSync(
175169
pattern: string | string[],
176170
options: GlobOptions = {}
@@ -186,8 +180,6 @@ export type {
186180
GlobOptionsWithFileTypesFalse,
187181
GlobOptionsWithFileTypesTrue,
188182
GlobOptionsWithFileTypesUnset,
189-
Result,
190-
Results,
191183
} from './glob.js'
192184
export { hasMagic } from './has-magic.js'
193185
export type { MatchStream } from './walker.js'

src/walker.ts

+16
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ export interface GlobWalkerOpts {
2626
ignore?: string | string[] | Ignore
2727
mark?: boolean
2828
matchBase?: boolean
29+
// Note: maxDepth here means "maximum actual Path.depth()",
30+
// not "maximum depth beyond cwd"
31+
maxDepth?: number
2932
nobrace?: boolean
3033
nocase?: boolean
3134
nodir?: boolean
@@ -100,6 +103,7 @@ export abstract class GlobUtil<O extends GlobWalkerOpts = GlobWalkerOpts> {
100103
#ignore?: Ignore
101104
#sep: '\\' | '/'
102105
signal?: AbortSignal
106+
maxDepth: number
103107

104108
constructor(patterns: Pattern[], path: Path, opts: O)
105109
constructor(patterns: Pattern[], path: Path, opts: O) {
@@ -110,6 +114,11 @@ export abstract class GlobUtil<O extends GlobWalkerOpts = GlobWalkerOpts> {
110114
if (opts.ignore) {
111115
this.#ignore = makeIgnore(opts.ignore, opts)
112116
}
117+
// ignore, always set with maxDepth, but it's optional on the
118+
// GlobOptions type
119+
/* c8 ignore start */
120+
this.maxDepth = opts.maxDepth || Infinity
121+
/* c8 ignore stop */
113122
if (opts.signal) {
114123
this.signal = opts.signal
115124
this.signal.addEventListener('abort', () => {
@@ -166,6 +175,7 @@ export abstract class GlobUtil<O extends GlobWalkerOpts = GlobWalkerOpts> {
166175

167176
matchCheckTest(e: Path | undefined, ifDir: boolean): Path | undefined {
168177
return e &&
178+
(this.maxDepth === Infinity || e.depth() <= this.maxDepth) &&
169179
!this.#ignored(e) &&
170180
(!ifDir || e.canReaddir()) &&
171181
(!this.opts.nodir || !e.isDirectory())
@@ -255,6 +265,9 @@ export abstract class GlobUtil<O extends GlobWalkerOpts = GlobWalkerOpts> {
255265
}
256266

257267
for (const t of processor.subwalkTargets()) {
268+
if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) {
269+
continue
270+
}
258271
tasks++
259272
const childrenCached = t.readdirCached()
260273
if (t.calledReaddir())
@@ -333,6 +346,9 @@ export abstract class GlobUtil<O extends GlobWalkerOpts = GlobWalkerOpts> {
333346
}
334347

335348
for (const t of processor.subwalkTargets()) {
349+
if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) {
350+
continue
351+
}
336352
tasks++
337353
const children = t.readdirSync()
338354
this.walkCB3Sync(t, children, processor, next)
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/* IMPORTANT
2+
* This snapshot file is auto-generated, but designed for humans.
3+
* It should be checked into source control and tracked carefully.
4+
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
5+
* Make sure to inspect the output below. Do not ignore changes!
6+
*/
7+
'use strict'
8+
exports[`test/max-depth.ts TAP set maxDepth > async results 1`] = `
9+
Array [
10+
"",
11+
"a",
12+
"a/abcdef",
13+
"a/abcfed",
14+
"a/b",
15+
"a/bc",
16+
"a/c",
17+
"a/cb",
18+
"a/symlink",
19+
"a/x",
20+
"a/z",
21+
]
22+
`
23+
24+
exports[`test/max-depth.ts TAP set maxDepth > sync results 1`] = `
25+
Array [
26+
"",
27+
"a",
28+
"a/abcdef",
29+
"a/abcfed",
30+
"a/b",
31+
"a/bc",
32+
"a/c",
33+
"a/cb",
34+
"a/symlink",
35+
"a/x",
36+
"a/z",
37+
]
38+
`

0 commit comments

Comments
 (0)