diff --git a/src/fs.ts b/src/fs.ts index 12ff2077..cb0b862a 100644 --- a/src/fs.ts +++ b/src/fs.ts @@ -1,8 +1,6 @@ -// promisify ourselves, because older nodes don't have fs.promises - import fs, { Dirent } from 'fs' +import fsPromises from 'fs/promises'; -// sync ones just take the sync version from node export { chmodSync, mkdirSync, @@ -14,79 +12,20 @@ export { unlinkSync, } from 'fs' -import { readdirSync as rdSync } from 'fs' export const readdirSync = (path: fs.PathLike): Dirent[] => - rdSync(path, { withFileTypes: true }) - -// unrolled for better inlining, this seems to get better performance -// than something like: -// const makeCb = (res, rej) => (er, ...d) => er ? rej(er) : res(...d) -// which would be a bit cleaner. - -const chmod = (path: fs.PathLike, mode: fs.Mode): Promise => - new Promise((res, rej) => - fs.chmod(path, mode, (er, ...d: any[]) => (er ? rej(er) : res(...d))), - ) - -const mkdir = ( - path: fs.PathLike, - options?: - | fs.Mode - | (fs.MakeDirectoryOptions & { recursive?: boolean | null }) - | undefined - | null, -): Promise => - new Promise((res, rej) => - fs.mkdir(path, options, (er, made) => (er ? rej(er) : res(made))), - ) + fs.readdirSync(path, { withFileTypes: true }) const readdir = (path: fs.PathLike): Promise => - new Promise((res, rej) => - fs.readdir(path, { withFileTypes: true }, (er, data) => - er ? rej(er) : res(data), - ), - ) - -const rename = (oldPath: fs.PathLike, newPath: fs.PathLike): Promise => - new Promise((res, rej) => - fs.rename(oldPath, newPath, (er, ...d: any[]) => - er ? rej(er) : res(...d), - ), - ) - -const rm = (path: fs.PathLike, options: fs.RmOptions): Promise => - new Promise((res, rej) => - fs.rm(path, options, (er, ...d: any[]) => (er ? rej(er) : res(...d))), - ) - -const rmdir = (path: fs.PathLike): Promise => - new Promise((res, rej) => - fs.rmdir(path, (er, ...d: any[]) => (er ? rej(er) : res(...d))), - ) - -const stat = (path: fs.PathLike): Promise => - new Promise((res, rej) => - fs.stat(path, (er, data) => (er ? rej(er) : res(data))), - ) - -const lstat = (path: fs.PathLike): Promise => - new Promise((res, rej) => - fs.lstat(path, (er, data) => (er ? rej(er) : res(data))), - ) - -const unlink = (path: fs.PathLike): Promise => - new Promise((res, rej) => - fs.unlink(path, (er, ...d: any[]) => (er ? rej(er) : res(...d))), - ) + fsPromises.readdir(path, { withFileTypes: true }) export const promises = { - chmod, - mkdir, + chmod: fsPromises.chmod, + mkdir: fsPromises.mkdir, readdir, - rename, - rm, - rmdir, - stat, - lstat, - unlink, + rename: fsPromises.rename, + rm: fsPromises.rm, + rmdir: fsPromises.rmdir, + stat: fsPromises.stat, + lstat: fsPromises.lstat, + unlink: fsPromises.unlink, } diff --git a/test/fs.ts b/test/fs.ts index c196ebb7..6989385d 100644 --- a/test/fs.ts +++ b/test/fs.ts @@ -5,6 +5,7 @@ import t from 'tap' // and that when the cb returns an error, the promised version fails, // and when the cb returns data, the promisified version resolves to it. import realFS from 'fs' +import realFSPromises from 'fs/promises' import * as fs from '../dist/esm/fs.js' const mockFSMethodPass = @@ -13,17 +14,27 @@ const mockFSMethodPass = const cb = args.pop() process.nextTick(() => cb(null, method, 1, 2, 3)) } +const mockFSPromiseMethodPass = (method: string) => () => + new Promise(resolve => { + resolve(method) + }) const mockFSMethodFail = (method: string) => (...args: any[]) => { const cb = args.pop() process.nextTick(() => cb(new Error('oops'), method, 1, 2, 3)) } +const mockFSPromiseMethodFail = (_: string) => () => + new Promise((_, reject) => { + reject(new Error('oops')) + }) import { useNative } from '../dist/esm/use-native.js' t.type(fs.promises, Object) const mockFSPass: Record any> = {} const mockFSFail: Record any> = {} +const mockFSPromisesPass: { [k: string]: (...a: any[]) => Promise } = {} +const mockFSPromisesFail: { [k: string]: (...a: any[]) => Promise } = {} for (const method of Object.keys( fs.promises as Record any>, @@ -49,7 +60,9 @@ for (const method of Object.keys( // set up our pass/fails for the next tests mockFSPass[method] = mockFSMethodPass(method) + mockFSPromisesPass[method] = mockFSPromiseMethodPass(method) mockFSFail[method] = mockFSMethodFail(method) + mockFSPromisesFail[method] = mockFSPromiseMethodFail(method) } // doesn't have any sync versions that aren't promisified @@ -68,6 +81,7 @@ for (const method of Object.keys(fs)) { t.test('passing resolves promise', async t => { const fs = (await t.mockImport('../src/fs.js', { fs: { ...realFS, ...mockFSPass }, + 'fs/promises': { ...realFSPromises, ...mockFSPromisesPass }, })) as typeof import('../src/fs.js') for (const [m, fn] of Object.entries( fs.promises as { [k: string]: (...a: any) => Promise }, @@ -79,6 +93,7 @@ t.test('passing resolves promise', async t => { t.test('failing rejects promise', async t => { const fs = (await t.mockImport('../src/fs.js', { fs: { ...realFS, ...mockFSFail }, + 'fs/promises': { ...realFSPromises, ...mockFSPromisesFail }, })) as typeof import('../src/fs.js') for (const [m, fn] of Object.entries( fs.promises as { [k: string]: (...a: any[]) => Promise },