├── examples └── pow.js ├── .travis.yml ├── bin ├── usage.txt └── cmd.js ├── test ├── root.js ├── return.js ├── return_sync.js ├── opts_fs_sync.js ├── perm.js ├── mkdirp.js ├── clobber.js ├── sync.js ├── umask.js ├── opts_fs.js ├── umask_sync.js ├── rel.js ├── race.js ├── perm_sync.js └── chmod.js ├── package.json ├── LICENSE ├── readme.markdown └── index.js /examples/pow.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('mkdirp'); 2 | 3 | mkdirp('/tmp/foo/bar/baz', function (err) { 4 | if (err) console.error(err) 5 | else console.log('pow!') 6 | }); 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.8" 4 | - "0.10" 5 | - "0.12" 6 | - "iojs" 7 | - "4" 8 | - "5" 9 | before_install: 10 | - npm install -g npm@~1.4.6 11 | -------------------------------------------------------------------------------- /bin/usage.txt: -------------------------------------------------------------------------------- 1 | usage: mkdirp [DIR1,DIR2..] {OPTIONS} 2 | 3 | Create each supplied directory including any necessary parent directories that 4 | don't yet exist. 5 | 6 | If the directory already exists, do nothing. 7 | 8 | OPTIONS are: 9 | 10 | -m, --mode If a directory needs to be created, set the mode as an octal 11 | permission string. 12 | 13 | -------------------------------------------------------------------------------- /test/root.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var test = require('tap').test; 5 | var _0755 = parseInt('0755', 8); 6 | 7 | test('root', function (t) { 8 | // '/' on unix, 'c:/' on windows. 9 | var file = path.resolve('/'); 10 | 11 | mkdirp(file, _0755, function (err) { 12 | if (err) throw err 13 | fs.stat(file, function (er, stat) { 14 | if (er) throw er 15 | t.ok(stat.isDirectory(), 'target is a directory'); 16 | t.end(); 17 | }) 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mkdirp", 3 | "description": "Recursively mkdir, like `mkdir -p`", 4 | "version": "0.5.1", 5 | "author": "James Halliday (http://substack.net)", 6 | "main": "index.js", 7 | "keywords": [ 8 | "mkdir", 9 | "directory" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/substack/node-mkdirp.git" 14 | }, 15 | "scripts": { 16 | "test": "tap test/*.js" 17 | }, 18 | "dependencies": { 19 | "minimist": "0.0.8" 20 | }, 21 | "devDependencies": { 22 | "mock-fs": "^3.7.0", 23 | "tap": "^5.4.2" 24 | }, 25 | "bin": "bin/cmd.js", 26 | "license": "MIT" 27 | } 28 | -------------------------------------------------------------------------------- /bin/cmd.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var mkdirp = require('../'); 4 | var minimist = require('minimist'); 5 | var fs = require('fs'); 6 | 7 | var argv = minimist(process.argv.slice(2), { 8 | alias: { m: 'mode', h: 'help' }, 9 | string: [ 'mode' ] 10 | }); 11 | if (argv.help) { 12 | fs.createReadStream(__dirname + '/usage.txt').pipe(process.stdout); 13 | return; 14 | } 15 | 16 | var paths = argv._.slice(); 17 | var mode = argv.mode ? parseInt(argv.mode, 8) : undefined; 18 | 19 | (function next () { 20 | if (paths.length === 0) return; 21 | var p = paths.shift(); 22 | 23 | if (mode === undefined) mkdirp(p, cb) 24 | else mkdirp(p, mode, cb) 25 | 26 | function cb (err) { 27 | if (err) { 28 | console.error(err.message); 29 | process.exit(1); 30 | } 31 | else next(); 32 | } 33 | })(); 34 | -------------------------------------------------------------------------------- /test/return.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var test = require('tap').test; 5 | 6 | test('return value', function (t) { 7 | t.plan(4); 8 | var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 9 | var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 10 | var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 11 | 12 | var file = '/tmp/' + [x,y,z].join('/'); 13 | 14 | // should return the first dir created. 15 | // By this point, it would be profoundly surprising if /tmp didn't 16 | // already exist, since every other test makes things in there. 17 | mkdirp(file, function (err, made) { 18 | t.ifError(err); 19 | t.equal(made, '/tmp/' + x); 20 | mkdirp(file, function (err, made) { 21 | t.ifError(err); 22 | t.equal(made, null); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/return_sync.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var test = require('tap').test; 5 | 6 | test('return value', function (t) { 7 | t.plan(2); 8 | var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 9 | var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 10 | var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 11 | 12 | var file = '/tmp/' + [x,y,z].join('/'); 13 | 14 | // should return the first dir created. 15 | // By this point, it would be profoundly surprising if /tmp didn't 16 | // already exist, since every other test makes things in there. 17 | // Note that this will throw on failure, which will fail the test. 18 | var made = mkdirp.sync(file); 19 | t.equal(made, '/tmp/' + x); 20 | 21 | // making the same file again should have no effect. 22 | made = mkdirp.sync(file); 23 | t.equal(made, null); 24 | }); 25 | -------------------------------------------------------------------------------- /test/opts_fs_sync.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../'); 2 | var path = require('path'); 3 | var test = require('tap').test; 4 | var mockfs = require('mock-fs'); 5 | var _0777 = parseInt('0777', 8); 6 | var _0755 = parseInt('0755', 8); 7 | 8 | test('opts.fs sync', function (t) { 9 | t.plan(4); 10 | 11 | var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 12 | var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 13 | var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 14 | 15 | var file = '/beep/boop/' + [x,y,z].join('/'); 16 | var xfs = mockfs.fs(); 17 | 18 | mkdirp.sync(file, { fs: xfs, mode: _0755 }); 19 | xfs.exists(file, function (ex) { 20 | t.ok(ex, 'created file'); 21 | xfs.stat(file, function (err, stat) { 22 | t.ifError(err); 23 | t.equal(stat.mode & _0777, _0755); 24 | t.ok(stat.isDirectory(), 'target not a directory'); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/perm.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var exists = fs.exists || path.exists; 5 | var test = require('tap').test; 6 | var _0777 = parseInt('0777', 8); 7 | var _0755 = parseInt('0755', 8); 8 | 9 | test('async perm', function (t) { 10 | t.plan(5); 11 | var file = '/tmp/' + (Math.random() * (1<<30)).toString(16); 12 | 13 | mkdirp(file, _0755, function (err) { 14 | t.ifError(err); 15 | exists(file, function (ex) { 16 | t.ok(ex, 'file created'); 17 | fs.stat(file, function (err, stat) { 18 | t.ifError(err); 19 | t.equal(stat.mode & _0777, _0755); 20 | t.ok(stat.isDirectory(), 'target not a directory'); 21 | }) 22 | }) 23 | }); 24 | }); 25 | 26 | test('async root perm', function (t) { 27 | mkdirp('/tmp', _0755, function (err) { 28 | if (err) t.fail(err); 29 | t.end(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/mkdirp.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var exists = fs.exists || path.exists; 5 | var test = require('tap').test; 6 | var _0777 = parseInt('0777', 8); 7 | var _0755 = parseInt('0755', 8); 8 | 9 | test('woo', function (t) { 10 | t.plan(5); 11 | var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 12 | var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 13 | var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 14 | 15 | var file = '/tmp/' + [x,y,z].join('/'); 16 | 17 | mkdirp(file, _0755, function (err) { 18 | t.ifError(err); 19 | exists(file, function (ex) { 20 | t.ok(ex, 'file created'); 21 | fs.stat(file, function (err, stat) { 22 | t.ifError(err); 23 | t.equal(stat.mode & _0777, _0755); 24 | t.ok(stat.isDirectory(), 'target not a directory'); 25 | }) 26 | }) 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/clobber.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../').mkdirp; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var test = require('tap').test; 5 | var _0755 = parseInt('0755', 8); 6 | 7 | var ps = [ '', 'tmp' ]; 8 | 9 | for (var i = 0; i < 25; i++) { 10 | var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 11 | ps.push(dir); 12 | } 13 | 14 | var file = ps.join('/'); 15 | 16 | // a file in the way 17 | var itw = ps.slice(0, 3).join('/'); 18 | 19 | 20 | test('clobber-pre', function (t) { 21 | console.error("about to write to "+itw) 22 | fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.'); 23 | 24 | fs.stat(itw, function (er, stat) { 25 | t.ifError(er) 26 | t.ok(stat && stat.isFile(), 'should be file') 27 | t.end() 28 | }) 29 | }) 30 | 31 | test('clobber', function (t) { 32 | t.plan(2); 33 | mkdirp(file, _0755, function (err) { 34 | t.ok(err); 35 | t.equal(err.code, 'ENOTDIR'); 36 | t.end(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/sync.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var exists = fs.exists || path.exists; 5 | var test = require('tap').test; 6 | var _0777 = parseInt('0777', 8); 7 | var _0755 = parseInt('0755', 8); 8 | 9 | test('sync', function (t) { 10 | t.plan(4); 11 | var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 12 | var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 13 | var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 14 | 15 | var file = '/tmp/' + [x,y,z].join('/'); 16 | 17 | try { 18 | mkdirp.sync(file, _0755); 19 | } catch (err) { 20 | t.fail(err); 21 | return t.end(); 22 | } 23 | 24 | exists(file, function (ex) { 25 | t.ok(ex, 'file created'); 26 | fs.stat(file, function (err, stat) { 27 | t.ifError(err); 28 | t.equal(stat.mode & _0777, _0755); 29 | t.ok(stat.isDirectory(), 'target not a directory'); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/umask.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var exists = fs.exists || path.exists; 5 | var test = require('tap').test; 6 | var _0777 = parseInt('0777', 8); 7 | var _0755 = parseInt('0755', 8); 8 | 9 | test('implicit mode from umask', function (t) { 10 | t.plan(5); 11 | var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 12 | var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 13 | var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 14 | 15 | var file = '/tmp/' + [x,y,z].join('/'); 16 | 17 | mkdirp(file, function (err) { 18 | t.ifError(err); 19 | exists(file, function (ex) { 20 | t.ok(ex, 'file created'); 21 | fs.stat(file, function (err, stat) { 22 | t.ifError(err); 23 | t.equal(stat.mode & _0777, _0777 & (~process.umask())); 24 | t.ok(stat.isDirectory(), 'target not a directory'); 25 | }); 26 | }) 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/opts_fs.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../'); 2 | var path = require('path'); 3 | var test = require('tap').test; 4 | var mockfs = require('mock-fs'); 5 | var _0777 = parseInt('0777', 8); 6 | var _0755 = parseInt('0755', 8); 7 | 8 | test('opts.fs', function (t) { 9 | t.plan(5); 10 | 11 | var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 12 | var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 13 | var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 14 | 15 | var file = '/beep/boop/' + [x,y,z].join('/'); 16 | var xfs = mockfs.fs(); 17 | 18 | mkdirp(file, { fs: xfs, mode: _0755 }, function (err) { 19 | t.ifError(err); 20 | xfs.exists(file, function (ex) { 21 | t.ok(ex, 'created file'); 22 | xfs.stat(file, function (err, stat) { 23 | t.ifError(err); 24 | t.equal(stat.mode & _0777, _0755); 25 | t.ok(stat.isDirectory(), 'target not a directory'); 26 | }); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/umask_sync.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var exists = fs.exists || path.exists; 5 | var test = require('tap').test; 6 | var _0777 = parseInt('0777', 8); 7 | var _0755 = parseInt('0755', 8); 8 | 9 | test('umask sync modes', function (t) { 10 | t.plan(4); 11 | var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 12 | var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 13 | var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 14 | 15 | var file = '/tmp/' + [x,y,z].join('/'); 16 | 17 | try { 18 | mkdirp.sync(file); 19 | } catch (err) { 20 | t.fail(err); 21 | return t.end(); 22 | } 23 | 24 | exists(file, function (ex) { 25 | t.ok(ex, 'file created'); 26 | fs.stat(file, function (err, stat) { 27 | t.ifError(err); 28 | t.equal(stat.mode & _0777, (_0777 & (~process.umask()))); 29 | t.ok(stat.isDirectory(), 'target not a directory'); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/rel.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var exists = fs.exists || path.exists; 5 | var test = require('tap').test; 6 | var _0777 = parseInt('0777', 8); 7 | var _0755 = parseInt('0755', 8); 8 | 9 | test('rel', function (t) { 10 | t.plan(5); 11 | var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 12 | var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 13 | var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 14 | 15 | var cwd = process.cwd(); 16 | process.chdir('/tmp'); 17 | 18 | var file = [x,y,z].join('/'); 19 | 20 | mkdirp(file, _0755, function (err) { 21 | t.ifError(err); 22 | exists(file, function (ex) { 23 | t.ok(ex, 'file created'); 24 | fs.stat(file, function (err, stat) { 25 | t.ifError(err); 26 | process.chdir(cwd); 27 | t.equal(stat.mode & _0777, _0755); 28 | t.ok(stat.isDirectory(), 'target not a directory'); 29 | }) 30 | }) 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/race.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../').mkdirp; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var exists = fs.exists || path.exists; 5 | var test = require('tap').test; 6 | var _0777 = parseInt('0777', 8); 7 | var _0755 = parseInt('0755', 8); 8 | 9 | test('race', function (t) { 10 | t.plan(10); 11 | var ps = [ '', 'tmp' ]; 12 | 13 | for (var i = 0; i < 25; i++) { 14 | var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 15 | ps.push(dir); 16 | } 17 | var file = ps.join('/'); 18 | 19 | var res = 2; 20 | mk(file); 21 | 22 | mk(file); 23 | 24 | function mk (file, cb) { 25 | mkdirp(file, _0755, function (err) { 26 | t.ifError(err); 27 | exists(file, function (ex) { 28 | t.ok(ex, 'file created'); 29 | fs.stat(file, function (err, stat) { 30 | t.ifError(err); 31 | t.equal(stat.mode & _0777, _0755); 32 | t.ok(stat.isDirectory(), 'target not a directory'); 33 | }); 34 | }) 35 | }); 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /test/perm_sync.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var exists = fs.exists || path.exists; 5 | var test = require('tap').test; 6 | var _0777 = parseInt('0777', 8); 7 | var _0755 = parseInt('0755', 8); 8 | 9 | test('sync perm', function (t) { 10 | t.plan(4); 11 | var file = '/tmp/' + (Math.random() * (1<<30)).toString(16) + '.json'; 12 | 13 | mkdirp.sync(file, _0755); 14 | exists(file, function (ex) { 15 | t.ok(ex, 'file created'); 16 | fs.stat(file, function (err, stat) { 17 | t.ifError(err); 18 | t.equal(stat.mode & _0777, _0755); 19 | t.ok(stat.isDirectory(), 'target not a directory'); 20 | }); 21 | }); 22 | }); 23 | 24 | test('sync root perm', function (t) { 25 | t.plan(3); 26 | 27 | var file = '/tmp'; 28 | mkdirp.sync(file, _0755); 29 | exists(file, function (ex) { 30 | t.ok(ex, 'file created'); 31 | fs.stat(file, function (err, stat) { 32 | t.ifError(err); 33 | t.ok(stat.isDirectory(), 'target not a directory'); 34 | }) 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2010 James Halliday (mail@substack.net) 2 | 3 | This project is free software released under the MIT/X11 license: 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/chmod.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('../').mkdirp; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var test = require('tap').test; 5 | var _0777 = parseInt('0777', 8); 6 | var _0755 = parseInt('0755', 8); 7 | var _0744 = parseInt('0744', 8); 8 | 9 | var ps = [ '', 'tmp' ]; 10 | 11 | for (var i = 0; i < 25; i++) { 12 | var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); 13 | ps.push(dir); 14 | } 15 | 16 | var file = ps.join('/'); 17 | 18 | test('chmod-pre', function (t) { 19 | var mode = _0744 20 | mkdirp(file, mode, function (er) { 21 | t.ifError(er, 'should not error'); 22 | fs.stat(file, function (er, stat) { 23 | t.ifError(er, 'should exist'); 24 | t.ok(stat && stat.isDirectory(), 'should be directory'); 25 | t.equal(stat && stat.mode & _0777, mode, 'should be 0744'); 26 | t.end(); 27 | }); 28 | }); 29 | }); 30 | 31 | test('chmod', function (t) { 32 | var mode = _0755 33 | mkdirp(file, mode, function (er) { 34 | t.ifError(er, 'should not error'); 35 | fs.stat(file, function (er, stat) { 36 | t.ifError(er, 'should exist'); 37 | t.ok(stat && stat.isDirectory(), 'should be directory'); 38 | t.end(); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /readme.markdown: -------------------------------------------------------------------------------- 1 | # mkdirp 2 | 3 | Like `mkdir -p`, but in node.js! 4 | 5 | [![build status](https://secure.travis-ci.org/substack/node-mkdirp.png)](http://travis-ci.org/substack/node-mkdirp) 6 | 7 | # example 8 | 9 | ## pow.js 10 | 11 | ```js 12 | var mkdirp = require('mkdirp'); 13 | 14 | mkdirp('/tmp/foo/bar/baz', function (err) { 15 | if (err) console.error(err) 16 | else console.log('pow!') 17 | }); 18 | ``` 19 | 20 | Output 21 | 22 | ``` 23 | pow! 24 | ``` 25 | 26 | And now /tmp/foo/bar/baz exists, huzzah! 27 | 28 | # methods 29 | 30 | ```js 31 | var mkdirp = require('mkdirp'); 32 | ``` 33 | 34 | ## mkdirp(dir, opts, cb) 35 | 36 | Create a new directory and any necessary subdirectories at `dir` with octal 37 | permission string `opts.mode`. If `opts` is a non-object, it will be treated as 38 | the `opts.mode`. 39 | 40 | If `opts.mode` isn't specified, it defaults to `0777 & (~process.umask())`. 41 | 42 | `cb(err, made)` fires with the error or the first directory `made` 43 | that had to be created, if any. 44 | 45 | You can optionally pass in an alternate `fs` implementation by passing in 46 | `opts.fs`. Your implementation should have `opts.fs.mkdir(path, mode, cb)` and 47 | `opts.fs.stat(path, cb)`. 48 | 49 | ## mkdirp.sync(dir, opts) 50 | 51 | Synchronously create a new directory and any necessary subdirectories at `dir` 52 | with octal permission string `opts.mode`. If `opts` is a non-object, it will be 53 | treated as the `opts.mode`. 54 | 55 | If `opts.mode` isn't specified, it defaults to `0777 & (~process.umask())`. 56 | 57 | Returns the first directory that had to be created, if any. 58 | 59 | You can optionally pass in an alternate `fs` implementation by passing in 60 | `opts.fs`. Your implementation should have `opts.fs.mkdirSync(path, mode)` and 61 | `opts.fs.statSync(path)`. 62 | 63 | # usage 64 | 65 | This package also ships with a `mkdirp` command. 66 | 67 | ``` 68 | usage: mkdirp [DIR1,DIR2..] {OPTIONS} 69 | 70 | Create each supplied directory including any necessary parent directories that 71 | don't yet exist. 72 | 73 | If the directory already exists, do nothing. 74 | 75 | OPTIONS are: 76 | 77 | -m, --mode If a directory needs to be created, set the mode as an octal 78 | permission string. 79 | 80 | ``` 81 | 82 | # install 83 | 84 | With [npm](http://npmjs.org) do: 85 | 86 | ``` 87 | npm install mkdirp 88 | ``` 89 | 90 | to get the library, or 91 | 92 | ``` 93 | npm install -g mkdirp 94 | ``` 95 | 96 | to get the command. 97 | 98 | # license 99 | 100 | MIT 101 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var fs = require('fs'); 3 | var _0777 = parseInt('0777', 8); 4 | 5 | module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; 6 | 7 | function mkdirP (p, opts, f, made) { 8 | if (typeof opts === 'function') { 9 | f = opts; 10 | opts = {}; 11 | } 12 | else if (!opts || typeof opts !== 'object') { 13 | opts = { mode: opts }; 14 | } 15 | 16 | var mode = opts.mode; 17 | var xfs = opts.fs || fs; 18 | 19 | if (mode === undefined) { 20 | mode = _0777 & (~process.umask()); 21 | } 22 | if (!made) made = null; 23 | 24 | var cb = f || function () {}; 25 | p = path.resolve(p); 26 | 27 | xfs.mkdir(p, mode, function (er) { 28 | if (!er) { 29 | made = made || p; 30 | return cb(null, made); 31 | } 32 | switch (er.code) { 33 | case 'ENOENT': 34 | mkdirP(path.dirname(p), opts, function (er, made) { 35 | if (er) cb(er, made); 36 | else mkdirP(p, opts, cb, made); 37 | }); 38 | break; 39 | 40 | // In the case of any other error, just see if there's a dir 41 | // there already. If so, then hooray! If not, then something 42 | // is borked. 43 | default: 44 | xfs.stat(p, function (er2, stat) { 45 | // if the stat fails, then that's super weird. 46 | // let the original error be the failure reason. 47 | if (er2 || !stat.isDirectory()) cb(er, made) 48 | else cb(null, made); 49 | }); 50 | break; 51 | } 52 | }); 53 | } 54 | 55 | mkdirP.sync = function sync (p, opts, made) { 56 | if (!opts || typeof opts !== 'object') { 57 | opts = { mode: opts }; 58 | } 59 | 60 | var mode = opts.mode; 61 | var xfs = opts.fs || fs; 62 | 63 | if (mode === undefined) { 64 | mode = _0777 & (~process.umask()); 65 | } 66 | if (!made) made = null; 67 | 68 | p = path.resolve(p); 69 | 70 | try { 71 | xfs.mkdirSync(p, mode); 72 | made = made || p; 73 | } 74 | catch (err0) { 75 | switch (err0.code) { 76 | case 'ENOENT' : 77 | made = sync(path.dirname(p), opts, made); 78 | sync(p, opts, made); 79 | break; 80 | 81 | // In the case of any other error, just see if there's a dir 82 | // there already. If so, then hooray! If not, then something 83 | // is borked. 84 | default: 85 | var stat; 86 | try { 87 | stat = xfs.statSync(p); 88 | if (!stat.isDirectory()) throw err1; 89 | } 90 | catch (err1) { 91 | throw err0; 92 | } 93 | break; 94 | } 95 | } 96 | 97 | return made; 98 | }; 99 | --------------------------------------------------------------------------------