From 2920cdff96b40df78c0f11c2f0b22bb312d2b197 Mon Sep 17 00:00:00 2001 From: Feng Yu Date: Tue, 7 Dec 2021 00:24:06 +0800 Subject: [PATCH] test(node/fs): enable `fs.appendFile` native tests (#1675) --- node/_fs/_fs_appendFile.ts | 112 ++-------- node/_fs/_fs_appendFile_test.ts | 12 +- node/_fs/_fs_common.ts | 9 + node/_fs/_fs_writeFile.ts | 15 +- node/_tools/config.json | 4 + .../parallel/test-fs-append-file-sync.js | 115 ++++++++++ .../suites/parallel/test-fs-append-file.js | 202 ++++++++++++++++++ node/internal/fs/utils.js | 42 ++-- node/internal/validators.js | 2 +- 9 files changed, 392 insertions(+), 121 deletions(-) create mode 100644 node/_tools/suites/parallel/test-fs-append-file-sync.js create mode 100644 node/_tools/suites/parallel/test-fs-append-file.js diff --git a/node/_fs/_fs_appendFile.ts b/node/_fs/_fs_appendFile.ts index ce18250fc4da..4609874be0db 100644 --- a/node/_fs/_fs_appendFile.ts +++ b/node/_fs/_fs_appendFile.ts @@ -1,71 +1,36 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. import { CallbackWithError, - getOpenOptions, - isFileOptions, + isFd, + maybeCallback, WriteFileOptions, } from "./_fs_common.ts"; import { Encodings } from "../_utils.ts"; -import { fromFileUrl } from "../path.ts"; +import { copyObject, getOptions } from "../internal/fs/utils.js"; +import { writeFile, writeFileSync } from "./_fs_writeFile.ts"; /** * TODO: Also accept 'data' parameter as a Node polyfill Buffer type once these * are implemented. See https://github.com/denoland/deno/issues/3403 */ export function appendFile( - pathOrRid: string | number | URL, + path: string | number | URL, data: string | Uint8Array, - optionsOrCallback: Encodings | WriteFileOptions | CallbackWithError, + options: Encodings | WriteFileOptions | CallbackWithError, callback?: CallbackWithError, ): void { - pathOrRid = pathOrRid instanceof URL ? fromFileUrl(pathOrRid) : pathOrRid; - const callbackFn: CallbackWithError | undefined = - optionsOrCallback instanceof Function ? optionsOrCallback : callback; - const options: Encodings | WriteFileOptions | undefined = - optionsOrCallback instanceof Function ? undefined : optionsOrCallback; - if (!callbackFn) { - throw new Error("No callback function supplied"); - } - - validateEncoding(options); - let rid = -1; - const buffer: Uint8Array = data instanceof Uint8Array - ? data - : new TextEncoder().encode(data); - new Promise((resolve, reject) => { - if (typeof pathOrRid === "number") { - rid = pathOrRid; - Deno.write(rid, buffer).then(resolve, reject); - } else { - const mode: number | undefined = isFileOptions(options) - ? options.mode - : undefined; - const flag: string | undefined = isFileOptions(options) - ? options.flag - : undefined; + callback = maybeCallback(callback || options); + options = getOptions(options, { encoding: "utf8", mode: 0o666, flag: "a" }); - Deno.open(pathOrRid as string, { ...getOpenOptions(flag), mode }) - .then(({ rid: openedFileRid }) => { - rid = openedFileRid; - return Deno.write(openedFileRid, buffer); - }) - .then(resolve, reject); - } - }) - .then(() => { - closeRidIfNecessary(typeof pathOrRid === "string", rid); - callbackFn(null); - }, (err) => { - closeRidIfNecessary(typeof pathOrRid === "string", rid); - callbackFn(err); - }); -} + // Don't make changes directly on options object + options = copyObject(options); -function closeRidIfNecessary(isPathString: boolean, rid: number): void { - if (isPathString && rid != -1) { - //Only close if a path was supplied and a rid allocated - Deno.close(rid); + // Force append behavior when using a supplied file descriptor + if (!options.flag || isFd(path)) { + options.flag = "a"; } + + writeFile(path, data, options, callback); } /** @@ -73,50 +38,19 @@ function closeRidIfNecessary(isPathString: boolean, rid: number): void { * are implemented. See https://github.com/denoland/deno/issues/3403 */ export function appendFileSync( - pathOrRid: string | number | URL, + path: string | number | URL, data: string | Uint8Array, options?: Encodings | WriteFileOptions, ): void { - let rid = -1; - - validateEncoding(options); - pathOrRid = pathOrRid instanceof URL ? fromFileUrl(pathOrRid) : pathOrRid; - - try { - if (typeof pathOrRid === "number") { - rid = pathOrRid; - } else { - const mode: number | undefined = isFileOptions(options) - ? options.mode - : undefined; - const flag: string | undefined = isFileOptions(options) - ? options.flag - : undefined; + options = getOptions(options, { encoding: "utf8", mode: 0o666, flag: "a" }); - const file = Deno.openSync(pathOrRid, { ...getOpenOptions(flag), mode }); - rid = file.rid; - } + // Don't make changes directly on options object + options = copyObject(options); - const buffer: Uint8Array = data instanceof Uint8Array - ? data - : new TextEncoder().encode(data); - - Deno.writeSync(rid, buffer); - } finally { - closeRidIfNecessary(typeof pathOrRid === "string", rid); + // Force append behavior when using a supplied file descriptor + if (!options.flag || isFd(path)) { + options.flag = "a"; } -} -function validateEncoding( - encodingOption: Encodings | WriteFileOptions | undefined, -): void { - if (!encodingOption) return; - - if (typeof encodingOption === "string") { - if (encodingOption !== "utf8") { - throw new Error("Only 'utf8' encoding is currently supported"); - } - } else if (encodingOption.encoding && encodingOption.encoding !== "utf8") { - throw new Error("Only 'utf8' encoding is currently supported"); - } + writeFileSync(path, data, options); } diff --git a/node/_fs/_fs_appendFile_test.ts b/node/_fs/_fs_appendFile_test.ts index 6a77974fd816..7cdf2d408e55 100644 --- a/node/_fs/_fs_appendFile_test.ts +++ b/node/_fs/_fs_appendFile_test.ts @@ -14,7 +14,7 @@ Deno.test({ appendFile("some/path", "some data", "utf8"); }, Error, - "No callback function supplied", + "Callback must be a function. Received 'utf8'", ); }, }); @@ -28,7 +28,7 @@ Deno.test({ appendFile("some/path", "some data", "made-up-encoding", () => {}); }, Error, - "Only 'utf8' encoding is currently supported", + "The argument 'made-up-encoding' is invalid encoding. Received 'encoding'", ); assertThrows( () => { @@ -41,13 +41,13 @@ Deno.test({ ); }, Error, - "Only 'utf8' encoding is currently supported", + "The argument 'made-up-encoding' is invalid encoding. Received 'encoding'", ); assertThrows( // @ts-expect-error Type '"made-up-encoding"' is not assignable to type () => appendFileSync("some/path", "some data", "made-up-encoding"), Error, - "Only 'utf8' encoding is currently supported", + "The argument 'made-up-encoding' is invalid encoding. Received 'encoding'", ); assertThrows( () => @@ -56,7 +56,7 @@ Deno.test({ encoding: "made-up-encoding", }), Error, - "Only 'utf8' encoding is currently supported", + "The argument 'made-up-encoding' is invalid encoding. Received 'encoding'", ); }, }); @@ -196,7 +196,7 @@ Deno.test({ const tempFile: string = Deno.makeTempFileSync(); assertThrows( () => appendFileSync(tempFile, "hello world", { flag: "ax" }), - Deno.errors.AlreadyExists, + Error, "", ); assertEquals(Deno.resources(), openResourcesBeforeAppend); diff --git a/node/_fs/_fs_common.ts b/node/_fs/_fs_common.ts index 606317ecc2cd..4c69e51dd6d1 100644 --- a/node/_fs/_fs_common.ts +++ b/node/_fs/_fs_common.ts @@ -1,4 +1,5 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +import { validateCallback } from "../internal/validators.js"; import { BinaryEncodings, Encodings, @@ -162,3 +163,11 @@ export function getOpenOptions(flag: string | undefined): Deno.OpenOptions { return openOptions; } + +export { isUint32 as isFd } from "../internal/validators.js"; + +export function maybeCallback(cb: unknown) { + validateCallback(cb); + + return cb as CallbackWithError; +} diff --git a/node/_fs/_fs_writeFile.ts b/node/_fs/_fs_writeFile.ts index f500d63f911c..f11c5aba64cb 100644 --- a/node/_fs/_fs_writeFile.ts +++ b/node/_fs/_fs_writeFile.ts @@ -1,5 +1,5 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { Encodings, notImplemented } from "../_utils.ts"; +import { Encodings } from "../_utils.ts"; import { fromFileUrl } from "../path.ts"; import { Buffer } from "../buffer.ts"; import { writeAllSync } from "../../streams/conversion.ts"; @@ -13,6 +13,7 @@ import { } from "./_fs_common.ts"; import { isWindows } from "../../_util/os.ts"; import { AbortError, denoErrorToNodeError } from "../_errors.ts"; +import { validateStringAfterArrayBufferView } from "../internal/fs/utils.js"; export function writeFile( pathOrRid: string | number | URL, @@ -44,6 +45,7 @@ export function writeFile( const openOptions = getOpenOptions(flag || "w"); if (!ArrayBuffer.isView(data)) { + validateStringAfterArrayBufferView(data, "data"); data = Buffer.from(String(data), encoding); } @@ -57,8 +59,9 @@ export function writeFile( ? new Deno.File(pathOrRid as number) : await Deno.open(pathOrRid as string, openOptions); - if (!isRid && mode) { - if (isWindows) notImplemented(`"mode" on Windows`); + // ignore mode because it's not supported on windows + // TODO: remove `!isWindows` when `Deno.chmod` is supported + if (!isRid && mode && !isWindows) { await Deno.chmod(pathOrRid as string, mode); } @@ -98,6 +101,7 @@ export function writeFileSync( const openOptions = getOpenOptions(flag || "w"); if (!ArrayBuffer.isView(data)) { + validateStringAfterArrayBufferView(data, "data"); data = Buffer.from(String(data), encoding); } @@ -110,8 +114,9 @@ export function writeFileSync( ? new Deno.File(pathOrRid as number) : Deno.openSync(pathOrRid as string, openOptions); - if (!isRid && mode) { - if (isWindows) notImplemented(`"mode" on Windows`); + // ignore mode because it's not supported on windows + // TODO: remove `!isWindows` when `Deno.chmod` is supported + if (!isRid && mode && !isWindows) { Deno.chmodSync(pathOrRid as string, mode); } diff --git a/node/_tools/config.json b/node/_tools/config.json index c56287f55af2..ce6998df0020 100644 --- a/node/_tools/config.json +++ b/node/_tools/config.json @@ -26,6 +26,7 @@ "test-event-emitter-max-listeners.js", "test-event-emitter-no-error-provided-to-error-event.js", "test-event-emitter-prepend.js", + "test-fs-append-file.js", "test-fs-rmdir-recursive.js", "test-fs-write-file.js", "test-querystring.js", @@ -135,6 +136,8 @@ "test-event-emitter-symbols.js", "test-events-list.js", "test-events-once.js", + "test-fs-append-file-sync.js", + "test-fs-append-file.js", "test-fs-rm.js", "test-fs-rmdir-recursive-sync-warns-not-found.js", "test-fs-rmdir-recursive-sync-warns-on-file.js", @@ -142,6 +145,7 @@ "test-fs-rmdir-recursive-throws-on-file.js", "test-fs-rmdir-recursive-warns-not-found.js", "test-fs-rmdir-recursive-warns-on-file.js", + "test-fs-rmdir-recursive.js", "test-fs-rmdir-type-check.js", "test-fs-write-file-buffer.js", "test-fs-write-file-invalid-path.js", diff --git a/node/_tools/suites/parallel/test-fs-append-file-sync.js b/node/_tools/suites/parallel/test-fs-append-file-sync.js new file mode 100644 index 000000000000..2c51038dd1d6 --- /dev/null +++ b/node/_tools/suites/parallel/test-fs-append-file-sync.js @@ -0,0 +1,115 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const join = require('path').join; +const fs = require('fs'); + +const currentFileData = 'ABCD'; +const m = 0o600; +const num = 220; +const data = '南越国是前203年至前111年存在于岭南地区的一个国家,国都位于番禺,疆域包括今天中国的广东、' + + '广西两省区的大部份地区,福建省、湖南、贵州、云南的一小部份地区和越南的北部。' + + '南越国是秦朝灭亡后,由南海郡尉赵佗于前203年起兵兼并桂林郡和象郡后建立。' + + '前196年和前179年,南越国曾先后两次名义上臣属于西汉,成为西汉的“外臣”。前112年,' + + '南越国末代君主赵建德与西汉发生战争,被汉武帝于前111年所灭。南越国共存在93年,' + + '历经五代君主。南越国是岭南地区的第一个有记载的政权国家,采用封建制和郡县制并存的制度,' + + '它的建立保证了秦末乱世岭南地区社会秩序的稳定,有效的改善了岭南地区落后的政治、##济现状。\n'; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Test that empty file will be created and have content added. +const filename = join(tmpdir.path, 'append-sync.txt'); + +fs.appendFileSync(filename, data); + +const fileData = fs.readFileSync(filename); + +assert.strictEqual(Buffer.byteLength(data), fileData.length); + +// Test that appends data to a non empty file. +const filename2 = join(tmpdir.path, 'append-sync2.txt'); +fs.writeFileSync(filename2, currentFileData); + +fs.appendFileSync(filename2, data); + +const fileData2 = fs.readFileSync(filename2); + +assert.strictEqual(Buffer.byteLength(data) + currentFileData.length, + fileData2.length); + +// Test that appendFileSync accepts buffers. +const filename3 = join(tmpdir.path, 'append-sync3.txt'); +fs.writeFileSync(filename3, currentFileData); + +const buf = Buffer.from(data, 'utf8'); +fs.appendFileSync(filename3, buf); + +const fileData3 = fs.readFileSync(filename3); + +assert.strictEqual(buf.length + currentFileData.length, fileData3.length); + +const filename4 = join(tmpdir.path, 'append-sync4.txt'); +fs.writeFileSync(filename4, currentFileData, { mode: m }); + +[ + true, false, 0, 1, Infinity, () => {}, {}, [], undefined, null, +].forEach((value) => { + assert.throws( + () => fs.appendFileSync(filename4, value, { mode: m }), + { message: /data/, code: 'ERR_INVALID_ARG_TYPE' } + ); +}); +fs.appendFileSync(filename4, `${num}`, { mode: m }); + +// Windows permissions aren't Unix. +if (!common.isWindows) { + const st = fs.statSync(filename4); + assert.strictEqual(st.mode & 0o700, m); +} + +const fileData4 = fs.readFileSync(filename4); + +assert.strictEqual(Buffer.byteLength(String(num)) + currentFileData.length, + fileData4.length); + +// Test that appendFile accepts file descriptors. +const filename5 = join(tmpdir.path, 'append-sync5.txt'); +fs.writeFileSync(filename5, currentFileData); + +const filename5fd = fs.openSync(filename5, 'a+', 0o600); +fs.appendFileSync(filename5fd, data); +fs.closeSync(filename5fd); + +const fileData5 = fs.readFileSync(filename5); + +assert.strictEqual(Buffer.byteLength(data) + currentFileData.length, + fileData5.length); diff --git a/node/_tools/suites/parallel/test-fs-append-file.js b/node/_tools/suites/parallel/test-fs-append-file.js new file mode 100644 index 000000000000..86e193a656eb --- /dev/null +++ b/node/_tools/suites/parallel/test-fs-append-file.js @@ -0,0 +1,202 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const join = require('path').join; + +const tmpdir = require('../common/tmpdir'); + +const currentFileData = 'ABCD'; + +const s = '南越国是前203年至前111年存在于岭南地区的一个国家,国都位于番禺,疆域包括今天中国的广东、' + + '广西两省区的大部份地区,福建省、湖南、贵州、云南的一小部份地区和越南的北部。' + + '南越国是秦朝灭亡后,由南海郡尉赵佗于前203年起兵兼并桂林郡和象郡后建立。' + + '前196年和前179年,南越国曾先后两次名义上臣属于西汉,成为西汉的“外臣”。前112年,' + + '南越国末代君主赵建德与西汉发生战争,被汉武帝于前111年所灭。南越国共存在93年,' + + '历经五代君主。南越国是岭南地区的第一个有记载的政权国家,采用封建制和郡县制并存的制度,' + + '它的建立保证了秦末乱世岭南地区社会秩序的稳定,有效的改善了岭南地区落后的政治、##济现状。\n'; + +tmpdir.refresh(); + +const throwNextTick = (e) => { process.nextTick(() => { throw e; }); }; + +// Test that empty file will be created and have content added (callback API). +{ + const filename = join(tmpdir.path, 'append.txt'); + + fs.appendFile(filename, s, common.mustSucceed(() => { + fs.readFile(filename, common.mustSucceed((buffer) => { + assert.strictEqual(Buffer.byteLength(s), buffer.length); + })); + })); +} + +// Test that empty file will be created and have content added (promise API). +{ + const filename = join(tmpdir.path, 'append-promise.txt'); + + fs.promises.appendFile(filename, s) + .then(common.mustCall(() => fs.promises.readFile(filename))) + .then((buffer) => { + assert.strictEqual(Buffer.byteLength(s), buffer.length); + }) + .catch(throwNextTick); +} + +// Test that appends data to a non-empty file (callback API). +{ + const filename = join(tmpdir.path, 'append-non-empty.txt'); + fs.writeFileSync(filename, currentFileData); + + fs.appendFile(filename, s, common.mustSucceed(() => { + fs.readFile(filename, common.mustSucceed((buffer) => { + assert.strictEqual(Buffer.byteLength(s) + currentFileData.length, + buffer.length); + })); + })); +} + +// Test that appends data to a non-empty file (promise API). +{ + const filename = join(tmpdir.path, 'append-non-empty-promise.txt'); + fs.writeFileSync(filename, currentFileData); + + fs.promises.appendFile(filename, s) + .then(common.mustCall(() => fs.promises.readFile(filename))) + .then((buffer) => { + assert.strictEqual(Buffer.byteLength(s) + currentFileData.length, + buffer.length); + }) + .catch(throwNextTick); +} + +// Test that appendFile accepts buffers (callback API). +{ + const filename = join(tmpdir.path, 'append-buffer.txt'); + fs.writeFileSync(filename, currentFileData); + + const buf = Buffer.from(s, 'utf8'); + + fs.appendFile(filename, buf, common.mustSucceed(() => { + fs.readFile(filename, common.mustSucceed((buffer) => { + assert.strictEqual(buf.length + currentFileData.length, buffer.length); + })); + })); +} + +// Test that appendFile accepts buffers (promises API). +{ + const filename = join(tmpdir.path, 'append-buffer-promises.txt'); + fs.writeFileSync(filename, currentFileData); + + const buf = Buffer.from(s, 'utf8'); + + fs.promises.appendFile(filename, buf) + .then(common.mustCall(() => fs.promises.readFile(filename))) + .then((buffer) => { + assert.strictEqual(buf.length + currentFileData.length, buffer.length); + }) + .catch(throwNextTick); +} + +// Test that appendFile does not accept invalid data type (callback API). +[false, 5, {}, null, undefined].forEach(async (data) => { + const errObj = { + code: 'ERR_INVALID_ARG_TYPE', + message: /"data"|"buffer"/ + }; + const filename = join(tmpdir.path, 'append-invalid-data.txt'); + + assert.throws( + () => fs.appendFile(filename, data, common.mustNotCall()), + errObj + ); + + assert.throws( + () => fs.appendFileSync(filename, data), + errObj + ); + + await assert.rejects( + fs.promises.appendFile(filename, data), + errObj + ); + // The filename shouldn't exist if throwing error. + assert.throws( + () => fs.statSync(filename), + { + code: 'ENOENT', + message: /no such file or directory/ + } + ); +}); + +// Test that appendFile accepts file descriptors (callback API). +{ + const filename = join(tmpdir.path, 'append-descriptors.txt'); + fs.writeFileSync(filename, currentFileData); + + fs.open(filename, 'a+', common.mustSucceed((fd) => { + fs.appendFile(fd, s, common.mustSucceed(() => { + fs.close(fd, common.mustSucceed(() => { + fs.readFile(filename, common.mustSucceed((buffer) => { + assert.strictEqual(Buffer.byteLength(s) + currentFileData.length, + buffer.length); + })); + })); + })); + })); +} + +// FIXME(F3n67u): fs.promises.appendFile support FileHandle +// Test that appendFile accepts file descriptors (promises API). +// { +// const filename = join(tmpdir.path, 'append-descriptors-promises.txt'); +// fs.writeFileSync(filename, currentFileData); + +// let fd; +// fs.promises.open(filename, 'a+') +// .then(common.mustCall((fileDescriptor) => { +// fd = fileDescriptor; +// return fs.promises.appendFile(fd, s); +// })) +// .then(common.mustCall(() => fd.close())) +// .then(common.mustCall(() => fs.promises.readFile(filename))) +// .then(common.mustCall((buffer) => { +// assert.strictEqual(Buffer.byteLength(s) + currentFileData.length, +// buffer.length); +// })) +// .catch(throwNextTick); +// } + +assert.throws( + () => fs.appendFile(join(tmpdir.path, 'append6.txt'), console.log), + { code: 'ERR_INVALID_CALLBACK' }); diff --git a/node/internal/fs/utils.js b/node/internal/fs/utils.js index 7f091fad7801..f192dca87e4e 100644 --- a/node/internal/fs/utils.js +++ b/node/internal/fs/utils.js @@ -176,7 +176,7 @@ for (const name of Reflect.ownKeys(Dirent.prototype)) { }; } -function copyObject(source) { +export function copyObject(source) { const target = {}; for (const key in source) { target[key] = source[key]; @@ -287,7 +287,7 @@ function getDirent(path, name, type, callback) { } } -function getOptions(options, defaultOptions) { +export function getOptions(options, defaultOptions) { if ( options === null || options === undefined || typeof options === "function" @@ -951,26 +951,28 @@ const getValidMode = hideStackFrames((mode, type) => { ); }); -const validateStringAfterArrayBufferView = hideStackFrames((buffer, name) => { - if (typeof buffer === "string") { - return; - } +export const validateStringAfterArrayBufferView = hideStackFrames( + (buffer, name) => { + if (typeof buffer === "string") { + return; + } - if ( - typeof buffer === "object" && - buffer !== null && - typeof buffer.toString === "function" && - Object.prototype.hasOwnProperty.call(buffer, "toString") - ) { - return; - } + if ( + typeof buffer === "object" && + buffer !== null && + typeof buffer.toString === "function" && + Object.prototype.hasOwnProperty.call(buffer, "toString") + ) { + return; + } - throw new ERR_INVALID_ARG_TYPE( - name, - ["string", "Buffer", "TypedArray", "DataView"], - buffer, - ); -}); + throw new ERR_INVALID_ARG_TYPE( + name, + ["string", "Buffer", "TypedArray", "DataView"], + buffer, + ); + }, +); const validatePosition = hideStackFrames((position) => { if (typeof position === "number") { diff --git a/node/internal/validators.js b/node/internal/validators.js index 5a99f1307908..cf571a751281 100644 --- a/node/internal/validators.js +++ b/node/internal/validators.js @@ -19,7 +19,7 @@ function isInt32(value) { } /** - * @param {number} value + * @param {unknown} value * @returns {boolean} */ function isUint32(value) {