Skip to content

Commit

Permalink
add stat:true option
Browse files Browse the repository at this point in the history
Fix: #353
  • Loading branch information
isaacs committed Mar 2, 2023
1 parent 9d3609e commit 95ffddf
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 2 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,21 @@ g3.stream().on('data', path => {
path.readdirSync().map(e => e.name)
)
})

// if you use stat:true and withFileTypes, you can sort results
// by things like modified time, filter by permission mode, etc.
// All Stats fields will be avialable in that case. Slightly
// slower, though.
// For example:
const results = await glob('**', { stat: true, withFileTypes: true })

const timeSortedFiles = results
.sort((a, b) => a.mtimeMS - b.mtimeMS)
.map(path => path.fullpath())

const groupReadableFiles = results
.filter(path => path.mode & 0o040)
.map(path => path.fullpath())
```

**Note** Glob patterns should always use `/` as a path separator,
Expand Down Expand Up @@ -321,6 +336,12 @@ share the previously loaded cache.
- `nodir` Do not match directories, only files. (Note: to match
_only_ directories, put a `/` at the end of the pattern.)

- `stat` Call `lstat()` on all entries, whether required or not
to determine whether it's a valid match. When used with
`withFileTypes`, this means that matches will include data such
as modified time, permissions, and so on. Note that this will
incur a performance cost due to the added system calls.

- `ignore` string or string[]. A glob pattern or array of glob
patterns to exclude from matches. To ignore all children within
a directory, as well as the entry itself, append `/**'` to the
Expand Down
11 changes: 11 additions & 0 deletions src/glob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,15 @@ export interface GlobOptions {
*/
scurry?: PathScurry

/**
* Call `lstat()` on all entries, whether required or not to determine
* whether it's a valid match. When used with {@link withFileTypes}, this
* means that matches will include data such as modified time, permissions,
* and so on. Note that this will incur a performance cost due to the added
* system calls.
*/
stat?: boolean

/**
* An AbortSignal which will cancel the Glob walk when
* triggered.
Expand Down Expand Up @@ -311,6 +320,7 @@ export class Glob<Opts extends GlobOptions> implements GlobOptions {
platform: NodeJS.Platform
realpath: boolean
scurry: PathScurry
stat: boolean
signal?: AbortSignal
windowsPathsNoEscape: boolean
withFileTypes: FileTypes<Opts>
Expand Down Expand Up @@ -362,6 +372,7 @@ export class Glob<Opts extends GlobOptions> implements GlobOptions {
this.matchBase = !!opts.matchBase
this.maxDepth =
typeof opts.maxDepth === 'number' ? opts.maxDepth : Infinity
this.stat = !!opts.stat

if (this.withFileTypes && this.absolute !== undefined) {
throw new Error('cannot set absolute and withFileTypes:true')
Expand Down
5 changes: 3 additions & 2 deletions src/walker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export interface GlobWalkerOpts {
platform?: NodeJS.Platform
realpath?: boolean
root?: string
stat?: boolean
signal?: AbortSignal
windowsPathsNoEscape?: boolean
withFileTypes?: boolean
Expand Down Expand Up @@ -169,7 +170,7 @@ export abstract class GlobUtil<O extends GlobWalkerOpts = GlobWalkerOpts> {
if (!rpc) return undefined
e = rpc
}
const needStat = e.isUnknown()
const needStat = e.isUnknown() || this.opts.stat
return this.matchCheckTest(needStat ? await e.lstat() : e, ifDir)
}

Expand All @@ -191,7 +192,7 @@ export abstract class GlobUtil<O extends GlobWalkerOpts = GlobWalkerOpts> {
if (!rpc) return undefined
e = rpc
}
const needStat = e.isUnknown()
const needStat = e.isUnknown() || this.opts.stat
return this.matchCheckTest(needStat ? e.lstatSync() : e, ifDir)
}

Expand Down
23 changes: 23 additions & 0 deletions test/stat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { resolve } from 'path'
import t from 'tap'
import { glob, globSync } from '../'

t.test('stat: true', async t => {
const cwd = resolve(__dirname, 'fixtures')
const pattern = '*'
const asyncRes = await glob(pattern, {
cwd,
withFileTypes: true,
stat: true,
})
const syncRes = globSync(pattern, {
cwd,
withFileTypes: true,
stat: true,
})
t.type(asyncRes[0].mode, 'number')
t.type(syncRes[0].mode, 'number')

const noStat = await glob(pattern, { cwd, withFileTypes: true })
t.equal(noStat[0].mode, undefined)
})

0 comments on commit 95ffddf

Please sign in to comment.