diff --git a/lib/file-operations.js b/lib/file-operations.js index 6ce4e83f..c5398dba 100644 --- a/lib/file-operations.js +++ b/lib/file-operations.js @@ -195,47 +195,60 @@ function writeFile(filepath, data, options, callback) { } } -function mkdirp(dirpath, mode, callback) { - if (typeof mode === 'function') { - callback = mode; - mode = undefined; +function mkdirp(dirpath, customMode, callback) { + if (typeof customMode === 'function') { + callback = customMode; + customMode = undefined; } - var m = mode || constants.DEFAULT_DIR_MODE; - var cb = callback || function() {}; + var mode = customMode || constants.DEFAULT_DIR_MODE; dirpath = path.resolve(dirpath); - fs.mkdir(dirpath, m, function(er) { - if (!er) { - return cb(); + fs.mkdir(dirpath, mode, onMkdir); + + function onMkdir(mkdirErr) { + if (!mkdirErr) { + return callback(); } - switch (er.code) { + + switch (mkdirErr.code) { case 'ENOENT': { - mkdirp(path.dirname(dirpath), m, function(er) { - if (er) { - cb(er); - } else { - mkdirp(dirpath, m, cb); - } - }); - break; + return mkdirp(path.dirname(dirpath), mode, onRecurse); } case 'EEXIST': { - if (mode) { - fs.chmod(dirpath, mode, cb); - } else { - cb(); - } - break; + return fs.stat(dirpath, onStat); } default: { - cb(er); - break; + return callback(mkdirErr); + } + } + + function onStat(statErr, stats) { + if (statErr) { + return callback(statErr); + } + + if (!stats.isDirectory()) { + return callback(mkdirErr); } + + if (!customMode) { + return callback(); + } + + fs.chmod(dirpath, customMode, callback); + } + } + + function onRecurse(recurseErr) { + if (recurseErr) { + return callback(recurseErr); } - }); + + mkdirp(dirpath, mode, callback); + } } module.exports = { diff --git a/test/file-operations.js b/test/file-operations.js index 517a161f..74c0d5bd 100644 --- a/test/file-operations.js +++ b/test/file-operations.js @@ -917,7 +917,7 @@ describe('mkdirp', function() { var DEFAULT_DIR_MODE = parseInt('0777', 8); var MODE_MASK = parseInt('0777', 8); - var dir = path.join(__dirname, './fixtures/bif'); + var dir = path.join(__dirname, './fixtures/bar'); afterEach(function() { return del(dir); @@ -955,7 +955,7 @@ describe('mkdirp', function() { }); it('makes multiple directories', function(done) { - var nestedDir = path.join(dir, './bam/bof'); + var nestedDir = path.join(dir, './baz/foo'); mkdirp(nestedDir, function(err) { expect(err).toNotExist(); @@ -974,7 +974,7 @@ describe('mkdirp', function() { return; } - var nestedDir = path.join(dir, './bam/bof'); + var nestedDir = path.join(dir, './baz/foo'); mkdirp(nestedDir, function(err) { expect(err).toNotExist(); @@ -1012,7 +1012,7 @@ describe('mkdirp', function() { return; } - var nestedDir = path.join(dir, './bam/bof'); + var nestedDir = path.join(dir, './baz/foo'); var mode = parseInt('0700',8); mkdirp(nestedDir, mode, function(err) { expect(err).toNotExist(); @@ -1053,4 +1053,51 @@ describe('mkdirp', function() { }); }); }); + + it('errors with EEXIST if file in path', function(done) { + var file = path.join(dir, './bar.txt'); + mkdirp(dir, function(err) { + expect(err).toNotExist(); + + fs.writeFile(file, 'hello world', function(err2) { + expect(err2).toNotExist(); + + mkdirp(file, function(err3) { + expect(err3).toExist(); + expect(err3.code).toEqual('EEXIST'); + + done(); + }); + }); + }); + }); + + it('does not change mode of existing file', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var file = path.join(dir, './bar.txt'); + var mode = parseInt('0700', 8); + mkdirp(dir, function(err) { + expect(err).toNotExist(); + + fs.writeFile(file, 'hello world', function(err2) { + expect(err2).toNotExist(); + + var stats = fs.statSync(file); + var expectedMode = stats.mode & MODE_MASK; + + mkdirp(file, mode, function(err3) { + expect(err3).toExist(); + + var stats = fs.statSync(file); + expect(stats.mode & MODE_MASK).toEqual(expectedMode); + + done(); + }); + }); + }); + }); });