Skip to content

Commit 106dc61

Browse files
Ethan Arrowoodtargos
Ethan Arrowood
authored andcommitted
fs: make readdir recursive algorithm iterative
PR-URL: #47650 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
1 parent c9ffc55 commit 106dc61

File tree

2 files changed

+58
-32
lines changed

2 files changed

+58
-32
lines changed

lib/fs.js

+58-30
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
const {
2828
ArrayPrototypePush,
2929
BigIntPrototypeToString,
30+
Boolean,
3031
MathMax,
3132
Number,
3233
ObjectDefineProperties,
@@ -97,6 +98,7 @@ const {
9798
copyObject,
9899
Dirent,
99100
emitRecursiveRmdirWarning,
101+
getDirent,
100102
getDirents,
101103
getOptions,
102104
getValidatedFd,
@@ -1404,34 +1406,60 @@ function mkdirSync(path, options) {
14041406
}
14051407
}
14061408

1407-
// TODO(Ethan-Arrowood): Make this iterative too
1408-
function readdirSyncRecursive(path, origPath, options) {
1409-
nullCheck(path, 'path', true);
1410-
const ctx = { path };
1411-
const result = binding.readdir(pathModule.toNamespacedPath(path),
1412-
options.encoding, !!options.withFileTypes, undefined, ctx);
1413-
handleErrorFromBinding(ctx);
1414-
return options.withFileTypes ?
1415-
getDirents(path, result).flatMap((dirent) => {
1416-
return [
1417-
dirent,
1418-
...(dirent.isDirectory() ?
1419-
readdirSyncRecursive(
1420-
pathModule.join(path, dirent.name),
1421-
origPath,
1422-
options,
1423-
) : []),
1424-
];
1425-
}) :
1426-
result.flatMap((ent) => {
1427-
const innerPath = pathModule.join(path, ent);
1428-
const relativePath = pathModule.relative(origPath, innerPath);
1429-
const stat = binding.internalModuleStat(innerPath);
1430-
return [
1431-
relativePath,
1432-
...(stat === 1 ? readdirSyncRecursive(innerPath, origPath, options) : []),
1433-
];
1434-
});
1409+
/**
1410+
* An iterative algorithm for reading the entire contents of the `basePath` directory.
1411+
* This function does not validate `basePath` as a directory. It is passed directly to
1412+
* `binding.readdir` after a `nullCheck`.
1413+
* @param {string} basePath
1414+
* @param {{ encoding: string, withFileTypes: boolean }} options
1415+
* @returns {string[] | Dirent[]}
1416+
*/
1417+
function readdirSyncRecursive(basePath, options) {
1418+
nullCheck(basePath, 'path', true);
1419+
1420+
const withFileTypes = Boolean(options.withFileTypes);
1421+
const encoding = options.encoding;
1422+
1423+
const readdirResults = [];
1424+
const pathsQueue = [basePath];
1425+
1426+
const ctx = { path: basePath };
1427+
function read(path) {
1428+
ctx.path = path;
1429+
const readdirResult = binding.readdir(
1430+
pathModule.toNamespacedPath(path),
1431+
encoding,
1432+
withFileTypes,
1433+
undefined,
1434+
ctx,
1435+
);
1436+
handleErrorFromBinding(ctx);
1437+
1438+
for (let i = 0; i < readdirResult.length; i++) {
1439+
if (withFileTypes) {
1440+
const dirent = getDirent(path, readdirResult[0][i], readdirResult[1][i]);
1441+
ArrayPrototypePush(readdirResults, dirent);
1442+
if (dirent.isDirectory()) {
1443+
ArrayPrototypePush(pathsQueue, pathModule.join(dirent.path, dirent.name));
1444+
}
1445+
} else {
1446+
const resultPath = pathModule.join(path, readdirResult[i]);
1447+
const relativeResultPath = pathModule.relative(basePath, resultPath);
1448+
const stat = binding.internalModuleStat(resultPath);
1449+
ArrayPrototypePush(readdirResults, relativeResultPath);
1450+
// 1 indicates directory
1451+
if (stat === 1) {
1452+
ArrayPrototypePush(pathsQueue, resultPath);
1453+
}
1454+
}
1455+
}
1456+
}
1457+
1458+
for (let i = 0; i < pathsQueue.length; i++) {
1459+
read(pathsQueue[i]);
1460+
}
1461+
1462+
return readdirResults;
14351463
}
14361464

14371465
/**
@@ -1456,7 +1484,7 @@ function readdir(path, options, callback) {
14561484
}
14571485

14581486
if (options.recursive) {
1459-
callback(null, readdirSyncRecursive(path, path, options));
1487+
callback(null, readdirSyncRecursive(path, options));
14601488
return;
14611489
}
14621490

@@ -1494,7 +1522,7 @@ function readdirSync(path, options) {
14941522
}
14951523

14961524
if (options.recursive) {
1497-
return readdirSyncRecursive(path, path, options);
1525+
return readdirSyncRecursive(path, options);
14981526
}
14991527

15001528
const ctx = { path };

lib/internal/fs/dir.js

-2
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,6 @@ class Dir {
160160
}
161161
}
162162

163-
// TODO(Ethan-Arrowood): Review this implementation. Make it iterative.
164-
// Can we better leverage the `kDirOperationQueue`?
165163
readSyncRecursive(dirent) {
166164
const ctx = { path: dirent.path };
167165
const handle = dirBinding.opendir(

0 commit comments

Comments
 (0)