Skip to content

Commit 2002d90

Browse files
iansubcoe
authored andcommitted
fs: deprecation warning on recursive rmdir
This is a follow up to #35494 to add a deprecation warning when using recursive rmdir. This only warns if you are attempting to remove a file or a nonexistent path. PR-URL: #35562 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Ben Coe <bencoe@gmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
1 parent ab0af50 commit 2002d90

6 files changed

+95
-15
lines changed

doc/api/deprecations.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -2666,12 +2666,15 @@ changes:
26662666
- version: REPLACEME
26672667
pr-url: https://github.com/nodejs/node/pull/35579
26682668
description: Documentation-only deprecation.
2669+
- version: REPLACEME
2670+
pr-url: https://github.com/nodejs/node/pull/35562
2671+
description: Runtime deprecation.
26692672
-->
26702673

2671-
Type: Documentation-only
2674+
Type: Runtime
26722675

26732676
In future versions of Node.js, `fs.rmdir(path, { recursive: true })` will throw
2674-
on nonexistent paths, or when given a file as a target.
2677+
if `path` does not exist or is a file.
26752678
Use `fs.rm(path, { recursive: true, force: true })` instead.
26762679

26772680
[Legacy URL API]: url.md#url_legacy_url_api

lib/fs.js

+14-10
Original file line numberDiff line numberDiff line change
@@ -859,14 +859,18 @@ function rmdir(path, options, callback) {
859859
path = pathModule.toNamespacedPath(getValidatedPath(path));
860860

861861
if (options && options.recursive) {
862-
validateRmOptions(path, { ...options, force: true }, (err, options) => {
863-
if (err) {
864-
return callback(err);
865-
}
862+
options = validateRmOptions(
863+
path,
864+
{ ...options, force: true },
865+
true,
866+
(err, options) => {
867+
if (err) {
868+
return callback(err);
869+
}
866870

867-
lazyLoadRimraf();
868-
return rimraf(path, options, callback);
869-
});
871+
lazyLoadRimraf();
872+
return rimraf(path, options, callback);
873+
});
870874
} else {
871875
validateRmdirOptions(options);
872876
const req = new FSReqCallback();
@@ -879,7 +883,7 @@ function rmdirSync(path, options) {
879883
path = getValidatedPath(path);
880884

881885
if (options && options.recursive) {
882-
options = validateRmOptionsSync(path, { ...options, force: true });
886+
options = validateRmOptionsSync(path, { ...options, force: true }, true);
883887
lazyLoadRimraf();
884888
return rimrafSync(pathModule.toNamespacedPath(path), options);
885889
}
@@ -896,7 +900,7 @@ function rm(path, options, callback) {
896900
options = undefined;
897901
}
898902

899-
validateRmOptions(path, options, (err, options) => {
903+
validateRmOptions(path, options, false, (err, options) => {
900904
if (err) {
901905
return callback(err);
902906
}
@@ -906,7 +910,7 @@ function rm(path, options, callback) {
906910
}
907911

908912
function rmSync(path, options) {
909-
options = validateRmOptionsSync(path, options);
913+
options = validateRmOptionsSync(path, options, false);
910914

911915
lazyLoadRimraf();
912916
return rimrafSync(pathModule.toNamespacedPath(path), options);

lib/internal/fs/promises.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ async function ftruncate(handle, len = 0) {
421421

422422
async function rm(path, options) {
423423
path = pathModule.toNamespacedPath(getValidatedPath(path));
424-
options = await validateRmOptionsPromise(path, options);
424+
options = await validateRmOptionsPromise(path, options, false);
425425
return rimrafPromises(path, options);
426426
}
427427

lib/internal/fs/utils.js

+30-2
Original file line numberDiff line numberDiff line change
@@ -672,18 +672,25 @@ const defaultRmdirOptions = {
672672
recursive: false,
673673
};
674674

675-
const validateRmOptions = hideStackFrames((path, options, callback) => {
675+
const validateRmOptions = hideStackFrames((path, options, warn, callback) => {
676676
options = validateRmdirOptions(options, defaultRmOptions);
677677
validateBoolean(options.force, 'options.force');
678678

679679
lazyLoadFs().stat(path, (err, stats) => {
680680
if (err) {
681681
if (options.force && err.code === 'ENOENT') {
682+
if (warn) {
683+
emitPermissiveRmdirWarning();
684+
}
682685
return callback(null, options);
683686
}
684687
return callback(err, options);
685688
}
686689

690+
if (warn && !stats.isDirectory()) {
691+
emitPermissiveRmdirWarning();
692+
}
693+
687694
if (stats.isDirectory() && !options.recursive) {
688695
return callback(new ERR_FS_EISDIR({
689696
code: 'EISDIR',
@@ -697,13 +704,17 @@ const validateRmOptions = hideStackFrames((path, options, callback) => {
697704
});
698705
});
699706

700-
const validateRmOptionsSync = hideStackFrames((path, options) => {
707+
const validateRmOptionsSync = hideStackFrames((path, options, warn) => {
701708
options = validateRmdirOptions(options, defaultRmOptions);
702709
validateBoolean(options.force, 'options.force');
703710

704711
try {
705712
const stats = lazyLoadFs().statSync(path);
706713

714+
if (warn && !stats.isDirectory()) {
715+
emitPermissiveRmdirWarning();
716+
}
717+
707718
if (stats.isDirectory() && !options.recursive) {
708719
throw new ERR_FS_EISDIR({
709720
code: 'EISDIR',
@@ -718,12 +729,29 @@ const validateRmOptionsSync = hideStackFrames((path, options) => {
718729
throw err;
719730
} else if (err.code === 'ENOENT' && !options.force) {
720731
throw err;
732+
} else if (warn && err.code === 'ENOENT') {
733+
emitPermissiveRmdirWarning();
721734
}
722735
}
723736

724737
return options;
725738
});
726739

740+
let permissiveRmdirWarned = false;
741+
742+
function emitPermissiveRmdirWarning() {
743+
if (!permissiveRmdirWarned) {
744+
process.emitWarning(
745+
'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' +
746+
'will throw if path does not exist or is a file. Use fs.rm(path, ' +
747+
'{ recursive: true, force: true }) instead',
748+
'DeprecationWarning',
749+
'DEP0147'
750+
);
751+
permissiveRmdirWarned = true;
752+
}
753+
}
754+
727755
const validateRmdirOptions = hideStackFrames(
728756
(options, defaults = defaultRmdirOptions) => {
729757
if (options === undefined)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
const common = require('../common');
3+
const tmpdir = require('../common/tmpdir');
4+
const fs = require('fs');
5+
const path = require('path');
6+
7+
tmpdir.refresh();
8+
9+
10+
{
11+
// Should warn when trying to delete a nonexistent path
12+
common.expectWarning(
13+
'DeprecationWarning',
14+
'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' +
15+
'will throw if path does not exist or is a file. Use fs.rm(path, ' +
16+
'{ recursive: true, force: true }) instead',
17+
'DEP0147'
18+
);
19+
fs.rmdir(
20+
path.join(tmpdir.path, 'noexist.txt'),
21+
{ recursive: true },
22+
common.mustCall()
23+
);
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use strict';
2+
const common = require('../common');
3+
const tmpdir = require('../common/tmpdir');
4+
const fs = require('fs');
5+
const path = require('path');
6+
7+
tmpdir.refresh();
8+
9+
{
10+
// Should warn when trying to delete a file
11+
common.expectWarning(
12+
'DeprecationWarning',
13+
'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' +
14+
'will throw if path does not exist or is a file. Use fs.rm(path, ' +
15+
'{ recursive: true, force: true }) instead',
16+
'DEP0147'
17+
);
18+
const filePath = path.join(tmpdir.path, 'rmdir-recursive.txt');
19+
fs.writeFileSync(filePath, '');
20+
fs.rmdirSync(filePath, { recursive: true });
21+
}

0 commit comments

Comments
 (0)