diff --git a/cli/tests/unit/write_file_test.ts b/cli/tests/unit/write_file_test.ts index 0c3ff491d6381a..78d0f5badbee79 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 c8353492847f13..ee9cac177f9462 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 ab53a064e17923..1c62cd36f77714 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -3351,7 +3351,7 @@ declare namespace Deno { */ export function writeFile( path: string | URL, - data: Uint8Array, + data: Uint8Array | ReadableStream, options?: WriteFileOptions, ): Promise; @@ -3394,7 +3394,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 f8d8b3ab781243..a32ef441bc0a53 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 = {