From a6b3910bdfe0183e458015d00a61295779e46eb1 Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Thu, 12 Jan 2023 03:37:23 +0100 Subject: [PATCH] feat: allow passing a ReadableStream to Deno.writeFile/Deno.writeTextFile (#17329) Closes #13229 --- cli/tests/unit/write_file_test.ts | 16 +++++++++ cli/tests/unit/write_text_file_test.ts | 16 +++++++++ cli/tsc/dts/lib.deno.ns.d.ts | 4 +-- runtime/js/40_write_file.js | 48 +++++++++++++++++++------- 4 files changed, 70 insertions(+), 14 deletions(-) diff --git a/cli/tests/unit/write_file_test.ts b/cli/tests/unit/write_file_test.ts index 0c3ff491d6..78d0f5badb 100644 --- a/cli/tests/unit/write_file_test.ts +++ b/cli/tests/unit/write_file_test.ts @@ -393,3 +393,19 @@ function pathExists(path: string | URL) { return false; } } + +Deno.test( + { permissions: { read: true, write: true } }, + async function writeFileStream() { + const stream = new ReadableStream({ + pull(controller) { + controller.enqueue(new Uint8Array([1])); + controller.enqueue(new Uint8Array([2])); + controller.close(); + }, + }); + const filename = Deno.makeTempDirSync() + "/test.txt"; + await Deno.writeFile(filename, stream); + assertEquals(Deno.readFileSync(filename), new Uint8Array([1, 2])); + }, +); diff --git a/cli/tests/unit/write_text_file_test.ts b/cli/tests/unit/write_text_file_test.ts index c835349284..ee9cac177f 100644 --- a/cli/tests/unit/write_text_file_test.ts +++ b/cli/tests/unit/write_text_file_test.ts @@ -198,3 +198,19 @@ Deno.test( assertEquals(Deno.readTextFileSync(filename), "Hello"); }, ); + +Deno.test( + { permissions: { read: true, write: true } }, + async function writeTextFileStream() { + const stream = new ReadableStream({ + pull(controller) { + controller.enqueue("Hello"); + controller.enqueue("World"); + controller.close(); + }, + }); + const filename = Deno.makeTempDirSync() + "/test.txt"; + await Deno.writeTextFile(filename, stream); + assertEquals(Deno.readTextFileSync(filename), "HelloWorld"); + }, +); diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index 5941a5d571..3ddd0263b1 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -3364,7 +3364,7 @@ declare namespace Deno { */ export function writeFile( path: string | URL, - data: Uint8Array, + data: Uint8Array | ReadableStream, options?: WriteFileOptions, ): Promise; @@ -3407,7 +3407,7 @@ declare namespace Deno { */ export function writeTextFile( path: string | URL, - data: string, + data: string | ReadableStream, options?: WriteFileOptions, ): Promise; diff --git a/runtime/js/40_write_file.js b/runtime/js/40_write_file.js index f8d8b3ab78..a32ef441bc 100644 --- a/runtime/js/40_write_file.js +++ b/runtime/js/40_write_file.js @@ -5,6 +5,9 @@ const ops = core.ops; const { abortSignal } = window.__bootstrap; const { pathFromURL } = window.__bootstrap.util; + const { open } = window.__bootstrap.files; + const { ReadableStreamPrototype } = window.__bootstrap.streams; + const { ObjectPrototypeIsPrototypeOf } = window.__bootstrap.primordials; function writeFileSync( path, @@ -36,16 +39,29 @@ options.signal[abortSignal.add](abortHandler); } try { - await core.opAsync( - "op_write_file_async", - pathFromURL(path), - options.mode, - options.append ?? false, - options.create ?? true, - options.createNew ?? false, - data, - cancelRid, - ); + if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, data)) { + const file = await open(path, { + mode: options.mode, + append: options.append ?? false, + create: options.create ?? true, + createNew: options.createNew ?? false, + write: true, + }); + await data.pipeTo(file.writable, { + signal: options.signal, + }); + } else { + await core.opAsync( + "op_write_file_async", + pathFromURL(path), + options.mode, + options.append ?? false, + options.create ?? true, + options.createNew ?? false, + data, + cancelRid, + ); + } } finally { if (options.signal) { options.signal[abortSignal.remove](abortHandler); @@ -70,8 +86,16 @@ data, options = {}, ) { - const encoder = new TextEncoder(); - return writeFile(path, encoder.encode(data), options); + if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, data)) { + return writeFile( + path, + data.pipeThrough(new TextEncoderStream()), + options, + ); + } else { + const encoder = new TextEncoder(); + return writeFile(path, encoder.encode(data), options); + } } window.__bootstrap.writeFile = {