Skip to content

Commit

Permalink
add maxDepth option
Browse files Browse the repository at this point in the history
Fix: #296
  • Loading branch information
isaacs committed Mar 2, 2023
1 parent ef3f0fb commit 9d3609e
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 34 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,9 @@ share the previously loaded cache.
systems, or `false` on case-insensitive file systems, then the
walk may return more or less results than expected.

- `maxDepth` Specify a number to limit the depth of the directory
traversal to this many levels below the `cwd`.

- `matchBase` Perform a basename-only match if the pattern does
not contain any slash characters. That is, `*.js` would be
treated as equivalent to `**/*.js`, matching all js files in
Expand Down
27 changes: 27 additions & 0 deletions src/glob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@ export interface GlobOptions {
*/
matchBase?: boolean

/**
* Limit the directory traversal to a given depth below the cwd.
* Note that this does NOT prevent traversal to sibling folders,
* root patterns, and so on. It only limits the maximum folder depth
* that the walk will descend, relative to the cwd.
*/
maxDepth?: number

/**
* Do not expand `{a,b}` and `{1..3}` brace sets.
*/
Expand Down Expand Up @@ -293,6 +301,7 @@ export class Glob<Opts extends GlobOptions> implements GlobOptions {
magicalBraces: boolean
mark?: boolean
matchBase: boolean
maxDepth: number
nobrace: boolean
nocase: boolean
nodir: boolean
Expand Down Expand Up @@ -351,6 +360,8 @@ export class Glob<Opts extends GlobOptions> implements GlobOptions {

this.noglobstar = !!opts.noglobstar
this.matchBase = !!opts.matchBase
this.maxDepth =
typeof opts.maxDepth === 'number' ? opts.maxDepth : Infinity

if (this.withFileTypes && this.absolute !== undefined) {
throw new Error('cannot set absolute and withFileTypes:true')
Expand Down Expand Up @@ -445,6 +456,10 @@ export class Glob<Opts extends GlobOptions> implements GlobOptions {
return [
...(await new GlobWalker(this.patterns, this.scurry.cwd, {
...this.opts,
maxDepth:
this.maxDepth !== Infinity
? this.maxDepth + this.scurry.cwd.depth()
: Infinity,
platform: this.platform,
nocase: this.nocase,
}).walk()),
Expand All @@ -459,6 +474,10 @@ export class Glob<Opts extends GlobOptions> implements GlobOptions {
return [
...new GlobWalker(this.patterns, this.scurry.cwd, {
...this.opts,
maxDepth:
this.maxDepth !== Infinity
? this.maxDepth + this.scurry.cwd.depth()
: Infinity,
platform: this.platform,
nocase: this.nocase,
}).walkSync(),
Expand All @@ -472,6 +491,10 @@ export class Glob<Opts extends GlobOptions> implements GlobOptions {
stream(): Minipass<string | Path, string | Path> {
return new GlobStream(this.patterns, this.scurry.cwd, {
...this.opts,
maxDepth:
this.maxDepth !== Infinity
? this.maxDepth + this.scurry.cwd.depth()
: Infinity,
platform: this.platform,
nocase: this.nocase,
}).stream()
Expand All @@ -484,6 +507,10 @@ export class Glob<Opts extends GlobOptions> implements GlobOptions {
streamSync(): Minipass<string | Path, string | Path> {
return new GlobStream(this.patterns, this.scurry.cwd, {
...this.opts,
maxDepth:
this.maxDepth !== Infinity
? this.maxDepth + this.scurry.cwd.depth()
: Infinity,
platform: this.platform,
nocase: this.nocase,
}).streamSync()
Expand Down
60 changes: 26 additions & 34 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import { escape, unescape } from 'minimatch'
import Minipass from 'minipass'
import { Path } from 'path-scurry'
import type {
GlobOptions,
GlobOptionsWithFileTypesFalse,
GlobOptionsWithFileTypesTrue,
GlobOptionsWithFileTypesUnset,
Results,
} from './glob.js'
import { Glob } from './glob.js'
import { hasMagic } from './has-magic.js'
import type {
GWOFileTypesFalse,
GWOFileTypesTrue,
GWOFileTypesUnset,
MatchStream,
Result,
} from './walker.js'

/**
* Syncronous form of {@link globStream}. Will read all the matches as fast as
Expand All @@ -24,19 +18,19 @@ import type {
export function globStreamSync(
pattern: string | string[],
options: GlobOptionsWithFileTypesTrue
): MatchStream<GWOFileTypesTrue>
): Minipass<Path, Path>
export function globStreamSync(
pattern: string | string[],
options: GlobOptionsWithFileTypesFalse
): MatchStream<GWOFileTypesFalse>
): Minipass<string, string>
export function globStreamSync(
pattern: string | string[],
options: GlobOptionsWithFileTypesUnset
): MatchStream<GWOFileTypesUnset>
): Minipass<string, string>
export function globStreamSync(
pattern: string | string[],
options: GlobOptions
): MatchStream<GlobOptions>
): Minipass<Path, Path> | Minipass<string, string>
export function globStreamSync(
pattern: string | string[],
options: GlobOptions = {}
Expand All @@ -51,19 +45,19 @@ export function globStreamSync(
export function globStream(
pattern: string | string[],
options: GlobOptionsWithFileTypesFalse
): MatchStream<GWOFileTypesFalse>
): Minipass<string, string>
export function globStream(
pattern: string | string[],
options: GlobOptionsWithFileTypesTrue
): MatchStream<GWOFileTypesTrue>
): Minipass<Path, Path>
export function globStream(
pattern: string | string[],
options?: GlobOptionsWithFileTypesUnset | undefined
): MatchStream<GWOFileTypesUnset>
): Minipass<string, string>
export function globStream(
pattern: string | string[],
options: GlobOptions
): MatchStream<GlobOptions>
): Minipass<Path, Path> | Minipass<string, string>
export function globStream(
pattern: string | string[],
options: GlobOptions = {}
Expand All @@ -77,19 +71,19 @@ export function globStream(
export function globSync(
pattern: string | string[],
options: GlobOptionsWithFileTypesFalse
): Results<GWOFileTypesFalse>
): string[]
export function globSync(
pattern: string | string[],
options: GlobOptionsWithFileTypesTrue
): Results<GWOFileTypesTrue>
): Path[]
export function globSync(
pattern: string | string[],
options?: GlobOptionsWithFileTypesUnset | undefined
): Results<GWOFileTypesUnset>
): string[]
export function globSync(
pattern: string | string[],
options: GlobOptions
): Results<GlobOptions>
): Path[] | string[]
export function globSync(
pattern: string | string[],
options: GlobOptions = {}
Expand All @@ -106,19 +100,19 @@ export function globSync(
export async function glob(
pattern: string | string[],
options?: GlobOptionsWithFileTypesUnset | undefined
): Promise<Results<GWOFileTypesUnset>>
): Promise<string[]>
export async function glob(
pattern: string | string[],
options: GlobOptionsWithFileTypesTrue
): Promise<Results<GWOFileTypesTrue>>
): Promise<Path[]>
export async function glob(
pattern: string | string[],
options: GlobOptionsWithFileTypesFalse
): Promise<Results<GWOFileTypesFalse>>
): Promise<string[]>
export async function glob(
pattern: string | string[],
options: GlobOptions
): Promise<Results<GlobOptions>>
): Promise<Path[] | string[]>
export async function glob(
pattern: string | string[],
options: GlobOptions = {}
Expand All @@ -132,19 +126,19 @@ export async function glob(
export function globIterate(
pattern: string | string[],
options?: GlobOptionsWithFileTypesUnset | undefined
): AsyncGenerator<Result<GWOFileTypesUnset>, void, void>
): AsyncGenerator<string, void, void>
export function globIterate(
pattern: string | string[],
options: GlobOptionsWithFileTypesTrue
): AsyncGenerator<Result<GWOFileTypesTrue>, void, void>
): AsyncGenerator<Path, void, void>
export function globIterate(
pattern: string | string[],
options: GlobOptionsWithFileTypesFalse
): AsyncGenerator<Result<GWOFileTypesFalse>, void, void>
): AsyncGenerator<string, void, void>
export function globIterate(
pattern: string | string[],
options: GlobOptions
): AsyncGenerator<Result<GlobOptions>, void, void>
): AsyncGenerator<Path, void, void> | AsyncGenerator<string, void, void>
export function globIterate(
pattern: string | string[],
options: GlobOptions = {}
Expand All @@ -158,19 +152,19 @@ export function globIterate(
export function globIterateSync(
pattern: string | string[],
options?: GlobOptionsWithFileTypesUnset | undefined
): Generator<Result<GWOFileTypesUnset>, void, void>
): Generator<string, void, void>
export function globIterateSync(
pattern: string | string[],
options: GlobOptionsWithFileTypesTrue
): Generator<Result<GWOFileTypesTrue>, void, void>
): Generator<Path, void, void>
export function globIterateSync(
pattern: string | string[],
options: GlobOptionsWithFileTypesFalse
): Generator<Result<GWOFileTypesFalse>, void, void>
): Generator<string, void, void>
export function globIterateSync(
pattern: string | string[],
options: GlobOptions
): Generator<Result<GlobOptions>, void, void>
): Generator<Path, void, void> | Generator<string, void, void>
export function globIterateSync(
pattern: string | string[],
options: GlobOptions = {}
Expand All @@ -186,8 +180,6 @@ export type {
GlobOptionsWithFileTypesFalse,
GlobOptionsWithFileTypesTrue,
GlobOptionsWithFileTypesUnset,
Result,
Results,
} from './glob.js'
export { hasMagic } from './has-magic.js'
export type { MatchStream } from './walker.js'
Expand Down
16 changes: 16 additions & 0 deletions src/walker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export interface GlobWalkerOpts {
ignore?: string | string[] | Ignore
mark?: boolean
matchBase?: boolean
// Note: maxDepth here means "maximum actual Path.depth()",
// not "maximum depth beyond cwd"
maxDepth?: number
nobrace?: boolean
nocase?: boolean
nodir?: boolean
Expand Down Expand Up @@ -100,6 +103,7 @@ export abstract class GlobUtil<O extends GlobWalkerOpts = GlobWalkerOpts> {
#ignore?: Ignore
#sep: '\\' | '/'
signal?: AbortSignal
maxDepth: number

constructor(patterns: Pattern[], path: Path, opts: O)
constructor(patterns: Pattern[], path: Path, opts: O) {
Expand All @@ -110,6 +114,11 @@ export abstract class GlobUtil<O extends GlobWalkerOpts = GlobWalkerOpts> {
if (opts.ignore) {
this.#ignore = makeIgnore(opts.ignore, opts)
}
// ignore, always set with maxDepth, but it's optional on the
// GlobOptions type
/* c8 ignore start */
this.maxDepth = opts.maxDepth || Infinity
/* c8 ignore stop */
if (opts.signal) {
this.signal = opts.signal
this.signal.addEventListener('abort', () => {
Expand Down Expand Up @@ -166,6 +175,7 @@ export abstract class GlobUtil<O extends GlobWalkerOpts = GlobWalkerOpts> {

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

for (const t of processor.subwalkTargets()) {
if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) {
continue
}
tasks++
const childrenCached = t.readdirCached()
if (t.calledReaddir())
Expand Down Expand Up @@ -333,6 +346,9 @@ export abstract class GlobUtil<O extends GlobWalkerOpts = GlobWalkerOpts> {
}

for (const t of processor.subwalkTargets()) {
if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) {
continue
}
tasks++
const children = t.readdirSync()
this.walkCB3Sync(t, children, processor, next)
Expand Down
38 changes: 38 additions & 0 deletions tap-snapshots/test/max-depth.ts.test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* IMPORTANT
* This snapshot file is auto-generated, but designed for humans.
* It should be checked into source control and tracked carefully.
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
* Make sure to inspect the output below. Do not ignore changes!
*/
'use strict'
exports[`test/max-depth.ts TAP set maxDepth > async results 1`] = `
Array [
"",
"a",
"a/abcdef",
"a/abcfed",
"a/b",
"a/bc",
"a/c",
"a/cb",
"a/symlink",
"a/x",
"a/z",
]
`

exports[`test/max-depth.ts TAP set maxDepth > sync results 1`] = `
Array [
"",
"a",
"a/abcdef",
"a/abcfed",
"a/b",
"a/bc",
"a/c",
"a/cb",
"a/symlink",
"a/x",
"a/z",
]
`
Loading

0 comments on commit 9d3609e

Please sign in to comment.