Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7d7a762

Browse filesBrowse files
avivkellertargos
authored andcommitted
fs: allow 'withFileTypes' to be used with globs
PR-URL: #52837 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com>
1 parent 0bbc62c commit 7d7a762
Copy full SHA for 7d7a762

File tree

Expand file treeCollapse file tree

4 files changed

+92
-16
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

4 files changed

+92
-16
lines changed
Open diff view settings
Collapse file

‎doc/api/fs.md‎

Copy file name to clipboardExpand all lines: doc/api/fs.md
+18Lines changed: 18 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,10 @@ behavior is similar to `cp dir1/ dir2/`.
10731073
10741074
<!-- YAML
10751075
added: v22.0.0
1076+
changes:
1077+
- version: REPLACEME
1078+
pr-url: https://github.com/nodejs/node/pull/52837
1079+
description: Add support for `withFileTypes` as an option.
10761080
-->
10771081
10781082
> Stability: 1 - Experimental
@@ -1082,6 +1086,8 @@ added: v22.0.0
10821086
* `cwd` {string} current working directory. **Default:** `process.cwd()`
10831087
* `exclude` {Function} Function to filter out files/directories. Return
10841088
`true` to exclude the item, `false` to include it. **Default:** `undefined`.
1089+
* `withFileTypes` {boolean} `true` if the glob should return paths as Dirents,
1090+
`false` otherwise. **Default:** `false`.
10851091
* Returns: {AsyncIterator} An AsyncIterator that yields the paths of files
10861092
that match the pattern.
10871093
@@ -3109,6 +3115,10 @@ descriptor. See [`fs.utimes()`][].
31093115
31103116
<!-- YAML
31113117
added: v22.0.0
3118+
changes:
3119+
- version: REPLACEME
3120+
pr-url: https://github.com/nodejs/node/pull/52837
3121+
description: Add support for `withFileTypes` as an option.
31123122
-->
31133123
31143124
> Stability: 1 - Experimental
@@ -3119,6 +3129,8 @@ added: v22.0.0
31193129
* `cwd` {string} current working directory. **Default:** `process.cwd()`
31203130
* `exclude` {Function} Function to filter out files/directories. Return
31213131
`true` to exclude the item, `false` to include it. **Default:** `undefined`.
3132+
* `withFileTypes` {boolean} `true` if the glob should return paths as Dirents,
3133+
`false` otherwise. **Default:** `false`.
31223134
31233135
* `callback` {Function}
31243136
* `err` {Error}
@@ -5603,6 +5615,10 @@ Synchronous version of [`fs.futimes()`][]. Returns `undefined`.
56035615
56045616
<!-- YAML
56055617
added: v22.0.0
5618+
changes:
5619+
- version: REPLACEME
5620+
pr-url: https://github.com/nodejs/node/pull/52837
5621+
description: Add support for `withFileTypes` as an option.
56065622
-->
56075623
56085624
> Stability: 1 - Experimental
@@ -5612,6 +5628,8 @@ added: v22.0.0
56125628
* `cwd` {string} current working directory. **Default:** `process.cwd()`
56135629
* `exclude` {Function} Function to filter out files/directories. Return
56145630
`true` to exclude the item, `false` to include it. **Default:** `undefined`.
5631+
* `withFileTypes` {boolean} `true` if the glob should return paths as Dirents,
5632+
`false` otherwise. **Default:** `false`.
56155633
* Returns: {string\[]} paths of files that match the pattern.
56165634
56175635
```mjs
Collapse file

‎lib/internal/fs/glob.js‎

Copy file name to clipboardExpand all lines: lib/internal/fs/glob.js
+32-14Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const {
1616

1717
const { lstatSync, readdirSync } = require('fs');
1818
const { lstat, readdir } = require('fs/promises');
19-
const { join, resolve } = require('path');
19+
const { join, resolve, basename, isAbsolute } = require('path');
2020

2121
const {
2222
kEmptyObject,
@@ -27,6 +27,7 @@ const {
2727
validateString,
2828
validateStringArray,
2929
} = require('internal/validators');
30+
const { DirentFromStats } = require('internal/fs/utils');
3031

3132
let minimatch;
3233
function lazyMinimatch() {
@@ -37,6 +38,14 @@ function lazyMinimatch() {
3738
const isWindows = process.platform === 'win32';
3839
const isOSX = process.platform === 'darwin';
3940

41+
async function getDirent(path) {
42+
return new DirentFromStats(basename(path), await lstat(path), path);
43+
}
44+
45+
function getDirentSync(path) {
46+
return new DirentFromStats(basename(path), lstatSync(path), path);
47+
}
48+
4049
class Cache {
4150
#cache = new SafeMap();
4251
#statsCache = new SafeMap();
@@ -47,7 +56,7 @@ class Cache {
4756
if (cached) {
4857
return cached;
4958
}
50-
const promise = PromisePrototypeThen(lstat(path), null, () => null);
59+
const promise = PromisePrototypeThen(getDirent(path), null, () => null);
5160
this.#statsCache.set(path, promise);
5261
return promise;
5362
}
@@ -58,7 +67,7 @@ class Cache {
5867
}
5968
let val;
6069
try {
61-
val = lstatSync(path);
70+
val = getDirentSync(path);
6271
} catch {
6372
val = null;
6473
}
@@ -175,14 +184,16 @@ class Glob {
175184
#queue = [];
176185
#subpatterns = new SafeMap();
177186
#patterns;
187+
#withFileTypes;
178188
constructor(pattern, options = kEmptyObject) {
179189
validateObject(options, 'options');
180-
const { exclude, cwd } = options;
190+
const { exclude, cwd, withFileTypes } = options;
181191
if (exclude != null) {
182192
validateFunction(exclude, 'options.exclude');
183193
}
184194
this.#root = cwd ?? '.';
185195
this.#exclude = exclude;
196+
this.#withFileTypes = !!withFileTypes;
186197
let patterns;
187198
if (typeof pattern === 'object') {
188199
validateStringArray(pattern, 'patterns');
@@ -222,7 +233,14 @@ class Glob {
222233
.forEach((patterns, path) => ArrayPrototypePush(this.#queue, { __proto__: null, path, patterns }));
223234
this.#subpatterns.clear();
224235
}
225-
return ArrayFrom(this.#results);
236+
return ArrayFrom(
237+
this.#results,
238+
this.#withFileTypes ? (path) => this.#cache.statSync(
239+
isAbsolute(path) ?
240+
path :
241+
join(this.#root, path),
242+
) : undefined,
243+
);
226244
}
227245
#addSubpattern(path, pattern) {
228246
if (!this.#subpatterns.has(path)) {
@@ -317,7 +335,7 @@ class Glob {
317335
const fromSymlink = pattern.symlinks.has(index);
318336

319337
if (current === lazyMinimatch().GLOBSTAR) {
320-
if (entry.name[0] === '.' || (this.#exclude && this.#exclude(entry.name))) {
338+
if (entry.name[0] === '.' || (this.#exclude && this.#exclude(this.#withFileTypes ? entry : entry.name))) {
321339
continue;
322340
}
323341
if (!fromSymlink && entry.isDirectory()) {
@@ -460,7 +478,7 @@ class Glob {
460478
const result = join(path, p);
461479
if (!this.#results.has(result)) {
462480
this.#results.add(result);
463-
yield result;
481+
yield this.#withFileTypes ? stat : result;
464482
}
465483
}
466484
if (pattern.indexes.size === 1 && pattern.indexes.has(last)) {
@@ -472,7 +490,7 @@ class Glob {
472490
// if path is ".", add it only if pattern starts with "." or pattern is exactly "**"
473491
if (!this.#results.has(path)) {
474492
this.#results.add(path);
475-
yield path;
493+
yield this.#withFileTypes ? stat : path;
476494
}
477495
}
478496

@@ -522,7 +540,7 @@ class Glob {
522540
// If ** is last, add to results
523541
if (!this.#results.has(entryPath)) {
524542
this.#results.add(entryPath);
525-
yield entryPath;
543+
yield this.#withFileTypes ? entry : entryPath;
526544
}
527545
}
528546

@@ -533,7 +551,7 @@ class Glob {
533551
// If next pattern is the last one, add to results
534552
if (!this.#results.has(entryPath)) {
535553
this.#results.add(entryPath);
536-
yield entryPath;
554+
yield this.#withFileTypes ? entry : entryPath;
537555
}
538556
} else if (nextMatches && entry.isDirectory()) {
539557
// Pattern mached, meaning two patterns forward
@@ -569,14 +587,14 @@ class Glob {
569587
this.#cache.add(path, pattern.child(new SafeSet().add(nextIndex)));
570588
if (!this.#results.has(path)) {
571589
this.#results.add(path);
572-
yield path;
590+
yield this.#withFileTypes ? this.#cache.statSync(fullpath) : path;
573591
}
574592
}
575593
if (!this.#cache.seen(path, pattern, nextIndex) || !this.#cache.seen(parent, pattern, nextIndex)) {
576594
this.#cache.add(parent, pattern.child(new SafeSet().add(nextIndex)));
577595
if (!this.#results.has(parent)) {
578596
this.#results.add(parent);
579-
yield parent;
597+
yield this.#withFileTypes ? this.#cache.statSync(join(this.#root, parent)) : parent;
580598
}
581599
}
582600
}
@@ -592,7 +610,7 @@ class Glob {
592610
if (nextIndex === last) {
593611
if (!this.#results.has(entryPath)) {
594612
this.#results.add(entryPath);
595-
yield entryPath;
613+
yield this.#withFileTypes ? entry : entryPath;
596614
}
597615
} else {
598616
subPatterns.add(nextIndex + 1);
@@ -605,7 +623,7 @@ class Glob {
605623
if (index === last) {
606624
if (!this.#results.has(entryPath)) {
607625
this.#results.add(entryPath);
608-
yield entryPath;
626+
yield this.#withFileTypes ? entry : entryPath;
609627
}
610628
} else if (entry.isDirectory()) {
611629
subPatterns.add(nextIndex);
Collapse file

‎lib/internal/fs/utils.js‎

Copy file name to clipboardExpand all lines: lib/internal/fs/utils.js
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,7 @@ module.exports = {
961961
BigIntStats, // for testing
962962
copyObject,
963963
Dirent,
964+
DirentFromStats,
964965
emitRecursiveRmdirWarning,
965966
getDirent,
966967
getDirents,
Collapse file

‎test/parallel/test-fs-glob.mjs‎

Copy file name to clipboardExpand all lines: test/parallel/test-fs-glob.mjs
+41-2Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import * as common from '../common/index.mjs';
22
import tmpdir from '../common/tmpdir.js';
3-
import { resolve, dirname, sep } from 'node:path';
3+
import { resolve, dirname, sep, basename } from 'node:path';
44
import { mkdir, writeFile, symlink, glob as asyncGlob } from 'node:fs/promises';
5-
import { glob, globSync } from 'node:fs';
5+
import { glob, globSync, Dirent } from 'node:fs';
66
import { test, describe } from 'node:test';
77
import { promisify } from 'node:util';
88
import assert from 'node:assert';
99

10+
function assertDirents(dirents) {
11+
assert.ok(dirents.every((dirent) => dirent instanceof Dirent));
12+
}
13+
1014
tmpdir.refresh();
1115

1216
const fixtureDir = tmpdir.resolve('fixtures');
@@ -333,3 +337,38 @@ describe('fsPromises glob', function() {
333337
});
334338
}
335339
});
340+
341+
describe('glob - withFileTypes', function() {
342+
const promisified = promisify(glob);
343+
for (const [pattern, expected] of Object.entries(patterns)) {
344+
test(pattern, async () => {
345+
const actual = await promisified(pattern, { cwd: fixtureDir, withFileTypes: true });
346+
assertDirents(actual);
347+
const normalized = expected.filter(Boolean).map((item) => basename(item)).sort();
348+
assert.deepStrictEqual(actual.map((dirent) => dirent.name).sort(), normalized.sort());
349+
});
350+
}
351+
});
352+
353+
describe('globSync - withFileTypes', function() {
354+
for (const [pattern, expected] of Object.entries(patterns)) {
355+
test(pattern, () => {
356+
const actual = globSync(pattern, { cwd: fixtureDir, withFileTypes: true });
357+
assertDirents(actual);
358+
const normalized = expected.filter(Boolean).map((item) => basename(item)).sort();
359+
assert.deepStrictEqual(actual.map((dirent) => dirent.name).sort(), normalized.sort());
360+
});
361+
}
362+
});
363+
364+
describe('fsPromises glob - withFileTypes', function() {
365+
for (const [pattern, expected] of Object.entries(patterns)) {
366+
test(pattern, async () => {
367+
const actual = [];
368+
for await (const item of asyncGlob(pattern, { cwd: fixtureDir, withFileTypes: true })) actual.push(item);
369+
assertDirents(actual);
370+
const normalized = expected.filter(Boolean).map((item) => basename(item)).sort();
371+
assert.deepStrictEqual(actual.map((dirent) => dirent.name).sort(), normalized.sort());
372+
});
373+
}
374+
});

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.