From a1dbfa88fca4eecc1a54d43e6e2edae9c2dbef52 Mon Sep 17 00:00:00 2001 From: James Bronder <36022278+jbronder@users.noreply.github.com> Date: Wed, 4 Jun 2025 15:27:12 -0700 Subject: [PATCH 1/6] chore: add darwin and windows OS's to node test runner --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 106830f75682..bb861e5e22d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,6 +87,8 @@ jobs: - latest os: - ubuntu-latest + - windows-latest + - macOS-latest steps: - name: Clone repository From dddf98d5791cd65425471d0f70c43a7c86dc1ec3 Mon Sep 17 00:00:00 2001 From: James Bronder <36022278+jbronder@users.noreply.github.com> Date: Wed, 4 Jun 2025 22:26:15 -0700 Subject: [PATCH 2/6] test: address `writeFile` test errors for windows with `platform()` --- fs/unstable_write_file_test.ts | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/fs/unstable_write_file_test.ts b/fs/unstable_write_file_test.ts index cdf9836e8c89..3ad651792dfb 100644 --- a/fs/unstable_write_file_test.ts +++ b/fs/unstable_write_file_test.ts @@ -82,10 +82,16 @@ Deno.test("writeFile() handles 'create' when writing to a file", async () => { const encoder = new TextEncoder(); const data = encoder.encode("Hello"); - // Rejects with NotFound when file does not initally exist. - await assertRejects(async () => { - await writeFile(testFile, data, { create: false }); - }, NotFound); + // Rejects with NotFound/Error when file does not initally exist. + if (platform() === "win32") { + await assertRejects(async () => { + await writeFile(testFile, data, { create: false }); + }, Error); + } else { + await assertRejects(async () => { + await writeFile(testFile, data, { create: false }); + }, NotFound); + } // Creates a file that does not initially exist. (This is default behavior). await writeFile(testFile, data, { create: true }); @@ -331,10 +337,16 @@ Deno.test("writeFileSync() handles 'create' when writing to a file", () => { const encoder = new TextEncoder(); const data = encoder.encode("Hello"); - // Throws with NotFound when file does not initally exist. - assertThrows(() => { - writeFileSync(testFile, data, { create: false }); - }, NotFound); + // Throws with NotFound/Error when file does not initally exist. + if (platform() === "win32") { + assertThrows(() => { + writeFileSync(testFile, data, { create: false }); + }, Error); + } else { + assertThrows(() => { + writeFileSync(testFile, data, { create: false }); + }, NotFound); + } // Creates a file that does not initially exist. (This is default behavior). writeFileSync(testFile, data, { create: true }); From 3fe9e384409b3454623faae094a437319fd34ec1 Mon Sep 17 00:00:00 2001 From: James Bronder <36022278+jbronder@users.noreply.github.com> Date: Thu, 12 Jun 2025 17:49:19 -0700 Subject: [PATCH 3/6] fix(fs/unstable): fixes to handle windows for flag options --- fs/_get_fs_flag.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/fs/_get_fs_flag.ts b/fs/_get_fs_flag.ts index 2fbabeb7cbf7..ddaffe038254 100644 --- a/fs/_get_fs_flag.ts +++ b/fs/_get_fs_flag.ts @@ -1,6 +1,6 @@ // Copyright 2018-2025 the Deno authors. MIT license. -import { getNodeFs } from "./_utils.ts"; +import { getNodeFs, getNodeOs } from "./_utils.ts"; import type { WriteFileOptions } from "./unstable_types.ts"; import type { OpenOptions } from "./unstable_open.ts"; @@ -22,11 +22,23 @@ type OpenBooleanOptions = Pick< export function getWriteFsFlag(opt: WriteBooleanOptions): number { const { O_APPEND, O_CREAT, O_EXCL, O_TRUNC, O_WRONLY } = getNodeFs().constants; - - let flag = O_WRONLY; - if (opt.create) { + const { platform } = getNodeOs(); + + // On Windows: The O_CREAT flag is set by default to prevent throwing an + // EINVAL error (code -4071) when running Node on a Windows OS. The O_CREAT + // flag will create a new file if the file does not exist and is a no-op when + // the file exists on Windows. This makes the `WriteBooleanOption`, + // `{ create: true }`, the default option. Passing `{ create: false }` will + // throw an Error. + let flag = platform() !== "win32"? O_WRONLY : O_CREAT | O_WRONLY; + + if (platform() === "win32" && !opt.create) { + flag ^= O_CREAT; + } + if (platform() !== "win32" && opt.create) { flag |= O_CREAT; } + if (opt.createNew) { flag |= O_EXCL; } From e601d11d02aff49cc19b0acb4e74849b4ae0704b Mon Sep 17 00:00:00 2001 From: James Bronder <36022278+jbronder@users.noreply.github.com> Date: Thu, 12 Jun 2025 17:54:03 -0700 Subject: [PATCH 4/6] test(fs/unstable): update `writeFile(Sync)` tests for windows --- fs/unstable_write_file_test.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/fs/unstable_write_file_test.ts b/fs/unstable_write_file_test.ts index 3ad651792dfb..34259b678575 100644 --- a/fs/unstable_write_file_test.ts +++ b/fs/unstable_write_file_test.ts @@ -82,7 +82,8 @@ Deno.test("writeFile() handles 'create' when writing to a file", async () => { const encoder = new TextEncoder(); const data = encoder.encode("Hello"); - // Rejects with NotFound/Error when file does not initally exist. + // Rejects with NotFound when file does not initally exist. + // TODO: This should throw NotFound on Windows. if (platform() === "win32") { await assertRejects(async () => { await writeFile(testFile, data, { create: false }); @@ -102,7 +103,11 @@ Deno.test("writeFile() handles 'create' when writing to a file", async () => { // Overwrites the existing file with new content. const dataAgain = encoder.encode("Hello, Standard Library"); - await writeFile(testFile, dataAgain, { create: false }); + if (platform() === "win32") { + await writeFile(testFile, dataAgain); + } else { + await writeFile(testFile, dataAgain, { create: false }); + } const dataReadAgain = await readFile(testFile); const readDataAgain = decoder.decode(dataReadAgain); assertEquals(readDataAgain, "Hello, Standard Library"); @@ -337,7 +342,8 @@ Deno.test("writeFileSync() handles 'create' when writing to a file", () => { const encoder = new TextEncoder(); const data = encoder.encode("Hello"); - // Throws with NotFound/Error when file does not initally exist. + // Throws with NotFound when file does not initally exist. + // TODO: This should throw NotFound on Windows. if (platform() === "win32") { assertThrows(() => { writeFileSync(testFile, data, { create: false }); @@ -357,7 +363,11 @@ Deno.test("writeFileSync() handles 'create' when writing to a file", () => { // Overwrites the existing file with new content. const dataAgain = encoder.encode("Hello, Standard Library"); - writeFileSync(testFile, dataAgain, { create: false }); + if (platform() === "win32") { + writeFileSync(testFile, dataAgain); + } else { + writeFileSync(testFile, dataAgain, { create: false }); + } const dataReadAgain = readFileSync(testFile); const readDataAgain = decoder.decode(dataReadAgain); assertEquals(readDataAgain, "Hello, Standard Library"); From bbbd2947bb459ab2910ebce27e46380a82fd036a Mon Sep 17 00:00:00 2001 From: James Bronder <36022278+jbronder@users.noreply.github.com> Date: Thu, 12 Jun 2025 17:56:39 -0700 Subject: [PATCH 5/6] chore: fix code format --- fs/_get_fs_flag.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/_get_fs_flag.ts b/fs/_get_fs_flag.ts index ddaffe038254..8b53f9288cf3 100644 --- a/fs/_get_fs_flag.ts +++ b/fs/_get_fs_flag.ts @@ -30,7 +30,7 @@ export function getWriteFsFlag(opt: WriteBooleanOptions): number { // the file exists on Windows. This makes the `WriteBooleanOption`, // `{ create: true }`, the default option. Passing `{ create: false }` will // throw an Error. - let flag = platform() !== "win32"? O_WRONLY : O_CREAT | O_WRONLY; + let flag = platform() !== "win32" ? O_WRONLY : O_CREAT | O_WRONLY; if (platform() === "win32" && !opt.create) { flag ^= O_CREAT; From 367b5c47ab0e3a38e2ce9b38b322c87bf32d6c56 Mon Sep 17 00:00:00 2001 From: James Bronder <36022278+jbronder@users.noreply.github.com> Date: Thu, 12 Jun 2025 23:16:25 -0700 Subject: [PATCH 6/6] test(fs/unstable): update "handles 'create'" tests for windows --- fs/unstable_write_text_file_test.ts | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/fs/unstable_write_text_file_test.ts b/fs/unstable_write_text_file_test.ts index 874c300d7566..3e2215321198 100644 --- a/fs/unstable_write_text_file_test.ts +++ b/fs/unstable_write_text_file_test.ts @@ -93,9 +93,16 @@ Deno.test("writeTextFile() handles 'create' for a file", async () => { const tempDirPath = await makeTempDir({ prefix: "writeTextFile_" }); const testFile = join(tempDirPath, "testFile.txt"); - await assertRejects(async () => { - await writeTextFile(testFile, "Hello", { create: false }); - }, NotFound); + // TODO: This should throw NotFound on Windows. + if (platform() === "win32") { + await assertRejects(async () => { + await writeTextFile(testFile, "Hello", { create: false }); + }, Error); + } else { + await assertRejects(async () => { + await writeTextFile(testFile, "Hello", { create: false }); + }, NotFound); + } await writeTextFile(testFile, "Hello", { create: true }); const readData = await readTextFile(testFile); @@ -300,9 +307,16 @@ Deno.test("writeTextFileSync() handles 'create' for a file", () => { const tempDirPath = makeTempDirSync({ prefix: "writeTextFileSync_" }); const testFile = join(tempDirPath, "testFile.txt"); - assertThrows(() => { - writeTextFileSync(testFile, "Hello", { create: false }); - }, NotFound); + // TODO: This should throw NotFound on Windows. + if (platform() === "win32") { + assertThrows(() => { + writeTextFileSync(testFile, "Hello", { create: false }); + }, Error); + } else { + assertThrows(() => { + writeTextFileSync(testFile, "Hello", { create: false }); + }, NotFound); + } writeTextFileSync(testFile, "Hello", { create: true }); const readData = readTextFileSync(testFile);