diff --git a/src/emulation/callbacks.ts b/src/emulation/callbacks.ts index a78b38124..087669f95 100644 --- a/src/emulation/callbacks.ts +++ b/src/emulation/callbacks.ts @@ -1,8 +1,8 @@ -import type { FSWatcher, StatOptions, symlink as _symlink } from 'fs'; +import type { BaseEncodingOptions, BufferEncodingOption, FSWatcher, StatOptions, symlink as _symlink } from 'fs'; import { ApiError, ErrorCode } from '../ApiError.js'; import { TwoArgCallback, NoArgCallback, ThreeArgCallback, FileContents } from '../filesystem.js'; import { BigIntStats, Stats } from '../stats.js'; -import { nop, normalizeMode } from './shared.js'; +import { nop, normalizeMode, PathLike } from './shared.js'; import * as promises from './promises.js'; import { R_OK } from './constants.js'; import { decode, encode } from '../utils.js'; @@ -16,7 +16,7 @@ import { Dirent } from './dir.js'; * @param newPath * @param callback */ -export function rename(oldPath: string, newPath: string, cb: NoArgCallback = nop): void { +export function rename(oldPath: PathLike, newPath: PathLike, cb: NoArgCallback = nop): void { promises .rename(oldPath, newPath) .then(() => cb()) @@ -33,7 +33,7 @@ export function rename(oldPath: string, newPath: string, cb: NoArgCallback = nop * @param path * @param callback */ -export function exists(path: string, cb: (exists: boolean) => unknown = nop): void { +export function exists(path: PathLike, cb: (exists: boolean) => unknown = nop): void { promises .exists(path) .then(cb) @@ -45,11 +45,11 @@ export function exists(path: string, cb: (exists: boolean) => unknown = nop): vo * @param path * @param callback */ -export function stat(path: string, callback: TwoArgCallback): void; -export function stat(path: string, options: StatOptions & { bigint?: false }, callback: TwoArgCallback): void; -export function stat(path: string, options: StatOptions & { bigint: true }, callback: TwoArgCallback): void; -export function stat(path: string, options: StatOptions, callback: TwoArgCallback): void; -export function stat(path: string, options?: StatOptions | TwoArgCallback, callback: TwoArgCallback | TwoArgCallback = nop): void { +export function stat(path: PathLike, callback: TwoArgCallback): void; +export function stat(path: PathLike, options: StatOptions & { bigint?: false }, callback: TwoArgCallback): void; +export function stat(path: PathLike, options: StatOptions & { bigint: true }, callback: TwoArgCallback): void; +export function stat(path: PathLike, options: StatOptions, callback: TwoArgCallback): void; +export function stat(path: PathLike, options?: StatOptions | TwoArgCallback, callback: TwoArgCallback | TwoArgCallback = nop): void { callback = typeof options == 'function' ? options : callback; promises .stat(path, typeof options != 'function' ? options : ({} as object)) @@ -64,11 +64,11 @@ export function stat(path: string, options?: StatOptions | TwoArgCallback * @param path * @param callback */ -export function lstat(path: string, callback: TwoArgCallback): void; -export function lstat(path: string, options: StatOptions & { bigint?: false }, callback: TwoArgCallback): void; -export function lstat(path: string, options: StatOptions & { bigint: true }, callback: TwoArgCallback): void; -export function lstat(path: string, options: StatOptions, callback: TwoArgCallback): void; -export function lstat(path: string, options?: StatOptions | TwoArgCallback, callback: TwoArgCallback | TwoArgCallback = nop): void { +export function lstat(path: PathLike, callback: TwoArgCallback): void; +export function lstat(path: PathLike, options: StatOptions & { bigint?: false }, callback: TwoArgCallback): void; +export function lstat(path: PathLike, options: StatOptions & { bigint: true }, callback: TwoArgCallback): void; +export function lstat(path: PathLike, options: StatOptions, callback: TwoArgCallback): void; +export function lstat(path: PathLike, options?: StatOptions | TwoArgCallback, callback: TwoArgCallback | TwoArgCallback = nop): void { callback = typeof options == 'function' ? options : callback; promises .lstat(path, typeof options != 'function' ? options : ({} as object)) @@ -82,9 +82,9 @@ export function lstat(path: string, options?: StatOptions | TwoArgCallback cb()) @@ -130,9 +130,9 @@ export function unlink(path: string, cb: NoArgCallback = nop): void { * @param mode defaults to `0644` * @param callback */ -export function open(path: string, flag: string, cb?: TwoArgCallback): void; -export function open(path: string, flag: string, mode: number | string, cb?: TwoArgCallback): void; -export function open(path: string, flag: string, arg2?: number | string | TwoArgCallback, cb: TwoArgCallback = nop): void { +export function open(path: PathLike, flag: string, cb?: TwoArgCallback): void; +export function open(path: PathLike, flag: string, mode: number | string, cb?: TwoArgCallback): void; +export function open(path: PathLike, flag: string, arg2?: number | string | TwoArgCallback, cb: TwoArgCallback = nop): void { const mode = normalizeMode(arg2, 0o644); cb = typeof arg2 === 'function' ? arg2 : cb; promises @@ -154,11 +154,11 @@ export function open(path: string, flag: string, arg2?: number | string | TwoArg * @option options [String] flag Defaults to `'r'`. * @param callback If no encoding is specified, then the raw buffer is returned. */ -export function readFile(filename: string, cb: TwoArgCallback): void; -export function readFile(filename: string, options: { flag?: string }, callback?: TwoArgCallback): void; -export function readFile(filename: string, options: { encoding: string; flag?: string }, callback?: TwoArgCallback): void; -export function readFile(filename: string, encoding: string, cb: TwoArgCallback): void; -export function readFile(filename: string, arg2: any = {}, cb: TwoArgCallback | TwoArgCallback = nop) { +export function readFile(filename: PathLike, cb: TwoArgCallback): void; +export function readFile(filename: PathLike, options: { flag?: string }, callback?: TwoArgCallback): void; +export function readFile(filename: PathLike, options: { encoding: string; flag?: string }, callback?: TwoArgCallback): void; +export function readFile(filename: PathLike, encoding: string, cb: TwoArgCallback): void; +export function readFile(filename: PathLike, arg2: any = {}, cb: TwoArgCallback | TwoArgCallback = nop) { cb = typeof arg2 === 'function' ? arg2 : cb; promises.readFile(filename, typeof arg2 === 'function' ? null : arg2); @@ -183,11 +183,11 @@ export function readFile(filename: string, arg2: any = {}, cb: TwoArgCallback cb()) @@ -436,7 +436,7 @@ export function rmdir(path: string, cb: NoArgCallback = nop): void { * @param mode defaults to `0777` * @param callback */ -export function mkdir(path: string, mode?: any, cb: NoArgCallback = nop): void { +export function mkdir(path: PathLike, mode?: any, cb: NoArgCallback = nop): void { promises .mkdir(path, mode) .then(() => cb()) @@ -450,10 +450,10 @@ export function mkdir(path: string, mode?: any, cb: NoArgCallback = nop): void { * @param path * @param callback */ -export function readdir(path: string, cb: TwoArgCallback): void; -export function readdir(path: string, options: { withFileTypes?: false }, cb: TwoArgCallback): void; -export function readdir(path: string, options: { withFileTypes: true }, cb: TwoArgCallback): void; -export function readdir(path: string, _options: { withFileTypes?: boolean } | TwoArgCallback, cb: TwoArgCallback | TwoArgCallback = nop): void { +export function readdir(path: PathLike, cb: TwoArgCallback): void; +export function readdir(path: PathLike, options: { withFileTypes?: false }, cb: TwoArgCallback): void; +export function readdir(path: PathLike, options: { withFileTypes: true }, cb: TwoArgCallback): void; +export function readdir(path: PathLike, _options: { withFileTypes?: boolean } | TwoArgCallback, cb: TwoArgCallback | TwoArgCallback = nop): void { cb = typeof _options == 'function' ? _options : cb; const options = typeof _options != 'function' ? _options : {}; promises @@ -469,7 +469,7 @@ export function readdir(path: string, _options: { withFileTypes?: boolean } | Tw * @param dstpath * @param callback */ -export function link(srcpath: string, dstpath: string, cb: NoArgCallback = nop): void { +export function link(srcpath: PathLike, dstpath: PathLike, cb: NoArgCallback = nop): void { promises .link(srcpath, dstpath) .then(() => cb()) @@ -483,9 +483,9 @@ export function link(srcpath: string, dstpath: string, cb: NoArgCallback = nop): * @param type can be either `'dir'` or `'file'` (default is `'file'`) * @param callback */ -export function symlink(srcpath: string, dstpath: string, cb?: NoArgCallback): void; -export function symlink(srcpath: string, dstpath: string, type?: _symlink.Type, cb?: NoArgCallback): void; -export function symlink(srcpath: string, dstpath: string, arg3?: _symlink.Type | NoArgCallback, cb: NoArgCallback = nop): void { +export function symlink(srcpath: PathLike, dstpath: PathLike, cb?: NoArgCallback): void; +export function symlink(srcpath: PathLike, dstpath: PathLike, type?: _symlink.Type, cb?: NoArgCallback): void; +export function symlink(srcpath: PathLike, dstpath: PathLike, arg3?: _symlink.Type | NoArgCallback, cb: NoArgCallback = nop): void { const type = typeof arg3 === 'string' ? arg3 : 'file'; cb = typeof arg3 === 'function' ? arg3 : cb; promises @@ -499,11 +499,16 @@ export function symlink(srcpath: string, dstpath: string, arg3?: _symlink.Type | * @param path * @param callback */ -export function readlink(path: string, cb: TwoArgCallback = nop): void { +export function readlink(path: PathLike, callback: TwoArgCallback & any): void; +export function readlink(path: PathLike, options: BufferEncodingOption, callback: TwoArgCallback): void; +export function readlink(path: PathLike, options: BaseEncodingOptions | string, callback: TwoArgCallback): void; +export function readlink(path: PathLike, options: BaseEncodingOptions | BufferEncoding, callback: TwoArgCallback): void; +export function readlink(path: PathLike, options: BufferEncodingOption | BaseEncodingOptions | string | TwoArgCallback, callback: TwoArgCallback | TwoArgCallback = nop): void { + callback = typeof options == 'function' ? options : callback; promises .readlink(path) - .then(result => cb(null, result)) - .catch(cb); + .then(result => callback(null, result as string & Uint8Array)) + .catch(callback); } /** @@ -513,7 +518,7 @@ export function readlink(path: string, cb: TwoArgCallback = nop): void { * @param gid * @param callback */ -export function chown(path: string, uid: number, gid: number, cb: NoArgCallback = nop): void { +export function chown(path: PathLike, uid: number, gid: number, cb: NoArgCallback = nop): void { promises .chown(path, uid, gid) .then(() => cb()) @@ -527,7 +532,7 @@ export function chown(path: string, uid: number, gid: number, cb: NoArgCallback * @param gid * @param callback */ -export function lchown(path: string, uid: number, gid: number, cb: NoArgCallback = nop): void { +export function lchown(path: PathLike, uid: number, gid: number, cb: NoArgCallback = nop): void { promises .lchown(path, uid, gid) .then(() => cb()) @@ -540,7 +545,7 @@ export function lchown(path: string, uid: number, gid: number, cb: NoArgCallback * @param mode * @param callback */ -export function chmod(path: string, mode: number | string, cb: NoArgCallback = nop): void { +export function chmod(path: PathLike, mode: number | string, cb: NoArgCallback = nop): void { promises .chmod(path, mode) .then(() => cb()) @@ -553,7 +558,7 @@ export function chmod(path: string, mode: number | string, cb: NoArgCallback = n * @param mode * @param callback */ -export function lchmod(path: string, mode: number | string, cb: NoArgCallback = nop): void { +export function lchmod(path: PathLike, mode: number | string, cb: NoArgCallback = nop): void { promises .lchmod(path, mode) .then(() => cb()) @@ -567,7 +572,7 @@ export function lchmod(path: string, mode: number | string, cb: NoArgCallback = * @param mtime * @param callback */ -export function utimes(path: string, atime: number | Date, mtime: number | Date, cb: NoArgCallback = nop): void { +export function utimes(path: PathLike, atime: number | Date, mtime: number | Date, cb: NoArgCallback = nop): void { promises .utimes(path, atime, mtime) .then(() => cb()) @@ -581,7 +586,7 @@ export function utimes(path: string, atime: number | Date, mtime: number | Date, * @param mtime * @param callback */ -export function lutimes(path: string, atime: number | Date, mtime: number | Date, cb: NoArgCallback = nop): void { +export function lutimes(path: PathLike, atime: number | Date, mtime: number | Date, cb: NoArgCallback = nop): void { promises .lutimes(path, atime, mtime) .then(() => cb()) @@ -593,22 +598,17 @@ export function lutimes(path: string, atime: number | Date, mtime: number | Date * `(err, resolvedPath)`. May use `process.cwd` to resolve relative paths. * * @example Usage example - * let cache = {'/etc':'/private/etc'}; - * fs.realpath('/etc/passwd', cache, function (err, resolvedPath) { + * fs.realpath('/etc/passwd', function (err, resolvedPath) { * if (err) throw err; * console.log(resolvedPath); * }); * * @param path - * @param cache An object literal of mapped paths that can be used to - * force a specific path resolution or avoid additional `fs.stat` calls for - * known real paths. * @param callback */ -export function realpath(path: string, cb?: TwoArgCallback): void; -export function realpath(path: string, cache: { [path: string]: string }, cb: TwoArgCallback): void; -export function realpath(path: string, arg2?: any, cb: TwoArgCallback = nop): void { - const cache = typeof arg2 === 'object' ? arg2 : {}; +export function realpath(path: PathLike, cb?: TwoArgCallback): void; +export function realpath(path: PathLike, options: BaseEncodingOptions, cb: TwoArgCallback): void; +export function realpath(path: PathLike, arg2?: TwoArgCallback | BaseEncodingOptions, cb: TwoArgCallback = nop): void { cb = typeof arg2 === 'function' ? arg2 : cb; promises .realpath(path, typeof arg2 === 'function' ? null : arg2) @@ -622,9 +622,9 @@ export function realpath(path: string, arg2?: any, cb: TwoArgCallback = * @param mode * @param callback */ -export function access(path: string, cb: NoArgCallback): void; -export function access(path: string, mode: number, cb: NoArgCallback): void; -export function access(path: string, arg2: any, cb: NoArgCallback = nop): void { +export function access(path: PathLike, cb: NoArgCallback): void; +export function access(path: PathLike, mode: number, cb: NoArgCallback): void; +export function access(path: PathLike, arg2: any, cb: NoArgCallback = nop): void { const mode = typeof arg2 === 'number' ? arg2 : R_OK; cb = typeof arg2 === 'function' ? arg2 : cb; promises @@ -633,24 +633,24 @@ export function access(path: string, arg2: any, cb: NoArgCallback = nop): void { .catch(cb); } -export function watchFile(filename: string, listener: (curr: Stats, prev: Stats) => void): void; -export function watchFile(filename: string, options: { persistent?: boolean; interval?: number }, listener: (curr: Stats, prev: Stats) => void): void; -export function watchFile(filename: string, arg2: any, listener: (curr: Stats, prev: Stats) => void = nop): void { +export function watchFile(filename: PathLike, listener: (curr: Stats, prev: Stats) => void): void; +export function watchFile(filename: PathLike, options: { persistent?: boolean; interval?: number }, listener: (curr: Stats, prev: Stats) => void): void; +export function watchFile(filename: PathLike, arg2: any, listener: (curr: Stats, prev: Stats) => void = nop): void { throw new ApiError(ErrorCode.ENOTSUP); } -export function unwatchFile(filename: string, listener: (curr: Stats, prev: Stats) => void = nop): void { +export function unwatchFile(filename: PathLike, listener: (curr: Stats, prev: Stats) => void = nop): void { throw new ApiError(ErrorCode.ENOTSUP); } -export function watch(filename: string, listener?: (event: string, filename: string) => any): FSWatcher; -export function watch(filename: string, options: { persistent?: boolean }, listener?: (event: string, filename: string) => any): FSWatcher; -export function watch(filename: string, arg2: any, listener: (event: string, filename: string) => any = nop): FSWatcher { +export function watch(filename: PathLike, listener?: (event: string, filename: string) => any): FSWatcher; +export function watch(filename: PathLike, options: { persistent?: boolean }, listener?: (event: string, filename: string) => any): FSWatcher; +export function watch(filename: PathLike, arg2: any, listener: (event: string, filename: string) => any = nop): FSWatcher { throw new ApiError(ErrorCode.ENOTSUP); } export function createReadStream( - path: string, + path: PathLike, options?: { flags?: string; encoding?: string; @@ -663,7 +663,7 @@ export function createReadStream( } export function createWriteStream( - path: string, + path: PathLike, options?: { flags?: string; encoding?: string; @@ -674,21 +674,21 @@ export function createWriteStream( throw new ApiError(ErrorCode.ENOTSUP); } -export function rm(path: string) { +export function rm(path: PathLike) { new ApiError(ErrorCode.ENOTSUP); } -export function mkdtemp(path: string) { +export function mkdtemp(path: PathLike) { new ApiError(ErrorCode.ENOTSUP); } -export function copyFile(src: string, dest: string, callback: NoArgCallback): void; -export function copyFile(src: string, dest: string, flags: number, callback: NoArgCallback): void; -export function copyFile(src: string, dest: string, flags: number | NoArgCallback, callback?: NoArgCallback): void { +export function copyFile(src: PathLike, dest: PathLike, callback: NoArgCallback): void; +export function copyFile(src: PathLike, dest: PathLike, flags: number, callback: NoArgCallback): void; +export function copyFile(src: PathLike, dest: PathLike, flags: number | NoArgCallback, callback?: NoArgCallback): void { new ApiError(ErrorCode.ENOTSUP); } -export function readv(path: string) { +export function readv(path: PathLike) { new ApiError(ErrorCode.ENOTSUP); } @@ -700,6 +700,6 @@ export function writev(fd: number, buffers: Uint8Array[], position: number | wri throw new ApiError(ErrorCode.ENOTSUP); } -export function opendir(path: string) { +export function opendir(path: PathLike) { throw new ApiError(ErrorCode.ENOTSUP); } diff --git a/src/emulation/fs.ts b/src/emulation/fs.ts index 2beed2a44..c360474c9 100644 --- a/src/emulation/fs.ts +++ b/src/emulation/fs.ts @@ -1,20 +1,7 @@ import * as fs_mock from './index.js'; import type * as fs_node from 'node:fs'; -/** - * fixes __promisify__ - */ -type FSMock = { - [K in keyof typeof fs_mock]: K extends keyof typeof fs_mock.promises - ? (typeof fs_mock.promises)[K] extends (...args) => Promise - ? (typeof fs_node)[K] extends { __promisify__(...args): unknown } - ? (typeof fs_mock)[K] & { __promisify__: (typeof fs_node)[K]['__promisify__'] } - : (typeof fs_mock)[K] - : (typeof fs_mock)[K] - : (typeof fs_mock)[K]; -}; - -const fs: typeof fs_node & typeof fs_mock = fs_mock as FSMock; +const fs = fs_mock as typeof fs_node & typeof fs_mock; export * from './index.js'; export default fs; diff --git a/src/emulation/promises.ts b/src/emulation/promises.ts index 4f7cc65a4..c5fb7d31e 100644 --- a/src/emulation/promises.ts +++ b/src/emulation/promises.ts @@ -1,11 +1,11 @@ -import type { ReadStream, WriteStream, FSWatcher, symlink as _symlink, StatOptions } from 'node:fs'; +import type { ReadStream, WriteStream, FSWatcher, symlink as _symlink, StatOptions, BaseEncodingOptions, BufferEncodingOption } from 'node:fs'; import { ApiError, ErrorCode } from '../ApiError.js'; import * as constants from './constants.js'; export { constants }; import { File, FileFlag } from '../file.js'; -import { normalizePath, normalizeMode, getFdForFile, normalizeOptions, fd2file, fdMap, normalizeTime, cred, nop, resolveFS, fixError, mounts } from './shared.js'; +import { normalizePath, normalizeMode, getFdForFile, normalizeOptions, fd2file, fdMap, normalizeTime, cred, nop, resolveFS, fixError, mounts, PathLike } from './shared.js'; import { FileContents, FileSystem } from '../filesystem.js'; import { BigIntStats, Stats } from '../stats.js'; import { decode, encode } from '../utils.js'; @@ -48,7 +48,7 @@ async function doOp>(...[na * @param oldPath * @param newPath */ -export async function rename(oldPath: string, newPath: string): Promise { +export async function rename(oldPath: PathLike, newPath: PathLike): Promise { oldPath = normalizePath(oldPath); newPath = normalizePath(newPath); const _old = resolveFS(oldPath); @@ -71,7 +71,7 @@ export async function rename(oldPath: string, newPath: string): Promise { * Test whether or not the given path exists by checking with the file system. * @param path */ -export async function exists(path: string): Promise { +export async function exists(path: PathLike): Promise { path = normalizePath(path); try { const { fs, path: resolvedPath } = resolveFS(path); @@ -90,9 +90,9 @@ export async function exists(path: string): Promise { * @param path * @returns Stats */ -export async function stat(path: string, options?: { bigint?: false }): Promise; -export async function stat(path: string, options: { bigint: true }): Promise; -export async function stat(path: string, options?: StatOptions): Promise { +export async function stat(path: PathLike, options?: { bigint?: false }): Promise; +export async function stat(path: PathLike, options: { bigint: true }): Promise; +export async function stat(path: PathLike, options?: StatOptions): Promise { const _stats: Stats = await doOp('stat', true, path, cred); let stats: Stats | BigIntStats = _stats; if (options?.bigint) { @@ -108,9 +108,9 @@ export async function stat(path: string, options?: StatOptions): Promise; -export async function lstat(path: string, options: { bigint: true }): Promise; -export async function lstat(path: string, options?: StatOptions): Promise { +export async function lstat(path: PathLike, options?: { bigint?: false }): Promise; +export async function lstat(path: PathLike, options: { bigint: true }): Promise; +export async function lstat(path: PathLike, options?: StatOptions): Promise { const _stats: Stats = await doOp('stat', false, path, cred); let stats: Stats | BigIntStats = _stats; if (options?.bigint) { @@ -126,7 +126,7 @@ export async function lstat(path: string, options?: StatOptions): Promise { +export async function truncate(path: PathLike, len: number = 0): Promise { if (len < 0) { throw new ApiError(ErrorCode.EINVAL); } @@ -137,7 +137,7 @@ export async function truncate(path: string, len: number = 0): Promise { * `unlink`. * @param path */ -export async function unlink(path: string): Promise { +export async function unlink(path: PathLike): Promise { return doOp('unlink', false, path, cred); } @@ -148,7 +148,7 @@ export async function unlink(path: string): Promise { * @param flags * @param mode defaults to `0644` */ -export async function open(path: string, flag: string, mode: number | string = 0o644): Promise { +export async function open(path: PathLike, flag: string, mode: number | string = 0o644): Promise { const file: File = await doOp('open', true, path, FileFlag.getFileFlag(flag), normalizeMode(mode, 0o644), cred); return getFdForFile(file); } @@ -157,14 +157,14 @@ export async function open(path: string, flag: string, mode: number | string = 0 * Synchronously reads the entire contents of a file. * @param filename * @param options - * @option options [String] encoding The string encoding for the file contents. Defaults to `null`. - * @option options [String] flag Defaults to `'r'`. - * @return [String | BrowserFS.node.Uint8Array] - */ -export async function readFile(filename: string, options?: { flag?: string }): Promise; -export async function readFile(filename: string, options: { encoding: string; flag?: string }): Promise; -export async function readFile(filename: string, encoding: string): Promise; -export async function readFile(filename: string, arg2 = {}): Promise { + * options.encoding The string encoding for the file contents. Defaults to `null`. + * options.flag Defaults to `'r'`. + * @return Uint8Array + */ +export async function readFile(filename: PathLike, options?: { flag?: string }): Promise; +export async function readFile(filename: PathLike, options: { encoding: string; flag?: string }): Promise; +export async function readFile(filename: PathLike, encoding: string): Promise; +export async function readFile(filename: PathLike, arg2 = {}): Promise { const options = normalizeOptions(arg2, null, 'r', null); const flag = FileFlag.getFileFlag(options.flag); if (!flag.isReadable()) { @@ -192,10 +192,10 @@ export async function readFile(filename: string, arg2 = {}): Promise; -export async function writeFile(filename: string, data: FileContents, encoding?: string): Promise; -export async function writeFile(filename: string, data: FileContents, options?: { encoding?: string; mode?: number | string; flag?: string } | string): Promise; -export async function writeFile(filename: string, data: FileContents, arg3?: { encoding?: string; mode?: number | string; flag?: string } | string): Promise { +export async function writeFile(filename: PathLike, data: FileContents, options?: { encoding?: string; mode?: number | string; flag?: string }): Promise; +export async function writeFile(filename: PathLike, data: FileContents, encoding?: string): Promise; +export async function writeFile(filename: PathLike, data: FileContents, options?: { encoding?: string; mode?: number | string; flag?: string } | string): Promise; +export async function writeFile(filename: PathLike, data: FileContents, arg3?: { encoding?: string; mode?: number | string; flag?: string } | string): Promise { const options = normalizeOptions(arg3, 'utf8', 'w', 0o644); const flag = FileFlag.getFileFlag(options.flag); if (!flag.isWriteable()) { @@ -224,9 +224,9 @@ export async function writeFile(filename: string, data: FileContents, arg3?: { e * @option options [Number] mode Defaults to `0644`. * @option options [String] flag Defaults to `'a'`. */ -export async function appendFile(filename: string, data: FileContents, options?: { encoding?: string; mode?: number | string; flag?: string }): Promise; -export async function appendFile(filename: string, data: FileContents, encoding?: string): Promise; -export async function appendFile(filename: string, data: FileContents, arg3?): Promise { +export async function appendFile(filename: PathLike, data: FileContents, options?: { encoding?: string; mode?: number | string; flag?: string }): Promise; +export async function appendFile(filename: PathLike, data: FileContents, encoding?: string): Promise; +export async function appendFile(filename: PathLike, data: FileContents, arg3?): Promise { const options = normalizeOptions(arg3, 'utf8', 'a', 0o644); const flag = FileFlag.getFileFlag(options.flag); if (!flag.isAppendable()) { @@ -398,7 +398,7 @@ export async function futimes(fd: number, atime: number | Date, mtime: number | * `rmdir`. * @param path */ -export async function rmdir(path: string): Promise { +export async function rmdir(path: PathLike): Promise { return doOp('rmdir', true, path, cred); } @@ -407,7 +407,7 @@ export async function rmdir(path: string): Promise { * @param path * @param mode defaults to `0777` */ -export async function mkdir(path: string, mode?: number | string): Promise { +export async function mkdir(path: PathLike, mode?: number | string): Promise { return doOp('mkdir', true, path, normalizeMode(mode, 0o777), cred); } @@ -415,9 +415,9 @@ export async function mkdir(path: string, mode?: number | string): Promise * `readdir`. Reads the contents of a directory. * @param path */ -export async function readdir(path: string, options: { withFileTypes?: false }): Promise; -export async function readdir(path: string, options: { withFileTypes: true }): Promise; -export async function readdir(path: string, options?: { withFileTypes?: boolean }): Promise { +export async function readdir(path: PathLike, options: { withFileTypes?: false }): Promise; +export async function readdir(path: PathLike, options: { withFileTypes: true }): Promise; +export async function readdir(path: PathLike, options?: { withFileTypes?: boolean }): Promise { path = normalizePath(path); const entries: string[] = await doOp('readdir', true, path, cred); const points = [...mounts.keys()]; @@ -445,7 +445,7 @@ export async function readdir(path: string, options?: { withFileTypes?: boolean * @param srcpath * @param dstpath */ -export async function link(srcpath: string, dstpath: string): Promise { +export async function link(srcpath: PathLike, dstpath: PathLike): Promise { dstpath = normalizePath(dstpath); return doOp('link', false, srcpath, dstpath, cred); } @@ -456,7 +456,7 @@ export async function link(srcpath: string, dstpath: string): Promise { * @param dstpath * @param type can be either `'dir'` or `'file'` (default is `'file'`) */ -export async function symlink(srcpath: string, dstpath: string, type: _symlink.Type = 'file'): Promise { +export async function symlink(srcpath: PathLike, dstpath: PathLike, type: _symlink.Type = 'file'): Promise { if (!['file', 'dir', 'junction'].includes(type)) { throw new ApiError(ErrorCode.EINVAL, 'Invalid type: ' + type); } @@ -467,10 +467,13 @@ export async function symlink(srcpath: string, dstpath: string, type: _symlink.T /** * readlink. * @param path - * @return [String] */ -export async function readlink(path: string): Promise { - return doOp('readlink', false, path, cred); +export async function readlink(path: PathLike, options?: BaseEncodingOptions | BufferEncoding): Promise; +export async function readlink(path: PathLike, options: BufferEncodingOption): Promise; +export async function readlink(path: PathLike, options?: BaseEncodingOptions | string): Promise; +export async function readlink(path: PathLike, options?: BufferEncodingOption | BaseEncodingOptions | string): Promise { + const value: string = await doOp('readlink', false, path, cred); + return encode(value, typeof options == 'object' ? options.encoding : options); } // PROPERTY OPERATIONS @@ -481,7 +484,7 @@ export async function readlink(path: string): Promise { * @param uid * @param gid */ -export async function chown(path: string, uid: number, gid: number): Promise { +export async function chown(path: PathLike, uid: number, gid: number): Promise { return doOp('chown', true, path, uid, gid, cred); } @@ -491,7 +494,7 @@ export async function chown(path: string, uid: number, gid: number): Promise { +export async function lchown(path: PathLike, uid: number, gid: number): Promise { return doOp('chown', false, path, uid, gid, cred); } @@ -500,7 +503,7 @@ export async function lchown(path: string, uid: number, gid: number): Promise { +export async function chmod(path: PathLike, mode: string | number): Promise { const numMode = normalizeMode(mode, -1); if (numMode < 0) { throw new ApiError(ErrorCode.EINVAL, `Invalid mode.`); @@ -513,7 +516,7 @@ export async function chmod(path: string, mode: string | number): Promise * @param path * @param mode */ -export async function lchmod(path: string, mode: number | string): Promise { +export async function lchmod(path: PathLike, mode: number | string): Promise { const numMode = normalizeMode(mode, -1); if (numMode < 1) { throw new ApiError(ErrorCode.EINVAL, `Invalid mode.`); @@ -527,7 +530,7 @@ export async function lchmod(path: string, mode: number | string): Promise * @param atime * @param mtime */ -export async function utimes(path: string, atime: number | Date, mtime: number | Date): Promise { +export async function utimes(path: PathLike, atime: number | Date, mtime: number | Date): Promise { return doOp('utimes', true, path, normalizeTime(atime), normalizeTime(mtime), cred); } @@ -537,19 +540,19 @@ export async function utimes(path: string, atime: number | Date, mtime: number | * @param atime * @param mtime */ -export async function lutimes(path: string, atime: number | Date, mtime: number | Date): Promise { +export async function lutimes(path: PathLike, atime: number | Date, mtime: number | Date): Promise { return doOp('utimes', false, path, normalizeTime(atime), normalizeTime(mtime), cred); } /** * `realpath`. * @param path - * @param cache An object literal of mapped paths that can be used to - * force a specific path resolution or avoid additional `fs.stat` calls for - * known real paths. - * @return [String] + * @param options + * @return resolved path + * + * Note: This *Can not* use doOp since doOp depends on it */ -export async function realpath(path: string, cache: { [path: string]: string } = {}): Promise { +export async function realpath(path: PathLike, options?: BaseEncodingOptions): Promise { path = normalizePath(path); const { fs, path: resolvedPath, mountPoint } = resolveFS(path); try { @@ -564,19 +567,19 @@ export async function realpath(path: string, cache: { [path: string]: string } = } } -export async function watchFile(filename: string, listener: (curr: Stats, prev: Stats) => void): Promise; -export async function watchFile(filename: string, options: { persistent?: boolean; interval?: number }, listener: (curr: Stats, prev: Stats) => void): Promise; -export async function watchFile(filename: string, arg2: any, listener: (curr: Stats, prev: Stats) => void = nop): Promise { +export async function watchFile(filename: PathLike, listener: (curr: Stats, prev: Stats) => void): Promise; +export async function watchFile(filename: PathLike, options: { persistent?: boolean; interval?: number }, listener: (curr: Stats, prev: Stats) => void): Promise; +export async function watchFile(filename: PathLike, arg2: any, listener: (curr: Stats, prev: Stats) => void = nop): Promise { throw new ApiError(ErrorCode.ENOTSUP); } -export async function unwatchFile(filename: string, listener: (curr: Stats, prev: Stats) => void = nop): Promise { +export async function unwatchFile(filename: PathLike, listener: (curr: Stats, prev: Stats) => void = nop): Promise { throw new ApiError(ErrorCode.ENOTSUP); } -export async function watch(filename: string, listener?: (event: string, filename: string) => any): Promise; -export async function watch(filename: string, options: { persistent?: boolean }, listener?: (event: string, filename: string) => any): Promise; -export async function watch(filename: string, arg2: any, listener: (event: string, filename: string) => any = nop): Promise { +export async function watch(filename: PathLike, listener?: (event: string, filename: PathLike) => any): Promise; +export async function watch(filename: PathLike, options: { persistent?: boolean }, listener?: (event: string, filename: string) => any): Promise; +export async function watch(filename: PathLike, arg2: any, listener: (event: string, filename: string) => any = nop): Promise { throw new ApiError(ErrorCode.ENOTSUP); } @@ -585,12 +588,12 @@ export async function watch(filename: string, arg2: any, listener: (event: strin * @param path * @param mode */ -export async function access(path: string, mode: number = 0o600): Promise { +export async function access(path: PathLike, mode: number = 0o600): Promise { return doOp('access', true, path, mode, cred); } export async function createReadStream( - path: string, + path: PathLike, options?: { flags?: string; encoding?: string; @@ -603,7 +606,7 @@ export async function createReadStream( } export async function createWriteStream( - path: string, + path: PathLike, options?: { flags?: string; encoding?: string; @@ -614,14 +617,14 @@ export async function createWriteStream( throw new ApiError(ErrorCode.ENOTSUP); } -export async function rm(path: string) { +export async function rm(path: PathLike) { throw new ApiError(ErrorCode.ENOTSUP); } -export async function mkdtemp(path: string) { +export async function mkdtemp(path: PathLike) { throw new ApiError(ErrorCode.ENOTSUP); } -export async function copyFile(path: string) { +export async function copyFile(path: PathLike) { throw new ApiError(ErrorCode.ENOTSUP); } diff --git a/src/emulation/shared.ts b/src/emulation/shared.ts index 838bdeb9b..b8716ab3c 100644 --- a/src/emulation/shared.ts +++ b/src/emulation/shared.ts @@ -220,3 +220,24 @@ export function initialize(mountMapping: MountMapping): void { mount(point, fs); } } + +/** + * Types supports as path parameters. + * + * In the future, maybe support URL? + */ +export type PathLike = string; + +/** + * @internal + * + * Converts any Buffer in T to Uint8Array + */ +export type BufferToUint8Array = T extends Buffer + ? Uint8Array + : T extends (...args) => unknown + ? (...args: Parameters) => BufferToUint8Array> + : T extends object + ? { [K in keyof T]: BufferToUint8Array } + : T; + diff --git a/src/emulation/sync.ts b/src/emulation/sync.ts index 2fc418332..a610878e0 100644 --- a/src/emulation/sync.ts +++ b/src/emulation/sync.ts @@ -2,10 +2,25 @@ import { ApiError, ErrorCode } from '../ApiError.js'; import { File, FileFlag } from '../file.js'; import { FileContents, FileSystem } from '../filesystem.js'; import { BigIntStats, Stats } from '../stats.js'; -import type { symlink, ReadSyncOptions, StatOptions } from 'fs'; -import { normalizePath, cred, getFdForFile, normalizeMode, normalizeOptions, fdMap, fd2file, normalizeTime, resolveFS, fixError, mounts } from './shared.js'; +import type { symlink, ReadSyncOptions, StatOptions, BaseEncodingOptions, BufferEncodingOption } from 'fs'; +import type * as Node from 'fs'; +import { + normalizePath, + cred, + getFdForFile, + normalizeMode, + normalizeOptions, + fdMap, + fd2file, + normalizeTime, + resolveFS, + fixError, + mounts, + BufferToUint8Array, + PathLike, +} from './shared.js'; import { decode, encode } from '../utils.js'; -import { Dirent } from './dir.js'; +import { Dir, Dirent } from './dir.js'; import { join } from './path.js'; type FileSystemMethod = { @@ -30,7 +45,7 @@ function doOp>(...[name, re * @param oldPath * @param newPath */ -export function renameSync(oldPath: string, newPath: string): void { +export function renameSync(oldPath: PathLike, newPath: PathLike): void { oldPath = normalizePath(oldPath); newPath = normalizePath(newPath); const _old = resolveFS(oldPath); @@ -48,12 +63,13 @@ export function renameSync(oldPath: string, newPath: string): void { throw fixError(e, paths); } } +renameSync satisfies typeof Node.renameSync; /** * Test whether or not the given path exists by checking with the file system. * @param path */ -export function existsSync(path: string): boolean { +export function existsSync(path: PathLike): boolean { path = normalizePath(path); try { const { fs, path: resolvedPath } = resolveFS(path); @@ -66,15 +82,16 @@ export function existsSync(path: string): boolean { throw e; } } +existsSync satisfies typeof Node.existsSync; /** * Synchronous `stat`. * @param path * @returns Stats */ -export function statSync(path: string, options?: { bigint: false }): Stats; -export function statSync(path: string, options: { bigint: true }): BigIntStats; -export function statSync(path: string, options?: StatOptions): Stats | BigIntStats { +export function statSync(path: PathLike, options?: { bigint: false }): Stats; +export function statSync(path: PathLike, options: { bigint: true }): BigIntStats; +export function statSync(path: PathLike, options?: StatOptions): Stats | BigIntStats { const _stats: Stats = doOp('statSync', true, path, cred); let stats: Stats | BigIntStats = _stats; if (options?.bigint) { @@ -82,6 +99,7 @@ export function statSync(path: string, options?: StatOptions): Stats | BigIntSta } return stats; } +statSync satisfies typeof Node.statSync; /** * Synchronous `lstat`. @@ -89,9 +107,9 @@ export function statSync(path: string, options?: StatOptions): Stats | BigIntSta * then the link itself is stat-ed, not the file that it refers to. * @param path */ -export function lstatSync(path: string, options?: { bigint: false }): Stats; -export function lstatSync(path: string, options: { bigint: true }): BigIntStats; -export function lstatSync(path: string, options?: StatOptions): Stats | BigIntStats { +export function lstatSync(path: PathLike, options?: { bigint: false }): Stats; +export function lstatSync(path: PathLike, options: { bigint: true }): BigIntStats; +export function lstatSync(path: PathLike, options?: StatOptions): Stats | BigIntStats { const _stats: Stats = doOp('statSync', false, path, cred); let stats: Stats | BigIntStats = _stats; if (options?.bigint) { @@ -99,26 +117,29 @@ export function lstatSync(path: string, options?: StatOptions): Stats | BigIntSt } return stats; } +lstatSync satisfies typeof Node.lstatSync; /** * Synchronous `truncate`. * @param path * @param len */ -export function truncateSync(path: string, len: number = 0): void { +export function truncateSync(path: PathLike, len: number = 0): void { if (len < 0) { throw new ApiError(ErrorCode.EINVAL); } return doOp('truncateSync', true, path, len, cred); } +truncateSync satisfies typeof Node.truncateSync; /** * Synchronous `unlink`. * @param path */ -export function unlinkSync(path: string): void { +export function unlinkSync(path: PathLike): void { return doOp('unlinkSync', false, path, cred); } +unlinkSync satisfies typeof Node.unlinkSync; /** * Synchronous file open. @@ -126,20 +147,21 @@ export function unlinkSync(path: string): void { * @param path * @param flags * @param mode defaults to `0644` - * @return [BrowserFS.File] + * @returns file descriptor */ -export function openSync(path: string, flag: string, mode: number | string = 0o644): number { +export function openSync(path: PathLike, flag: string, mode: number | string = 0o644): number { const file: File = doOp('openSync', true, path, FileFlag.getFileFlag(flag), normalizeMode(mode, 0o644), cred); return getFdForFile(file); } +openSync satisfies typeof Node.openSync; /** * Synchronously reads the entire contents of a file. * @param filename * @param options - * @option options [String] encoding The string encoding for the file contents. Defaults to `null`. - * @option options [String] flag Defaults to `'r'`. - * @return [String | BrowserFS.node.Uint8Array] + * @option options encoding The string encoding for the file contents. Defaults to `null`. + * @option options flag Defaults to `'r'`. + * @returns file contents */ export function readFileSync(filename: string, options?: { flag?: string }): Uint8Array; export function readFileSync(filename: string, options: { encoding: string; flag?: string }): string; @@ -159,6 +181,7 @@ export function readFileSync(filename: string, arg2: { encoding: string; flag?: return data; } } +readFileSync satisfies BufferToUint8Array; /** * Synchronously writes data to a file, replacing the file if it already @@ -168,13 +191,13 @@ export function readFileSync(filename: string, arg2: { encoding: string; flag?: * @param filename * @param data * @param options - * @option options [String] encoding Defaults to `'utf8'`. - * @option options [Number] mode Defaults to `0644`. - * @option options [String] flag Defaults to `'w'`. + * @option options encoding Defaults to `'utf8'`. + * @option options mode Defaults to `0644`. + * @option options flag Defaults to `'w'`. */ -export function writeFileSync(filename: string, data: FileContents, options?: { encoding?: string; mode?: number | string; flag?: string }): void; +export function writeFileSync(filename: string, data: FileContents, options?: Node.WriteFileOptions): void; export function writeFileSync(filename: string, data: FileContents, encoding?: string): void; -export function writeFileSync(filename: string, data: FileContents, arg3?: { encoding?: string; mode?: number | string; flag?: string } | string): void { +export function writeFileSync(filename: string, data: FileContents, arg3?: Node.WriteFileOptions | string): void { const options = normalizeOptions(arg3, 'utf8', 'w', 0o644); const flag = FileFlag.getFileFlag(options.flag); if (!flag.isWriteable()) { @@ -186,6 +209,7 @@ export function writeFileSync(filename: string, data: FileContents, arg3?: { enc const encodedData = typeof data == 'string' ? encode(data) : data; return doOp('writeFileSync', true, filename, encodedData, flag, options.mode, cred); } +writeFileSync satisfies typeof Node.writeFileSync; /** * Asynchronously append data to a file, creating the file if it not yet @@ -199,13 +223,13 @@ export function writeFileSync(filename: string, data: FileContents, arg3?: { enc * @param filename * @param data * @param options - * @option options [String] encoding Defaults to `'utf8'`. - * @option options [Number] mode Defaults to `0644`. - * @option options [String] flag Defaults to `'a'`. + * @option options encoding Defaults to `'utf8'`. + * @option options mode Defaults to `0644`. + * @option options flag Defaults to `'a'`. */ -export function appendFileSync(filename: string, data: FileContents, options?: { encoding?: string; mode?: number | string; flag?: string }): void; +export function appendFileSync(filename: string, data: FileContents, options?: Node.WriteFileOptions): void; export function appendFileSync(filename: string, data: FileContents, encoding?: string): void; -export function appendFileSync(filename: string, data: FileContents, arg3?: { encoding?: string; mode?: number | string; flag?: string } | string): void { +export function appendFileSync(filename: string, data: FileContents, arg3?: Node.WriteFileOptions | string): void { const options = normalizeOptions(arg3, 'utf8', 'a', 0o644); const flag = FileFlag.getFileFlag(options.flag); if (!flag.isAppendable()) { @@ -217,6 +241,7 @@ export function appendFileSync(filename: string, data: FileContents, arg3?: { en const encodedData = typeof data == 'string' ? encode(data) : data; return doOp('appendFileSync', true, filename, encodedData, flag, options.mode, cred); } +appendFileSync satisfies typeof Node.appendFileSync; /** * Synchronous `fstat`. @@ -234,6 +259,7 @@ export function fstatSync(fd: number, options?: StatOptions): Stats | BigIntStat } return stats; } +fstatSync satisfies typeof Node.fstatSync; /** * Synchronous close. @@ -243,6 +269,7 @@ export function closeSync(fd: number): void { fd2file(fd).closeSync(); fdMap.delete(fd); } +closeSync satisfies typeof Node.closeSync; /** * Synchronous ftruncate. @@ -256,6 +283,7 @@ export function ftruncateSync(fd: number, len: number = 0): void { } file.truncateSync(len); } +ftruncateSync satisfies typeof Node.ftruncateSync; /** * Synchronous fsync. @@ -264,6 +292,7 @@ export function ftruncateSync(fd: number, len: number = 0): void { export function fsyncSync(fd: number): void { fd2file(fd).syncSync(); } +fsyncSync satisfies typeof Node.fsyncSync; /** * Synchronous fdatasync. @@ -272,6 +301,7 @@ export function fsyncSync(fd: number): void { export function fdatasyncSync(fd: number): void { fd2file(fd).datasyncSync(); } +fdatasyncSync satisfies typeof Node.fdatasyncSync; /** * Write buffer to the file specified by `fd`. @@ -314,6 +344,7 @@ export function writeSync(fd: number, arg2: Uint8Array | string, arg3?: number, } return file.writeSync(buffer, offset, length, position); } +writeSync satisfies typeof Node.writeSync; /** * Read data from the file specified by `fd`. @@ -342,6 +373,7 @@ export function readSync(fd: number, buffer: Uint8Array, opts?: ReadSyncOptions return file.readSync(buffer, offset, length, position); } +readSync satisfies typeof Node.readSync; /** * Synchronous `fchown`. @@ -352,6 +384,7 @@ export function readSync(fd: number, buffer: Uint8Array, opts?: ReadSyncOptions export function fchownSync(fd: number, uid: number, gid: number): void { fd2file(fd).chownSync(uid, gid); } +fchownSync satisfies typeof Node.fchownSync; /** * Synchronous `fchmod`. @@ -362,6 +395,7 @@ export function fchmodSync(fd: number, mode: number | string): void { const numMode = typeof mode === 'string' ? parseInt(mode, 8) : mode; fd2file(fd).chmodSync(numMode); } +fchmodSync satisfies typeof Node.fchmodSync; /** * Change the file timestamps of a file referenced by the supplied file @@ -373,6 +407,7 @@ export function fchmodSync(fd: number, mode: number | string): void { export function futimesSync(fd: number, atime: number | Date, mtime: number | Date): void { fd2file(fd).utimesSync(normalizeTime(atime), normalizeTime(mtime)); } +futimesSync satisfies typeof Node.futimesSync; // DIRECTORY-ONLY METHODS @@ -380,41 +415,60 @@ export function futimesSync(fd: number, atime: number | Date, mtime: number | Da * Synchronous `rmdir`. * @param path */ -export function rmdirSync(path: string): void { +export function rmdirSync(path: PathLike): void { return doOp('rmdirSync', true, path, cred); } +rmdirSync satisfies typeof Node.rmdirSync; /** * Synchronous `mkdir`. * @param path - * @param mode defaults to `0777` - */ -export function mkdirSync(path: string, mode?: number | string): void { + * @param mode defaults to o777 + * @todo Implement recursion + */ +export function mkdirSync(path: PathLike, options: Node.MakeDirectoryOptions & { recursive: true }): string; +export function mkdirSync(path: PathLike, options?: Node.Mode | (Node.MakeDirectoryOptions & { recursive?: false })): void; +export function mkdirSync(path: PathLike, options?: Node.Mode | Node.MakeDirectoryOptions): string | void { + const mode: Node.Mode = typeof options == 'number' || typeof options == 'string' ? options : options?.mode; + const recursive = typeof options == 'object' && options?.recursive; doOp('mkdirSync', true, path, normalizeMode(mode, 0o777), cred); } +mkdirSync satisfies typeof Node.mkdirSync; /** * Synchronous `readdir`. Reads the contents of a directory. * @param path */ -export function readdirSync(path: string, options: { withFileTypes: false }): string[]; -export function readdirSync(path: string, options: { withFileTypes: true }): Dirent[]; -export function readdirSync(path: string, options?: { withFileTypes?: boolean }): string[] | Dirent[] { +export function readdirSync(path: PathLike, options: { encoding?: BufferEncoding; withFileTypes?: false } | BufferEncoding): string[]; +export function readdirSync(path: PathLike, options: { encoding: 'buffer'; withFileTypes?: false } | 'buffer'): Uint8Array[]; +export function readdirSync(path: PathLike, options: { withFileTypes: true }): Dirent[]; +export function readdirSync(path: PathLike, options?: { encoding?: BufferEncoding | 'buffer'; withFileTypes?: boolean } | string): string[] | Dirent[] | Uint8Array[] { path = normalizePath(path); const entries: string[] = doOp('readdirSync', true, path, cred); - const points = [...mounts.keys()]; - for (const point of points) { - if (point.startsWith(path)) { - const entry = point.slice(path.length); - if (entry.includes('/') || entry.length == 0) { - // ignore FSs mounted in subdirectories and any FS mounted to `path`. - continue; - } - entries.push(entry); + for (const mount of mounts.keys()) { + if (!mount.startsWith(path)) { + continue; + } + const entry = mount.slice(path.length); + if (entry.includes('/') || entry.length == 0) { + // ignore FSs mounted in subdirectories and any FS mounted to `path`. + continue; } + entries.push(entry); } - return options?.withFileTypes ? entries.map(entry => new Dirent(entry, statSync(join(path, entry)))) : entries; + return entries.map((entry: string) => { + if (typeof options == 'object' && options?.withFileTypes) { + return new Dirent(entry, statSync(join(path, entry))); + } + + if (options == 'buffer' || (typeof options == 'object' && options.encoding == 'buffer')) { + return encode(entry); + } + + return entry; + }); } +readdirSync satisfies BufferToUint8Array; // SYMLINK METHODS @@ -423,10 +477,11 @@ export function readdirSync(path: string, options?: { withFileTypes?: boolean }) * @param srcpath * @param dstpath */ -export function linkSync(srcpath: string, dstpath: string): void { +export function linkSync(srcpath: PathLike, dstpath: PathLike): void { dstpath = normalizePath(dstpath); return doOp('linkSync', false, srcpath, dstpath, cred); } +linkSync satisfies typeof Node.linkSync; /** * Synchronous `symlink`. @@ -434,22 +489,26 @@ export function linkSync(srcpath: string, dstpath: string): void { * @param dstpath * @param type can be either `'dir'` or `'file'` (default is `'file'`) */ -export function symlinkSync(srcpath: string, dstpath: string, type?: symlink.Type): void { +export function symlinkSync(srcpath: PathLike, dstpath: PathLike, type?: symlink.Type): void { if (!['file', 'dir', 'junction'].includes(type)) { throw new ApiError(ErrorCode.EINVAL, 'Invalid type: ' + type); } dstpath = normalizePath(dstpath); return doOp('symlinkSync', false, srcpath, dstpath, type, cred); } +symlinkSync satisfies typeof Node.symlinkSync; /** * Synchronous readlink. * @param path - * @return [String] */ -export function readlinkSync(path: string): string { - return doOp('readlinkSync', false, path, cred); +export function readlinkSync(path: PathLike, options?: BufferEncodingOption): Uint8Array; +export function readlinkSync(path: PathLike, options: BaseEncodingOptions | BufferEncoding): string; +export function readlinkSync(path: PathLike, options?: BaseEncodingOptions | string | BufferEncodingOption): Uint8Array | string { + const value: string = doOp('readlinkSync', false, path, cred); + return encode(value, typeof options == 'object' ? options.encoding : options); } +readlinkSync satisfies BufferToUint8Array; // PROPERTY OPERATIONS @@ -459,9 +518,10 @@ export function readlinkSync(path: string): string { * @param uid * @param gid */ -export function chownSync(path: string, uid: number, gid: number): void { +export function chownSync(path: PathLike, uid: number, gid: number): void { doOp('chownSync', true, path, uid, gid, cred); } +chownSync satisfies typeof Node.chownSync; /** * Synchronous `lchown`. @@ -469,35 +529,38 @@ export function chownSync(path: string, uid: number, gid: number): void { * @param uid * @param gid */ -export function lchownSync(path: string, uid: number, gid: number): void { +export function lchownSync(path: PathLike, uid: number, gid: number): void { doOp('chownSync', false, path, uid, gid, cred); } +lchownSync satisfies typeof Node.lchownSync; /** * Synchronous `chmod`. * @param path * @param mode */ -export function chmodSync(path: string, mode: string | number): void { +export function chmodSync(path: PathLike, mode: string | number): void { const numMode = normalizeMode(mode, -1); if (numMode < 0) { throw new ApiError(ErrorCode.EINVAL, `Invalid mode.`); } doOp('chmodSync', true, path, numMode, cred); } +chmodSync satisfies typeof Node.chmodSync; /** * Synchronous `lchmod`. * @param path * @param mode */ -export function lchmodSync(path: string, mode: number | string): void { +export function lchmodSync(path: PathLike, mode: number | string): void { const numMode = normalizeMode(mode, -1); if (numMode < 1) { throw new ApiError(ErrorCode.EINVAL, `Invalid mode.`); } doOp('chmodSync', false, path, numMode, cred); } +lchmodSync satisfies typeof Node.lchmodSync; /** * Change file timestamps of the file referenced by the supplied path. @@ -505,9 +568,10 @@ export function lchmodSync(path: string, mode: number | string): void { * @param atime * @param mtime */ -export function utimesSync(path: string, atime: number | Date, mtime: number | Date): void { +export function utimesSync(path: PathLike, atime: number | Date, mtime: number | Date): void { doOp('utimesSync', true, path, normalizeTime(atime), normalizeTime(mtime), cred); } +utimesSync satisfies typeof Node.utimesSync; /** * Change file timestamps of the file referenced by the supplied path. @@ -515,9 +579,10 @@ export function utimesSync(path: string, atime: number | Date, mtime: number | D * @param atime * @param mtime */ -export function lutimesSync(path: string, atime: number | Date, mtime: number | Date): void { +export function lutimesSync(path: PathLike, atime: number | Date, mtime: number | Date): void { doOp('utimesSync', false, path, normalizeTime(atime), normalizeTime(mtime), cred); } +lutimesSync satisfies typeof Node.lutimesSync; /** * Synchronous `realpath`. @@ -525,9 +590,11 @@ export function lutimesSync(path: string, atime: number | Date, mtime: number | * @param cache An object literal of mapped paths that can be used to * force a specific path resolution or avoid additional `fs.stat` calls for * known real paths. - * @return [String] + * @returns the real path */ -export function realpathSync(path: string, cache: { [path: string]: string } = {}): string { +export function realpathSync(path: PathLike, options: BufferEncodingOption): Uint8Array; +export function realpathSync(path: PathLike, options?: BaseEncodingOptions | BufferEncoding): string; +export function realpathSync(path: PathLike, options?: BaseEncodingOptions | BufferEncoding | BufferEncodingOption): string | Uint8Array { path = normalizePath(path); const { fs, path: resolvedPath, mountPoint } = resolveFS(path); try { @@ -541,36 +608,46 @@ export function realpathSync(path: string, cache: { [path: string]: string } = { throw fixError(e, { [resolvedPath]: path }); } } +realpathSync satisfies BufferToUint8Array; /** * Synchronous `access`. * @param path * @param mode */ -export function accessSync(path: string, mode: number = 0o600): void { +export function accessSync(path: PathLike, mode: number = 0o600): void { return doOp('accessSync', true, path, mode, cred); } +accessSync satisfies typeof Node.accessSync; -export function rmSync(path: string) { +export function rmSync(path: PathLike) { throw new ApiError(ErrorCode.ENOTSUP); } +rmSync satisfies typeof Node.rmSync; -export function mkdtempSync(path: string) { +export function mkdtempSync(prefix: string, options: BufferEncodingOption): Uint8Array; +export function mkdtempSync(prefix: string, options?: BaseEncodingOptions | BufferEncoding): string; +export function mkdtempSync(prefix: string, options?: BaseEncodingOptions | BufferEncoding | BufferEncodingOption): string | Uint8Array { throw new ApiError(ErrorCode.ENOTSUP); } +mkdtempSync satisfies BufferToUint8Array; export function copyFileSync(src: string, dest: string, flags?: number): void { throw new ApiError(ErrorCode.ENOTSUP); } +copyFileSync satisfies typeof Node.copyFileSync; -export function readvSync(path: string) { +export function readvSync(fd: number, buffers: readonly Uint8Array[], position?: number): number { throw new ApiError(ErrorCode.ENOTSUP); } +readvSync satisfies typeof Node.readvSync; -export function writevSync(path: string) { +export function writevSync(fd: number, buffers: readonly Uint8Array[], position?: number): number { throw new ApiError(ErrorCode.ENOTSUP); } +writevSync satisfies typeof Node.writevSync; -export function opendirSync(path: string) { +export function opendirSync(path: PathLike, options?: Node.OpenDirOptions): Dir { throw new ApiError(ErrorCode.ENOTSUP); } +opendirSync satisfies typeof Node.opendirSync; diff --git a/src/utils.ts b/src/utils.ts index f953bf980..56a9931f9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -253,7 +253,7 @@ const decoderCache: Map = new Map(); const textEncoder = new globalThis.TextEncoder(); -export function encode(input?: string, encoding = 'utf8'): Uint8Array { +export function encode(input: string, encoding = 'utf8'): Uint8Array { return textEncoder.encode(input); }