Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save rsms/319051 to your computer and use it in GitHub Desktop.
Save rsms/319051 to your computer and use it in GitHub Desktop.
From c7c0b49be9e272aa6421ed26f7b03d3c465d2313 Mon Sep 17 00:00:00 2001
From: Rasmus Andersson <rasmus@notion.se>
Date: Tue, 2 Mar 2010 03:13:41 +0100
Subject: [PATCH] Added fs.mkdirs and fs.mkdirsSync for recursively creating directories as needed
---
doc/api.txt | 10 +++++
lib/fs.js | 82 +++++++++++++++++++++++++++++++++++++++++
test/simple/test-fs-mkdirs.js | 47 +++++++++++++++++++++++
3 files changed, 139 insertions(+), 0 deletions(-)
create mode 100644 test/simple/test-fs-mkdirs.js
diff --git a/doc/api.txt b/doc/api.txt
index 071b88a..50baa79 100644
--- a/doc/api.txt
+++ b/doc/api.txt
@@ -646,6 +646,16 @@ No arguments other than a possible exception are given to the completion callbac
Synchronous mkdir(2).
++fs.mkdirs(path, [mode=(0777^umask)], [callback])+ ::
+Asynchronous recursive mkdir(2).
+The callback gets two arguments +(err, directories)+ where +directories+ is an
+array of the names of the directories which was created.
+
++fs.mkdirsSync(path, [mode=(0777^umask)])+ ::
+Synchronous vesion of +fs.mkdirs+.
+Returns a list with the names of the directories which was created.
+
+
+fs.readdir(path, callback)+ ::
Asynchronous readdir(3). Reads the contents of a directory.
The callback gets two arguments +(err, files)+ where +files+ is an array of
diff --git a/lib/fs.js b/lib/fs.js
index 0bc68a5..cfdf88d 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -1,3 +1,5 @@
+var path = require('path');
+
exports.Stats = process.Stats;
process.Stats.prototype._checkModeProperty = function (property) {
@@ -289,4 +291,84 @@ exports.unwatchFile = function (filename) {
}
};
+// Recursive mkdir
+
+// mkdirsSync(path, [mode=(0777^umask)]) -> pathsCreated
+exports.mkdirsSync = function (dirname, mode) {
+ if (mode === undefined) mode = 0777 ^ process.umask();
+ var pathsCreated = [], pathsFound = [];
+ var fn = dirname;
+ while (true) {
+ try {
+ var stats = exports.statSync(fn);
+ if (stats.isDirectory())
+ break;
+ throw new Error('Unable to create directory at '+fn);
+ }
+ catch (e) {
+ if (e.errno === process.ENOENT) {
+ pathsFound.push(fn);
+ fn = path.dirname(fn);
+ }
+ else {
+ throw e;
+ }
+ }
+ }
+ for (var i=pathsFound.length-1; i>-1; i--) {
+ var fn = pathsFound[i];
+ exports.mkdirSync(fn, mode);
+ pathsCreated.push(fn);
+ }
+ return pathsCreated;
+};
+// mkdirs(path, [mode=(0777^umask)], [callback(err, pathsCreated)])
+exports.mkdirs = function (dirname, mode, callback) {
+ if (typeof mode === 'function') {
+ callback = mode;
+ mode = undefined;
+ }
+ if (mode === undefined) mode = 0777 ^ process.umask();
+ var pathsCreated = [], pathsFound = [];
+ var makeNext = function() {
+ var fn = pathsFound.pop();
+ if (!fn) {
+ if (callback) callback(null, pathsCreated);
+ }
+ else {
+ exports.mkdir(fn, mode, function(err) {
+ if (!err) {
+ pathsCreated.push(fn);
+ makeNext();
+ }
+ else if (callback) {
+ callback(err);
+ }
+ });
+ }
+ }
+ var findNext = function(fn){
+ exports.stat(fn, function(err, stats) {
+ if (err) {
+ if (err.errno === process.ENOENT) {
+ pathsFound.push(fn);
+ findNext(path.dirname(fn));
+ }
+ else if (callback) {
+ callback(err);
+ }
+ }
+ else if (stats.isDirectory()) {
+ // create all dirs we found up to this dir
+ makeNext();
+ }
+ else {
+ if (callback) {
+ callback(new Error('Unable to create directory at '+fn));
+ }
+ }
+ });
+ }
+ findNext(dirname);
+};
diff --git a/test/simple/test-fs-mkdirs.js b/test/simple/test-fs-mkdirs.js
new file mode 100644
index 0000000..e96f67c
--- /dev/null
+++ b/test/simple/test-fs-mkdirs.js
@@ -0,0 +1,47 @@
+process.mixin(require("../common"));
+
+var dirname = path.dirname(__filename);
+var fixtures = path.join(dirname, "../fixtures");
+var dbase = path.join(fixtures, "mkdirs");
+var d = path.join(dbase, "foo/bar/deep");
+var mkdirs_error = false;
+
+function rmrf(fn, cb) {
+ return exec("rm -rf '"+fn.replace("'","\\'")+"'", function(err){
+ if (err) puts("rm -rf error: " + err.message);
+ cb(err);
+ });
+}
+
+function testAsync() {
+ fs.mkdirs(d, function (err) {
+ if (err) {
+ puts("mkdirs error: " + err.message);
+ mkdir_errors = err;
+ } else {
+ rmrf(dbase, function(err) {
+ if (err) rmdir_error = err;
+ });
+ }
+ });
+}
+
+function testSync() {
+ fs.mkdirsSync(d);
+}
+
+// rm -rf dbase
+rmrf(dbase, function(err) {
+// mkdir -p d
+ testSync();
+ // rm -rf dbase
+ rmrf(dbase, function(err) {
+ // mkdir -p d & # implies rm -rf dbase
+ testAsync();
+ });
+});
+
+process.addListener("exit", function () {
+ assert.equal(mkdirs_error, false);
+ puts("fs.mkdirs test OK");
+});
--
1.6.6
@rsms
Copy link
Author

rsms commented Mar 6, 2011

Thanks. This code is more than a year old — it was made for a much older Node.js than we have today. Maybe we should consider this fore node-core again...

@lapo-luchini
Copy link

In a project of mine I used the following simplified form:

function mkdirsSync(pathname, mode) {
    try {
    if (!fs.statSync(pathname).isDirectory())
        throw new Error('Unable to create directory at: ' + pathname);
    } catch (e) {
        if (e.code === 'ENOENT') {
            mkdirsSync(path.dirname(pathname));
            fs.mkdirSync(pathname, mode);
    } else
            throw e;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment