◐ Shell
clean mode source ↗

lib: fix `fs.readdir` recursive async · nodejs/node@d2007ae

@@ -1372,6 +1372,102 @@ function mkdirSync(path, options) {

13721372

}

13731373

}

137413741375+

/*

1376+

* An recursive algorithm for reading the entire contents of the `basePath` directory.

1377+

* This function does not validate `basePath` as a directory. It is passed directly to

1378+

* `binding.readdir`.

1379+

* @param {string} basePath

1380+

* @param {{ encoding: string, withFileTypes: boolean }} options

1381+

* @param {(

1382+

* err?: Error,

1383+

* files?: string[] | Buffer[] | Dirent[]

1384+

* ) => any} callback

1385+

* @returns {void}

1386+

*/

1387+

function readdirRecursive(basePath, options, callback) {

1388+

const context = {

1389+

withFileTypes: Boolean(options.withFileTypes),

1390+

encoding: options.encoding,

1391+

basePath,

1392+

readdirResults: [],

1393+

pathsQueue: [basePath],

1394+

};

1395+1396+

let i = 0;

1397+1398+

function read(path) {

1399+

const req = new FSReqCallback();

1400+

req.oncomplete = (err, result) => {

1401+

if (err) {

1402+

callback(err);

1403+

return;

1404+

}

1405+1406+

if (result === undefined) {

1407+

callback(null, context.readdirResults);

1408+

return;

1409+

}

1410+1411+

processReaddirResult({

1412+

result,

1413+

currentPath: path,

1414+

context,

1415+

});

1416+1417+

if (i < context.pathsQueue.length) {

1418+

read(context.pathsQueue[i++]);

1419+

} else {

1420+

callback(null, context.readdirResults);

1421+

}

1422+

};

1423+1424+

binding.readdir(

1425+

path,

1426+

context.encoding,

1427+

context.withFileTypes,

1428+

req,

1429+

);

1430+

}

1431+1432+

read(context.pathsQueue[i++]);

1433+

}

1434+1435+

// Calling `readdir` with `withFileTypes=true`, the result is an array of arrays.

1436+

// The first array is the names, and the second array is the types.

1437+

// They are guaranteed to be the same length; hence, setting `length` to the length

1438+

// of the first array within the result.

1439+

const processReaddirResult = (args) => (args.context.withFileTypes ? handleDirents(args) : handleFilePaths(args));

1440+1441+

function handleDirents({ result, currentPath, context }) {

1442+

const { 0: names, 1: types } = result;

1443+

const { length } = names;

1444+1445+

for (let i = 0; i < length; i++) {

1446+

// Avoid excluding symlinks, as they are not directories.

1447+

// Refs: https://github.com/nodejs/node/issues/52663

1448+

const fullPath = pathModule.join(currentPath, names[i]);

1449+

const dirent = getDirent(currentPath, names[i], types[i]);

1450+

ArrayPrototypePush(context.readdirResults, dirent);

1451+1452+

if (dirent.isDirectory() || binding.internalModuleStat(binding, fullPath) === 1) {

1453+

ArrayPrototypePush(context.pathsQueue, fullPath);

1454+

}

1455+

}

1456+

}

1457+1458+

function handleFilePaths({ result, currentPath, context }) {

1459+

for (let i = 0; i < result.length; i++) {

1460+

const resultPath = pathModule.join(currentPath, result[i]);

1461+

const relativeResultPath = pathModule.relative(context.basePath, resultPath);

1462+

const stat = binding.internalModuleStat(binding, resultPath);

1463+

ArrayPrototypePush(context.readdirResults, relativeResultPath);

1464+1465+

if (stat === 1) {

1466+

ArrayPrototypePush(context.pathsQueue, resultPath);

1467+

}

1468+

}

1469+

}

1470+13751471

/**

13761472

* An iterative algorithm for reading the entire contents of the `basePath` directory.

13771473

* This function does not validate `basePath` as a directory. It is passed directly to

@@ -1381,58 +1477,37 @@ function mkdirSync(path, options) {

13811477

* @returns {string[] | Dirent[]}

13821478

*/

13831479

function readdirSyncRecursive(basePath, options) {

1384-

const withFileTypes = Boolean(options.withFileTypes);

1385-

const encoding = options.encoding;

1386-1387-

const readdirResults = [];

1388-

const pathsQueue = [basePath];

1480+

const context = {

1481+

withFileTypes: Boolean(options.withFileTypes),

1482+

encoding: options.encoding,

1483+

basePath,

1484+

readdirResults: [],

1485+

pathsQueue: [basePath],

1486+

};

1389148713901488

function read(path) {

13911489

const readdirResult = binding.readdir(

13921490

path,

1393-

encoding,

1394-

withFileTypes,

1491+

context.encoding,

1492+

context.withFileTypes,

13951493

);

1396149413971495

if (readdirResult === undefined) {

13981496

return;

13991497

}

140014981401-

if (withFileTypes) {

1402-

// Calling `readdir` with `withFileTypes=true`, the result is an array of arrays.

1403-

// The first array is the names, and the second array is the types.

1404-

// They are guaranteed to be the same length; hence, setting `length` to the length

1405-

// of the first array within the result.

1406-

const length = readdirResult[0].length;

1407-

for (let i = 0; i < length; i++) {

1408-

// Avoid excluding symlinks, as they are not directories.

1409-

// Refs: https://github.com/nodejs/node/issues/52663

1410-

const stat = binding.internalModuleStat(binding, pathModule.join(path, readdirResult[0][i]));

1411-

const dirent = getDirent(path, readdirResult[0][i], readdirResult[1][i]);

1412-

ArrayPrototypePush(readdirResults, dirent);

1413-

if (dirent.isDirectory() || stat === 1) {

1414-

ArrayPrototypePush(pathsQueue, pathModule.join(dirent.parentPath, dirent.name));

1415-

}

1416-

}

1417-

} else {

1418-

for (let i = 0; i < readdirResult.length; i++) {

1419-

const resultPath = pathModule.join(path, readdirResult[i]);

1420-

const relativeResultPath = pathModule.relative(basePath, resultPath);

1421-

const stat = binding.internalModuleStat(binding, resultPath);

1422-

ArrayPrototypePush(readdirResults, relativeResultPath);

1423-

// 1 indicates directory

1424-

if (stat === 1) {

1425-

ArrayPrototypePush(pathsQueue, resultPath);

1426-

}

1427-

}

1428-

}

1499+

processReaddirResult({

1500+

result: readdirResult,

1501+

currentPath: path,

1502+

context,

1503+

});

14291504

}

143015051431-

for (let i = 0; i < pathsQueue.length; i++) {

1432-

read(pathsQueue[i]);

1506+

for (let i = 0; i < context.pathsQueue.length; i++) {

1507+

read(context.pathsQueue[i]);

14331508

}

143415091435-

return readdirResults;

1510+

return context.readdirResults;

14361511

}

1437151214381513

/**

@@ -1458,7 +1533,7 @@ function readdir(path, options, callback) {

14581533

}

1459153414601535

if (options.recursive) {

1461-

callback(null, readdirSyncRecursive(path, options));

1536+

readdirRecursive(path, options, callback);

14621537

return;

14631538

}

14641539