From ce57a1824d3c89d19460efb315b273a43d18373e Mon Sep 17 00:00:00 2001 From: Marcos Casagrande Date: Fri, 15 May 2020 15:50:27 +0200 Subject: [PATCH] feat(std/node): fs.writeFileSync polyfill (#5414) --- std/node/_fs/_fs_writeFile.ts | 44 ++++++++++++++++ std/node/_fs/_fs_writeFile_test.ts | 80 +++++++++++++++++++++++++++++- std/node/fs.ts | 3 +- 3 files changed, 124 insertions(+), 3 deletions(-) diff --git a/std/node/_fs/_fs_writeFile.ts b/std/node/_fs/_fs_writeFile.ts index c9f7fe1252..b46620d9b4 100644 --- a/std/node/_fs/_fs_writeFile.ts +++ b/std/node/_fs/_fs_writeFile.ts @@ -63,3 +63,47 @@ export function writeFile( } })(); } + +export function writeFileSync( + pathOrRid: string | number, + data: string | Uint8Array, + options?: string | WriteFileOptions +): void { + const flag: string | undefined = isFileOptions(options) + ? options.flag + : undefined; + + const mode: number | undefined = isFileOptions(options) + ? options.mode + : undefined; + + const encoding = getEncoding(options) || "utf8"; + const openOptions = getOpenOptions(flag || "w"); + + if (typeof data === "string" && encoding === "utf8") + data = new TextEncoder().encode(data) as Uint8Array; + + const isRid = typeof pathOrRid === "number"; + let file; + + let error: Error | null = null; + try { + file = isRid + ? new Deno.File(pathOrRid as number) + : Deno.openSync(pathOrRid as string, openOptions); + + if (!isRid && mode) { + if (Deno.build.os === "windows") notImplemented(`"mode" on Windows`); + Deno.chmodSync(pathOrRid as string, mode); + } + + Deno.writeAllSync(file, data as Uint8Array); + } catch (e) { + error = e; + } finally { + // Make sure to close resource + if (!isRid && file) file.close(); + + if (error) throw error; + } +} diff --git a/std/node/_fs/_fs_writeFile_test.ts b/std/node/_fs/_fs_writeFile_test.ts index 427062034e..f3598f0b09 100644 --- a/std/node/_fs/_fs_writeFile_test.ts +++ b/std/node/_fs/_fs_writeFile_test.ts @@ -7,11 +7,11 @@ import { assertNotEquals, assertThrows, } from "../../testing/asserts.ts"; -import { writeFile } from "./_fs_writeFile.ts"; +import { writeFile, writeFileSync } from "./_fs_writeFile.ts"; const decoder = new TextDecoder("utf-8"); -test("Invalid encoding results in error()", function fn() { +test("Callback must be a function error", function fn() { assertThrows( () => { writeFile("some/path", "some data", "utf8"); @@ -29,6 +29,15 @@ test("Invalid encoding results in error()", function testEncodingErrors() { Error, `The value "made-up-encoding" is invalid for option "encoding"` ); + + assertThrows( + () => { + writeFileSync("some/path", "some data", "made-up-encoding"); + }, + Error, + `The value "made-up-encoding" is invalid for option "encoding"` + ); + assertThrows( () => { writeFile( @@ -43,6 +52,16 @@ test("Invalid encoding results in error()", function testEncodingErrors() { Error, `The value "made-up-encoding" is invalid for option "encoding"` ); + + assertThrows( + () => { + writeFileSync("some/path", "some data", { + encoding: "made-up-encoding", + }); + }, + Error, + `The value "made-up-encoding" is invalid for option "encoding"` + ); }); test("Unsupported encoding results in error()", function testUnsupportedEncoding() { @@ -53,6 +72,15 @@ test("Unsupported encoding results in error()", function testUnsupportedEncoding Error, `Not implemented: "hex" encoding` ); + + assertThrows( + () => { + writeFileSync("some/path", "some data", "hex"); + }, + Error, + `Not implemented: "hex" encoding` + ); + assertThrows( () => { writeFile( @@ -67,6 +95,16 @@ test("Unsupported encoding results in error()", function testUnsupportedEncoding Error, `Not implemented: "base64" encoding` ); + + assertThrows( + () => { + writeFileSync("some/path", "some data", { + encoding: "base64", + }); + }, + Error, + `Not implemented: "base64" encoding` + ); }); test("Data is written to correct rid", async function testCorrectWriteUsingRid() { @@ -160,3 +198,41 @@ test("Mode is not set when rid is passed", async function testCorrectFileModeRid assert(fileInfo.mode); assertNotEquals(fileInfo.mode & 0o777, 0o777); }); + +test("Data is written synchronously to correct rid", function testCorrectWriteSyncUsingRid() { + const tempFile: string = Deno.makeTempFileSync(); + const file: Deno.File = Deno.openSync(tempFile, { + create: true, + write: true, + read: true, + }); + + writeFileSync(file.rid, "hello world"); + Deno.close(file.rid); + + const data = Deno.readFileSync(tempFile); + Deno.removeSync(tempFile); + assertEquals(decoder.decode(data), "hello world"); +}); + +test("Data is written synchronously to correct file", function testCorrectWriteSyncUsingPath() { + const file = "_fs_writeFileSync_test_file"; + + writeFileSync(file, "hello world"); + + const data = Deno.readFileSync(file); + Deno.removeSync(file); + assertEquals(decoder.decode(data), "hello world"); +}); + +test("Mode is correctly set when writing synchronously", function testCorrectFileModeSync() { + if (Deno.build.os === "windows") return; + const filename = "_fs_writeFileSync_test_file.txt"; + + writeFileSync(filename, "hello world", { mode: 0o777 }); + + const fileInfo = Deno.statSync(filename); + Deno.removeSync(filename); + assert(fileInfo && fileInfo.mode); + assertEquals(fileInfo.mode & 0o777, 0o777); +}); diff --git a/std/node/fs.ts b/std/node/fs.ts index 737f2a0e98..4bb514c7a6 100644 --- a/std/node/fs.ts +++ b/std/node/fs.ts @@ -11,7 +11,7 @@ import { readlink, readlinkSync } from "./_fs/_fs_readlink.ts"; import { exists, existsSync } from "./_fs/_fs_exists.ts"; import { mkdir, mkdirSync } from "./_fs/_fs_mkdir.ts"; import { copyFile, copyFileSync } from "./_fs/_fs_copy.ts"; -import { writeFile } from "./_fs/_fs_writeFile.ts"; +import { writeFile, writeFileSync } from "./_fs/_fs_writeFile.ts"; import * as promises from "./_fs/promises/mod.ts"; export { @@ -37,5 +37,6 @@ export { mkdir, mkdirSync, writeFile, + writeFileSync, promises, };