diff --git a/cli/tests/unit/write_file_test.ts b/cli/tests/unit/write_file_test.ts index 9cbc0b272c..945807b3f8 100644 --- a/cli/tests/unit/write_file_test.ts +++ b/cli/tests/unit/write_file_test.ts @@ -96,6 +96,20 @@ Deno.test( }, ); +Deno.test( + { permissions: { read: true, write: true } }, + function writeFileSyncCreateNew() { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + Deno.writeFileSync(filename, data, { createNew: true }); + + assertThrows(() => { + Deno.writeFileSync(filename, data, { createNew: true }); + }, Deno.errors.AlreadyExists); + }, +); + Deno.test( { permissions: { read: true, write: true } }, function writeFileSyncAppend() { @@ -216,6 +230,19 @@ Deno.test( }, ); +Deno.test( + { permissions: { read: true, write: true } }, + async function writeFileCreateNew() { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + await Deno.writeFile(filename, data, { createNew: true }); + await assertRejects(async () => { + await Deno.writeFile(filename, data, { createNew: true }); + }, Deno.errors.AlreadyExists); + }, +); + Deno.test( { permissions: { read: true, write: true } }, async function writeFileAppend() { diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index 1049bf6439..da4ec24ab6 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -3296,6 +3296,10 @@ declare namespace Deno { /** Sets the option to allow creating a new file, if one doesn't already * exist at the specified path (defaults to `true`). */ create?: boolean; + /** Defaults to `false`. If set to `true`, no file, directory, or symlink is + * allowed to exist at the target location. When createNew is set to `true`, + * `create` is ignored. */ + createNew?: boolean; /** Permissions always applied to file. */ mode?: number; /** An abort signal to allow cancellation of the file write operation. diff --git a/runtime/js/40_write_file.js b/runtime/js/40_write_file.js index 36bd317b79..8d026245ff 100644 --- a/runtime/js/40_write_file.js +++ b/runtime/js/40_write_file.js @@ -17,6 +17,7 @@ options.mode, options.append ?? false, options.create ?? true, + options.createNew ?? false, data, ); } @@ -41,6 +42,7 @@ options.mode, options.append ?? false, options.create ?? true, + options.createNew ?? false, data, cancelRid, ); diff --git a/runtime/ops/fs.rs b/runtime/ops/fs.rs index 8d719393f3..b661c6ea80 100644 --- a/runtime/ops/fs.rs +++ b/runtime/ops/fs.rs @@ -222,14 +222,18 @@ async fn op_open_async( } #[inline] -fn write_open_options(create: bool, append: bool) -> OpenOptions { +fn write_open_options( + create: bool, + append: bool, + create_new: bool, +) -> OpenOptions { OpenOptions { read: false, write: true, create, truncate: !append, append, - create_new: false, + create_new, } } @@ -240,13 +244,14 @@ fn op_write_file_sync( mode: Option, append: bool, create: bool, + create_new: bool, data: ZeroCopyBuf, ) -> Result<(), AnyError> { let (path, open_options) = open_helper( state, &path, mode, - Some(&write_open_options(create, append)), + Some(&write_open_options(create, append, create_new)), "Deno.writeFileSync()", )?; write_file(&path, open_options, mode, data) @@ -259,6 +264,7 @@ async fn op_write_file_async( mode: Option, append: bool, create: bool, + create_new: bool, data: ZeroCopyBuf, cancel_rid: Option, ) -> Result<(), AnyError> { @@ -274,7 +280,7 @@ async fn op_write_file_async( &mut state.borrow_mut(), &path, mode, - Some(&write_open_options(create, append)), + Some(&write_open_options(create, append, create_new)), "Deno.writeFile()", )?; let write_future = tokio::task::spawn_blocking(move || {