Created
March 2, 2010 02:16
-
-
Save rsms/319051 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From c7c0b49be9e272aa6421ed26f7b03d3c465d2313 Mon Sep 17 00:00:00 2001 | |
From: Rasmus Andersson <[email protected]> | |
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 |
:)
two modifications need to be done to this though...
- node is beginning to use strict mode so 0777 won't work... it can be 0x1ff though... you also won't be able to use ^ either, as it's passed in as a string
- it will always fail because process.ENOENT does not exist... should read: err.code === 'ENOENT'
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...
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
saved my life!
+1 for including these