1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00
denoland-deno/ext/node/polyfills/_fs/_fs_writeFile.ts
Bartek Iwańczuk 0b0fb94ce2
fix(fs): instanceof check for Deno.FsFile (#22121)
Regression caused by https://github.com/denoland/deno/pull/22072.

I added a relevant test so we don't regress again.

Fixes https://github.com/denoland/deno/issues/22115
2024-01-25 23:51:29 +01:00

198 lines
5.6 KiB
TypeScript

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// TODO(petamoriken): enable prefer-primordials for node polyfills
// deno-lint-ignore-file prefer-primordials
import { Encodings } from "ext:deno_node/_utils.ts";
import { pathFromURL } from "ext:deno_web/00_infra.js";
import { Buffer } from "node:buffer";
import {
CallbackWithError,
checkEncoding,
getEncoding,
getOpenOptions,
isFileOptions,
WriteFileOptions,
} from "ext:deno_node/_fs/_fs_common.ts";
import { isWindows } from "ext:deno_node/_util/os.ts";
import {
AbortError,
denoErrorToNodeError,
} from "ext:deno_node/internal/errors.ts";
import {
showStringCoercionDeprecation,
validateStringAfterArrayBufferView,
} from "ext:deno_node/internal/fs/utils.mjs";
import { promisify } from "ext:deno_node/internal/util.mjs";
import { FsFile } from "ext:deno_fs/30_fs.js";
interface Writer {
write(p: Uint8Array): Promise<number>;
}
export function writeFile(
pathOrRid: string | number | URL,
// deno-lint-ignore ban-types
data: string | Uint8Array | Object,
optOrCallback: Encodings | CallbackWithError | WriteFileOptions | undefined,
callback?: CallbackWithError,
) {
const callbackFn: CallbackWithError | undefined =
optOrCallback instanceof Function ? optOrCallback : callback;
const options: Encodings | WriteFileOptions | undefined =
optOrCallback instanceof Function ? undefined : optOrCallback;
if (!callbackFn) {
throw new TypeError("Callback must be a function.");
}
pathOrRid = pathOrRid instanceof URL ? pathFromURL(pathOrRid) : pathOrRid;
const flag: string | undefined = isFileOptions(options)
? options.flag
: undefined;
const mode: number | undefined = isFileOptions(options)
? options.mode
: undefined;
const encoding = checkEncoding(getEncoding(options)) || "utf8";
const openOptions = getOpenOptions(flag || "w");
if (!ArrayBuffer.isView(data)) {
validateStringAfterArrayBufferView(data, "data");
if (typeof data !== "string") {
showStringCoercionDeprecation();
}
data = Buffer.from(String(data), encoding);
}
const isRid = typeof pathOrRid === "number";
let file;
let error: Error | null = null;
(async () => {
try {
file = isRid
? new FsFile(pathOrRid as number, Symbol.for("Deno.internal.FsFile"))
: await Deno.open(pathOrRid as string, openOptions);
// ignore mode because it's not supported on windows
// TODO(@bartlomieju): remove `!isWindows` when `Deno.chmod` is supported
if (!isRid && mode && !isWindows) {
await Deno.chmod(pathOrRid as string, mode);
}
const signal: AbortSignal | undefined = isFileOptions(options)
? options.signal
: undefined;
await writeAll(file, data as Uint8Array, { signal });
} catch (e) {
error = e instanceof Error
? denoErrorToNodeError(e, { syscall: "write" })
: new Error("[non-error thrown]");
} finally {
// Make sure to close resource
if (!isRid && file) file.close();
callbackFn(error);
}
})();
}
export const writeFilePromise = promisify(writeFile) as (
pathOrRid: string | number | URL,
// deno-lint-ignore ban-types
data: string | Uint8Array | Object,
options?: Encodings | WriteFileOptions,
) => Promise<void>;
export function writeFileSync(
pathOrRid: string | number | URL,
// deno-lint-ignore ban-types
data: string | Uint8Array | Object,
options?: Encodings | WriteFileOptions,
) {
pathOrRid = pathOrRid instanceof URL ? pathFromURL(pathOrRid) : pathOrRid;
const flag: string | undefined = isFileOptions(options)
? options.flag
: undefined;
const mode: number | undefined = isFileOptions(options)
? options.mode
: undefined;
const encoding = checkEncoding(getEncoding(options)) || "utf8";
const openOptions = getOpenOptions(flag || "w");
if (!ArrayBuffer.isView(data)) {
validateStringAfterArrayBufferView(data, "data");
if (typeof data !== "string") {
showStringCoercionDeprecation();
}
data = Buffer.from(String(data), encoding);
}
const isRid = typeof pathOrRid === "number";
let file;
let error: Error | null = null;
try {
file = isRid
? new FsFile(pathOrRid as number, Symbol.for("Deno.internal.FsFile"))
: Deno.openSync(pathOrRid as string, openOptions);
// ignore mode because it's not supported on windows
// TODO(@bartlomieju): remove `!isWindows` when `Deno.chmod` is supported
if (!isRid && mode && !isWindows) {
Deno.chmodSync(pathOrRid as string, mode);
}
// TODO(crowlKats): duplicate from runtime/js/13_buffer.js
let nwritten = 0;
while (nwritten < (data as Uint8Array).length) {
nwritten += file.writeSync((data as Uint8Array).subarray(nwritten));
}
} catch (e) {
error = e instanceof Error
? denoErrorToNodeError(e, { syscall: "write" })
: new Error("[non-error thrown]");
} finally {
// Make sure to close resource
if (!isRid && file) file.close();
}
if (error) throw error;
}
interface WriteAllOptions {
offset?: number;
length?: number;
signal?: AbortSignal;
}
async function writeAll(
w: Writer,
arr: Uint8Array,
options: WriteAllOptions = {},
) {
const { offset = 0, length = arr.byteLength, signal } = options;
checkAborted(signal);
const written = await w.write(arr.subarray(offset, offset + length));
if (written === length) {
return;
}
await writeAll(w, arr, {
offset: offset + written,
length: length - written,
signal,
});
}
function checkAborted(signal?: AbortSignal) {
if (signal?.aborted) {
throw new AbortError();
}
}