From 2e4fcaee51c7c73b07a4328430bf0f25e922f389 Mon Sep 17 00:00:00 2001 From: Ryan Zimmerman <17342435+RyanZim@users.noreply.github.com> Date: Mon, 3 Feb 2020 09:33:14 -0500 Subject: [PATCH] Update fs promise shims to support latest Node functions (#747) * Add promise support for fs.writev() * Add promise support for fs.opendir() Sort array to match Node docs * Fix tests for fs.writev() * Fix requested changes --- README.md | 2 +- ...-read-write.md => fs-read-write-writev.md} | 11 +++- lib/fs/__tests__/multi-param.test.js | 58 +++++++++++++++++++ lib/fs/index.js | 27 ++++++++- 4 files changed, 93 insertions(+), 5 deletions(-) rename docs/{fs-read-write.md => fs-read-write-writev.md} (68%) diff --git a/README.md b/README.md index 070a5e76..5b1851f1 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ Methods - [writeJsonSync](docs/writeJson-sync.md) -**NOTE:** You can still use the native Node.js methods. They are promisified and copied over to `fs-extra`. See [notes on `fs.read()` & `fs.write()`](docs/fs-read-write.md) +**NOTE:** You can still use the native Node.js methods. They are promisified and copied over to `fs-extra`. See [notes on `fs.read()`, `fs.write()`, & `fs.writev()`](docs/fs-read-write-writev.md) ### What happened to `walk()` and `walkSync()`? diff --git a/docs/fs-read-write.md b/docs/fs-read-write-writev.md similarity index 68% rename from docs/fs-read-write.md rename to docs/fs-read-write-writev.md index 1e3f15c0..be9bd1de 100644 --- a/docs/fs-read-write.md +++ b/docs/fs-read-write-writev.md @@ -1,6 +1,6 @@ # About `fs.read()` & `fs.write()` -[`fs.read()`](https://nodejs.org/api/fs.html#fs_fs_read_fd_buffer_offset_length_position_callback) & [`fs.write()`](https://nodejs.org/api/fs.html#fs_fs_write_fd_buffer_offset_length_position_callback) are different from other `fs` methods in that their callbacks are called with 3 arguments instead of the usual 2 arguments. +[`fs.read()`](https://nodejs.org/api/fs.html#fs_fs_read_fd_buffer_offset_length_position_callback), [`fs.write()`](https://nodejs.org/api/fs.html#fs_fs_write_fd_buffer_offset_length_position_callback), & [`fs.writev()`](https://nodejs.org/api/fs.html#fs_fs_writev_fd_buffers_position_callback) are different from other `fs` methods in that their callbacks are called with 3 arguments instead of the usual 2 arguments. If you're using them with callbacks, they will behave as usual. However, their promise usage is a little different. `fs-extra` promisifies these methods like [`util.promisify()`](https://nodejs.org/api/util.html#util_util_promisify_original) (only available in Node 8+) does. @@ -37,3 +37,12 @@ async function example () { const { bytesWritten, buffer } = await fs.write(fd, Buffer.alloc(length), offset, length, position) } ``` + +## `fs.writev()` + +```js +// With async/await: +async function example () { + const { bytesWritten, buffers } = await fs.writev(fd, buffers, position) +} +``` diff --git a/lib/fs/__tests__/multi-param.test.js b/lib/fs/__tests__/multi-param.test.js index 6854b22a..690a1306 100644 --- a/lib/fs/__tests__/multi-param.test.js +++ b/lib/fs/__tests__/multi-param.test.js @@ -11,6 +11,8 @@ const SIZE = 1000 // Used for tests on Node 7.2.0+ only const onNode7it = semver.gte(process.version, '7.2.0') ? it : it.skip +// Used for tests on Node 12.9.0+ only +const describeNode12 = semver.gte(process.version, '12.9.0') ? describe : describe.skip describe('fs.read()', () => { let TEST_FILE @@ -152,3 +154,59 @@ describe('fs.write()', () => { }) }) }) + +describeNode12('fs.writev()', () => { + let TEST_FILE + let TEST_DATA + let TEST_FD + + beforeEach(() => { + TEST_FILE = path.join(os.tmpdir(), 'fs-extra', 'writev-test-file') + TEST_DATA = [crypto.randomBytes(SIZE / 2), crypto.randomBytes(SIZE / 2)] + fs.ensureDirSync(path.dirname(TEST_FILE)) + TEST_FD = fs.openSync(TEST_FILE, 'w') + }) + + afterEach(() => { + return fs.close(TEST_FD) + .then(() => fs.remove(TEST_FILE)) + }) + + describe('with promises', () => { + it('returns an object', () => { + return fs.writev(TEST_FD, TEST_DATA, 0) + .then(({ bytesWritten, buffers }) => { + assert.strictEqual(bytesWritten, SIZE, 'bytesWritten is correct') + assert.deepStrictEqual(buffers, TEST_DATA, 'data is correct') + }) + }) + + it('returns an object when minimal arguments are passed', () => { + return fs.writev(TEST_FD, TEST_DATA) + .then(({ bytesWritten, buffers }) => { + assert.strictEqual(bytesWritten, SIZE, 'bytesWritten is correct') + assert.deepStrictEqual(buffers, TEST_DATA, 'data is correct') + }) + }) + }) + + describe('with callbacks', () => { + it('works', done => { + fs.writev(TEST_FD, TEST_DATA, 0, (err, bytesWritten, buffers) => { + assert.ifError(err) + assert.strictEqual(bytesWritten, SIZE, 'bytesWritten is correct') + assert.deepStrictEqual(buffers, TEST_DATA, 'data is correct') + done() + }) + }) + + it('works when minimal arguments are passed', done => { + fs.writev(TEST_FD, TEST_DATA, (err, bytesWritten, buffers) => { + assert.ifError(err) + assert.strictEqual(bytesWritten, SIZE, 'bytesWritten is correct') + assert.deepStrictEqual(buffers, TEST_DATA, 'data is correct') + done() + }) + }) + }) +}) diff --git a/lib/fs/index.js b/lib/fs/index.js index a7b22922..fe54a131 100644 --- a/lib/fs/index.js +++ b/lib/fs/index.js @@ -18,15 +18,16 @@ const api = [ 'fsync', 'ftruncate', 'futimes', - 'lchown', 'lchmod', + 'lchown', 'link', 'lstat', 'mkdir', 'mkdtemp', 'open', - 'readFile', + 'opendir', 'readdir', + 'readFile', 'readlink', 'realpath', 'rename', @@ -39,6 +40,7 @@ const api = [ 'writeFile' ].filter(key => { // Some commands are not available on some systems. Ex: + // fs.opendir was added in Node.js v12.12.0 // fs.copyFile was added in Node.js v8.5.0 // fs.mkdtemp was added in Node.js v5.10.0 // fs.lchown is not available on at least some Linux @@ -71,7 +73,7 @@ exports.exists = function (filename, callback) { }) } -// fs.read() & fs.write need special treatment due to multiple callback args +// fs.read(), fs.write(), & fs.writev() need special treatment due to multiple callback args exports.read = function (fd, buffer, offset, length, position, callback) { if (typeof callback === 'function') { @@ -103,6 +105,25 @@ exports.write = function (fd, buffer, ...args) { }) } +// fs.writev only available in Node v12.9.0+ +if (typeof fs.writev === 'function') { + // Function signature is + // s.writev(fd, buffers[, position], callback) + // We need to handle the optional arg, so we use ...args + exports.writev = function (fd, buffers, ...args) { + if (typeof args[args.length - 1] === 'function') { + return fs.writev(fd, buffers, ...args) + } + + return new Promise((resolve, reject) => { + fs.writev(fd, buffers, ...args, (err, bytesWritten, buffers) => { + if (err) return reject(err) + resolve({ bytesWritten, buffers }) + }) + }) + } +} + // fs.realpath.native only available in Node v9.2+ if (typeof fs.realpath.native === 'function') { exports.realpath.native = u(fs.realpath.native)