Skip to content

Commit ffbf4c4

Browse files
chrisblossomsindresorhus
authored andcommitted
Fix the cwd option (#96)
1 parent 8efdbcd commit ffbf4c4

File tree

3 files changed

+148
-16
lines changed

3 files changed

+148
-16
lines changed

index.js

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,41 @@ const {promisify} = require('util');
33
const path = require('path');
44
const globby = require('globby');
55
const isPathCwd = require('is-path-cwd');
6-
const isPathInCwd = require('is-path-in-cwd');
6+
const isPathInside = require('is-path-inside');
77
const rimraf = require('rimraf');
88
const pMap = require('p-map');
99

1010
const rimrafP = promisify(rimraf);
1111

12-
function safeCheck(file) {
12+
function safeCheck(file, cwd) {
1313
if (isPathCwd(file)) {
1414
throw new Error('Cannot delete the current working directory. Can be overridden with the `force` option.');
1515
}
1616

17-
if (!isPathInCwd(file)) {
17+
if (!isPathInside(file, cwd)) {
1818
throw new Error('Cannot delete files/directories outside the current working directory. Can be overridden with the `force` option.');
1919
}
2020
}
2121

22-
module.exports = async (patterns, {force, dryRun, ...options} = {}) => {
22+
module.exports = async (patterns, {force, dryRun, cwd = process.cwd(), ...options} = {}) => {
2323
options = {
2424
expandDirectories: false,
2525
onlyFiles: false,
2626
followSymbolicLinks: false,
27+
cwd,
2728
...options
2829
};
2930

3031
const files = (await globby(patterns, options))
3132
.sort((a, b) => b.localeCompare(a));
3233

3334
const mapper = async file => {
35+
file = path.resolve(cwd, file);
36+
3437
if (!force) {
35-
safeCheck(file);
38+
safeCheck(file, cwd);
3639
}
3740

38-
file = path.resolve(options.cwd || '', file);
39-
4041
if (!dryRun) {
4142
await rimrafP(file, {glob: false});
4243
}
@@ -47,24 +48,25 @@ module.exports = async (patterns, {force, dryRun, ...options} = {}) => {
4748
return pMap(files, mapper, options);
4849
};
4950

50-
module.exports.sync = (patterns, {force, dryRun, ...options} = {}) => {
51+
module.exports.sync = (patterns, {force, dryRun, cwd = process.cwd(), ...options} = {}) => {
5152
options = {
5253
expandDirectories: false,
5354
onlyFiles: false,
5455
followSymbolicLinks: false,
56+
cwd,
5557
...options
5658
};
5759

5860
const files = globby.sync(patterns, options)
5961
.sort((a, b) => b.localeCompare(a));
6062

6163
return files.map(file => {
64+
file = path.resolve(cwd, file);
65+
6266
if (!force) {
63-
safeCheck(file);
67+
safeCheck(file, cwd);
6468
}
6569

66-
file = path.resolve(options.cwd || '', file);
67-
6870
if (!dryRun) {
6971
rimraf.sync(file, {glob: false});
7072
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"dependencies": {
4747
"globby": "^10.0.0",
4848
"is-path-cwd": "^2.0.0",
49-
"is-path-in-cwd": "^2.0.0",
49+
"is-path-inside": "^3.0.1",
5050
"p-map": "^2.0.0",
5151
"rimraf": "^2.6.3"
5252
},

test.js

Lines changed: 134 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import path from 'path';
22
import fs from 'fs';
3-
import test from 'ava';
3+
import {serial as test} from 'ava';
44
import tempy from 'tempy';
55
import makeDir from 'make-dir';
66
import del from '.';
77

8+
const processCwd = process.cwd();
9+
810
function exists(t, files) {
911
for (const file of files) {
1012
t.true(fs.existsSync(path.join(t.context.tmp, file)));
@@ -67,7 +69,7 @@ test('take options into account - sync', t => {
6769
notExists(t, ['2.tmp', '3.tmp', '4.tmp', '.dot.tmp']);
6870
});
6971

70-
test.serial('return deleted files - async', async t => {
72+
test('return deleted files - async', async t => {
7173
t.deepEqual(
7274
await del('1.tmp', {cwd: t.context.tmp}),
7375
[path.join(t.context.tmp, '1.tmp')]
@@ -109,7 +111,7 @@ test('don\'t delete files, but return them - sync', t => {
109111

110112
// Currently this only testable locally on an osx machine.
111113
// https://github.com/sindresorhus/del/issues/68
112-
test.serial('does not throw EINVAL - async', async t => {
114+
test('does not throw EINVAL - async', async t => {
113115
await del('**/*', {
114116
cwd: t.context.tmp,
115117
dot: true
@@ -144,7 +146,7 @@ test.serial('does not throw EINVAL - async', async t => {
144146
t.is(count, totalAttempts);
145147
});
146148

147-
test.serial('does not throw EINVAL - sync', t => {
149+
test('does not throw EINVAL - sync', t => {
148150
del.sync('**/*', {
149151
cwd: t.context.tmp,
150152
dot: true
@@ -177,3 +179,131 @@ test.serial('does not throw EINVAL - sync', t => {
177179
notExists(t, [...fixtures, 'a']);
178180
t.is(count, totalAttempts);
179181
});
182+
183+
test('delete relative files outside of process.cwd using cwd - async', async t => {
184+
await del(['1.tmp'], {cwd: t.context.tmp});
185+
186+
exists(t, ['2.tmp', '3.tmp', '4.tmp', '.dot.tmp']);
187+
notExists(t, ['1.tmp']);
188+
});
189+
190+
test('delete relative files outside of process.cwd using cwd - sync', t => {
191+
del.sync(['1.tmp'], {cwd: t.context.tmp});
192+
193+
exists(t, ['2.tmp', '3.tmp', '4.tmp', '.dot.tmp']);
194+
notExists(t, ['1.tmp']);
195+
});
196+
197+
test('delete absolute files outside of process.cwd using cwd - async', async t => {
198+
const absolutePath = path.resolve(t.context.tmp, '1.tmp');
199+
await del([absolutePath], {cwd: t.context.tmp});
200+
201+
exists(t, ['2.tmp', '3.tmp', '4.tmp', '.dot.tmp']);
202+
notExists(t, ['1.tmp']);
203+
});
204+
205+
test('delete absolute files outside of process.cwd using cwd - sync', t => {
206+
const absolutePath = path.resolve(t.context.tmp, '1.tmp');
207+
del.sync([absolutePath], {cwd: t.context.tmp});
208+
209+
exists(t, ['2.tmp', '3.tmp', '4.tmp', '.dot.tmp']);
210+
notExists(t, ['1.tmp']);
211+
});
212+
213+
test('cannot delete actual working directory without force: true - async', async t => {
214+
process.chdir(t.context.tmp);
215+
216+
await t.throwsAsync(() => del([t.context.tmp]), {
217+
instanceOf: Error,
218+
message: 'Cannot delete the current working directory. Can be overridden with the `force` option.'
219+
});
220+
221+
exists(t, ['', '1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']);
222+
process.chdir(processCwd);
223+
});
224+
225+
test('cannot delete actual working directory without force: true - sync', t => {
226+
process.chdir(t.context.tmp);
227+
228+
t.throws(() => del.sync([t.context.tmp]), {
229+
instanceOf: Error,
230+
message: 'Cannot delete the current working directory. Can be overridden with the `force` option.'
231+
});
232+
233+
exists(t, ['', '1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']);
234+
process.chdir(processCwd);
235+
});
236+
237+
test('cannot delete actual working directory with cwd option without force: true - async', async t => {
238+
process.chdir(t.context.tmp);
239+
240+
await t.throwsAsync(() => del([t.context.tmp], {cwd: __dirname}), {
241+
instanceOf: Error,
242+
message: 'Cannot delete the current working directory. Can be overridden with the `force` option.'
243+
});
244+
245+
exists(t, ['', '1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']);
246+
process.chdir(processCwd);
247+
});
248+
249+
test('cannot delete actual working directory with cwd option without force: true - sync', t => {
250+
process.chdir(t.context.tmp);
251+
252+
t.throws(() => del.sync([t.context.tmp], {cwd: __dirname}), {
253+
instanceOf: Error,
254+
message: 'Cannot delete the current working directory. Can be overridden with the `force` option.'
255+
});
256+
257+
exists(t, ['', '1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']);
258+
process.chdir(processCwd);
259+
});
260+
261+
test('cannot delete files outside cwd without force: true - async', async t => {
262+
const absolutePath = path.resolve(t.context.tmp, '1.tmp');
263+
264+
await t.throwsAsync(() => del([absolutePath]), {
265+
instanceOf: Error,
266+
message: 'Cannot delete files/directories outside the current working directory. Can be overridden with the `force` option.'
267+
});
268+
269+
exists(t, ['1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']);
270+
});
271+
272+
test('cannot delete files outside cwd without force: true - sync', t => {
273+
const absolutePath = path.resolve(t.context.tmp, '1.tmp');
274+
275+
t.throws(() => del.sync([absolutePath]), {
276+
instanceOf: Error,
277+
message: 'Cannot delete files/directories outside the current working directory. Can be overridden with the `force` option.'
278+
});
279+
280+
exists(t, ['', '1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']);
281+
});
282+
283+
test('cannot delete files inside process.cwd when outside cwd without force: true - async', async t => {
284+
process.chdir(t.context.tmp);
285+
const removeFile = path.resolve(t.context.tmp, '2.tmp');
286+
const cwd = path.resolve(t.context.tmp, '1.tmp');
287+
288+
await t.throwsAsync(() => del([removeFile], {cwd}), {
289+
instanceOf: Error,
290+
message: 'Cannot delete files/directories outside the current working directory. Can be overridden with the `force` option.'
291+
});
292+
293+
exists(t, ['1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']);
294+
process.chdir(processCwd);
295+
});
296+
297+
test('cannot delete files inside process.cwd when outside cwd without force: true - sync', t => {
298+
process.chdir(t.context.tmp);
299+
const removeFile = path.resolve(t.context.tmp, '2.tmp');
300+
const cwd = path.resolve(t.context.tmp, '1.tmp');
301+
302+
t.throws(() => del.sync([removeFile], {cwd}), {
303+
instanceOf: Error,
304+
message: 'Cannot delete files/directories outside the current working directory. Can be overridden with the `force` option.'
305+
});
306+
307+
exists(t, ['1.tmp', '2.tmp', '3.tmp', '4.tmp', '.dot.tmp']);
308+
process.chdir(processCwd);
309+
});

0 commit comments

Comments
 (0)