Remove a directory that is not empty in NodeJS

A quick pro-tip for handling this situation.

NodeJS provides an easy to use fs.rmdir command that follows the POSIX standard. This unfortunately means that it will error ENOTEMPTY if there is any file in the directory you are attempting to remove. NodeJS doesn’t have an easy way to force the removal, so you have to get fancy.

By far, the easiest, safest and most cross environment approach is to use rimraf whose source code shows nearly 250 lines of premium quality work.

It’s 2018, use del.

Or if you must, try trash. It’s really neat too.

But, if you are still reading, doing it yourself is easy and a valid option. The following is a synchronous way to handle the deletion of a directory that may not be empty.

var fs = require('fs');
var deleteFolderRecursive = function(path) {
  if( fs.existsSync(path) ) {
    fs.readdirSync(path).forEach(function(file,index){
      var curPath = path + "/" + file;
      if(fs.lstatSync(curPath).isDirectory()) { // recurse
        deleteFolderRecursive(curPath);
      } else { // delete file
        fs.unlinkSync(curPath);
      }
    });
    fs.rmdirSync(path);
  }
};

I posted this solution on Stack Overflow as well.

I’ve further added an Asyncronous method for removing the directories. While it’s more code (and that means possibly brittle), and I’m afraid I haven’t battle tested it as much, it’s seemingly twice as fast in my rudimentary testing (However, all sorts of factors can affect a test like that).

var fs = require('fs');
var rmdirAsync = function(path, callback) {
    fs.readdir(path, function(err, files) {
        if(err) {
            // Pass the error on to callback
            callback(err, []);
            return;
        }
        var wait = files.length,
            count = 0,
            folderDone = function(err) {
            count++;
            // If we cleaned out all the files, continue
            if( count >= wait || err) {
                fs.rmdir(path,callback);
            }
        };
        // Empty directory to bail early
        if(!wait) {
            folderDone();
            return;
        }

        // Remove one or more trailing slash to keep from doubling up
        path = path.replace(/\/+$/,"");
        files.forEach(function(file) {
            var curPath = path + "/" + file;
            fs.lstat(curPath, function(err, stats) {
                if( err ) {
                    callback(err, []);
                    return;
                }
                if( stats.isDirectory() ) {
                    rmdirAsync(curPath, folderDone);
                } else {
                    fs.unlink(curPath, folderDone);
                }
            });
        });
    });
};