pax_global_header00006660000000000000000000000064144736710560014527gustar00rootroot0000000000000052 comment=7df7949f4fc98477b904fec0e3e444536d579ddb del-7.1.0/000077500000000000000000000000001447367105600123005ustar00rootroot00000000000000del-7.1.0/.editorconfig000066400000000000000000000002571447367105600147610ustar00rootroot00000000000000root = true [*] indent_style = tab end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.yml] indent_style = space indent_size = 2 del-7.1.0/.gitattributes000066400000000000000000000000231447367105600151660ustar00rootroot00000000000000* text=auto eol=lf del-7.1.0/.github/000077500000000000000000000000001447367105600136405ustar00rootroot00000000000000del-7.1.0/.github/funding.yml000066400000000000000000000001551447367105600160160ustar00rootroot00000000000000github: sindresorhus open_collective: sindresorhus tidelift: npm/del custom: https://sindresorhus.com/donate del-7.1.0/.github/security.md000066400000000000000000000002631447367105600160320ustar00rootroot00000000000000# Security Policy To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. del-7.1.0/.github/workflows/000077500000000000000000000000001447367105600156755ustar00rootroot00000000000000del-7.1.0/.github/workflows/main.yml000066400000000000000000000010451447367105600173440ustar00rootroot00000000000000name: CI on: - push - pull_request jobs: test: name: Node.js ${{ matrix.node-version }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: node-version: - 20 - 18 - 16 os: - ubuntu-latest - macos-latest - windows-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - run: npm install - run: npm test del-7.1.0/.gitignore000066400000000000000000000000271447367105600142670ustar00rootroot00000000000000node_modules yarn.lock del-7.1.0/.npmrc000066400000000000000000000000231447367105600134130ustar00rootroot00000000000000package-lock=false del-7.1.0/benchmark.js000066400000000000000000000035061447367105600145740ustar00rootroot00000000000000import path from 'node:path'; import process from 'node:process'; import Benchmark from 'benchmark'; import makeDir from 'make-dir'; import {temporaryDirectory} from 'tempy'; import {deleteAsync, deleteSync} from './index.js'; const suite = new Benchmark.Suite('concurrency'); const temporaryDirectoryPath = temporaryDirectory(); const fixtures = Array.from({length: 2000}, (_, index) => path.resolve(temporaryDirectoryPath, (index + 1).toString())); function createFixtures() { for (const fixture of fixtures) { makeDir.sync(path.resolve(temporaryDirectoryPath, fixture)); } } const concurrencies = [ 1, 3, 5, 10, 15, 20, 50, 100, 200, 300, 400, 500, 1000, Number.POSITIVE_INFINITY, ]; for (const concurrency of concurrencies) { const name = `concurrency: ${concurrency.toString()}`; suite.add({ name, defer: true, async fn(deferred) { // Can't use `setup()` because it isn't called after every // defer and it breaks using `async` keyword here. // https://github.com/bestiejs/benchmark.js/issues/136 createFixtures(); const removedFiles = await deleteAsync(['**/*'], { cwd: temporaryDirectoryPath, concurrency, }); if (removedFiles.length !== fixtures.length) { const error = new Error( `"${name}": files removed: ${removedFiles.length}, expected: ${fixtures.length}`, ); console.error(error); deleteSync(temporaryDirectoryPath, {cwd: temporaryDirectoryPath, force: true}); // eslint-disable-next-line unicorn/no-process-exit process.exit(1); } deferred.resolve(); }, }); } suite .on('cycle', event => { console.log(String(event.target)); }) .on('complete', function () { console.log(`Fastest is ${this.filter('fastest').map('name')}`); deleteSync(temporaryDirectoryPath, {cwd: temporaryDirectoryPath, force: true}); }) .run({async: true}); del-7.1.0/index.d.ts000066400000000000000000000067161447367105600142130ustar00rootroot00000000000000import {type Options as GlobbyOptions} from 'globby'; export type ProgressData = { /** Deleted files and directories count. */ readonly deletedCount: number; /** Total files and directories count. */ readonly totalCount: number; /** Completed percentage. A value between `0` and `1`. */ readonly percent: number; /** The absolute path of the deleted file or directory. It will not be present if nothing was deleted. */ readonly path?: string; }; export type Options = { /** Allow deleting the current working directory and outside. @default false */ readonly force?: boolean; /** See what would be deleted. @default false @example ``` import {deleteAsync} from 'del'; const deletedPaths = await deleteAsync(['temp/*.js'], {dryRun: true}); console.log('Files and directories that would be deleted:\n', deletedPaths.join('\n')); ``` */ readonly dryRun?: boolean; /** Concurrency limit. Minimum: `1`. @default Infinity */ readonly concurrency?: number; /** Called after each file or directory is deleted. @example ``` import {deleteAsync} from 'del'; await deleteAsync(patterns, { onProgress: progress => { // … }}); ``` */ readonly onProgress?: (progress: ProgressData) => void; } & GlobbyOptions; /** Delete files and directories using glob patterns. Note that glob patterns can only contain forward-slashes, not backward-slashes. Windows file paths can use backward-slashes as long as the path does not contain any glob-like characters, otherwise use `path.posix.join()` instead of `path.join()`. @param patterns - See the supported [glob patterns](https://github.com/sindresorhus/globby#globbing-patterns). - [Pattern examples with expected matches](https://github.com/sindresorhus/multimatch/blob/main/test/test.js) - [Quick globbing pattern overview](https://github.com/sindresorhus/multimatch#globbing-patterns) @param options - You can specify any of the [`globby` options](https://github.com/sindresorhus/globby#options) in addition to the `del` options. In contrast to the `globby` defaults, `expandDirectories`, `onlyFiles`, and `followSymbolicLinks` are `false` by default. @returns The deleted paths. @example ``` import {deleteAsync} from 'del'; const deletedPaths = await deleteAsync(['temp/*.js', '!temp/unicorn.js']); console.log('Deleted files and directories:\n', deletedPaths.join('\n')); ``` */ export function deleteAsync( patterns: string | readonly string[], options?: Options ): Promise; /** Synchronously delete files and directories using glob patterns. Note that glob patterns can only contain forward-slashes, not backward-slashes. Windows file paths can use backward-slashes as long as the path does not contain any glob-like characters, otherwise use `path.posix.join()` instead of `path.join()`. @param patterns - See the supported [glob patterns](https://github.com/sindresorhus/globby#globbing-patterns). - [Pattern examples with expected matches](https://github.com/sindresorhus/multimatch/blob/main/test/test.js) - [Quick globbing pattern overview](https://github.com/sindresorhus/multimatch#globbing-patterns) @param options - You can specify any of the [`globby` options](https://github.com/sindresorhus/globby#options) in addition to the `del` options. In contrast to the `globby` defaults, `expandDirectories`, `onlyFiles`, and `followSymbolicLinks` are `false` by default. @returns The deleted paths. */ export function deleteSync( patterns: string | readonly string[], options?: Options ): string[]; del-7.1.0/index.js000066400000000000000000000057631447367105600137600ustar00rootroot00000000000000import {promisify} from 'node:util'; import path from 'node:path'; import process from 'node:process'; import {globby, globbySync} from 'globby'; import isGlob from 'is-glob'; import slash from 'slash'; import gracefulFs from 'graceful-fs'; import isPathCwd from 'is-path-cwd'; import isPathInside from 'is-path-inside'; import rimraf from 'rimraf'; import pMap from 'p-map'; const rimrafP = promisify(rimraf); const rimrafOptions = { glob: false, unlink: gracefulFs.unlink, unlinkSync: gracefulFs.unlinkSync, chmod: gracefulFs.chmod, chmodSync: gracefulFs.chmodSync, stat: gracefulFs.stat, statSync: gracefulFs.statSync, lstat: gracefulFs.lstat, lstatSync: gracefulFs.lstatSync, rmdir: gracefulFs.rmdir, rmdirSync: gracefulFs.rmdirSync, readdir: gracefulFs.readdir, readdirSync: gracefulFs.readdirSync, }; function safeCheck(file, cwd) { if (isPathCwd(file)) { throw new Error('Cannot delete the current working directory. Can be overridden with the `force` option.'); } if (!isPathInside(file, cwd)) { throw new Error('Cannot delete files/directories outside the current working directory. Can be overridden with the `force` option.'); } } function normalizePatterns(patterns) { patterns = Array.isArray(patterns) ? patterns : [patterns]; patterns = patterns.map(pattern => { if (process.platform === 'win32' && isGlob(pattern) === false) { return slash(pattern); } return pattern; }); return patterns; } export async function deleteAsync(patterns, {force, dryRun, cwd = process.cwd(), onProgress = () => {}, ...options} = {}) { options = { expandDirectories: false, onlyFiles: false, followSymbolicLinks: false, cwd, ...options, }; patterns = normalizePatterns(patterns); const paths = await globby(patterns, options); const files = paths.sort((a, b) => b.localeCompare(a)); if (files.length === 0) { onProgress({ totalCount: 0, deletedCount: 0, percent: 1, }); } let deletedCount = 0; const mapper = async file => { file = path.resolve(cwd, file); if (!force) { safeCheck(file, cwd); } if (!dryRun) { await rimrafP(file, rimrafOptions); } deletedCount += 1; onProgress({ totalCount: files.length, deletedCount, percent: deletedCount / files.length, path: file, }); return file; }; const removedFiles = await pMap(files, mapper, options); removedFiles.sort((a, b) => a.localeCompare(b)); return removedFiles; } export function deleteSync(patterns, {force, dryRun, cwd = process.cwd(), ...options} = {}) { options = { expandDirectories: false, onlyFiles: false, followSymbolicLinks: false, cwd, ...options, }; patterns = normalizePatterns(patterns); const files = globbySync(patterns, options) .sort((a, b) => b.localeCompare(a)); const removedFiles = files.map(file => { file = path.resolve(cwd, file); if (!force) { safeCheck(file, cwd); } if (!dryRun) { rimraf.sync(file, rimrafOptions); } return file; }); removedFiles.sort((a, b) => a.localeCompare(b)); return removedFiles; } del-7.1.0/index.test-d.ts000066400000000000000000000015211447367105600151550ustar00rootroot00000000000000import {expectType} from 'tsd'; import {deleteAsync, deleteSync} from './index.js'; const paths = [ 'temp/*.js', '!temp/unicorn.js', ]; // Del expectType>(deleteAsync('temp/*.js')); expectType>(deleteAsync(paths)); expectType>(deleteAsync(paths, {force: true})); expectType>(deleteAsync(paths, {dryRun: true})); expectType>(deleteAsync(paths, {concurrency: 20})); expectType>(deleteAsync(paths, {cwd: ''})); // Del (sync) expectType(deleteSync('tmp/*.js')); expectType(deleteSync(paths)); expectType(deleteSync(paths, {force: true})); expectType(deleteSync(paths, {dryRun: true})); expectType(deleteSync(paths, {concurrency: 20})); expectType(deleteSync(paths, {cwd: ''})); del-7.1.0/license000066400000000000000000000021351447367105600136460ustar00rootroot00000000000000MIT License Copyright (c) Sindre Sorhus (https://sindresorhus.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. del-7.1.0/package.json000066400000000000000000000024721447367105600145730ustar00rootroot00000000000000{ "name": "del", "version": "7.1.0", "description": "Delete files and directories", "license": "MIT", "repository": "sindresorhus/del", "funding": "https://github.com/sponsors/sindresorhus", "author": { "name": "Sindre Sorhus", "email": "sindresorhus@gmail.com", "url": "https://sindresorhus.com" }, "type": "module", "exports": "./index.js", "types": "./index.d.ts", "engines": { "node": ">=14.16" }, "scripts": { "test": "xo && ava && tsd", "bench": "node benchmark.js" }, "files": [ "index.js", "index.d.ts" ], "keywords": [ "delete", "files", "folders", "directories", "remove", "destroy", "trash", "unlink", "clean", "cleaning", "cleanup", "rm", "rmrf", "rimraf", "rmdir", "glob", "gulpfriendly", "file", "folder", "directory", "fs", "filesystem" ], "dependencies": { "globby": "^13.1.2", "graceful-fs": "^4.2.10", "is-glob": "^4.0.3", "is-path-cwd": "^3.0.0", "is-path-inside": "^4.0.0", "p-map": "^5.5.0", "rimraf": "^3.0.2", "slash": "^4.0.0" }, "devDependencies": { "ava": "^4.3.1", "benchmark": "^2.1.4", "make-dir": "^3.1.0", "tempy": "^3.0.0", "tsd": "^0.22.0", "xo": "^0.56.0" }, "ava": { "serial": true, "workerThreads": false }, "xo": { "rules": { "unicorn/prefer-string-replace-all": "off" } } } del-7.1.0/readme.md000066400000000000000000000110521447367105600140560ustar00rootroot00000000000000# del > Delete files and directories using [globs](https://github.com/sindresorhus/globby#globbing-patterns) Similar to [rimraf](https://github.com/isaacs/rimraf), but with a Promise API and support for multiple files and globbing. It also protects you against deleting the current working directory and above. ## Install ```sh npm install del ``` ## Usage ```js import {deleteAsync} from 'del'; const deletedFilePaths = await deleteAsync(['temp/*.js', '!temp/unicorn.js']); const deletedDirectoryPaths = await deleteAsync(['temp', 'public']); console.log('Deleted files:\n', deletedFilePaths.join('\n')); console.log('\n\n'); console.log('Deleted directories:\n', deletedDirectoryPaths.join('\n')); ``` ## Beware The glob pattern `**` matches all children and *the parent*. So this won't work: ```js deleteSync(['public/assets/**', '!public/assets/goat.png']); ``` You have to explicitly ignore the parent directories too: ```js deleteSync(['public/assets/**', '!public/assets', '!public/assets/goat.png']); ``` To delete all subdirectories inside `public/`, you can do: ```js deleteSync(['public/*/']); ``` Suggestions on how to improve this welcome! ## API Note that glob patterns can only contain forward-slashes, not backward-slashes. Windows file paths can use backward-slashes as long as the path does not contain any glob-like characters, otherwise use `path.posix.join()` instead of `path.join()`. ### deleteAsync(patterns, options?) Returns `Promise` with the deleted paths. ### deleteSync(patterns, options?) Returns `string[]` with the deleted paths. #### patterns Type: `string | string[]` See the supported [glob patterns](https://github.com/sindresorhus/globby#globbing-patterns). - [Pattern examples with expected matches](https://github.com/sindresorhus/multimatch/blob/main/test/test.js) - [Quick globbing pattern overview](https://github.com/sindresorhus/multimatch#globbing-patterns) #### options Type: `object` You can specify any of the [`globby` options](https://github.com/sindresorhus/globby#options) in addition to the below options. In contrast to the `globby` defaults, `expandDirectories`, `onlyFiles`, and `followSymbolicLinks` are `false` by default. ##### force Type: `boolean`\ Default: `false` Allow deleting the current working directory and outside. ##### dryRun Type: `boolean`\ Default: `false` See what would be deleted. ```js import {deleteAsync} from 'del'; const deletedPaths = await deleteAsync(['temp/*.js'], {dryRun: true}); console.log('Files and directories that would be deleted:\n', deletedPaths.join('\n')); ``` ##### dot Type: `boolean`\ Default: `false` Allow patterns to match files/folders that start with a period (`.`). This option is passed through to [`fast-glob`](https://github.com/mrmlnc/fast-glob#dot). Note that an explicit dot in a portion of the pattern will always match dot files. **Example** ``` directory/ ├── .editorconfig └── package.json ``` ```js import {deleteSync} from 'del'; deleteSync('*', {dot: false}); //=> ['package.json'] deleteSync('*', {dot: true}); //=> ['.editorconfig', 'package.json'] ``` ##### concurrency Type: `number`\ Default: `Infinity`\ Minimum: `1` Concurrency limit. ##### onProgress Type: `(progress: ProgressData) => void` Called after each file or directory is deleted. ```js import {deleteAsync} from 'del'; await deleteAsync(patterns, { onProgress: progress => { // … }}); ``` ###### ProgressData ```js { totalCount: number, deletedCount: number, percent: number, path?: string } ``` - `percent` is a value between `0` and `1` - `path` is the absolute path of the deleted file or directory. It will not be present if nothing was deleted. ## CLI See [del-cli](https://github.com/sindresorhus/del-cli) for a CLI for this module and [trash-cli](https://github.com/sindresorhus/trash-cli) for a safe version that is suitable for running by hand. ## del for enterprise Available as part of the Tidelift Subscription. The maintainers of del and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-del?utm_source=npm-del&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) ## Related - [make-dir](https://github.com/sindresorhus/make-dir) - Make a directory and its parents if needed - [globby](https://github.com/sindresorhus/globby) - User-friendly glob matching del-7.1.0/test.js000066400000000000000000000256131447367105600136240ustar00rootroot00000000000000import {fileURLToPath} from 'node:url'; import fs from 'node:fs'; import path from 'node:path'; import process from 'node:process'; import test from 'ava'; import {temporaryDirectory} from 'tempy'; import makeDir from 'make-dir'; import {deleteAsync, deleteSync} from './index.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const processCwd = process.cwd(); function exists(t, files) { for (const file of files) { t.true(fs.existsSync(path.join(t.context.tmp, file))); } } function notExists(t, files) { for (const file of files) { t.false(fs.existsSync(path.join(t.context.tmp, file))); } } const fixtures = [ '1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp', ]; test.beforeEach(t => { t.context.tmp = temporaryDirectory(); for (const fixture of fixtures) { makeDir.sync(path.join(t.context.tmp, fixture)); } }); test('delete files - async', async t => { await deleteAsync(['*.tmp', '!1*'], {cwd: t.context.tmp}); exists(t, ['1.tmp', '.dot.tmp']); notExists(t, ['2.tmp', '3.tmp', '4.tmp']); }); test('delete files - sync', t => { deleteSync(['*.tmp', '!1*'], {cwd: t.context.tmp}); exists(t, ['1.tmp', '.dot.tmp']); notExists(t, ['2.tmp', '3.tmp', '4.tmp']); }); test('take options into account - async', async t => { await deleteAsync(['*.tmp', '!1*'], { cwd: t.context.tmp, dot: true, }); exists(t, ['1.tmp']); notExists(t, ['2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); }); test('take options into account - sync', t => { deleteSync(['*.tmp', '!1*'], { cwd: t.context.tmp, dot: true, }); exists(t, ['1.tmp']); notExists(t, ['2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); }); test('return deleted files - async', async t => { t.deepEqual( await deleteAsync('1.tmp', {cwd: t.context.tmp}), [path.join(t.context.tmp, '1.tmp')], ); }); test('return deleted files - sync', t => { t.deepEqual( deleteSync('1.tmp', {cwd: t.context.tmp}), [path.join(t.context.tmp, '1.tmp')], ); }); test('don\'t delete files, but return them - async', async t => { const deletedFiles = await deleteAsync(['*.tmp', '!1*'], { cwd: t.context.tmp, dryRun: true, }); exists(t, ['1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); t.deepEqual(deletedFiles, [ path.join(t.context.tmp, '2.tmp'), path.join(t.context.tmp, '3.tmp'), path.join(t.context.tmp, '4.tmp'), ]); }); test('don\'t delete files, but return them - sync', t => { const deletedFiles = deleteSync(['*.tmp', '!1*'], { cwd: t.context.tmp, dryRun: true, }); exists(t, ['1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); t.deepEqual(deletedFiles, [ path.join(t.context.tmp, '2.tmp'), path.join(t.context.tmp, '3.tmp'), path.join(t.context.tmp, '4.tmp'), ]); }); // Currently this is only testable locally on macOS. // https://github.com/sindresorhus/del/issues/68 test('does not throw EINVAL - async', async t => { await deleteAsync('**/*', { cwd: t.context.tmp, dot: true, }); const nestedFile = path.resolve(t.context.tmp, 'a/b/c/nested.js'); const totalAttempts = 200; let count = 0; while (count !== totalAttempts) { makeDir.sync(nestedFile); // eslint-disable-next-line no-await-in-loop const removed = await deleteAsync('**/*', { cwd: t.context.tmp, dot: true, }); const expected = [ path.resolve(t.context.tmp, 'a'), path.resolve(t.context.tmp, 'a/b'), path.resolve(t.context.tmp, 'a/b/c'), path.resolve(t.context.tmp, 'a/b/c/nested.js'), ]; t.deepEqual(removed, expected); count += 1; } notExists(t, [...fixtures, 'a']); t.is(count, totalAttempts); }); test('does not throw EINVAL - sync', t => { deleteSync('**/*', { cwd: t.context.tmp, dot: true, }); const nestedFile = path.resolve(t.context.tmp, 'a/b/c/nested.js'); const totalAttempts = 200; let count = 0; while (count !== totalAttempts) { makeDir.sync(nestedFile); const removed = deleteSync('**/*', { cwd: t.context.tmp, dot: true, }); const expected = [ path.resolve(t.context.tmp, 'a'), path.resolve(t.context.tmp, 'a/b'), path.resolve(t.context.tmp, 'a/b/c'), path.resolve(t.context.tmp, 'a/b/c/nested.js'), ]; t.deepEqual(removed, expected); count += 1; } notExists(t, [...fixtures, 'a']); t.is(count, totalAttempts); }); test('delete relative files outside of process.cwd using cwd - async', async t => { await deleteAsync(['1.tmp'], {cwd: t.context.tmp}); exists(t, ['2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); notExists(t, ['1.tmp']); }); test('delete relative files outside of process.cwd using cwd - sync', t => { deleteSync(['1.tmp'], {cwd: t.context.tmp}); exists(t, ['2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); notExists(t, ['1.tmp']); }); test('delete absolute files outside of process.cwd using cwd - async', async t => { const absolutePath = path.resolve(t.context.tmp, '1.tmp'); await deleteAsync([absolutePath], {cwd: t.context.tmp}); exists(t, ['2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); notExists(t, ['1.tmp']); }); test('delete absolute files outside of process.cwd using cwd - sync', t => { const absolutePath = path.resolve(t.context.tmp, '1.tmp'); deleteSync([absolutePath], {cwd: t.context.tmp}); exists(t, ['2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); notExists(t, ['1.tmp']); }); test('cannot delete actual working directory without force: true - async', async t => { process.chdir(t.context.tmp); await t.throwsAsync(deleteAsync([t.context.tmp]), { instanceOf: Error, message: 'Cannot delete the current working directory. Can be overridden with the `force` option.', }); exists(t, ['', '1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); process.chdir(processCwd); }); test('cannot delete actual working directory without force: true - sync', t => { process.chdir(t.context.tmp); t.throws(() => { deleteSync([t.context.tmp]); }, { instanceOf: Error, message: 'Cannot delete the current working directory. Can be overridden with the `force` option.', }); exists(t, ['', '1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); process.chdir(processCwd); }); test('cannot delete actual working directory with cwd option without force: true - async', async t => { process.chdir(t.context.tmp); await t.throwsAsync(deleteAsync([t.context.tmp], {cwd: __dirname}), { instanceOf: Error, message: 'Cannot delete the current working directory. Can be overridden with the `force` option.', }); exists(t, ['', '1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); process.chdir(processCwd); }); test('cannot delete actual working directory with cwd option without force: true - sync', t => { process.chdir(t.context.tmp); t.throws(() => { deleteSync([t.context.tmp], {cwd: __dirname}); }, { instanceOf: Error, message: 'Cannot delete the current working directory. Can be overridden with the `force` option.', }); exists(t, ['', '1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); process.chdir(processCwd); }); test('cannot delete files outside cwd without force: true - async', async t => { const absolutePath = path.resolve(t.context.tmp, '1.tmp'); await t.throwsAsync(deleteAsync([absolutePath]), { instanceOf: Error, message: 'Cannot delete files/directories outside the current working directory. Can be overridden with the `force` option.', }); exists(t, ['1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); }); test('cannot delete files outside cwd without force: true - sync', t => { const absolutePath = path.resolve(t.context.tmp, '1.tmp'); t.throws(() => { deleteSync([absolutePath]); }, { instanceOf: Error, message: 'Cannot delete files/directories outside the current working directory. Can be overridden with the `force` option.', }); exists(t, ['', '1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); }); test('cannot delete files inside process.cwd when outside cwd without force: true - async', async t => { process.chdir(t.context.tmp); const removeFile = path.resolve(t.context.tmp, '2.tmp'); const cwd = path.resolve(t.context.tmp, '1.tmp'); await t.throwsAsync(deleteAsync([removeFile], {cwd}), { instanceOf: Error, message: 'Cannot delete files/directories outside the current working directory. Can be overridden with the `force` option.', }); exists(t, ['1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); process.chdir(processCwd); }); test('cannot delete files inside process.cwd when outside cwd without force: true - sync', t => { process.chdir(t.context.tmp); const removeFile = path.resolve(t.context.tmp, '2.tmp'); const cwd = path.resolve(t.context.tmp, '1.tmp'); t.throws(() => { deleteSync([removeFile], {cwd}); }, { instanceOf: Error, message: 'Cannot delete files/directories outside the current working directory. Can be overridden with the `force` option.', }); exists(t, ['1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']); process.chdir(processCwd); }); test('windows can pass absolute paths with "\\" - async', async t => { const filePath = path.resolve(t.context.tmp, '1.tmp'); const removeFiles = await deleteAsync([filePath], {cwd: t.context.tmp, dryRun: true}); t.deepEqual(removeFiles, [filePath]); }); test('windows can pass absolute paths with "\\" - sync', t => { const filePath = path.resolve(t.context.tmp, '1.tmp'); const removeFiles = deleteSync([filePath], {cwd: t.context.tmp, dryRun: true}); t.deepEqual(removeFiles, [filePath]); }); test('windows can pass relative paths with "\\" - async', async t => { const nestedFile = path.resolve(t.context.tmp, 'a/b/c/nested.js'); makeDir.sync(nestedFile); const removeFiles = await deleteAsync([nestedFile], {cwd: t.context.tmp, dryRun: true}); t.deepEqual(removeFiles, [nestedFile]); }); test('windows can pass relative paths with "\\" - sync', t => { const nestedFile = path.resolve(t.context.tmp, 'a/b/c/nested.js'); makeDir.sync(nestedFile); const removeFiles = deleteSync([nestedFile], {cwd: t.context.tmp, dryRun: true}); t.deepEqual(removeFiles, [nestedFile]); }); test('onProgress option - progress of non-existent file', async t => { let report; await deleteAsync('non-existent-directory', {onProgress(event) { report = event; }}); t.deepEqual(report, { totalCount: 0, deletedCount: 0, percent: 1, }); }); test('onProgress option - progress of single file', async t => { let report; await deleteAsync(t.context.tmp, {cwd: __dirname, force: true, onProgress(event) { report = event; }}); t.deepEqual(report, { totalCount: 1, deletedCount: 1, percent: 1, path: t.context.tmp, }); }); test('onProgress option - progress of multiple files', async t => { const reports = []; const sourcePath = process.platform === 'win32' ? path.resolve(`${t.context.tmp}/*`).replace(/\\/g, '/') : `${t.context.tmp}/*`; await deleteAsync(sourcePath, { cwd: __dirname, force: true, onProgress(event) { reports.push(event); }, }); t.is(reports.length, 4); t.deepEqual(reports.map(r => r.totalCount), [4, 4, 4, 4]); t.deepEqual(reports.map(r => r.deletedCount).sort(), [1, 2, 3, 4]); const expectedPaths = ['1', '2', '3', '4'].map(x => path.join(t.context.tmp, `${x}.tmp`)); t.deepEqual(reports.map(r => r.path).sort(), expectedPaths.sort()); });