// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials import { O_APPEND, O_CREAT, O_EXCL, O_RDWR, O_TRUNC, O_WRONLY, } from "ext:deno_node/_fs/_fs_constants.ts"; import { getOpenOptions } from "ext:deno_node/_fs/_fs_common.ts"; import { parseFileMode } from "ext:deno_node/internal/validators.mjs"; import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts"; import { getValidatedPath } from "ext:deno_node/internal/fs/utils.mjs"; import { FileHandle } from "ext:deno_node/internal/fs/handle.ts"; import type { Buffer } from "node:buffer"; function existsSync(filePath: string | URL): boolean { try { Deno.lstatSync(filePath); return true; } catch (error) { if (error instanceof Deno.errors.NotFound) { return false; } throw error; } } const FLAGS_AX = O_APPEND | O_CREAT | O_WRONLY | O_EXCL; const FLAGS_AX_PLUS = O_APPEND | O_CREAT | O_RDWR | O_EXCL; const FLAGS_WX = O_TRUNC | O_CREAT | O_WRONLY | O_EXCL; const FLAGS_WX_PLUS = O_TRUNC | O_CREAT | O_RDWR | O_EXCL; export type openFlags = | "a" | "ax" | "a+" | "ax+" | "as" | "as+" | "r" | "r+" | "rs+" | "w" | "wx" | "w+" | "wx+" | number; type openCallback = (err: Error | null, fd: number) => void; function convertFlagAndModeToOptions( flag?: openFlags, mode?: number, ): Deno.OpenOptions | undefined { if (flag === undefined && mode === undefined) return undefined; if (flag === undefined && mode) return { mode }; return { ...getOpenOptions(flag), mode }; } export function open(path: string | Buffer | URL, callback: openCallback): void; export function open( path: string | Buffer | URL, flags: openFlags, callback: openCallback, ): void; export function open( path: string | Buffer | URL, flags: openFlags, mode: number, callback: openCallback, ): void; export function open( path: string | Buffer | URL, flags: openCallback | openFlags, mode?: openCallback | number, callback?: openCallback, ) { if (flags === undefined) { throw new ERR_INVALID_ARG_TYPE( "flags or callback", ["string", "function"], flags, ); } path = getValidatedPath(path); if (arguments.length < 3) { // deno-lint-ignore no-explicit-any callback = flags as any; flags = "r"; mode = 0o666; } else if (typeof mode === "function") { callback = mode; mode = 0o666; } else { mode = parseFileMode(mode, "mode", 0o666); } if (typeof callback !== "function") { throw new ERR_INVALID_ARG_TYPE( "callback", "function", callback, ); } if (flags === undefined) { flags = "r"; } if ( existenceCheckRequired(flags as openFlags) && existsSync(path as string) ) { const err = new Error(`EEXIST: file already exists, open '${path}'`); (callback as (err: Error) => void)(err); } else { if (flags === "as" || flags === "as+") { let err: Error | null = null, res: number; try { res = openSync(path, flags, mode); } catch (error) { err = error instanceof Error ? error : new Error("[non-error thrown]"); } if (err) { (callback as (err: Error) => void)(err); } else { callback(null, res!); } return; } Deno.open( path as string, convertFlagAndModeToOptions(flags as openFlags, mode), ).then( (file) => callback!(null, file.rid), (err) => (callback as (err: Error) => void)(err), ); } } export function openPromise( path: string | Buffer | URL, flags?: openFlags = "r", mode? = 0o666, ): Promise { return new Promise((resolve, reject) => { open(path, flags, mode, (err, fd) => { if (err) reject(err); else resolve(new FileHandle(fd)); }); }); } export function openSync(path: string | Buffer | URL): number; export function openSync( path: string | Buffer | URL, flags?: openFlags, ): number; export function openSync(path: string | Buffer | URL, mode?: number): number; export function openSync( path: string | Buffer | URL, flags?: openFlags, mode?: number, ): number; export function openSync( path: string | Buffer | URL, flags?: openFlags, maybeMode?: number, ) { const mode = parseFileMode(maybeMode, "mode", 0o666); path = getValidatedPath(path); if (flags === undefined) { flags = "r"; } if ( existenceCheckRequired(flags) && existsSync(path as string) ) { throw new Error(`EEXIST: file already exists, open '${path}'`); } return Deno.openSync(path as string, convertFlagAndModeToOptions(flags, mode)) .rid; } function existenceCheckRequired(flags: openFlags | number) { return ( (typeof flags === "string" && ["ax", "ax+", "wx", "wx+"].includes(flags)) || (typeof flags === "number" && ( ((flags & FLAGS_AX) === FLAGS_AX) || ((flags & FLAGS_AX_PLUS) === FLAGS_AX_PLUS) || ((flags & FLAGS_WX) === FLAGS_WX) || ((flags & FLAGS_WX_PLUS) === FLAGS_WX_PLUS) )) ); }