525

In my Node application I need to remove a directory which has some files, but fs.rmdir only works on empty directories. How can I do this?

ZachB
  • 13,051
  • 4
  • 61
  • 89
sachin
  • 13,605
  • 14
  • 42
  • 55
  • `fs.rm(dirPath, { recursive: true, force: true })` - see https://nodejs.org/api/fs.html#fs_fspromises_rm_path_options (note, I'm using `rm` and not `rmdir`) – Luis Perez Jul 03 '21 at 22:18

35 Answers35

667

As of Node.js 14.14.0, the recommended way is to use fs.rmSync:

fs.rmSync(dir, { recursive: true, force: true });
Michael Hays
  • 2,947
  • 3
  • 20
  • 30
  • 7
    @anneb That happens if you are using an older version of Node.js (< 12.10). The latest version recognizes the option `recursive: true` and deletes non-empty folders without complaint. – GOTO 0 Sep 16 '19 at 07:52
  • 8
    Function signature is actually `fs.rmdir(path[, options], callback)` or `fs.rmdirSync(path[, options])` – conceptdeluxe Nov 27 '19 at 22:51
  • @Tim what do you mean by experimental ? – Emeric Apr 18 '20 at 16:44
  • 3
    @Emerica In the official [node.js docs](https://nodejs.org/api/fs.html#fs_fs_rmdir_path_options_callback) there is a big orange notification saying `fs.rmdir` is experimental with stability 1. "Stability: 1 - Experimental. The feature is not subject to Semantic Versioning rules. Non-backward compatible changes or removal may occur in any future release. Use of the feature is not recommended in production environments." – Tim Apr 20 '20 at 18:01
  • 6
    recursive rmdir uses rimraf internally as can be seen in pull request https://github.com/nodejs/node/pull/29168/files – kaznovac Apr 21 '20 at 02:53
  • As of May 2020, Node's native solution sometimes fails. As @Tim reports, it is still an experimental function. IMO, continue using `rimraf` library. – Sergio May 26 '20 at 17:55
  • How does it sometimes fail if it's just using `rimraf` under the hood anyway? – Timmmm May 29 '20 at 11:56
  • From the [documentation](https://nodejs.org/api/fs.html#fs_fspromises_rmdir_path_options): To get a behavior similar to the `rm -rf` Unix command, use `fsPromises.rm()` with options `{ recursive: true, force: true }`. – Amin NAIRI Jun 01 '21 at 07:41
  • 21
    Please note that the `recursive` option [is deprecated](https://github.com/nodejs/node/pull/35579) since [v14.14.0](https://nodejs.org/en/blog/release/v14.14.0/). API-docs suggest to use `fs.rm(path, { recursive: true, force: true })` instead. – Siroj Matchanov Jun 09 '21 at 19:05
  • Since the recursive option has been deprecated, you should use fs.rm(). – Antonio Nov 14 '21 at 11:16
  • Misinformation. There's no deprecation warning by the above link. They only say about new behavior for non-existing paths. – Ivan Kleshnin Jun 06 '22 at 05:29
  • 1
    @IvanKleshnin `recursive` option is deprecated for `rmdir()` but isn't deprecated for `rm()` – Maxim Mazurok Sep 22 '22 at 01:04
  • I'm using exactly the statement shown in this response but I still get: ENOTEMPTY: directory not empty. I am using Node 18.13.0. – Dave Munger Mar 17 '23 at 17:23
391

There is a module for this called rimraf (https://npmjs.org/package/rimraf). It provides the same functionality as rm -Rf

Async usage:

var rimraf = require("rimraf");
rimraf("/some/directory", function () { console.log("done"); });

Sync usage:

rimraf.sync("/some/directory");
Morgan ARR Allen
  • 10,556
  • 3
  • 35
  • 33
  • 1
    Strange, I've never seen behavior like that. I'd suggest searching for and/or filing a bug. https://github.com/isaacs/rimraf/issues – Morgan ARR Allen May 02 '18 at 05:13
  • 107
    This is something that can be done easily with NodeJS Core libs why install an unmaintained 3rd party package? – SudoKid Jun 05 '18 at 23:07
  • 5
    @EmettSpeer When do you mean by "be done easily"? Self writing a function like `deleteFolderRecursive` in following answer? – Freewind Sep 10 '18 at 08:59
  • 1
    @Freewind this was so long ago and I'm not a node developer that I don't remember how I did it but even with the function below its better then adding an unneeded package to your system. – SudoKid Sep 12 '18 at 20:37
  • 36
    "but even with the function below its better then adding an unneeded package to your system." I strongly disagree. You're reinventing the wheel for the 19 millionth time for absolutely no reason at all, and risking introducing bugs or security vulnerabilities in the process. At the very least it's a waste of time. Inb4 "what if they drop the package": in the extremely unlikely event that the package is removed from the npm registry you can always replace it with your own _then._ There's no point in bandaging your head before you break it. – Demonblack Oct 16 '18 at 13:22
  • 3
    now you can use del (https://www.npmjs.com/package/del) instead of @rimraf – Alessio Campanelli Oct 26 '18 at 22:59
  • I would suggest avoiding any function that could potentially trash a users home directory. Ideally, it would be better to itemise the files you need to delete and _only_ delete them. Baking this sort of 'functionality' into your code can potentially lead to unintended and disastrous consequences. There is also the further question, why replicate a feature that the underlying OS already provides? Especially one that is so risky? Any cleanup you need to do can be done after the code has completed, or by a third party script. Just some food for thought. – sijpkes May 14 '19 at 23:48
  • 7
    you can now use a `recursive` option: https://stackoverflow.com/a/57866165/6269864 –  Sep 10 '19 at 12:30
  • Best to use rimraf as I am facing some issue with using fs.unlinkSync(). "operation not permitted" error was getting displayed. may be due to removing a file and removing is different action. – Sandip Jadhav Nov 21 '19 at 14:21
  • I'd have to argue that anybody writing a simple **if/else** control flow is certainly reinventing the wheel for the 19th billion time... So where's the issue in doing so? File system modules are constantly going unmaintained and becoming recommended for replacement by npm. I think if one would argue that it's best to 'avoid any function that could potentially trash a user's home directory' -> you certainly don't want to be leaving it up to a third party script to not do so. I know I would never trust that over my own code. Be a 'developer' and 'develop'. – Rik Nov 30 '20 at 06:58
  • @SandipJadhav That's because `unlink` is for unlinking a file descriptor (deleting a file). To remove a directory you'd want to use `fs.rmdir`. Currently (as of v12) an experimental `recursive` flag exists, but you'd otherwise need to traverse the list of 'children', recursively for any sub directories - and `unlink` the files contained, before `rmdir`ing your target. – Rik Nov 30 '20 at 07:05
  • @Demonblack You can be sure that when you don't reinvent the wheel the 3rd party one will break soon or later. – Fis Nov 23 '21 at 01:00
  • https://www.npmjs.com/package/rimraf Look at the documentation. I can't even: rimraf.sync It can remove stuff synchronously, too. But that's not so good. Use the async API. It's better. – BanAnanas May 06 '22 at 09:24
299

To remove folder synchronously

    const fs = require('fs');
    const Path = require('path');

    const deleteFolderRecursive = function (directoryPath) {
    if (fs.existsSync(directoryPath)) {
        fs.readdirSync(directoryPath).forEach((file, index) => {
          const curPath = path.join(directoryPath, file);
          if (fs.lstatSync(curPath).isDirectory()) {
           // recurse
            deleteFolderRecursive(curPath);
          } else {
            // delete file
            fs.unlinkSync(curPath);
          }
        });
        fs.rmdirSync(directoryPath);
      }
    };
Abhay
  • 3,151
  • 1
  • 19
  • 29
SharpCoder
  • 18,279
  • 43
  • 153
  • 249
  • 34
    Might want to add some checks that you're not going to accidentally run this on '/'. For example, being passed an empty path and a typo in file could result in curPath being the root dir. – Jake_Howard Mar 26 '16 at 14:48
  • 11
    More robust implementation: replace `var curPath = path + "/" + file;` with `var curPath = p.join(path, file);` provided you included the path module: `var p = require("path")` – Andry Aug 13 '16 at 06:47
  • 9
    Windows has \ slashes, so `path.join(dirpath, file)` should be better than `path + "/" + file` – thybzi Feb 28 '17 at 10:03
  • It is advised to create a local file in the current folder, e.g. ALLOW_RM, and make sure that it exists and that the given path starts with "./". Can be tested using: `if ((/^\.\//).exec(p) && fs.existsSync('ALLOW_RM')) { go-delete() }` – ishahak Mar 05 '17 at 06:32
  • 1
    Why synchronously? Isn't the main benefit of using node that the performance comes from async I/O? – Walf Mar 24 '17 at 13:11
  • 5
    You might get "Maximum call stack size exceeded" with this code due to too many operations in one tick time. @Walf if you run console application, you have 1 client, not more. So, no need to use async for console app in this case – Leonid Dashko Feb 28 '18 at 22:16
  • 1
    @Jake_Howard - to expound on Jake's statement, i ended up doing something like this...`if(dir_path == '/') return if(dir_path.split('/').length < 3) return` – buycanna.io Sep 12 '18 at 18:58
  • 5
    I receive 'Error: ENOTEMPTY: directory not empty' – Seagull Sep 20 '18 at 06:34
  • @Walf Unless you're just making simple tools, in which case async is totally unnecessary and makes simple things way overcomplicated. I don't get the "async is always better" mentality. Async has advantages and disadvantages, just like everything else. – Clonkex Jun 27 '19 at 06:25
  • @Clonkex It only takes one "simple tool" to block the entire event loop, and hang a server. – Walf Jun 27 '19 at 06:35
  • @Walf ? I'm not talking about use on a server... I just mean simple tools for doing work. Like batch image manipulation, data processing, etc. – Clonkex Jun 27 '19 at 06:48
  • @Clonkex So you're holding up image processing which could be done in parallel because you're waiting for file i/o? I'm not trying to tell you how to code, just wondering why *not* async. Yes the code is a little more verbose, but feels more natural because I'm used to writing script for browsers, where async events are prevalent. – Walf Jun 28 '19 at 00:23
  • @Walf I'm not holding up anything. It's virtually instant anyway. If I wanted it to be faster, I would opt to spend more time writing it as async. Also, it's not just "a little more verbose", it's way more complex. Instead of looping through a series of input files, determining which images need to be processed, loading them in sequence, processing them, then moving onto the next input file, I now need to manage which images are loaded and which still need loading. The first loop runs through the input files and creates a list of images to load, and records the number of images to load... – Clonkex Jun 28 '19 at 01:41
  • @Walf The second begins loading each image. On the complete callback for each image, it checks increments a counter of loaded images for that input file. If the counter == the required number of images, it can finally begin processing the images. For something that should take 10 minutes to write I now have to create a whole generic framework just to manage the images. Ugh. Drives me crazy when I can't just do things sequentially. Imagine if every function you called returned immediately, and you had to use a callback to know if it was finished. That's what it feels like to me. – Clonkex Jun 28 '19 at 01:43
  • Reuse of the variable name path is a problem here `path.join(path, file)` – Adam K Dean Oct 31 '19 at 17:12
  • RangeError: Maximum call stack size exceeded – Tyguy7 Dec 08 '19 at 20:29
  • 1
    Modern Windows is compatible with `/` for path slashes. No need to use `path.join()`. – Cameron Tacklind Jan 07 '20 at 08:40
  • I actually expected this to be faster. Compared to a `rmdir /s` it's extremely slow. Why ? – bvdb Sep 02 '21 at 22:05
192

Most of the people using fs with Node.js would like functions close to the "Unix way" of dealing with files. I'm using fs-extra to bring all the cool stuff :

fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as mkdir -p, cp -r, and rm -rf.

Even better, fs-extra is a drop in replacement for native fs. All methods in fs are unmodified and attached to it. It means that you can replace fs by fs-extra :

// this can be replaced
const fs = require('fs')

// by this
const fs = require('fs-extra')

And then you can remove a folder this way:

fs.removeSync('/tmp/myFolder'); 
//or
fs.remove('/tmp/myFolder', callback);
Masih Jahangiri
  • 9,489
  • 3
  • 45
  • 51
Pierre Maoui
  • 5,976
  • 2
  • 27
  • 28
40

As of Node v14 (October 2020), the fs module has fs.rm and rs.rmSync that support recursive, non-empty directory unlinking:

https://nodejs.org/docs/latest-v14.x/api/fs.html#fs_fs_rm_path_options_callback

So you can now do something like this:

const fs = require('fs');
fs.rm('/path/to/delete', { recursive: true }, () => console.log('done'));

or:

const fs = require('fs');
fs.rmSync('/path/to/delete', { recursive: true });
console.log('done');
Krzysztof Krzeszewski
  • 5,912
  • 2
  • 17
  • 30
Elliot Plant
  • 668
  • 6
  • 12
  • sorry for my fast, mistaken downvote. I want to correct my mistake... but I can't un-downvote till the answer is edited/updated. – Igor Savinkin Mar 19 '21 at 10:38
  • Unfortunately the documentation is misleading because it will still complain and refuse to delete empty directories, at least for me. With both recursive AND force set to true, I still get "ENOTEMPTY: directory not empty". – Dave Munger Mar 21 '23 at 05:10
28

My modified answer from @oconnecp (https://stackoverflow.com/a/25069828/3027390)

Uses path.join for better cross-platform experience. So, don't forget to require it.

var path = require('path');

Also renamed function to rimraf ;)

/**
 * Remove directory recursively
 * @param {string} dir_path
 * @see https://stackoverflow.com/a/42505874/3027390
 */
function rimraf(dir_path) {
    if (fs.existsSync(dir_path)) {
        fs.readdirSync(dir_path).forEach(function(entry) {
            var entry_path = path.join(dir_path, entry);
            if (fs.lstatSync(entry_path).isDirectory()) {
                rimraf(entry_path);
            } else {
                fs.unlinkSync(entry_path);
            }
        });
        fs.rmdirSync(dir_path);
    }
}
Rahul Kumar
  • 5,120
  • 5
  • 33
  • 44
thybzi
  • 1,223
  • 13
  • 15
21

I don't usually resurrect old threads but there is a lot on churn here and sans the rimraf answer these all seem overly complicated to me.

First in modern Node (>= v8.0.0) you can simplify the process using only node core modules, fully asynchronous, and parallelize the unlinking of files concurrently all in a function of five lines and still keep readability:

const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const readdir = promisify(fs.readdir);
const rmdir = promisify(fs.rmdir);
const unlink = promisify(fs.unlink);

exports.rmdirs = async function rmdirs(dir) {
  let entries = await readdir(dir, { withFileTypes: true });
  await Promise.all(entries.map(entry => {
    let fullPath = path.join(dir, entry.name);
    return entry.isDirectory() ? rmdirs(fullPath) : unlink(fullPath);
  }));
  await rmdir(dir);
};

On another note a guard for path traversal attacks is inappropriate for this function because

  1. It is out of scope based on the Single Responsibility Principle.
  2. Should be handled by the caller not this function. This is akin to the command-line rm -rf in that it takes an argument and will allow the user to rm -rf / if asked to. It would be the responsibility of a script to guard not the rm program itself.
  3. This function would be unable to determine such an attack since it does not have a frame of reference. Again that is the responsibility of the caller who would have the context of intent which would provide it a reference to compare the path traversal.
  4. Sym-links are not a concern as .isDirectory() is false for sym-links and are unlinked not recursed into.

Last but not least, there is a rare race condition that the recursion could error if one of the entries was unlinked or deleted outside this script at just the right time while this recursion is running. Since this scenario is not typical in most environments it can likely be overlooked. However, if required (for some edge cases) this issue can be mitigated with this slightly more complex example:

exports.rmdirs = async function rmdirs(dir) {
  let entries = await readdir(dir, { withFileTypes: true });
  let results = await Promise.all(entries.map(entry => {
    let fullPath = path.join(dir, entry.name);
    let task = entry.isDirectory() ? rmdirs(fullPath) : unlink(fullPath);
    return task.catch(error => ({ error }));
  }));
  results.forEach(result => {
    // Ignore missing files/directories; bail on other errors
    if (result && result.error.code !== 'ENOENT') throw result.error;
  });
  await rmdir(dir);
};

EDIT: Make isDirectory() a function. Remove the actual directory at the end. Fix missing recursion.

Sukima
  • 9,965
  • 3
  • 46
  • 60
  • 1
    This is a really neat solution. Question regarding the second code sample: you don’t call `await` on your `Promise.all(…)`; is this intentional? It seems like in its current state `results.forEach` would iterate over promises, while the code expects to iterate over results. Am I missing something? – Anton Strogonoff Jul 02 '19 at 23:09
  • @Tony you are correct it is a typo/bug. Good catch! – Sukima Jul 02 '19 at 23:31
  • Maybe a check first to ensure that the directory exists? something like `if (!fs.existsSync(dir)) return` – GTPV Jul 10 '19 at 14:12
  • 1
    @GTPV Why? This increases the responsibility of this function. `readdir` will throw an error as it should. If you `rmdir non-existing-dir` the exit code is an error. It would be the responsibility of the consumer to try/catch. This is the same method described in the Node docs when it comes to using fs functions. They expect you to try/catch and look at the error's `code` to determine what to do. An extra check introduces a race condition. – Sukima Jul 10 '19 at 14:15
  • I definitely see your point. I would expect though intuitively that attempting to delete a folder which does not exists would be successful as it would simply do nothing. No race condition if the synchronous version of `fs.exists` is used. P.S. this is a great solution. – GTPV Jul 10 '19 at 14:51
16

From Node docs, as one can see here.

To get a behavior similar to the rm -rf Unix command, use fs.rm() with options { recursive: true, force: true }.

For Example (ESM)

import { rm } from 'node:fs/promises';

await rm('/path/to', { recursive: true, force: true });
mdmundo
  • 1,988
  • 2
  • 23
  • 37
12

I wrote this function called remove folder. It will recursively remove all the files and folders in a location. The only package it requires is async.

var async = require('async');

function removeFolder(location, next) {
    fs.readdir(location, function (err, files) {
        async.each(files, function (file, cb) {
            file = location + '/' + file
            fs.stat(file, function (err, stat) {
                if (err) {
                    return cb(err);
                }
                if (stat.isDirectory()) {
                    removeFolder(file, cb);
                } else {
                    fs.unlink(file, function (err) {
                        if (err) {
                            return cb(err);
                        }
                        return cb();
                    })
                }
            })
        }, function (err) {
            if (err) return next(err)
            fs.rmdir(location, function (err) {
                return next(err)
            })
        })
    })
}
oconnecp
  • 722
  • 5
  • 13
  • 4
    The idea is actually to not write your own code if it has been written by somebody else already. The better way to do it is use either rimraf or fs-extra or any other node module, to do the work for you. – Victor Pudeyev Oct 02 '14 at 22:53
  • 96
    Yeah, writing your own code is terrible, because using dozens of 3rd party modules for relatively trivial operations has never proven to have any drawbacks in large scale applications. – Eric Rini May 23 '15 at 05:03
12

Here is an async version of @SharpCoder's answer

const fs = require('fs');
const path = require('path');

function deleteFile(dir, file) {
    return new Promise(function (resolve, reject) {
        var filePath = path.join(dir, file);
        fs.lstat(filePath, function (err, stats) {
            if (err) {
                return reject(err);
            }
            if (stats.isDirectory()) {
                resolve(deleteDirectory(filePath));
            } else {
                fs.unlink(filePath, function (err) {
                    if (err) {
                        return reject(err);
                    }
                    resolve();
                });
            }
        });
    });
};

function deleteDirectory(dir) {
    return new Promise(function (resolve, reject) {
        fs.access(dir, function (err) {
            if (err) {
                return reject(err);
            }
            fs.readdir(dir, function (err, files) {
                if (err) {
                    return reject(err);
                }
                Promise.all(files.map(function (file) {
                    return deleteFile(dir, file);
                })).then(function () {
                    fs.rmdir(dir, function (err) {
                        if (err) {
                            return reject(err);
                        }
                        resolve();
                    });
                }).catch(reject);
            });
        });
    });
};
Community
  • 1
  • 1
Tony Brix
  • 4,085
  • 7
  • 41
  • 53
11

[EDIT: using node.js v15.5.0]

Having just tried using some of the solutions posted here, I encountered the following deprecation warning:

(node:13202) [DEP0147] DeprecationWarning: In future versions of Node.js, fs.rmdir(path, { recursive: true }) will throw if path does not exist or is a file. Use fs.rm(path, { recursive: true, force: true }) instead

fs.rm(path, { recursive: true, force: true }); works nicely, with fs.rmSync(path, { recursive: true, force: true }); if you want to use the blocking version.

therightstuff
  • 833
  • 1
  • 16
  • 21
8

If you are using node 8+ want asyncronicity and don't want external dependencies, here is the async/await version:

const path = require('path');
const fs = require('fs');
const util = require('util');

const readdir = util.promisify(fs.readdir);
const lstat = util.promisify(fs.lstat);
const unlink = util.promisify(fs.unlink);
const rmdir = util.promisify(fs.rmdir);

const removeDir = async (dir) => {
    try {
        const files = await readdir(dir);
        await Promise.all(files.map(async (file) => {
            try {
                const p = path.join(dir, file);
                const stat = await lstat(p);
                if (stat.isDirectory()) {
                    await removeDir(p);
                } else {
                    await unlink(p);
                    console.log(`Removed file ${p}`);
                }
            } catch (err) {
                console.error(err);
            }
        }))
        await rmdir(dir);
        console.log(`Removed dir ${dir}`);
    } catch (err) {
      console.error(err);
    }
}
RonZ
  • 367
  • 2
  • 8
7
const fs = require("fs");
fs.rmdir("./test", { recursive: true }, (err) => {
  if (err) {
    console.error(err);
  }
});

Provide the recursive: true option. And it will recursively delete all the files and directories of the given path. (Assuming test is directory present at root. )

Faizi
  • 410
  • 1
  • 8
  • 13
5

2020 Update

From version 12.10.0 recursiveOption has been added for options.

Note that recursive deletion is experimental.

So you would do for sync:

fs.rmdirSync(dir, {recursive: true});

or for async:

fs.rmdir(dir, {recursive: true});
Giovanni Patruno
  • 651
  • 9
  • 15
5

According to the fs documentation, fsPromises currently provides the recursive option on an experimental basis, which, at least in my own case on Windows, removes the directory and any files therein.

fsPromises.rmdir(path, {
  recursive: true
})

Does recursive: true remove the files on Linux and MacOS?

oldboy
  • 5,729
  • 6
  • 38
  • 86
5

Explanations

As of Node.js v14, we can now use the require("fs").promises.rm function to remove a file using a promise. The first argument will be the file or folder to remove (even non-existant ones). You can use the recursive and force options in the second argument's object to mimic the behavior of the rm Shell command utility with the -rf options.

Example

"use strict";

require("fs").promises.rm("directory", {recursive: true, force: true}).then(() => {
  console.log("removed");
}).catch(error => {
  console.error(error.message);
});

See

Node.js v14 Documentation

Mozilla Developer Promises Documentation

rm command manual page

Amin NAIRI
  • 2,292
  • 21
  • 20
5

If you prefer async/await, you can use the fs/promises API.

const fs = require('fs/promises');

const removeDir = async (dirPath) => {
  await fs.rm(dirPath, {recursive: true});
}

If you know the path of a single file in the folder and wish to delete the folder where that file is contained.

const fs = require('fs/promises');
const path = require('path');

const removeDir = async (filePath) => {
  const { dir } = path.parse(filePath);
  await fs.rm(dir, { recursive: true });
}
Amit Agarwal
  • 10,910
  • 1
  • 32
  • 43
4

I reached here while trying to get over with the gulp and I'm writing for further reaches.

When you want to delete files and folders using del, you should append /** for recursive deletion.

gulp.task('clean', function () {
    return del(['some/path/to/delete/**']);
});
Jin Kwon
  • 20,295
  • 14
  • 115
  • 184
4

Async version of @SharpCoder's answer using fs.promises:

const fs = require('fs');
const afs = fs.promises;

const deleteFolderRecursive = async path =>  {
    if (fs.existsSync(path)) {
        for (let entry of await afs.readdir(path)) {
            const curPath = path + "/" + entry;
            if ((await afs.lstat(curPath)).isDirectory())
                await deleteFolderRecursive(curPath);
            else await afs.unlink(curPath);
        }
        await afs.rmdir(path);
    }
};
Error404
  • 719
  • 9
  • 30
4

In the latest version of Node.js (12.10.0 or later), the rmdir style functions fs.rmdir(), fs.rmdirSync(), and fs.promises.rmdir() have a new experimental option recursive that allows deleting non-empty directories, e.g.

fs.rmdir(path, { recursive: true });

The related PR on GitHub: https://github.com/nodejs/node/pull/29168

GOTO 0
  • 42,323
  • 22
  • 125
  • 158
4
return new Promise((resolve, reject) => {
  const fs = require("fs");
  // directory path
  const dir = "your/dir";

  // delete directory recursively <------
  fs.rmdir(dir, { recursive: true }, (err) => {
    if (err) {
      reject(err);
    }
    resolve(`${dir} is deleted!`);
  });
});
phreakphreak
  • 43
  • 1
  • 5
3

In an asynchronous approach , to delete a non empty dir using the

rmdir(path,{recursive:true,force:true}
rm(path,{recursive:true,force:true}

will work

A code snippet:

const fsp = require("fs/promises");

deleteDirRecursively("./b");
removeRecursively("./BCD/b+");

async function deleteDirRecursively(dirPath) {
  try {
    // fsPromises.rmdir() on a file (not a directory) results in the promise being rejected
    // with an ENOENT error on Windows and an ENOTDIR error on POSIX.
    // To get a behavior similar to the rm -rf Unix command,
    // use fsPromises.rm() with options { recursive: true, force: true }.
    //will not thorw error if dir is empty
    //will thow error if dir is not present
    await fsp.rmdir(dirPath, { recursive: true, force: true });
    console.log(dirPath, "deleted successfully");
  } catch (err) {
    console.log(err);
  }

async function removeRecursively(path) {
  try {
    //has ability to remove both file and dir
    //can delete dir recursively and forcefully
    //will delete an empty dir.
    //will remove all the contents of a dir.
    // the only difference between rmdir and rm is that rmdir can only delete dir's
    await fsp.rm(path, { recursive: true, force: true });
    console.log(path, "deleted successfully");
  } catch (err) {
    console.log(err);
  }
}
Mohammed Shahed
  • 840
  • 2
  • 15
2

Just use rmdir module! it's easy and simple.

Aminovski
  • 145
  • 1
  • 5
  • 7
    It is not always a good idea to use a module for every small piece of code. If you have to create a license agreement for example, this generates a real pain. – Mijago Feb 08 '17 at 07:18
  • 4
    you need to add a code sample for your answer to be more interesting – Xeltor Aug 28 '17 at 16:43
2

The de facto package is rimraf, but here is my tiny async version:

const fs = require('fs')
const path = require('path')
const Q = require('q')

function rmdir (dir) {
  return Q.nfcall(fs.access, dir, fs.constants.W_OK)
    .then(() => {
      return Q.nfcall(fs.readdir, dir)
        .then(files => files.reduce((pre, f) => pre.then(() => {
          var sub = path.join(dir, f)
          return Q.nfcall(fs.lstat, sub).then(stat => {
            if (stat.isDirectory()) return rmdir(sub)
            return Q.nfcall(fs.unlink, sub)
          })
        }), Q()))
    })
    .then(() => Q.nfcall(fs.rmdir, dir))
}

clarkttfu
  • 577
  • 6
  • 11
2

A quick and dirty way (maybe for testing) could be to directly use the exec or spawn method to invoke OS call to remove the directory. Read more on NodeJs child_process.

let exec = require('child_process').exec
exec('rm -Rf /tmp/*.zip', callback)

Downsides are:

  1. You are depending on underlying OS i.e. the same method would run in unix/linux but probably not in windows.
  2. You cannot hijack the process on conditions or errors. You just give the task to underlying OS and wait for the exit code to be returned.

Benefits:

  1. These processes can run asynchronously.
  2. You can listen for the output/error of the command, hence command output is not lost. If operation is not completed, you can check the error code and retry.
Rash
  • 7,677
  • 1
  • 53
  • 74
  • 2
    Perfect for when you write script and don't want to install dependencies because you're about to delete this script in 30 seconds after you deleted all your files!! – Mathias Nov 01 '18 at 20:18
  • There are always ways to mess up and delete the root filesystem. In this case OP can remove the `-f` flag to be safe, or make sure while typing that he/she is not going to delete everything. `exec + rm` is a valid and useful command in node which I use often during testing. – Rash May 04 '19 at 03:49
1

Ultra-speed and fail-proof

You can use the lignator package (https://www.npmjs.com/package/lignator), it's faster than any async code (e.g. rimraf) and more fail-proof (especially in Windows, where file removal is not instantaneous and files might be locked by other processes).

4,36 GB of data, 28 042 files, 4 217 folders on Windows removed in 15 seconds vs rimraf's 60 seconds on old HDD.

const lignator = require('lignator');

lignator.remove('./build/');
HankMoody
  • 3,077
  • 1
  • 17
  • 38
1

Sync folder remove with the files or only a file.

I am not much of a giver nor a contributor but I couldn't find a good solution of this problem and I had to find my way... so I hope you'll like it :)

Works perfect for me with any number of nested directories and sub directories. Caution for the scope of 'this' when recursing the function, your implementation may be different. In my case this function stays into the return of another function that's why I am calling it with this.

    const fs = require('fs');

    deleteFileOrDir(path, pathTemp = false){
            if (fs.existsSync(path)) {
                if (fs.lstatSync(path).isDirectory()) {
                    var files = fs.readdirSync(path);
                    if (!files.length) return fs.rmdirSync(path);
                    for (var file in files) {
                        var currentPath = path + "/" + files[file];
                        if (!fs.existsSync(currentPath)) continue;
                        if (fs.lstatSync(currentPath).isFile()) {
                            fs.unlinkSync(currentPath);
                            continue;
                        }
                        if (fs.lstatSync(currentPath).isDirectory() && !fs.readdirSync(currentPath).length) {
                            fs.rmdirSync(currentPath);
                        } else {
                            this.deleteFileOrDir(currentPath, path);
                        }
                    }
                    this.deleteFileOrDir(path);
                } else {
                    fs.unlinkSync(path);
                }
            }
            if (pathTemp) this.deleteFileOrDir(pathTemp);
        }
MetaTron
  • 895
  • 9
  • 14
1

2020 Answer

If you want to do it in a npm script, You DON'T need to previously install any 3rd party package if you use the command npx

If for example, you want to delete the folders dist and .cache when you run npm run clean then just add this command to your package.json

{
  "scripts": {
    "clean": "npx rimraf dist .cache"
  }
}

It will work in any operative system

Juanma Menendez
  • 17,253
  • 7
  • 59
  • 56
  • Doesn't this still require installation of the `rimraf` dependency? – trebor Jan 08 '21 at 05:26
  • @trebor no, because we are running it with *npx* , which allows running a package without installing it – Juanma Menendez Jan 09 '21 at 01:05
  • 1
    @JuanmaMenendez Thats wrong. From the [docs](https://www.npmjs.com/package/npx) of npx: *"By default, npx will check whether exists in $PATH, or in the local project binaries, and execute that. If is not found, it will be installed prior to execution."* Which seems obvious, how would you execute something which isn't there? The advantage of npx is, that it will try to find your command in your PATH and locally in your project and only do an installation if it is nowhere to be found. – derpirscher Jan 09 '21 at 10:39
  • It won't add the package to the project, though. But nonetheless it will have to install it somewhere. So for packages which are mainly intented to be called from build scripts or commandline, I prefer installing them globally, so with one installation, they are available everywhere – derpirscher Jan 09 '21 at 10:56
  • @derpirscher 'npx' behind the curtain, temporally install the packages if it needs to do it, but for us that''s transparent. With 'npx' we don't need to add the package we want to use in our package.json. We, just execute it. – Juanma Menendez Jan 10 '21 at 15:19
0

I wish there was a way to do this without additional modules for something so minuscule and common, but this is the best I could come up with.

Update: Should now work on Windows (tested Windows 10), and should also work on Linux/Unix/BSD/Mac systems.

const
    execSync = require("child_process").execSync,
    fs = require("fs"),
    os = require("os");

let removeDirCmd, theDir;

removeDirCmd = os.platform() === 'win32' ? "rmdir /s /q " : "rm -rf ";

theDir = __dirname + "/../web-ui/css/";

// WARNING: Do not specify a single file as the windows rmdir command will error.
if (fs.existsSync(theDir)) {
    console.log(' removing the ' + theDir + ' directory.');
    execSync(removeDirCmd + '"' + theDir + '"', function (err) {
        console.log(err);
    });
}
b01
  • 4,076
  • 2
  • 30
  • 30
  • Maybe fs-extra's is the way to go if you want a single module. – b01 Sep 04 '16 at 12:39
  • 3
    This method is downright dangerous. String concatenation of a shell command, especially without escaping, invites code execution vulnerabilities and similar. If you're going to use rmdir, use `child_process.execFile` which does not invoke the shell, and pass arguments explicitly instead. – nevyn Mar 14 '18 at 12:29
  • @nevyn I will give that a try and update my answer if it works. – b01 Mar 16 '18 at 16:55
  • Always prefer not to use third parties! Thanks! – Anton Mitsev Oct 12 '18 at 10:03
  • In addition to that, this method is pretty slow. Nodejs native api is much faster. – mersey Dec 12 '19 at 12:27
0

Another alternative is using the fs-promise module that provides promisified versions of the fs-extra modules

you could then write like this example:

const { remove, mkdirp, writeFile, readFile } = require('fs-promise')
const { join, dirname } = require('path')

async function createAndRemove() {
  const content = 'Hello World!'
  const root = join(__dirname, 'foo')
  const file = join(root, 'bar', 'baz', 'hello.txt')

  await mkdirp(dirname(file))
  await writeFile(file, content)
  console.log(await readFile(file, 'utf-8'))
  await remove(join(__dirname, 'foo'))
}

createAndRemove().catch(console.error)

note: async/await requires a recent nodejs version (7.6+)

Max Fichtelmann
  • 3,366
  • 1
  • 22
  • 27
0
const fs = require("fs")
const path = require("path")

let _dirloc = '<path_do_the_directory>'

if (fs.existsSync(_dirloc)) {
  fs.readdir(path, (err, files) => {
    if (!err) {
      for (let file of files) {
        // Delete each file
        fs.unlinkSync(path.join(_dirloc, file))
      }
    }
  })
  // After the 'done' of each file delete,
  // Delete the directory itself.
  if (fs.unlinkSync(_dirloc)) {
    console.log('Directory has been deleted!')
  }
}
Federico Grandi
  • 6,785
  • 5
  • 30
  • 50
Erisan Olasheni
  • 2,395
  • 17
  • 20
0

This is one approach using promisify and two help functions (to and toAll) to resolve the promise.

It does all actions asynchrounous.

const fs = require('fs');
const { promisify } = require('util');
const to = require('./to');
const toAll = require('./toAll');

const readDirAsync = promisify(fs.readdir);
const rmDirAsync = promisify(fs.rmdir);
const unlinkAsync = promisify(fs.unlink);

/**
    * @author Aécio Levy
    * @function removeDirWithFiles
    * @usage: remove dir with files
    * @param {String} path
    */
const removeDirWithFiles = async path => {
    try {
        const file = readDirAsync(path);
        const [error, files] = await to(file);
        if (error) {
            throw new Error(error)
        }
        const arrayUnlink = files.map((fileName) => {
            return unlinkAsync(`${path}/${fileName}`);
        });
        const [errorUnlink, filesUnlink] = await toAll(arrayUnlink);
        if (errorUnlink) {
            throw new Error(errorUnlink);
        }
        const deleteDir = rmDirAsync(path);
        const [errorDelete, result] = await to(deleteDir);
        if (errorDelete) {
            throw new Error(errorDelete);
        }
    } catch (err) {
        console.log(err)
    }
}; 
Aecio Levy
  • 149
  • 1
  • 8
0

//without use of any third party lib

const fs = require('fs');
var FOLDER_PATH = "./dirname";
var files = fs.readdirSync(FOLDER_PATH);
files.forEach(element => {
    fs.unlinkSync(FOLDER_PATH + "/" + element);
});
fs.rmdirSync(FOLDER_PATH);
Amy
  • 35
  • 12
  • 1
    This will work for what I need, but you might want to use path rather than concatenating the slash: `fs.unllinkSync(path.join(FOLDER_PATH, element);` – jackofallcode Aug 21 '19 at 09:27
0

While recursive is an experimental option of fs.rmdir

function rm (path, cb) {
    fs.stat(path, function (err, stats) {
        if (err)
            return cb(err);

        if (stats.isFile())
            return fs.unlink(path, cb);

        fs.rmdir(path, function (err) {
            if (!err || err && err.code != 'ENOTEMPTY') 
                return cb(err);

            fs.readdir(path, function (err, files) {
                if (err)
                    return cb(err);

                let next = i => i == files.length ? 
                    rm(path, cb) : 
                    rm(path + '/' + files[i], err => err ? cb(err) : next(i + 1));

                next(0);
            });
        });
    });
}
Aikon Mogwai
  • 4,954
  • 2
  • 18
  • 31
0

❄️ You can use graph-fs

directory.delete()
Yairopro
  • 9,084
  • 6
  • 44
  • 51