- Version: v13.1.0
- Platform: Darwin White-Star.local 18.7.0 Darwin Kernel Version 18.7.0: Sat Oct 12 00:02:19 PDT 2019; root:xnu-4903.278.12~1/RELEASE_X86_64 x86_64
- Subsystem: fs
When listing a directory with readdir and withFileTypes, at least the promisified version of fs.readdir returns the wrong type for entities that are symbolic links to directories:
const { basename, join } = require('path');
const { readdir, lstat, stat, symlink, unlink } = require('fs').promises;
const LINK_NAME = basename(__filename) + '-link';
const LINK_PATH = join(__dirname, LINK_NAME);
const inspectEntity = (label, entity) => {
console.log(label, (entity.isDirectory() ? 'dir' : '') +
(entity.isFile() ? 'file' : '') +
(entity.isSymbolicLink() ? 'symlink' : ''));
};
(async function main() {
try {
await symlink(__dirname, LINK_PATH, 'dir');
for (const entity of await readdir(__dirname, { withFileTypes: true })) {
if (entity.name === LINK_NAME) inspectEntity('readdir', entity);
}
inspectEntity('lstat', await lstat(LINK_PATH));
inspectEntity('stat', await stat(LINK_PATH));
await unlink(LINK_PATH);
} catch (x) {
console.error(x.stack);
}
})();
Running the above script yields the following on my machine:
readdir file
lstat symlink
stat dir
Since the file system entity in question is a symbolic link to a directory, I would expect the code to either report a symlink or a directory, depending on whether the implementation of readdir uses stat or lstat under the covers. But in neither case would I expect a file to be reported. Yet that's exactly what's happening on my machine. Am I missing something or is this a genuine bug?
Additional testing with a second machine suggests that this may be related to remote file system usage. When running the test locally on a local disk, readdir reports a symlink (yay!). That stands in contrast to my original testing was performed on a remotely mounted file system.
As an aside, after reading a number of old issues before filing this issue, I am guessing that readdir uses stat not lstat under the covers. It would seem more consistent with the rest of the API. Yet the documentation has no disclaimer on dirent.isSymbolicLink() as it has on stats.isSymbolicLink(). Either way, being more upfront about readdir's behavior {withFileTypes: true} would go a long way towards a better developer experience. I'm happy to submit a PR for that—once I understand what's going on above.
Even with better documentation though, some developers will be disappointed when whatever readdir uses does not meet their use case. So I am also wondering whether configurability of stat/lstat for readdir withFileTypes: true would be desirable in the long term. If that suggestion falls under the heading "long-term costs of exposing things from core," I apologize. I'm gonna practice writing code without using withFileTypes for certain tonight. 😜
When listing a directory with
readdirandwithFileTypes, at least the promisified version offs.readdirreturns the wrong type for entities that are symbolic links to directories:Running the above script yields the following on my machine:
Since the file system entity in question is a symbolic link to a directory, I would expect the code to either report a symlink or a directory, depending on whether the implementation of
readdirusesstatorlstatunder the covers. But in neither case would I expect a file to be reported. Yet that's exactly what's happening on my machine. Am I missing something or is this a genuine bug?Additional testing with a second machine suggests that this may be related to remote file system usage. When running the test locally on a local disk,
readdirreports a symlink (yay!). That stands in contrast to my original testing was performed on a remotely mounted file system.As an aside, after reading a number of old issues before filing this issue, I am guessing that
readdirusesstatnotlstatunder the covers. It would seem more consistent with the rest of the API. Yet the documentation has no disclaimer ondirent.isSymbolicLink()as it has onstats.isSymbolicLink(). Either way, being more upfront aboutreaddir's behavior{withFileTypes: true}would go a long way towards a better developer experience. I'm happy to submit a PR for that—once I understand what's going on above.Even with better documentation though, some developers will be disappointed when whatever
readdiruses does not meet their use case. So I am also wondering whether configurability ofstat/lstatforreaddirwithFileTypes: truewould be desirable in the long term. If that suggestion falls under the heading "long-term costs of exposing things from core," I apologize. I'm gonna practice writing code without usingwithFileTypesfor certain tonight. 😜