From e14fe3a09a59fd2bc87dbbc44ebcf444b3f679ba Mon Sep 17 00:00:00 2001 From: David Sherret Date: Sun, 17 Dec 2023 13:54:07 +0100 Subject: [PATCH] feat: PathRef#append --- src/path.test.ts | 11 +++++++ src/path.ts | 77 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/path.test.ts b/src/path.test.ts index 0076d56..13c3ee1 100644 --- a/src/path.test.ts +++ b/src/path.test.ts @@ -902,3 +902,14 @@ Deno.test("toFileUrl", () => { assertEquals(path.toString(), stdPath.fromFileUrl(import.meta.url)); assertEquals(path.toFileUrl(), new URL(import.meta.url)); }); + +Deno.test("append", async () => { + await withTempDir(async (path) => { + const file = path.join("file.txt"); + await file.append(new TextEncoder().encode("1\n")); + file.appendSync(new TextEncoder().encode("2\n")); + await file.appendText("3\n"); + file.appendTextSync("4\n"); + assertEquals(file.readTextSync(), "1\n2\n3\n4\n"); + }); +}); diff --git a/src/path.ts b/src/path.ts index 59aa57a..71fe8fd 100644 --- a/src/path.ts +++ b/src/path.ts @@ -757,11 +757,54 @@ export class PathRef { }); } + /** Appends the provided bytes to the file. */ + async append(data: Uint8Array, options?: Omit): Promise { + await this.#withFileForAppending(options, (file) => file.write(data)); + return this; + } + + /** Synchronously appends the provided bytes to the file. */ + appendSync(data: Uint8Array, options?: Omit): this { + this.#withFileForAppendingSync(options, (file) => { + file.writeSync(data); + }); + return this; + } + + /** Appends the provided text to the file. */ + async appendText(text: string, options?: Omit): Promise { + await this.#withFileForAppending(options, (file) => file.writeText(text)); + return this; + } + + /** Synchronously appends the provided text to the file. */ + appendTextSync(text: string, options?: Omit): this { + this.#withFileForAppendingSync(options, (file) => { + file.writeTextSync(text); + }); + return this; + } + + #withFileForAppending( + options: Omit | undefined, + action: (file: FsFileWrapper) => Promise, + ) { + return this.#withFileForWriting({ + append: true, + ...options, + }, action); + } + async #withFileForWriting( options: Deno.WriteFileOptions | undefined, action: (file: FsFileWrapper) => Promise, ) { - const file = await this.#openFileForWriting(options); + const file = await this.#openFileMaybeCreatingDirectory({ + write: true, + create: true, + truncate: options?.append !== true, + ...options, + }); try { return await action(file); } finally { @@ -773,11 +816,11 @@ export class PathRef { } } - /** Opens a file for writing, but handles if the directory does not exist. */ - async #openFileForWriting(options: Deno.WriteFileOptions | undefined) { + /** Opens a file, but handles if the directory does not exist. */ + async #openFileMaybeCreatingDirectory(options: Deno.OpenOptions) { const resolvedPath = this.resolve(); // pre-resolve before going async in case the cwd changes try { - return await resolvedPath.open({ write: true, create: true, truncate: true, ...options }); + return await resolvedPath.open(options); } catch (err) { if (err instanceof Deno.errors.NotFound) { // attempt to create the parent directory when it doesn't exist @@ -789,7 +832,7 @@ export class PathRef { throw err; // throw the original error } } - return await resolvedPath.open({ write: true, create: true, truncate: true, ...options }); + return await resolvedPath.open(options); } else { throw err; } @@ -803,6 +846,16 @@ export class PathRef { }); } + #withFileForAppendingSync( + options: Omit | undefined, + action: (file: FsFileWrapper) => T, + ) { + return this.#withFileForWritingSync({ + append: true, + ...options, + }, action); + } + #withFileForWritingSync(options: Deno.WriteFileOptions | undefined, action: (file: FsFileWrapper) => T) { const file = this.#openFileForWritingSync(options); try { @@ -818,8 +871,18 @@ export class PathRef { /** Opens a file for writing, but handles if the directory does not exist. */ #openFileForWritingSync(options: Deno.WriteFileOptions | undefined) { + return this.#openFileMaybeCreatingDirectorySync({ + write: true, + create: true, + truncate: options?.append !== true, + ...options, + }); + } + + /** Opens a file for writing, but handles if the directory does not exist. */ + #openFileMaybeCreatingDirectorySync(options: Deno.OpenOptions) { try { - return this.openSync({ write: true, create: true, truncate: options?.append !== true, ...options }); + return this.openSync(options); } catch (err) { if (err instanceof Deno.errors.NotFound) { // attempt to create the parent directory when it doesn't exist @@ -831,7 +894,7 @@ export class PathRef { throw err; // throw the original error } } - return this.openSync({ write: true, create: true, truncate: true, ...options }); + return this.openSync(options); } else { throw err; }