diff --git a/ext/node/polyfills/_fs/_fs_lstat.ts b/ext/node/polyfills/_fs/_fs_lstat.ts index c8cdfc4e490e4a..6ce401444f341c 100644 --- a/ext/node/polyfills/_fs/_fs_lstat.ts +++ b/ext/node/polyfills/_fs/_fs_lstat.ts @@ -3,6 +3,7 @@ // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials +import { denoErrorToNodeError } from "ext:deno_node/internal/errors.ts"; import { BigIntStats, CFISBIS, @@ -56,16 +57,31 @@ export const lstatPromise = promisify(lstat) as ( export function lstatSync(path: string | URL): Stats; export function lstatSync( path: string | URL, - options: { bigint: false }, + options: { bigint: false; throwIfNoEntry?: boolean }, ): Stats; export function lstatSync( path: string | URL, - options: { bigint: true }, + options: { bigint: true; throwIfNoEntry?: boolean }, ): BigIntStats; export function lstatSync( path: string | URL, options?: statOptions, ): Stats | BigIntStats { - const origin = Deno.lstatSync(path); - return CFISBIS(origin, options?.bigint || false); + try { + const origin = Deno.lstatSync(path); + return CFISBIS(origin, options?.bigint || false); + } catch (err) { + if ( + options?.throwIfNoEntry === false && + err instanceof Deno.errors.NotFound + ) { + return; + } + + if (err instanceof Error) { + throw denoErrorToNodeError(err, { syscall: "stat" }); + } else { + throw err; + } + } } diff --git a/ext/node/polyfills/internal/fs/handle.ts b/ext/node/polyfills/internal/fs/handle.ts index ce218f24ec3847..e422e2ba0881ca 100644 --- a/ext/node/polyfills/internal/fs/handle.ts +++ b/ext/node/polyfills/internal/fs/handle.ts @@ -28,27 +28,27 @@ export class FileHandle extends EventEmitter { #rid: number; constructor(rid: number) { super(); - this.rid = rid; + this.#rid = rid; } get fd() { - return this.rid; + return this.#rid; } read( - buffer: Buffer, + buffer: Uint8Array, offset?: number, length?: number, position?: number | null, ): Promise; read(options?: ReadOptions): Promise; read( - bufferOrOpt: Buffer | ReadOptions, + bufferOrOpt: Uint8Array | ReadOptions, offset?: number, length?: number, position?: number | null, ): Promise { - if (bufferOrOpt instanceof Buffer) { + if (bufferOrOpt instanceof Uint8Array) { return new Promise((resolve, reject) => { read( this.fd, @@ -90,12 +90,12 @@ export class FileHandle extends EventEmitter { encoding: string, ): Promise; write( - bufferOrStr: Buffer | string, + bufferOrStr: Uint8Array | string, offsetOrPosition: number, lengthOrEncoding: number | string, position?: number, ): Promise { - if (bufferOrStr instanceof Buffer) { + if (bufferOrStr instanceof Uint8Array) { const buffer = bufferOrStr; const offset = offsetOrPosition; const length = lengthOrEncoding; diff --git a/tests/unit_node/_fs/_fs_write_test.ts b/tests/unit_node/_fs/_fs_write_test.ts index 43bc7a10ece6b7..148bc40e7d0ff7 100644 --- a/tests/unit_node/_fs/_fs_write_test.ts +++ b/tests/unit_node/_fs/_fs_write_test.ts @@ -49,3 +49,27 @@ Deno.test({ assertEquals(decoder.decode(data), "hello"); }, }); + +Deno.test({ + name: "Data is padded if position > length", + async fn() { + const tempFile: string = Deno.makeTempFileSync(); + + using file = await Deno.open(tempFile, { + create: true, + write: true, + read: true, + }); + + const str = "hello world"; + const buffer = Buffer.from(str); + const bytesWritten = writeSync(file.rid, buffer, 0, str.length, 4); + + const data = Deno.readFileSync(tempFile); + Deno.removeSync(tempFile); + + assertEquals(bytesWritten, str.length); + // Check if result is padded + assertEquals(decoder.decode(data), "\x00\x00\x00\x00hello world"); + }, +}); diff --git a/tests/unit_node/fs_test.ts b/tests/unit_node/fs_test.ts index e62a246fade669..1b121c71f8ef32 100644 --- a/tests/unit_node/fs_test.ts +++ b/tests/unit_node/fs_test.ts @@ -7,6 +7,7 @@ import { constants, createWriteStream, existsSync, + lstatSync, mkdtempSync, promises, readFileSync, @@ -14,7 +15,13 @@ import { statSync, writeFileSync, } from "node:fs"; -import { constants as fsPromiseConstants, cp } from "node:fs/promises"; +import { + constants as fsPromiseConstants, + cp, + FileHandle, + open, + writeFile, +} from "node:fs/promises"; import process from "node:process"; import { pathToAbsoluteFileUrl } from "../unit/test_util.ts"; @@ -156,3 +163,41 @@ Deno.test("[node/fs createWriteStream", async () => { await Deno.remove(tempDir, { recursive: true }); } }); + +Deno.test( + "[node/fs lstatSync] supports throwIfNoEntry option", + () => { + const result = lstatSync("non-existing-path", { throwIfNoEntry: false }); + assertEquals(result, undefined); + }, +); + +// Test for https://github.com/denoland/deno/issues/23707 +Deno.test( + "[node/fs/promises read] respect position argument", + async () => { + const file = mkdtempSync(join(tmpdir(), "foo-")) + "/test.bin"; + await writeFile(file, ""); + + const res: number[] = []; + let fd: FileHandle | undefined; + try { + fd = await open(file, "r+"); + + for (let i = 0; i <= 5; i++) { + const buffer = new Uint8Array([i]); + await fd.write(buffer, 0, 1, i + 10); + } + + for (let i = 10; i <= 15; i++) { + const buffer = new Uint8Array(1); + await fd.read(buffer, 0, 1, i); + res.push(Number(buffer.toString())); + } + } finally { + await fd?.close(); + } + + assertEquals(res, [0, 1, 2, 3, 4, 5]); + }, +);