Skip to content

Commit

Permalink
refactor: switch to native fs.promises
Browse files Browse the repository at this point in the history
  • Loading branch information
SukkaW committed Jul 12, 2023
1 parent bfda548 commit 0fae176
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 70 deletions.
82 changes: 14 additions & 68 deletions src/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,76 +15,22 @@ export {
} 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<void> =>
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<string | undefined> =>
new Promise((res, rej) =>
fs.mkdir(path, options, (er, made) => (er ? rej(er) : res(made)))
)

const readdir = (path: fs.PathLike): Promise<Dirent[]> =>
new Promise<Dirent[]>((res, rej) =>
fs.readdir(path, { withFileTypes: true }, (er, data) =>
er ? rej(er) : res(data)
)
)

const rename = (oldPath: fs.PathLike, newPath: fs.PathLike): Promise<void> =>
new Promise((res, rej) =>
fs.rename(oldPath, newPath, (er, ...d: any[]) => (er ? rej(er) : res(...d)))
)
import fsPromises from 'fs/promises'

const rm = (path: fs.PathLike, options: fs.RmOptions): Promise<void> =>
new Promise((res, rej) =>
fs.rm(path, options, (er, ...d: any[]) => (er ? rej(er) : res(...d)))
)

const rmdir = (path: fs.PathLike): Promise<void> =>
new Promise((res, rej) =>
fs.rmdir(path, (er, ...d: any[]) => (er ? rej(er) : res(...d)))
)

const stat = (path: fs.PathLike): Promise<fs.Stats> =>
new Promise((res, rej) =>
fs.stat(path, (er, data) => (er ? rej(er) : res(data)))
)

const lstat = (path: fs.PathLike): Promise<fs.Stats> =>
new Promise((res, rej) =>
fs.lstat(path, (er, data) => (er ? rej(er) : res(data)))
)

const unlink = (path: fs.PathLike): Promise<void> =>
new Promise((res, rej) =>
fs.unlink(path, (er, ...d: any[]) => (er ? rej(er) : res(...d)))
)
export const readdirSync = (path: fs.PathLike): Dirent[] =>
rdSync(path, { withFileTypes: true })

export const promises = {
chmod,
mkdir,
readdir,
rename,
rm,
rmdir,
stat,
lstat,
unlink,
chmod: fsPromises.chmod,
mkdir: fsPromises.mkdir,
readdir(path: fs.PathLike) {
return fsPromises.readdir(path, { withFileTypes: true })
},
rename: fsPromises.rename,
rm: fsPromises.rm,
rmdir: fsPromises.rmdir,
stat: fsPromises.stat,
lstat: fsPromises.lstat,
unlink: fsPromises.unlink,
}
20 changes: 18 additions & 2 deletions test/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,30 @@ const mockFSMethodPass =
const cb = args.pop()
process.nextTick(() => cb(null, method, 1, 2, 3))
}
const mockFSPromiseMethodPass =
(method: string) =>
() => new Promise((resolve, reject) => {
resolve(method)
})
const mockFSMethodFail =
(method: string) =>
(...args: any[]) => {
const cb = args.pop()
process.nextTick(() => cb(new Error('oops'), method, 1, 2, 3))
}
const mockFSPromiseMethodFail =
(method: string) =>
() => new Promise((resolve, reject) => {
reject(new Error('oops'))
})

import { useNative } from '../dist/cjs/src/use-native.js'
t.type(fs.promises, Object)
const mockFSPass: { [k: string]: (...a: any[]) => any } = {}
const mockFSFail: { [k: string]: (...a: any[]) => any } = {}
const mockFSPromisesPass: { [k: string]: (...a: any[]) => Promise<any> } = {}
const mockFSPromisesFail: { [k: string]: (...a: any[]) => Promise<any> } = {}

for (const method of Object.keys(
fs.promises as { [k: string]: (...a: any[]) => any }
)) {
Expand All @@ -48,7 +61,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
Expand All @@ -65,7 +80,7 @@ for (const method of Object.keys(fs)) {
}

t.test('passing resolves promise', async t => {
const fs = t.mock('../src/fs', { fs: mockFSPass })
const fs = t.mock('../src/fs', { fs: mockFSPass, 'fs/promises': mockFSPromisesPass })
for (const [m, fn] of Object.entries(
fs.promises as { [k: string]: (...a: any) => Promise<any> }
)) {
Expand All @@ -74,7 +89,8 @@ t.test('passing resolves promise', async t => {
})

t.test('failing rejects promise', async t => {
const fs = t.mock('../src/fs', { fs: mockFSFail })
const fs = t.mock('../src/fs', { fs: mockFSFail, 'fs/promises': mockFSPromisesFail })

for (const [m, fn] of Object.entries(
fs.promises as { [k: string]: (...a: any[]) => Promise<any> }
)) {
Expand Down

0 comments on commit 0fae176

Please sign in to comment.