Skip to content

Commit 7fa9316

Browse files
committed
fs: add new option depth to readdir which regulates depth of recursion
This commit contains the necessary fixes to code and documentation per manual testing, but I still need to complete test units for test autoamtion. fixes: https://github.com/nodjs/node/issues/49243
1 parent 41a3a1d commit 7fa9316

File tree

3 files changed

+81
-35
lines changed

3 files changed

+81
-35
lines changed

doc/api/fs.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,7 @@ changes:
13051305
* `encoding` {string} **Default:** `'utf8'`
13061306
* `withFileTypes` {boolean} **Default:** `false`
13071307
* `recursive` {boolean} **Default:** `false`
1308+
* `depth` {number} **Default:** `1`
13081309
* Returns: {Promise} Fulfills with an array of the names of the files in
13091310
the directory excluding `'.'` and `'..'`.
13101311
@@ -1318,6 +1319,12 @@ will be passed as {Buffer} objects.
13181319
If `options.withFileTypes` is set to `true`, the resolved array will contain
13191320
{fs.Dirent} objects.
13201321
1322+
The `options.depth` determines the amount of recursion to apply. A value less
1323+
than 1 applies full recursion, value of 1 applies no recursion, and greater
1324+
values determine the depth of descendant directies to traverse. If
1325+
`options.recursive` is value `true` then `options.depth` defaults to a value
1326+
of `0` and otherwise defaults to a value `1`.
1327+
13211328
```mjs
13221329
import { readdir } from 'node:fs/promises';
13231330
@@ -3615,6 +3622,7 @@ changes:
36153622
* `encoding` {string} **Default:** `'utf8'`
36163623
* `withFileTypes` {boolean} **Default:** `false`
36173624
* `recursive` {boolean} **Default:** `false`
3625+
* `depth` {number} **Default:** `1`
36183626
* `callback` {Function}
36193627
* `err` {Error}
36203628
* `files` {string\[]|Buffer\[]|fs.Dirent\[]}
@@ -3633,6 +3641,12 @@ the filenames returned will be passed as {Buffer} objects.
36333641
If `options.withFileTypes` is set to `true`, the `files` array will contain
36343642
{fs.Dirent} objects.
36353643
3644+
The `options.depth` determines the amount of recursion to apply. A value less
3645+
than 1 applies full recursion, value of 1 applies no recursion, and greater
3646+
values determine the depth of descendant directies to traverse. If
3647+
`options.recursive` is value `true` then `options.depth` defaults to a value
3648+
of `0` and otherwise defaults to a value `1`.
3649+
36363650
### `fs.readFile(path[, options], callback)`
36373651
36383652
<!-- YAML
@@ -5681,6 +5695,7 @@ changes:
56815695
* `encoding` {string} **Default:** `'utf8'`
56825696
* `withFileTypes` {boolean} **Default:** `false`
56835697
* `recursive` {boolean} **Default:** `false`
5698+
* `depth` {boolean} **Default:** `1`
56845699
* Returns: {string\[]|Buffer\[]|fs.Dirent\[]}
56855700
56865701
Reads the contents of the directory.
@@ -5695,6 +5710,12 @@ the filenames returned will be passed as {Buffer} objects.
56955710
If `options.withFileTypes` is set to `true`, the result will contain
56965711
{fs.Dirent} objects.
56975712
5713+
The `options.depth` determines the amount of recursion to apply. A value less
5714+
than 1 applies full recursion, value of 1 applies no recursion, and greater
5715+
values determine the depth of descendant directies to traverse. If
5716+
`options.recursive` is value `true` then `options.depth` defaults to a value
5717+
of `0` and otherwise defaults to a value `1`.
5718+
56985719
### `fs.readFileSync(path[, options])`
56995720
57005721
<!-- YAML

lib/fs.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,6 +1427,7 @@ function mkdirSync(path, options) {
14271427
function readdirSyncRecursive(basePath, options) {
14281428
const withFileTypes = Boolean(options.withFileTypes);
14291429
const encoding = options.encoding;
1430+
const sep = pathModule.sep;
14301431

14311432
const readdirResults = [];
14321433
const pathsQueue = [basePath];
@@ -1469,9 +1470,11 @@ function readdirSyncRecursive(basePath, options) {
14691470
}
14701471
}
14711472
}
1472-
1473+
const base = basePath.replace(/\//g, sep) + sep;
14731474
for (let i = 0; i < pathsQueue.length; i++) {
1474-
read(pathsQueue[i]);
1475+
if (options.depth < 1 || pathsQueue[i] === basePath || pathsQueue[i].replace(base, "").split(sep).length < options.depth) {
1476+
read(pathsQueue[i]);
1477+
}
14751478
}
14761479

14771480
return readdirResults;
@@ -1498,7 +1501,13 @@ function readdir(path, options, callback) {
14981501
validateBoolean(options.recursive, 'options.recursive');
14991502
}
15001503

1501-
if (options.recursive) {
1504+
const depth = (options !== null && options !== undefined && typeof options.depth === 'number')
1505+
? Math.floor(options.depth)
1506+
: options.recursive
1507+
? 0
1508+
: 1;
1509+
if (depth !== 1) {
1510+
options.depth = depth;
15021511
callback(null, readdirSyncRecursive(path, options));
15031512
return;
15041513
}
@@ -1536,7 +1545,13 @@ function readdirSync(path, options) {
15361545
validateBoolean(options.recursive, 'options.recursive');
15371546
}
15381547

1539-
if (options.recursive) {
1548+
const depth = (options !== null && options !== undefined && typeof options.depth === 'number')
1549+
? Math.floor(options.depth)
1550+
: options.recursive
1551+
? 0
1552+
: 1;
1553+
if (depth !== 1) {
1554+
options.depth = depth;
15401555
return readdirSyncRecursive(path, options);
15411556
}
15421557

lib/internal/fs/promises.js

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,8 @@ async function mkdir(path, options) {
781781

782782
async function readdirRecursive(originalPath, options) {
783783
const result = [];
784+
const sep = pathModule.sep;
785+
const origin = originalPath.replace(/\//g, sep) + sep;
784786
const queue = [
785787
[
786788
originalPath,
@@ -793,47 +795,50 @@ async function readdirRecursive(originalPath, options) {
793795
],
794796
];
795797

796-
797798
if (options.withFileTypes) {
798799
while (queue.length > 0) {
799800
// If we want to implement BFS make this a `shift` call instead of `pop`
800801
const { 0: path, 1: readdir } = ArrayPrototypePop(queue);
801-
for (const dirent of getDirents(path, readdir)) {
802-
ArrayPrototypePush(result, dirent);
803-
if (dirent.isDirectory()) {
804-
const direntPath = pathModule.join(path, dirent.name);
805-
ArrayPrototypePush(queue, [
806-
direntPath,
807-
await binding.readdir(
802+
if (options.depth < 1 || path === originalPath || path.replace(origin, "").split(sep).length < options.depth) {
803+
for (const dirent of getDirents(path, readdir)) {
804+
ArrayPrototypePush(result, dirent);
805+
if (dirent.isDirectory()) {
806+
const direntPath = pathModule.join(path, dirent.name);
807+
ArrayPrototypePush(queue, [
808808
direntPath,
809-
options.encoding,
810-
true,
811-
kUsePromises,
812-
),
813-
]);
809+
await binding.readdir(
810+
direntPath,
811+
options.encoding,
812+
true,
813+
kUsePromises,
814+
),
815+
]);
816+
}
814817
}
815818
}
816819
}
817820
} else {
818821
while (queue.length > 0) {
819822
const { 0: path, 1: readdir } = ArrayPrototypePop(queue);
820-
for (const ent of readdir) {
821-
const direntPath = pathModule.join(path, ent);
822-
const stat = binding.internalModuleStat(direntPath);
823-
ArrayPrototypePush(
824-
result,
825-
pathModule.relative(originalPath, direntPath),
826-
);
827-
if (stat === 1) {
828-
ArrayPrototypePush(queue, [
829-
direntPath,
830-
await binding.readdir(
831-
pathModule.toNamespacedPath(direntPath),
832-
options.encoding,
833-
false,
834-
kUsePromises,
835-
),
836-
]);
823+
if (options.depth < 1 || path === originalPath || path.replace(origin, "").split(sep).length < options.depth) {
824+
for (const ent of readdir) {
825+
const direntPath = pathModule.join(path, ent);
826+
const stat = binding.internalModuleStat(direntPath);
827+
ArrayPrototypePush(
828+
result,
829+
pathModule.relative(originalPath, direntPath),
830+
);
831+
if (stat === 1) {
832+
ArrayPrototypePush(queue, [
833+
direntPath,
834+
await binding.readdir(
835+
pathModule.toNamespacedPath(direntPath),
836+
options.encoding,
837+
false,
838+
kUsePromises,
839+
),
840+
]);
841+
}
837842
}
838843
}
839844
}
@@ -845,7 +850,12 @@ async function readdirRecursive(originalPath, options) {
845850
async function readdir(path, options) {
846851
options = getOptions(options);
847852
path = getValidatedPath(path);
848-
if (options.recursive) {
853+
const depth = (options !== null && options !== undefined && typeof options.depth === 'number')
854+
? Math.floor(options.depth)
855+
: options.recursive
856+
? 0
857+
: 1;
858+
if (depth !== 1) {
849859
return readdirRecursive(path, options);
850860
}
851861
const result = await binding.readdir(

0 commit comments

Comments
 (0)