diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 4c3df2038104a1..4d8b4f80c63c8b 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -388,8 +388,6 @@ deno_core::extension!(deno_node, "_fs/_fs_lutimes.ts", "_fs/_fs_read.ts", "_fs/_fs_readdir.ts", - "_fs/_fs_readFile.ts", - "_fs/_fs_readlink.ts", "_next_tick.ts", "_process/exiting.ts", "_process/process.ts", diff --git a/ext/node/polyfills/_fs/_fs_readFile.ts b/ext/node/polyfills/_fs/_fs_readFile.ts deleted file mode 100644 index 9cd5a480d7e757..00000000000000 --- a/ext/node/polyfills/_fs/_fs_readFile.ts +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright 2018-2026 the Deno authors. MIT license. - -// TODO(petamoriken): enable prefer-primordials for node polyfills -// deno-lint-ignore-file prefer-primordials - -import { - BinaryOptionsArgument, - FileOptions, - FileOptionsArgument, - TextOptionsArgument, -} from "ext:deno_node/_fs/_fs_common.ts"; -import { Buffer } from "node:buffer"; -import { readAllSync } from "ext:deno_io/12_io.js"; -import { FileHandle } from "ext:deno_node/internal/fs/handle.ts"; -import { Encodings } from "ext:deno_node/_utils.ts"; -import { FsFile } from "ext:deno_fs/30_fs.js"; -import { - AbortError, - denoErrorToNodeError, - ERR_FS_FILE_TOO_LARGE, -} from "ext:deno_node/internal/errors.ts"; -import { - getOptions, - getValidatedPathToString, - stringToFlags, -} from "ext:deno_node/internal/fs/utils.mjs"; -import * as abortSignal from "ext:deno_web/03_abort_signal.js"; -import { op_fs_read_file_async, op_fs_read_file_sync } from "ext:core/ops"; -import { core, primordials } from "ext:core/mod.js"; -import { constants } from "ext:deno_node/internal/fs/utils.mjs"; -import { S_IFMT, S_IFREG } from "ext:deno_node/_fs/_fs_constants.ts"; - -const { - kIoMaxLength, - kReadFileBufferLength, - kReadFileUnknownBufferLength, -} = constants; - -const { - ArrayPrototypePush, - MathMin, - ObjectPrototypeIsPrototypeOf, - TypedArrayPrototypeGetByteLength, - TypedArrayPrototypeSet, - TypedArrayPrototypeSubarray, - Uint8Array, -} = primordials; - -const defaultOptions = { - __proto__: null, - flag: "r", -}; - -function maybeDecode(data: Uint8Array, encoding: Encodings): string; -function maybeDecode( - data: Uint8Array, - encoding: null | undefined, -): Buffer; -function maybeDecode( - data: Uint8Array, - encoding: Encodings | null | undefined, -): string | Buffer { - const buffer = Buffer.from(data.buffer, data.byteOffset, data.byteLength); - if (encoding) return buffer.toString(encoding); - return buffer; -} - -type TextCallback = (err: Error | null, data?: string) => void; -type BinaryCallback = (err: Error | null, data?: Buffer) => void; -type GenericCallback = (err: Error | null, data?: string | Buffer) => void; -type Callback = TextCallback | BinaryCallback | GenericCallback; -type Path = string | URL | FileHandle | number; - -async function readFileAsync( - path: string, - options: FileOptions | undefined, -): Promise { - let cancelRid: number | undefined; - let abortHandler: (rid: number) => void; - const flagsNumber = stringToFlags(options!.flag, "options.flag"); - if (options?.signal) { - options.signal.throwIfAborted(); - cancelRid = core.createCancelHandle(); - abortHandler = () => core.tryClose(cancelRid as number); - options.signal[abortSignal.add](abortHandler); - } - - try { - const read = await op_fs_read_file_async( - path, - cancelRid, - flagsNumber, - ); - return read; - } finally { - if (options?.signal) { - options.signal[abortSignal.remove](abortHandler); - - // always throw the abort error when aborted - options.signal.throwIfAborted(); - } - } -} - -function checkAborted(signal: AbortSignal | undefined) { - if (signal?.aborted) { - throw new AbortError(undefined, { cause: signal.reason }); - } -} - -function concatBuffers(buffers: Uint8Array[]): Uint8Array { - let totalLen = 0; - for (let i = 0; i < buffers.length; ++i) { - totalLen += TypedArrayPrototypeGetByteLength(buffers[i]); - } - - const contents = new Uint8Array(totalLen); - let n = 0; - for (let i = 0; i < buffers.length; ++i) { - const buf = buffers[i]; - TypedArrayPrototypeSet(contents, buf, n); - n += TypedArrayPrototypeGetByteLength(buf); - } - - return contents; -} - -async function fsFileReadAll(fsFile: FsFile, options?: FileOptions) { - const signal = options?.signal; - const encoding = options?.encoding; - checkAborted(signal); - - const statFields = await fsFile.stat(); - checkAborted(signal); - - let size = 0; - let length = 0; - if ((statFields.mode & S_IFMT) === S_IFREG) { - size = statFields.size; - length = encoding ? MathMin(size, kReadFileBufferLength) : size; - } - if (length === 0) { - length = kReadFileUnknownBufferLength; - } - - if (size > kIoMaxLength) { - throw new ERR_FS_FILE_TOO_LARGE(size); - } - - const buffer = new Uint8Array(length); - const buffers: Uint8Array[] = []; - - while (true) { - checkAborted(signal); - const read = await fsFile.read(buffer); - if (typeof read !== "number") { - break; - } - ArrayPrototypePush(buffers, TypedArrayPrototypeSubarray(buffer, 0, read)); - } - - return concatBuffers(buffers); -} - -export function readFile( - path: Path, - options: TextOptionsArgument, - callback: TextCallback, -): void; -export function readFile( - path: Path, - options: BinaryOptionsArgument, - callback: BinaryCallback, -): void; -export function readFile( - path: Path, - options: null | undefined | FileOptionsArgument, - callback: BinaryCallback, -): void; -export function readFile(path: string | URL, callback: BinaryCallback): void; -export function readFile( - pathOrRid: Path, - optOrCallback?: FileOptionsArgument | Callback | null | undefined, - callback?: Callback, -) { - if (ObjectPrototypeIsPrototypeOf(FileHandle.prototype, pathOrRid)) { - pathOrRid = (pathOrRid as FileHandle).fd; - } else if (typeof pathOrRid !== "number") { - pathOrRid = getValidatedPathToString(pathOrRid as string); - } - - let cb: Callback | undefined; - if (typeof optOrCallback === "function") { - cb = optOrCallback; - } else { - cb = callback; - } - - const options = getOptions(optOrCallback, defaultOptions); - - let p: Promise; - if (typeof pathOrRid === "string") { - p = readFileAsync(pathOrRid, options); - } else { - const fsFile = new FsFile(pathOrRid, Symbol.for("Deno.internal.FsFile")); - p = fsFileReadAll(fsFile, options); - } - - if (cb) { - p.then( - (data: Uint8Array) => { - const textOrBuffer = maybeDecode(data, options?.encoding); - (cb as BinaryCallback)(null, textOrBuffer); - }, - (err) => - cb && - cb( - denoErrorToNodeError(err, { - path: typeof pathOrRid === "string" ? pathOrRid : undefined, - syscall: "open", - }), - ), - ); - } -} - -export function readFilePromise( - path: Path, - options?: FileOptionsArgument | null | undefined, - // deno-lint-ignore no-explicit-any -): Promise { - return new Promise((resolve, reject) => { - readFile(path, options, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); -} - -export function readFileSync( - path: string | URL | number, - opt: TextOptionsArgument, -): string; -export function readFileSync( - path: string | URL | number, - opt?: BinaryOptionsArgument, -): Buffer; -export function readFileSync( - path: string | URL | number, - opt?: FileOptionsArgument, -): string | Buffer { - const options = getOptions(opt, defaultOptions); - - let data; - if (typeof path === "number") { - const fsFile = new FsFile(path, Symbol.for("Deno.internal.FsFile")); - data = readAllSync(fsFile); - } else { - // Validate/convert path to string (throws on invalid types) - path = getValidatedPathToString(path as unknown as string); - - const flagsNumber = stringToFlags(options?.flag, "options.flag"); - try { - data = op_fs_read_file_sync(path, flagsNumber); - } catch (err) { - throw denoErrorToNodeError(err, { path, syscall: "open" }); - } - } - const textOrBuffer = maybeDecode(data, options?.encoding); - return textOrBuffer; -} diff --git a/ext/node/polyfills/_fs/_fs_readlink.ts b/ext/node/polyfills/_fs/_fs_readlink.ts deleted file mode 100644 index af6d76dda9cc77..00000000000000 --- a/ext/node/polyfills/_fs/_fs_readlink.ts +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2018-2026 the Deno authors. MIT license. - -// TODO(petamoriken): enable prefer-primordials for node polyfills -// deno-lint-ignore-file prefer-primordials - -import { TextEncoder } from "ext:deno_web/08_text_encoding.js"; -import { MaybeEmpty, notImplemented } from "ext:deno_node/_utils.ts"; -import { promisify } from "ext:deno_node/internal/util.mjs"; -import { denoErrorToNodeError } from "ext:deno_node/internal/errors.ts"; -import { Buffer } from "node:buffer"; -import { getValidatedPathToString } from "ext:deno_node/internal/fs/utils.mjs"; -import { makeCallback } from "ext:deno_node/_fs/_fs_common.ts"; - -type ReadlinkCallback = ( - err: MaybeEmpty, - linkString: MaybeEmpty, -) => void; - -interface ReadlinkOptions { - encoding?: string | null; -} - -function maybeEncode( - data: string, - encoding: string | null, -): string | Uint8Array { - if (encoding === "buffer") { - return new TextEncoder().encode(data); - } - return data; -} - -function getEncoding( - optOrCallback?: ReadlinkOptions | ReadlinkCallback, -): string | null { - if (!optOrCallback || typeof optOrCallback === "function") { - return null; - } else { - if (optOrCallback.encoding) { - if ( - optOrCallback.encoding === "utf8" || - optOrCallback.encoding === "utf-8" - ) { - return "utf8"; - } else if (optOrCallback.encoding === "buffer") { - return "buffer"; - } else { - notImplemented(`fs.readlink encoding=${optOrCallback.encoding}`); - } - } - return null; - } -} - -export function readlink( - path: string | Buffer | URL, - optOrCallback: ReadlinkCallback | ReadlinkOptions, - callback?: ReadlinkCallback, -) { - path = getValidatedPathToString(path); - - let cb: ReadlinkCallback | undefined; - if (typeof optOrCallback === "function") { - cb = optOrCallback; - } else { - cb = callback; - } - cb = makeCallback(cb); - - const encoding = getEncoding(optOrCallback); - - Deno.readLink(path).then((data: string) => { - const res = maybeEncode(data, encoding); - if (cb) cb(null, res); - }, (err: Error) => { - if (cb) { - (cb as (e: Error) => void)(denoErrorToNodeError(err, { - syscall: "readlink", - path, - })); - } - }); -} - -export const readlinkPromise = promisify(readlink) as ( - path: string | Buffer | URL, - opt?: ReadlinkOptions, -) => Promise; - -export function readlinkSync( - path: string | Buffer | URL, - opt?: ReadlinkOptions, -): string | Uint8Array { - path = getValidatedPathToString(path); - - try { - return maybeEncode(Deno.readLinkSync(path), getEncoding(opt)); - } catch (error) { - throw denoErrorToNodeError(error, { - syscall: "readlink", - path, - }); - } -} diff --git a/ext/node/polyfills/_fs/cp/cp_sync.ts b/ext/node/polyfills/_fs/cp/cp_sync.ts index 07d271719dcc33..52a5a7ed7add39 100644 --- a/ext/node/polyfills/_fs/cp/cp_sync.ts +++ b/ext/node/polyfills/_fs/cp/cp_sync.ts @@ -5,7 +5,7 @@ import { dirname, isAbsolute, join, parse, resolve } from "node:path"; import { copyFileSync } from "ext:deno_node/_fs/_fs_copy.ts"; import { existsSync } from "ext:deno_node/_fs/_fs_exists.ts"; import { mkdirSync, opendirSync } from "node:fs"; -import { readlinkSync } from "ext:deno_node/_fs/_fs_readlink.ts"; +import { readlinkSync } from "node:fs"; import { symlinkSync, utimesSync } from "node:fs"; import { ERR_FS_CP_DIR_TO_NON_DIR, diff --git a/ext/node/polyfills/fs.ts b/ext/node/polyfills/fs.ts index 941303d74a3810..cc9f1ff080b6d7 100644 --- a/ext/node/polyfills/fs.ts +++ b/ext/node/polyfills/fs.ts @@ -2,12 +2,16 @@ import { fs as fsConstants } from "ext:deno_node/internal_binding/constants.ts"; import { codeMap } from "ext:deno_node/internal_binding/uv.ts"; import { + type BinaryOptionsArgument, type CallbackWithError, + type FileOptions, + type FileOptionsArgument, getValidatedEncoding, isFd, isFileOptions, makeCallback, maybeCallback, + type TextOptionsArgument, type WriteFileOptions, } from "ext:deno_node/_fs/_fs_common.ts"; import type { Encodings } from "ext:deno_node/_utils.ts"; @@ -15,9 +19,17 @@ import { AbortError, denoErrorToNodeError, denoWriteFileErrorToNodeError, + ERR_FS_FILE_TOO_LARGE, } from "ext:deno_node/internal/errors.ts"; import * as constants from "ext:deno_node/_fs/_fs_constants.ts"; - +import { + CFISBIS, + convertFileInfoToBigIntStats, + convertFileInfoToStats, + type statCallback, + type statCallbackBigInt, + type statOptions, +} from "ext:deno_node/internal/fs/stat_utils.ts"; import { copyFile, copyFileSync } from "ext:deno_node/_fs/_fs_copy.ts"; import { cp, cpSync } from "ext:deno_node/_fs/_fs_cp.ts"; import Dir from "ext:deno_node/_fs/_fs_dir.ts"; @@ -27,10 +39,8 @@ import { lstat, lstatSync } from "ext:deno_node/_fs/_fs_lstat.ts"; import { lutimes, lutimesSync } from "ext:deno_node/_fs/_fs_lutimes.ts"; import { read, readSync } from "ext:deno_node/_fs/_fs_read.ts"; import { readdir, readdirSync } from "ext:deno_node/_fs/_fs_readdir.ts"; -import { readFile, readFileSync } from "ext:deno_node/_fs/_fs_readFile.ts"; -import { readlink, readlinkSync } from "ext:deno_node/_fs/_fs_readlink.ts"; import { EventEmitter } from "node:events"; -import { notImplemented } from "ext:deno_node/_utils.ts"; +import { type MaybeEmpty, notImplemented } from "ext:deno_node/_utils.ts"; import { promisify } from "node:util"; import { delay } from "ext:deno_node/_util/async.ts"; import promises from "ext:deno_node/internal/fs/promises.ts"; @@ -82,6 +92,8 @@ import { Buffer } from "node:buffer"; import process from "node:process"; import * as io from "ext:deno_io/12_io.js"; import { isArrayBufferView } from "ext:deno_node/internal/util/types.ts"; +import { TextEncoder } from "ext:deno_web/08_text_encoding.js"; +import * as abortSignal from "ext:deno_web/03_abort_signal.js"; import { pathFromURL } from "ext:deno_web/00_infra.js"; import { URLPrototype } from "ext:deno_web/00_url.js"; import { FileHandle } from "ext:deno_node/internal/fs/handle.ts"; @@ -94,6 +106,7 @@ import { op_fs_fchown_async, op_fs_fchown_sync, op_fs_read_file_async, + op_fs_read_file_sync, op_fs_seek_async, op_fs_seek_sync, op_node_lchmod, @@ -151,20 +164,18 @@ const { StringPrototypeToString, SymbolAsyncIterator, SymbolFor, + ArrayPrototypePush, TypedArrayPrototypeGetByteLength, + TypedArrayPrototypeSet, + TypedArrayPrototypeSubarray, Uint8Array, } = primordials; -// -- stat -- - -import { - CFISBIS, - convertFileInfoToBigIntStats, - convertFileInfoToStats, - type statCallback, - type statCallbackBigInt, - type statOptions, -} from "ext:deno_node/internal/fs/stat_utils.ts"; +const { + kIoMaxLength, + kReadFileBufferLength, + kReadFileUnknownBufferLength, +} = fsUtilConstants; const defaultStatOptions = { __proto__: null, bigint: false }; const defaultStatSyncOptions = { @@ -451,6 +462,358 @@ function readvPromise( }); } +// -- readFile -- + +const readFileDefaultOptions = { + __proto__: null, + flag: "r", +}; + +function readFileMaybeDecode(data: Uint8Array, encoding: Encodings): string; +function readFileMaybeDecode( + data: Uint8Array, + encoding: null | undefined, +): Buffer; +function readFileMaybeDecode( + data: Uint8Array, + encoding: Encodings | null | undefined, +): string | Buffer { + // deno-lint-ignore prefer-primordials + const buffer = Buffer.from(data.buffer, data.byteOffset, data.byteLength); + // deno-lint-ignore prefer-primordials + if (encoding) return buffer.toString(encoding); + return buffer; +} + +type ReadFileTextCallback = (err: Error | null, data?: string) => void; +type ReadFileBinaryCallback = (err: Error | null, data?: Buffer) => void; +type ReadFileGenericCallback = ( + err: Error | null, + data?: string | Buffer, +) => void; +type ReadFileCallback = + | ReadFileTextCallback + | ReadFileBinaryCallback + | ReadFileGenericCallback; +type ReadFilePath = string | URL | FileHandle | number; + +async function readFileAsync( + path: string, + options: FileOptions | undefined, +): Promise { + let cancelRid: number | undefined; + let abortHandler: (rid: number) => void; + const flagsNumber = stringToFlags(options!.flag, "options.flag"); + if (options?.signal) { + options.signal.throwIfAborted(); + cancelRid = core.createCancelHandle(); + abortHandler = () => core.tryClose(cancelRid as number); + options.signal[abortSignal.add](abortHandler); + } + + try { + const data = await op_fs_read_file_async( + path, + cancelRid, + flagsNumber, + ); + return data; + } finally { + if (options?.signal) { + options.signal[abortSignal.remove](abortHandler); + + // always throw the abort error when aborted + options.signal.throwIfAborted(); + } + } +} + +function readFileCheckAborted(signal: AbortSignal | undefined) { + if (signal?.aborted) { + throw new AbortError(undefined, { cause: signal.reason }); + } +} + +function readFileConcatBuffers(buffers: Uint8Array[]): Uint8Array { + let totalLen = 0; + for (let i = 0; i < buffers.length; ++i) { + totalLen += TypedArrayPrototypeGetByteLength(buffers[i]); + } + + const contents = new Uint8Array(totalLen); + let n = 0; + for (let i = 0; i < buffers.length; ++i) { + const buf = buffers[i]; + TypedArrayPrototypeSet(contents, buf, n); + n += TypedArrayPrototypeGetByteLength(buf); + } + + return contents; +} + +async function fsFileReadAll(fsFile: FsFile, options?: FileOptions) { + const signal = options?.signal; + const encoding = options?.encoding; + readFileCheckAborted(signal); + + const statFields = await fsFile.stat(); + readFileCheckAborted(signal); + + let size = 0; + let length = 0; + if ((statFields.mode & fsConstants.S_IFMT) === fsConstants.S_IFREG) { + size = statFields.size; + length = encoding ? MathMin(size, kReadFileBufferLength) : size; + } + if (length === 0) { + length = kReadFileUnknownBufferLength; + } + + if (size > kIoMaxLength) { + throw new ERR_FS_FILE_TOO_LARGE(size); + } + + const buffer = new Uint8Array(length); + const buffers: Uint8Array[] = []; + + while (true) { + readFileCheckAborted(signal); + const nread = await fsFile.read(buffer); + if (typeof nread !== "number") { + break; + } + ArrayPrototypePush(buffers, TypedArrayPrototypeSubarray(buffer, 0, nread)); + } + + return readFileConcatBuffers(buffers); +} + +function readFile( + path: ReadFilePath, + options: TextOptionsArgument, + callback: ReadFileTextCallback, +): void; +function readFile( + path: ReadFilePath, + options: BinaryOptionsArgument, + callback: ReadFileBinaryCallback, +): void; +function readFile( + path: ReadFilePath, + options: null | undefined | FileOptionsArgument, + callback: ReadFileBinaryCallback, +): void; +function readFile( + path: string | URL, + callback: ReadFileBinaryCallback, +): void; +function readFile( + pathOrRid: ReadFilePath, + optOrCallback?: + | FileOptionsArgument + | ReadFileCallback + | null + | undefined, + callback?: ReadFileCallback, +) { + if (ObjectPrototypeIsPrototypeOf(FileHandle.prototype, pathOrRid)) { + pathOrRid = (pathOrRid as FileHandle).fd; + } else if (typeof pathOrRid !== "number") { + pathOrRid = getValidatedPathToString(pathOrRid as string); + } + + let cb: ReadFileCallback | undefined; + if (typeof optOrCallback === "function") { + cb = optOrCallback; + } else { + cb = callback; + } + + const options = getOptions( + optOrCallback, + readFileDefaultOptions, + ); + + let p: Promise; + if (typeof pathOrRid === "string") { + p = readFileAsync(pathOrRid, options); + } else { + const fsFile = new FsFile( + pathOrRid, + SymbolFor("Deno.internal.FsFile"), + ); + p = fsFileReadAll(fsFile, options); + } + + if (cb) { + PromisePrototypeThen( + p, + (data: Uint8Array) => { + const textOrBuffer = readFileMaybeDecode(data, options?.encoding); + (cb as ReadFileBinaryCallback)(null, textOrBuffer); + }, + (err) => + cb && + cb( + denoErrorToNodeError(err, { + path: typeof pathOrRid === "string" ? pathOrRid : undefined, + syscall: "open", + }), + ), + ); + } +} + +function readFilePromise( + path: ReadFilePath, + options?: FileOptionsArgument | null | undefined, + // deno-lint-ignore no-explicit-any +): Promise { + return new Promise((resolve, reject) => { + readFile(path, options, (err, data) => { + if (err) reject(err); + else resolve(data); + }); + }); +} + +function readFileSync( + path: string | URL | number, + opt: TextOptionsArgument, +): string; +function readFileSync( + path: string | URL | number, + opt?: BinaryOptionsArgument, +): Buffer; +function readFileSync( + path: string | URL | number, + opt?: FileOptionsArgument, +): string | Buffer { + const options = getOptions(opt, readFileDefaultOptions); + + let data; + if (typeof path === "number") { + const fsFile = new FsFile( + path, + SymbolFor("Deno.internal.FsFile"), + ); + data = io.readAllSync(fsFile); + } else { + // Validate/convert path to string (throws on invalid types) + path = getValidatedPathToString(path as unknown as string); + + const flagsNumber = stringToFlags(options?.flag, "options.flag"); + try { + data = op_fs_read_file_sync(path, flagsNumber); + } catch (err) { + throw denoErrorToNodeError(err, { path, syscall: "open" }); + } + } + const textOrBuffer = readFileMaybeDecode(data, options?.encoding); + return textOrBuffer; +} + +// -- readlink -- + +type ReadlinkCallback = ( + err: MaybeEmpty, + linkString: MaybeEmpty, +) => void; + +interface ReadlinkOptions { + encoding?: string | null; +} + +function readlinkMaybeEncode( + data: string, + encoding: string | null, +): string | Uint8Array { + if (encoding === "buffer") { + return new TextEncoder().encode(data); + } + return data; +} + +function readlinkGetEncoding( + optOrCallback?: ReadlinkOptions | ReadlinkCallback, +): string | null { + if (!optOrCallback || typeof optOrCallback === "function") { + return null; + } else { + if (optOrCallback.encoding) { + if ( + optOrCallback.encoding === "utf8" || + optOrCallback.encoding === "utf-8" + ) { + return "utf8"; + } else if (optOrCallback.encoding === "buffer") { + return "buffer"; + } else { + notImplemented(`fs.readlink encoding=${optOrCallback.encoding}`); + } + } + return null; + } +} + +function readlink( + path: string | Buffer | URL, + optOrCallback: ReadlinkCallback | ReadlinkOptions, + callback?: ReadlinkCallback, +) { + path = getValidatedPathToString(path); + + let cb: ReadlinkCallback | undefined; + if (typeof optOrCallback === "function") { + cb = optOrCallback; + } else { + cb = callback; + } + cb = makeCallback(cb); + + const encoding = readlinkGetEncoding(optOrCallback); + + PromisePrototypeThen( + Deno.readLink(path), + (data: string) => { + const res = readlinkMaybeEncode(data, encoding); + if (cb) cb(null, res); + }, + (err: Error) => { + if (cb) { + (cb as (e: Error) => void)(denoErrorToNodeError(err, { + syscall: "readlink", + path, + })); + } + }, + ); +} + +const readlinkPromise = promisify(readlink) as ( + path: string | Buffer | URL, + opt?: ReadlinkOptions, +) => Promise; + +function readlinkSync( + path: string | Buffer | URL, + opt?: ReadlinkOptions, +): string | Uint8Array { + path = getValidatedPathToString(path); + + try { + return readlinkMaybeEncode( + Deno.readLinkSync(path), + readlinkGetEncoding(opt), + ); + } catch (error) { + throw denoErrorToNodeError(error, { + syscall: "readlink", + path, + }); + } +} + // -- statfs -- type StatFsCallback = (err: Error | null, stats?: StatFs) => void; @@ -3028,8 +3391,10 @@ export default { readdir, readdirSync, readFile, + readFilePromise, readFileSync, readlink, + readlinkPromise, readlinkSync, ReadStream, realpath, @@ -3139,8 +3504,10 @@ export { readdir, readdirSync, readFile, + readFilePromise, readFileSync, readlink, + readlinkPromise, readlinkSync, ReadStream, readSync, diff --git a/ext/node/polyfills/internal/fs/promises.ts b/ext/node/polyfills/internal/fs/promises.ts index 4794031e9cf756..7e262cdc6db435 100644 --- a/ext/node/polyfills/internal/fs/promises.ts +++ b/ext/node/polyfills/internal/fs/promises.ts @@ -8,8 +8,6 @@ import { copyFilePromise } from "ext:deno_node/_fs/_fs_copy.ts"; import { cpPromise } from "ext:deno_node/_fs/_fs_cp.ts"; import { lutimesPromise } from "ext:deno_node/_fs/_fs_lutimes.ts"; import { readdirPromise } from "ext:deno_node/_fs/_fs_readdir.ts"; -import { readFilePromise } from "ext:deno_node/_fs/_fs_readFile.ts"; -import { readlinkPromise } from "ext:deno_node/_fs/_fs_readlink.ts"; import { lstatPromise } from "ext:deno_node/_fs/_fs_lstat.ts"; import { access, @@ -22,6 +20,8 @@ import { mkdtemp, open, opendir, + readFile, + readlink, realpath, rename, rm, @@ -248,6 +248,15 @@ const statfsPromise = promisify(statfs) as ( options?: { bigint?: boolean }, ) => Promise; +// -- readFile / readlink -- + +const readFilePromise = promisify(readFile); + +const readlinkPromise = promisify(readlink) as ( + path: string | Buffer | URL, + opt?: { encoding?: string | null }, +) => Promise; + // -- promises object -- const promises = { diff --git a/libs/core/error.rs b/libs/core/error.rs index c61c4b1324721b..0e598376721111 100644 --- a/libs/core/error.rs +++ b/libs/core/error.rs @@ -807,6 +807,7 @@ impl JsError { if let (Some(file_name), Some(line_number)) = (&frame.file_name, frame.line_number) && !file_name.trim_start_matches('[').starts_with("ext:") + && !file_name.starts_with("node:") { source_line = source_mapper.get_source_line(file_name, line_number); source_line_frame_index = Some(i); @@ -954,6 +955,7 @@ impl JsError { if let (Some(file_name), Some(line_number)) = (&frame.file_name, frame.line_number) && !file_name.trim_start_matches('[').starts_with("ext:") + && !file_name.starts_with("node:") { source_line = source_mapper.get_source_line(file_name, line_number); source_line_frame_index = Some(i); diff --git a/tests/specs/node/error_stack_internal_frames/console_log.out b/tests/specs/node/error_stack_internal_frames/console_log.out index 61645be2ae7028..a074e32d3c75a5 100644 --- a/tests/specs/node/error_stack_internal_frames/console_log.out +++ b/tests/specs/node/error_stack_internal_frames/console_log.out @@ -1,5 +1,5 @@ Error: ENOENT: no such file or directory, open '/non/existent/file' - at readFileSync (ext:deno_node/_fs/_fs_readFile.ts:[WILDCARD]) + at readFileSync (node:fs:[WILDCARD]) at [WILDCARD]console_log.ts:6:5 at processTicksAndRejections (ext:core/01_core.js:[WILDCARD]) { errno: [WILDCARD], diff --git a/tests/specs/node/error_stack_internal_frames/thrown.out b/tests/specs/node/error_stack_internal_frames/thrown.out index 07504f4040c2c4..f2c07aaac26b8a 100644 --- a/tests/specs/node/error_stack_internal_frames/thrown.out +++ b/tests/specs/node/error_stack_internal_frames/thrown.out @@ -1,6 +1,6 @@ error: Uncaught Error: ENOENT: no such file or directory, open '/non/existent/file' readFileSync("/non/existent/file"); ^ - at readFileSync (ext:deno_node/_fs/_fs_readFile.ts:[WILDCARD]) + at readFileSync (node:fs:[WILDCARD]) at [WILDCARD]thrown.ts:5:3 at processTicksAndRejections (ext:core/01_core.js:[WILDCARD]) diff --git a/tests/specs/run/require_esm/main.out b/tests/specs/run/require_esm/main.out index 4890e1a492de05..14c80aa9544a05 100644 --- a/tests/specs/run/require_esm/main.out +++ b/tests/specs/run/require_esm/main.out @@ -1,4 +1,6 @@ [Module: null prototype] { sync_js: 1 } [Module: null prototype] { sync_mjs: 1 } error: Uncaught (in promise) Error: Top-level await is not allowed in synchronous evaluation +require("./async.js"); +^ at [WILDCARD]