diff options
-rw-r--r-- | doc/api/fs.md | 101 | ||||
-rw-r--r-- | lib/fs.js | 23 | ||||
-rw-r--r-- | lib/internal/fs/promises.js | 12 | ||||
-rw-r--r-- | lib/internal/fs/utils.js | 117 | ||||
-rw-r--r-- | src/node_constants.cc | 10 | ||||
-rw-r--r-- | src/node_file.cc | 217 | ||||
-rw-r--r-- | test/parallel/test-fs-readdir-types.js | 95 | ||||
-rw-r--r-- | tools/doc/type-parser.js | 1 |
8 files changed, 524 insertions, 52 deletions
diff --git a/doc/api/fs.md b/doc/api/fs.md index 1f71a537e3..01023eb56e 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -283,6 +283,92 @@ synchronous use libuv's threadpool, which can have surprising and negative performance implications for some applications. See the [`UV_THREADPOOL_SIZE`][] documentation for more information. +## Class: fs.Dirent +<!-- YAML +added: REPLACEME +--> + +When [`fs.readdir()`][] or [`fs.readdirSync()`][] is called with the +`withFileTypes` option set to `true`, the resulting array is filled with +`fs.Dirent` objects, rather than strings or `Buffers`. + +### dirent.isBlockDevice() +<!-- YAML +added: REPLACEME +--> + +* Returns: {boolean} + +Returns `true` if the `fs.Dirent` object describes a block device. + +### dirent.isCharacterDevice() +<!-- YAML +added: REPLACEME +--> + +* Returns: {boolean} + +Returns `true` if the `fs.Dirent` object describes a character device. + +### dirent.isDirectory() +<!-- YAML +added: REPLACEME +--> + +* Returns: {boolean} + +Returns `true` if the `fs.Dirent` object describes a file system +directory. + +### dirent.isFIFO() +<!-- YAML +added: REPLACEME +--> + +* Returns: {boolean} + +Returns `true` if the `fs.Dirent` object describes a first-in-first-out +(FIFO) pipe. + +### dirent.isFile() +<!-- YAML +added: REPLACEME +--> + +* Returns: {boolean} + +Returns `true` if the `fs.Dirent` object describes a regular file. + +### dirent.isSocket() +<!-- YAML +added: REPLACEME +--> + +* Returns: {boolean} + +Returns `true` if the `fs.Dirent` object describes a socket. + +### dirent.isSymbolicLink() +<!-- YAML +added: REPLACEME +--> + +* Returns: {boolean} + +Returns `true` if the `fs.Dirent` object describes a symbolic link. + + +### dirent.name +<!-- YAML +added: REPLACEME +--> + +* {string|Buffer} + +The file name that this `fs.Dirent` object refers to. The type of this +value is determined by the `options.encoding` passed to [`fs.readdir()`][] or +[`fs.readdirSync()`][]. + ## Class: fs.FSWatcher <!-- YAML added: v0.5.8 @@ -2319,9 +2405,10 @@ changes: * `path` {string|Buffer|URL} * `options` {string|Object} * `encoding` {string} **Default:** `'utf8'` + * `withFileTypes` {boolean} **Default:** `false` * `callback` {Function} * `err` {Error} - * `files` {string[]|Buffer[]} + * `files` {string[]|Buffer[]|fs.Dirent[]} Asynchronous readdir(3). Reads the contents of a directory. The callback gets two arguments `(err, files)` where `files` is an array of @@ -2332,6 +2419,9 @@ object with an `encoding` property specifying the character encoding to use for the filenames passed to the callback. If the `encoding` is set to `'buffer'`, the filenames returned will be passed as `Buffer` objects. +If `options.withFileTypes` is set to `true`, the `files` array will contain +[`fs.Dirent`][] objects. + ## fs.readdirSync(path[, options]) <!-- YAML added: v0.1.21 @@ -2345,7 +2435,8 @@ changes: * `path` {string|Buffer|URL} * `options` {string|Object} * `encoding` {string} **Default:** `'utf8'` -* Returns: {string[]} An array of filenames excluding `'.'` and `'..'`. + * `withFileTypes` {boolean} **Default:** `false` +* Returns: {string[]|Buffer[]|fs.Dirent[]} Synchronous readdir(3). @@ -2354,6 +2445,9 @@ object with an `encoding` property specifying the character encoding to use for the filenames returned. If the `encoding` is set to `'buffer'`, the filenames returned will be passed as `Buffer` objects. +If `options.withFileTypes` is set to `true`, the result will contain +[`fs.Dirent`][] objects. + ## fs.readFile(path[, options], callback) <!-- YAML added: v0.1.29 @@ -4637,6 +4731,7 @@ the file contents. [`WriteStream`]: #fs_class_fs_writestream [`EventEmitter`]: events.html [`event ports`]: http://illumos.org/man/port_create +[`fs.Dirent`]: #fs_class_fs_dirent [`fs.FSWatcher`]: #fs_class_fs_fswatcher [`fs.Stats`]: #fs_class_fs_stats [`fs.access()`]: #fs_fs_access_path_mode_callback @@ -4652,6 +4747,8 @@ the file contents. [`fs.mkdtemp()`]: #fs_fs_mkdtemp_prefix_options_callback [`fs.open()`]: #fs_fs_open_path_flags_mode_callback [`fs.read()`]: #fs_fs_read_fd_buffer_offset_length_position_callback +[`fs.readdir()`]: #fs_fs_readdir_path_options_callback +[`fs.readdirSync()`]: #fs_fs_readdirsync_path_options [`fs.readFile()`]: #fs_fs_readfile_path_options_callback [`fs.readFileSync()`]: #fs_fs_readfilesync_path_options [`fs.realpath()`]: #fs_fs_realpath_path_options_callback @@ -58,6 +58,8 @@ const { getPathFromURL } = require('internal/url'); const internalUtil = require('internal/util'); const { copyObject, + Dirent, + getDirents, getOptions, nullCheck, preprocessSymlinkDestination, @@ -773,8 +775,19 @@ function readdir(path, options, callback) { validatePath(path); const req = new FSReqCallback(); - req.oncomplete = callback; - binding.readdir(pathModule.toNamespacedPath(path), options.encoding, req); + if (!options.withFileTypes) { + req.oncomplete = callback; + } else { + req.oncomplete = (err, result) => { + if (err) { + callback(err); + return; + } + getDirents(path, result, callback); + }; + } + binding.readdir(pathModule.toNamespacedPath(path), options.encoding, + !!options.withFileTypes, req); } function readdirSync(path, options) { @@ -783,9 +796,10 @@ function readdirSync(path, options) { validatePath(path); const ctx = { path }; const result = binding.readdir(pathModule.toNamespacedPath(path), - options.encoding, undefined, ctx); + options.encoding, !!options.withFileTypes, + undefined, ctx); handleErrorFromBinding(ctx); - return result; + return options.withFileTypes ? getDirents(path, result) : result; } function fstat(fd, options, callback) { @@ -1819,6 +1833,7 @@ module.exports = fs = { writeFileSync, write, writeSync, + Dirent, Stats, get ReadStream() { diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index e8cb2317fa..e0ddee4c7f 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -19,6 +19,7 @@ const { getPathFromURL } = require('internal/url'); const { isUint8Array } = require('internal/util/types'); const { copyObject, + getDirents, getOptions, getStatsFromBinding, nullCheck, @@ -37,10 +38,13 @@ const { validateUint32 } = require('internal/validators'); const pathModule = require('path'); +const { promisify } = require('internal/util'); const kHandle = Symbol('handle'); const { kUsePromises } = binding; +const getDirectoryEntriesPromise = promisify(getDirents); + class FileHandle { constructor(filehandle) { this[kHandle] = filehandle; @@ -312,8 +316,12 @@ async function readdir(path, options) { options = getOptions(options, {}); path = getPathFromURL(path); validatePath(path); - return binding.readdir(pathModule.toNamespacedPath(path), - options.encoding, kUsePromises); + const result = await binding.readdir(pathModule.toNamespacedPath(path), + options.encoding, !!options.withTypes, + kUsePromises); + return options.withFileTypes ? + getDirectoryEntriesPromise(path, result) : + result; } async function readlink(path, options) { diff --git a/lib/internal/fs/utils.js b/lib/internal/fs/utils.js index a2f1015c62..f065ba41e8 100644 --- a/lib/internal/fs/utils.js +++ b/lib/internal/fs/utils.js @@ -12,6 +12,8 @@ const { const { isUint8Array } = require('internal/util/types'); const pathModule = require('path'); const util = require('util'); +const kType = Symbol('type'); +const kStats = Symbol('stats'); const { O_APPEND, @@ -31,17 +33,84 @@ const { S_IFREG, S_IFSOCK, UV_FS_SYMLINK_DIR, - UV_FS_SYMLINK_JUNCTION + UV_FS_SYMLINK_JUNCTION, + UV_DIRENT_UNKNOWN, + UV_DIRENT_FILE, + UV_DIRENT_DIR, + UV_DIRENT_LINK, + UV_DIRENT_FIFO, + UV_DIRENT_SOCKET, + UV_DIRENT_CHAR, + UV_DIRENT_BLOCK } = process.binding('constants').fs; const isWindows = process.platform === 'win32'; +let fs; +function lazyLoadFs() { + if (!fs) { + fs = require('fs'); + } + return fs; +} + function assertEncoding(encoding) { if (encoding && !Buffer.isEncoding(encoding)) { throw new ERR_INVALID_OPT_VALUE_ENCODING(encoding); } } +class Dirent { + constructor(name, type) { + this.name = name; + this[kType] = type; + } + + isDirectory() { + return this[kType] === UV_DIRENT_DIR; + } + + isFile() { + return this[kType] === UV_DIRENT_FILE; + } + + isBlockDevice() { + return this[kType] === UV_DIRENT_BLOCK; + } + + isCharacterDevice() { + return this[kType] === UV_DIRENT_CHAR; + } + + isSymbolicLink() { + return this[kType] === UV_DIRENT_LINK; + } + + isFIFO() { + return this[kType] === UV_DIRENT_FIFO; + } + + isSocket() { + return this[kType] === UV_DIRENT_SOCKET; + } +} + +class DirentFromStats extends Dirent { + constructor(name, stats) { + super(name, null); + this[kStats] = stats; + } +} + +for (const name of Reflect.ownKeys(Dirent.prototype)) { + if (name === 'constructor') { + continue; + } + DirentFromStats.prototype[name] = function() { + return this[kStats][name](); + }; +} + function copyObject(source) { var target = {}; for (var key in source) @@ -49,6 +118,50 @@ function copyObject(source) { return target; } +function getDirents(path, [names, types], callback) { + var i; + if (typeof callback == 'function') { + const len = names.length; + let toFinish = 0; + for (i = 0; i < len; i++) { + const type = types[i]; + if (type === UV_DIRENT_UNKNOWN) { + const name = names[i]; + const idx = i; + toFinish++; + lazyLoadFs().stat(pathModule.resolve(path, name), (err, stats) => { + if (err) { + callback(err); + return; + } + names[idx] = new DirentFromStats(name, stats); + if (--toFinish === 0) { + callback(null, names); + } + }); + } else { + names[i] = new Dirent(names[i], types[i]); + } + } + if (toFinish === 0) { + callback(null, names); + } + } else { + const len = names.length; + for (i = 0; i < len; i++) { + const type = types[i]; + if (type === UV_DIRENT_UNKNOWN) { + const name = names[i]; + const stats = lazyLoadFs().statSync(pathModule.resolve(path, name)); + names[i] = new DirentFromStats(name, stats); + } else { + names[i] = new Dirent(names[i], types[i]); + } + } + return names; + } +} + function getOptions(options, defaultOptions) { if (options === null || options === undefined || typeof options === 'function') { @@ -342,6 +455,8 @@ function validatePath(path, propName = 'path') { module.exports = { assertEncoding, copyObject, + Dirent, + getDirents, getOptions, nullCheck, preprocessSymlinkDestination, diff --git a/src/node_constants.cc b/src/node_constants.cc index 61aa42a8ef..28d5a9ca4e 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -1022,6 +1022,16 @@ void DefineSystemConstants(Local<Object> target) { NODE_DEFINE_CONSTANT(target, O_WRONLY); NODE_DEFINE_CONSTANT(target, O_RDWR); + // file types from readdir + NODE_DEFINE_CONSTANT(target, UV_DIRENT_UNKNOWN); + NODE_DEFINE_CONSTANT(target, UV_DIRENT_FILE); + NODE_DEFINE_CONSTANT(target, UV_DIRENT_DIR); + NODE_DEFINE_CONSTANT(target, UV_DIRENT_LINK); + NODE_DEFINE_CONSTANT(target, UV_DIRENT_FIFO); + NODE_DEFINE_CONSTANT(target, UV_DIRENT_SOCKET); + NODE_DEFINE_CONSTANT(target, UV_DIRENT_CHAR); + NODE_DEFINE_CONSTANT(target, UV_DIRENT_BLOCK); + NODE_DEFINE_CONSTANT(target, S_IFMT); NODE_DEFINE_CONSTANT(target, S_IFREG); NODE_DEFINE_CONSTANT(target, S_IFDIR); diff --git a/src/node_file.cc b/src/node_file.cc index 714dec157b..fb976a31a5 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -561,51 +561,139 @@ void AfterScanDir(uv_fs_t* req) { FSReqBase* req_wrap = FSReqBase::from_req(req); FSReqAfterScope after(req_wrap, req); - if (after.Proceed()) { - Environment* env = req_wrap->env(); - Local<Value> error; - int r; - Local<Array> names = Array::New(env->isolate(), 0); - Local<Function> fn = env->push_values_to_array_function(); - Local<Value> name_argv[NODE_PUSH_VAL_TO_ARRAY_MAX]; - size_t name_idx = 0; + if (!after.Proceed()) { + return; + } + Environment* env = req_wrap->env(); + Local<Value> error; + int r; + Local<Array> names = Array::New(env->isolate(), 0); + Local<Function> fn = env->push_values_to_array_function(); + Local<Value> name_argv[NODE_PUSH_VAL_TO_ARRAY_MAX]; + size_t name_idx = 0; - for (int i = 0; ; i++) { - uv_dirent_t ent; + for (int i = 0; ; i++) { + uv_dirent_t ent; - r = uv_fs_scandir_next(req, &ent); - if (r == UV_EOF) - break; - if (r != 0) { - return req_wrap->Reject( - UVException(r, nullptr, req_wrap->syscall(), - static_cast<const char*>(req->path))); + r = uv_fs_scandir_next(req, &ent); + if (r == UV_EOF) + break; + if (r != 0) { + return req_wrap->Reject( + UVException(r, nullptr, req_wrap->syscall(), + static_cast<const char*>(req->path))); + } + + MaybeLocal<Value> filename = + StringBytes::Encode(env->isolate(), + ent.name, + req_wrap->encoding(), + &error); + if (filename.IsEmpty()) + return req_wrap->Reject(error); + + name_argv[name_idx++] = filename.ToLocalChecked(); + + if (name_idx >= arraysize(name_argv)) { + MaybeLocal<Value> ret = fn->Call(env->context(), names, name_idx, + name_argv); + if (ret.IsEmpty()) { + return; } + name_idx = 0; + } + } - MaybeLocal<Value> filename = - StringBytes::Encode(env->isolate(), - ent.name, - req_wrap->encoding(), - &error); - if (filename.IsEmpty()) - return req_wrap->Reject(error); + if (name_idx > 0) { + fn->Call(env->context(), names, name_idx, name_argv) + .ToLocalChecked(); + } - name_argv[name_idx++] = filename.ToLocalChecked(); + req_wrap->Resolve(names); +} - if (name_idx >= arraysize(name_argv)) { - fn->Call(env->context(), names, name_idx, name_argv) - .ToLocalChecked(); - name_idx = 0; +void AfterScanDirWithTypes(uv_fs_t* req) { + FSReqBase* req_wrap = FSReqBase::from_req(req); + FSReqAfterScope after(req_wrap, req); + + if (!after.Proceed()) { + return; + } + + Environment* env = req_wrap->env(); + Local<Value> error; + int r; + Local<Array> names = Array::New(env->isolate(), 0); + Local<Function> fn = env->push_values_to_array_function(); + Local<Value> name_argv[NODE_PUSH_VAL_TO_ARRAY_MAX]; + size_t name_idx = 0; + Local<Value> types = Array::New(env->isolate(), 0); + Local<Value> type_argv[NODE_PUSH_VAL_TO_ARRAY_MAX]; + size_t type_idx = 0; + + for (int i = 0; ; i++) { + uv_dirent_t ent; + + r = uv_fs_scandir_next(req, &ent); + if (r == UV_EOF) + break; + if (r != 0) { + return req_wrap->Reject( + UVException(r, nullptr, req_wrap->syscall(), + static_cast<const char*>(req->path))); + } + + MaybeLocal<Value> filename = + StringBytes::Encode(env->isolate(), + ent.name, + req_wrap->encoding(), + &error); + if (filename.IsEmpty()) + return req_wrap->Reject(error); + + name_argv[name_idx++] = filename.ToLocalChecked(); + + if (name_idx >= arraysize(name_argv)) { + MaybeLocal<Value> ret = fn->Call(env->context(), names, name_idx, + name_argv); + if (ret.IsEmpty()) { + return; } + name_idx = 0; } - if (name_idx > 0) { - fn->Call(env->context(), names, name_idx, name_argv) - .ToLocalChecked(); + type_argv[type_idx++] = Integer::New(env->isolate(), ent.type); + + if (type_idx >= arraysize(type_argv)) { + MaybeLocal<Value> ret = fn->Call(env->context(), types, type_idx, + type_argv); + if (ret.IsEmpty()) { + return; + } + type_idx = 0; } + } - req_wrap->Resolve(names); + if (name_idx > 0) { + MaybeLocal<Value> ret = fn->Call(env->context(), names, name_idx, + name_argv); + if (ret.IsEmpty()) { + return; + } + } + + if (type_idx > 0) { + MaybeLocal<Value> ret = fn->Call(env->context(), types, type_idx, + type_argv); + if (ret.IsEmpty()) { + return; + } } + + Local<Array> result = Array::New(env->isolate(), 2); + result->Set(0, names); + result->Set(1, types); + req_wrap->Resolve(result); } @@ -1372,15 +1460,22 @@ static void ReadDir(const FunctionCallbackInfo<Value>& args) { const enum encoding encoding = ParseEncoding(env->isolate(), args[1], UTF8); - FSReqBase* req_wrap_async = GetReqWrap(env, args[2]); - if (req_wrap_async != nullptr) { // readdir(path, encoding, req) - AsyncCall(env, req_wrap_async, args, "scandir", encoding, AfterScanDir, - uv_fs_scandir, *path, 0 /*flags*/); - } else { // readdir(path, encoding, undefined, ctx) - CHECK_EQ(argc, 4); + bool with_types = args[2]->BooleanValue(); + + FSReqBase* req_wrap_async = GetReqWrap(env, args[3]); + if (req_wrap_async != nullptr) { // readdir(path, encoding, withTypes, req) + if (with_types) { + AsyncCall(env, req_wrap_async, args, "scandir", encoding, + AfterScanDirWithTypes, uv_fs_scandir, *path, 0 /*flags*/); + } else { + AsyncCall(env, req_wrap_async, args, "scandir", encoding, + AfterScanDir, uv_fs_scandir, *path, 0 /*flags*/); + } + } else { // readdir(path, encoding, withTypes, undefined, ctx) + CHECK_EQ(argc, 5); FSReqWrapSync req_wrap_sync; FS_SYNC_TRACE_BEGIN(readdir); - int err = SyncCall(env, args[3], &req_wrap_sync, "scandir", + int err = SyncCall(env, args[4], &req_wrap_sync, "scandir", uv_fs_scandir, *path, 0 /*flags*/); FS_SYNC_TRACE_END(readdir); if (err < 0) { @@ -1394,6 +1489,14 @@ static void ReadDir(const FunctionCallbackInfo<Value>& args) { Local<Value> name_v[NODE_PUSH_VAL_TO_ARRAY_MAX]; size_t name_idx = 0; + Local<Value> types; + Local<Value> type_v[NODE_PUSH_VAL_TO_ARRAY_MAX]; + size_t type_idx; + if (with_types) { + types = Array::New(env->isolate(), 0); + type_idx = 0; + } + for (int i = 0; ; i++) { uv_dirent_t ent; @@ -1401,7 +1504,7 @@ static void ReadDir(const FunctionCallbackInfo<Value>& args) { if (r == UV_EOF) break; if (r != 0) { - Local<Object> ctx = args[3].As<Object>(); + Local<Object> ctx = args[4].As<Object>(); ctx->Set(env->context(), env->errno_string(), Integer::New(env->isolate(), r)).FromJust(); ctx->Set(env->context(), env->syscall_string(), @@ -1414,8 +1517,9 @@ static void ReadDir(const FunctionCallbackInfo<Value>& args) { ent.name, encoding, &error); + if (filename.IsEmpty()) { - Local<Object> ctx = args[3].As<Object>(); + Local<Object> ctx = args[4].As<Object>(); ctx->Set(env->context(), env->error_string(), error).FromJust(); return; } @@ -1430,6 +1534,19 @@ static void ReadDir(const FunctionCallbackInfo<Value>& args) { } name_idx = 0; } + + if (with_types) { + type_v[type_idx++] = Integer::New(env->isolate(), ent.type); + + if (type_idx >= arraysize(type_v)) { + MaybeLocal<Value> ret = fn->Call(env->context(), types, type_idx, + type_v); + if (ret.IsEmpty()) { + return; + } + type_idx = 0; + } + } } if (name_idx > 0) { @@ -1439,7 +1556,21 @@ static void ReadDir(const FunctionCallbackInfo<Value>& args) { } } - args.GetReturnValue().Set(names); + if (with_types && type_idx > 0) { + MaybeLocal<Value> ret = fn->Call(env->context(), types, type_idx, type_v); + if (ret.IsEmpty()) { + return; + } + } + + if (with_types) { + Local<Array> result = Array::New(env->isolate(), 2); + result->Set(0, names); + result->Set(1, types); + args.GetReturnValue().Set(result); + } else { + args.GetReturnValue().Set(names); + } } } diff --git a/test/parallel/test-fs-readdir-types.js b/test/parallel/test-fs-readdir-types.js new file mode 100644 index 0000000000..75452895cc --- /dev/null +++ b/test/parallel/test-fs-readdir-types.js @@ -0,0 +1,95 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); + +const binding = process.binding('fs'); + +const readdirDir = tmpdir.path; +const files = ['empty', 'files', 'for', 'just', 'testing']; +const constants = process.binding('constants').fs; +const types = { + isDirectory: constants.UV_DIRENT_DIR, + isFile: constants.UV_DIRENT_FILE, + isBlockDevice: constants.UV_DIRENT_BLOCK, + isCharacterDevice: constants.UV_DIRENT_CHAR, + isSymbolicLink: constants.UV_DIRENT_LINK, + isFIFO: constants.UV_DIRENT_FIFO, + isSocket: constants.UV_DIRENT_SOCKET +}; +const typeMethods = Object.keys(types); + +// Make sure tmp directory is clean +tmpdir.refresh(); + +// Create the necessary files +files.forEach(function(currentFile) { + fs.closeSync(fs.openSync(`${readdirDir}/${currentFile}`, 'w')); +}); + + +function assertDirents(dirents) { + assert.strictEqual(files.length, dirents.length); + for (const [i, dirent] of dirents.entries()) { + assert(dirent instanceof fs.Dirent); + assert.strictEqual(dirent.name, files[i]); + assert.strictEqual(dirent.isFile(), true); + assert.strictEqual(dirent.isDirectory(), false); + assert.strictEqual(dirent.isSocket(), false); + assert.strictEqual(dirent.isBlockDevice(), false); + assert.strictEqual(dirent.isCharacterDevice(), false); + assert.strictEqual(dirent.isFIFO(), false); + assert.strictEqual(dirent.isSymbolicLink(), false); + } +} + +// Check the readdir Sync version +assertDirents(fs.readdirSync(readdirDir, { withFileTypes: true })); + +// Check the readdir async version +fs.readdir(readdirDir, { + withFileTypes: true +}, common.mustCall((err, dirents) => { + assert.ifError(err); + assertDirents(dirents); +})); + +// Check for correct types when the binding returns unknowns +const UNKNOWN = constants.UV_DIRENT_UNKNOWN; +const oldReaddir = binding.readdir; +binding.readdir = common.mustCall((path, encoding, types, req, ctx) => { + if (req) { + const oldCb = req.oncomplete; + req.oncomplete = (err, results) => { + if (err) { + oldCb(err); + return; + } + results[1] = results[1].map(() => UNKNOWN); + oldCb(null, results); + }; + oldReaddir(path, encoding, types, req); + } else { + const results = oldReaddir(path, encoding, types, req, ctx); + results[1] = results[1].map(() => UNKNOWN); + return results; + } +}, 2); +assertDirents(fs.readdirSync(readdirDir, { withFileTypes: true })); +fs.readdir(readdirDir, { + withFileTypes: true +}, common.mustCall((err, dirents) => { + assert.ifError(err); + assertDirents(dirents); +})); + +// Dirent types +for (const method of typeMethods) { + const dirent = new fs.Dirent('foo', types[method]); + for (const testMethod of typeMethods) { + assert.strictEqual(dirent[testMethod](), testMethod === method); + } +} diff --git a/tools/doc/type-parser.js b/tools/doc/type-parser.js index 42cc27f19c..2a63f97c79 100644 --- a/tools/doc/type-parser.js +++ b/tools/doc/type-parser.js @@ -58,6 +58,7 @@ const customTypesMap = { 'EventEmitter': 'events.html#events_class_eventemitter', 'FileHandle': 'fs.html#fs_class_filehandle', + 'fs.Dirent': 'fs.html#fs_class_fs_dirent', 'fs.FSWatcher': 'fs.html#fs_class_fs_fswatcher', 'fs.ReadStream': 'fs.html#fs_class_fs_readstream', 'fs.Stats': 'fs.html#fs_class_fs_stats', |