From bc6c09afb8c6cf0df336729ad700c64912122cef Mon Sep 17 00:00:00 2001 From: isaacs Date: Wed, 8 Mar 2023 15:50:39 -0800 Subject: [PATCH] provide dirent/stats as second arg to filter method --- CHANGELOG.md | 4 +++ README.md | 16 +++++++----- src/bin.ts | 51 ++++++++++++++++++++------------------ src/index.ts | 7 ++++-- src/rimraf-move-remove.ts | 8 +++--- src/rimraf-posix.ts | 8 +++--- src/rimraf-windows.ts | 8 +++--- test/rimraf-move-remove.js | 4 +-- test/rimraf-posix.js | 4 +-- test/rimraf-windows.js | 4 +-- 10 files changed, 64 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9f99c9..38d3429 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 4.4 + +- Provide Dirent or Stats object as second argument to filter + # 4.3 - Return boolean indicating whether the path was fully removed diff --git a/README.md b/README.md index 2dee9d8..0f57b05 100644 --- a/README.md +++ b/README.md @@ -77,12 +77,16 @@ Options: `fs.rm` because that implementation does not support abort signals. -- `filter` Method that receives a path string as an argument, and - returns a boolean indicating whether that path should be - deleted. With async rimraf methods, this may return a Promise - that resolves to a boolean. (Since Promises are truthy, - returning a Promise from a sync filter is the same as just not - filtering anything.) +- `filter` Method that returns a boolean indicating whether that + path should be deleted. With async rimraf methods, this may + return a Promise that resolves to a boolean. (Since Promises + are truthy, returning a Promise from a sync filter is the same + as just not filtering anything.) + + The first argument to the filter is the path string. The + second argument is either a `Dirent` or `Stats` object for that + path. (The first path explored will be a `Stats`, the rest + will be `Dirent`.) If a filter method is provided, it will _only_ remove entries if the filter returns (or resolves to) a truthy value. Omitting diff --git a/src/bin.ts b/src/bin.ts index 7aeffca..86163a9 100755 --- a/src/bin.ts +++ b/src/bin.ts @@ -46,6 +46,7 @@ import { parse, relative, resolve } from 'path' const cwd = process.cwd() import { createInterface, Interface } from 'readline' +import { Dirent, Stats } from 'fs' const prompt = async (rl: Interface, q: string) => new Promise(res => rl.question(q, res)) @@ -70,11 +71,11 @@ const interactiveRimraf = async ( processing = false } const oneAtATime = - (fn: (s: string) => Promise) => - async (s: string): Promise => { + (fn: (s: string, e: Dirent | Stats) => Promise) => + async (s: string, e: Dirent | Stats): Promise => { const p = new Promise(res => { queue.push(async () => { - const result = await fn(s) + const result = await fn(s, e) res(result) return result }) @@ -86,30 +87,32 @@ const interactiveRimraf = async ( input: process.stdin, output: process.stdout, }) - opt.filter = oneAtATime(async (path: string): Promise => { - if (noneRemaining) { - return false - } - while (!allRemaining) { - const a = ( - await prompt(rl, `rm? ${relative(cwd, path)}\n[(Yes)/No/All/Quit] > `) - ).trim() - if (/^n/i.test(a)) { - return false - } else if (/^a/i.test(a)) { - allRemaining = true - break - } else if (/^q/i.test(a)) { - noneRemaining = true + opt.filter = oneAtATime( + async (path: string, ent: Dirent | Stats): Promise => { + if (noneRemaining) { return false - } else if (a === '' || /^y/i.test(a)) { - break - } else { - continue } + while (!allRemaining) { + const a = ( + await prompt(rl, `rm? ${relative(cwd, path)}\n[(Yes)/No/All/Quit] > `) + ).trim() + if (/^n/i.test(a)) { + return false + } else if (/^a/i.test(a)) { + allRemaining = true + break + } else if (/^q/i.test(a)) { + noneRemaining = true + return false + } else if (a === '' || /^y/i.test(a)) { + break + } else { + continue + } + } + return existingFilter(path, ent) } - return existingFilter(path) - }) + ) await impl(paths, opt) rl.close() } diff --git a/src/index.ts b/src/index.ts index 3c5443c..5808c57 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,11 +12,13 @@ export interface RimrafAsyncOptions { maxBackoff?: number signal?: AbortSignal glob?: boolean | GlobOptions - filter?: ((path: string) => boolean) | ((path: string) => Promise) + filter?: + | ((path: string, ent: Dirent | Stats) => boolean) + | ((path: string, ent: Dirent | Stats) => Promise) } export interface RimrafSyncOptions extends RimrafAsyncOptions { - filter?: (path: string) => boolean + filter?: (path: string, ent: Dirent | Stats) => boolean } export type RimrafOptions = RimrafSyncOptions | RimrafAsyncOptions @@ -44,6 +46,7 @@ export const assertRimrafOptions: (o: any) => void = ( } } +import { Dirent, Stats } from 'fs' import { rimrafManual, rimrafManualSync } from './rimraf-manual.js' import { rimrafMoveRemove, rimrafMoveRemoveSync } from './rimraf-move-remove.js' import { rimrafNative, rimrafNativeSync } from './rimraf-native.js' diff --git a/src/rimraf-move-remove.ts b/src/rimraf-move-remove.ts index c64cc22..77f95df 100644 --- a/src/rimraf-move-remove.ts +++ b/src/rimraf-move-remove.ts @@ -119,7 +119,7 @@ const rimrafMoveRemoveDir = async ( } } /* c8 ignore stop */ - if (opt.filter && !(await opt.filter(path))) { + if (opt.filter && !(await opt.filter(path, ent))) { return false } await ignoreENOENT(tmpUnlink(path, opt.tmp, unlinkFixEPERM)) @@ -141,7 +141,7 @@ const rimrafMoveRemoveDir = async ( if (opt.preserveRoot === false && path === parse(path).root) { return false } - if (opt.filter && !(await opt.filter(path))) { + if (opt.filter && !(await opt.filter(path, ent))) { return false } await ignoreENOENT(tmpUnlink(path, opt.tmp, rmdir)) @@ -205,7 +205,7 @@ const rimrafMoveRemoveDirSync = ( } } /* c8 ignore stop */ - if (opt.filter && !opt.filter(path)) { + if (opt.filter && !opt.filter(path, ent)) { return false } ignoreENOENTSync(() => tmpUnlinkSync(path, tmp, unlinkFixEPERMSync)) @@ -223,7 +223,7 @@ const rimrafMoveRemoveDirSync = ( if (opt.preserveRoot === false && path === parse(path).root) { return false } - if (opt.filter && !opt.filter(path)) { + if (opt.filter && !opt.filter(path, ent)) { return false } ignoreENOENTSync(() => tmpUnlinkSync(path, tmp, rmdirSync)) diff --git a/src/rimraf-posix.ts b/src/rimraf-posix.ts index c26b488..64326d9 100644 --- a/src/rimraf-posix.ts +++ b/src/rimraf-posix.ts @@ -61,7 +61,7 @@ const rimrafPosixDir = async ( } } /* c8 ignore stop */ - if (opt.filter && !(await opt.filter(path))) { + if (opt.filter && !(await opt.filter(path, ent))) { return false } await ignoreENOENT(unlink(path)) @@ -85,7 +85,7 @@ const rimrafPosixDir = async ( return false } - if (opt.filter && !(await opt.filter(path))) { + if (opt.filter && !(await opt.filter(path, ent))) { return false } @@ -115,7 +115,7 @@ const rimrafPosixDirSync = ( } } /* c8 ignore stop */ - if (opt.filter && !opt.filter(path)) { + if (opt.filter && !opt.filter(path, ent)) { return false } ignoreENOENTSync(() => unlinkSync(path)) @@ -134,7 +134,7 @@ const rimrafPosixDirSync = ( return false } - if (opt.filter && !opt.filter(path)) { + if (opt.filter && !opt.filter(path, ent)) { return false } diff --git a/src/rimraf-windows.ts b/src/rimraf-windows.ts index 7ee088e..67b87cb 100644 --- a/src/rimraf-windows.ts +++ b/src/rimraf-windows.ts @@ -117,7 +117,7 @@ const rimrafWindowsDir = async ( } } /* c8 ignore stop */ - if (opt.filter && !(await opt.filter(path))) { + if (opt.filter && !(await opt.filter(path, ent))) { return false } // is a file @@ -141,7 +141,7 @@ const rimrafWindowsDir = async ( if (!removedAll) { return false } - if (opt.filter && !(await opt.filter(path))) { + if (opt.filter && !(await opt.filter(path, ent))) { return false } await ignoreENOENT(rimrafWindowsDirMoveRemoveFallback(path, opt)) @@ -169,7 +169,7 @@ const rimrafWindowsDirSync = ( } } /* c8 ignore stop */ - if (opt.filter && !opt.filter(path)) { + if (opt.filter && !opt.filter(path, ent)) { return false } // is a file @@ -193,7 +193,7 @@ const rimrafWindowsDirSync = ( if (!removedAll) { return false } - if (opt.filter && !opt.filter(path)) { + if (opt.filter && !opt.filter(path, ent)) { return false } ignoreENOENTSync(() => { diff --git a/test/rimraf-move-remove.js b/test/rimraf-move-remove.js index 78055b6..1035790 100644 --- a/test/rimraf-move-remove.js +++ b/test/rimraf-move-remove.js @@ -528,8 +528,8 @@ t.test( const { signal } = ac const opt = { signal, - filter: p => { - if (basename(p) === 'g') { + filter: (p, st) => { + if (basename(p) === 'g' && st.isFile()) { ac.abort(new Error('done')) } return true diff --git a/test/rimraf-posix.js b/test/rimraf-posix.js index 897129b..416a552 100644 --- a/test/rimraf-posix.js +++ b/test/rimraf-posix.js @@ -229,8 +229,8 @@ t.test( const { signal } = ac const opt = { signal, - filter: p => { - if (basename(p) === 'g') { + filter: (p, st) => { + if (basename(p) === 'g' && st.isFile()) { ac.abort(new Error('done')) } return true diff --git a/test/rimraf-windows.js b/test/rimraf-windows.js index 34ed7d7..a0b5038 100644 --- a/test/rimraf-windows.js +++ b/test/rimraf-windows.js @@ -508,8 +508,8 @@ t.test( const { signal } = ac const opt = { signal, - filter: p => { - if (basename(p) === 'g') { + filter: (p, st) => { + if (basename(p) === 'g' && st.isFile()) { ac.abort(new Error('done')) } return true