Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing "withFileTypes" option for fs.readdir method #287

Merged
merged 1 commit into from Jan 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 31 additions & 3 deletions lib/binding.js
Expand Up @@ -12,6 +12,16 @@ const getPathParts = require('./filesystem').getPathParts;
const bufferFrom = require('./buffer').from;
const bufferAlloc = require('./buffer').alloc;

const MODE_TO_KTYPE = {
[constants.S_IFREG]: constants.UV_DIRENT_FILE,
[constants.S_IFDIR]: constants.UV_DIRENT_DIR,
[constants.S_IFBLK]: constants.UV_DIRENT_BLOCK,
[constants.S_IFCHR]: constants.UV_DIRENT_CHAR,
[constants.S_IFLNK]: constants.UV_DIRENT_LINK,
[constants.S_IFIFO]: constants.UV_DIRENT_FIFO,
[constants.S_IFSOCK]: constants.UV_DIRENT_SOCKET
};

/** Workaround for optimizations in node 8+ */
const fsBinding = process.binding('fs');
const kUsePromises = fsBinding.kUsePromises;
Expand Down Expand Up @@ -140,6 +150,16 @@ function wrapStatsCallback(callback) {
}
}

function getDirentType(mode) {
const ktype = MODE_TO_KTYPE[mode & constants.S_IFMT];

if (ktype === undefined) {
return constants.UV_DIRENT_UNKNOWN;
}

return ktype;
}

function notImplemented() {
throw new Error('Method not implemented');
}
Expand Down Expand Up @@ -914,9 +934,6 @@ Binding.prototype.readdir = function(
} else if (arguments.length === 3) {
callback = withFileTypes;
}
if (withFileTypes === true) {
notImplemented();
}

markSyscall(ctx, 'scandir');

Expand All @@ -933,12 +950,23 @@ Binding.prototype.readdir = function(
if (!(dir instanceof Directory)) {
throw new FSError('ENOTDIR', dirpath);
}

let list = dir.list();
if (encoding === 'buffer') {
list = list.map(function(item) {
return bufferFrom(item);
});
}

if (withFileTypes === true) {
mrmlnc marked this conversation as resolved.
Show resolved Hide resolved
const types = list.map(function(name) {
const stats = dir.getItem(name).getStats();

return getDirentType(stats.mode);
});
list = [list, types];
}

return list;
});
};
Expand Down
90 changes: 90 additions & 0 deletions test/lib/fs.readdir.spec.js
Expand Up @@ -7,6 +7,7 @@ const path = require('path');

const assert = helper.assert;
const withPromise = helper.withPromise;
const inVersion = helper.inVersion;

describe('fs.readdir(path, callback)', function() {
beforeEach(function() {
Expand Down Expand Up @@ -81,6 +82,95 @@ describe('fs.readdir(path, callback)', function() {
}
);
});

inVersion('>=10.10').it('should support "withFileTypes" option', function(
done
) {
fs.readdir(
path.join('nested', 'sub', 'dir'),
{withFileTypes: true},
function(err, items) {
assert.isNull(err);
assert.isArray(items);
assert.deepEqual(items, [
{name: 'empty'},
{name: 'one.txt'},
{name: 'two.txt'}
]);
assert.ok(items[0].isDirectory());
assert.ok(items[1].isFile());
assert.ok(items[2].isFile());
done();
}
);
});

withPromise.it('should support "withFileTypes" option', function(done) {
fs.promises
.readdir(path.join('nested', 'sub', 'dir'), {withFileTypes: true})
.then(function(items) {
assert.isArray(items);
assert.deepEqual(items, [
{name: 'empty'},
{name: 'one.txt'},
{name: 'two.txt'}
]);
assert.ok(items[0].isDirectory());
assert.ok(items[1].isFile());
assert.ok(items[2].isFile());
done();
}, done);
});

inVersion('>=10.10').it(
'should support "withFileTypes" option with "encoding" option',
function(done) {
fs.readdir(
path.join('nested', 'sub', 'dir'),
{withFileTypes: true, encoding: 'buffer'},
function(err, items) {
assert.isNull(err);
assert.isArray(items);
items.forEach(function(item) {
assert.equal(Buffer.isBuffer(item.name), true);
});
mrmlnc marked this conversation as resolved.
Show resolved Hide resolved
const names = items.map(function(item) {
return item.name.toString();
});
assert.deepEqual(names, ['empty', 'one.txt', 'two.txt']);
assert.ok(items[0].isDirectory());
assert.ok(items[1].isFile());
assert.ok(items[2].isFile());
done();
}
);
}
);

withPromise.it(
'should support "withFileTypes" option with "encoding" option',
function(done) {
fs.promises
.readdir(path.join('nested', 'sub', 'dir'), {
withFileTypes: true,
encoding: 'buffer'
})
.then(function(items) {
assert.isArray(items);
items.forEach(function(item) {
assert.equal(Buffer.isBuffer(item.name), true);
});
const names = items.map(function(item) {
return item.name.toString();
});
assert.deepEqual(names, ['empty', 'one.txt', 'two.txt']);
assert.ok(items[0].isDirectory());
assert.ok(items[1].isFile());
assert.ok(items[2].isFile());
done();
});
}
);
});

describe('fs.readdirSync(path)', function() {
Expand Down