Node.js version 10.12.0 and later
Node.js version 10.12.0 has added a native support for both mkdir and mkdirSync to create a directory recursively with recursive: true option as the following:
fs.mkdirSync(targetDir, { recursive: true });
And if you prefer fs Promises API, you can write
fs.promises.mkdir(targetDir, { recursive: true });
Before Node.js version 10.12.0
Create directories recursively if they do not exist! (Zero dependencies)
const fs = require('fs'); const path = require('path'); function mkDirByPathSync(targetDir, { isRelativeToScript = false } = {}) { const sep = path.sep; const initDir = path.isAbsolute(targetDir) ? sep : ''; const baseDir = isRelativeToScript ? __dirname : '.'; return targetDir.split(sep).reduce((parentDir, childDir) => { const curDir = path.resolve(baseDir, parentDir, childDir); try { fs.mkdirSync(curDir); } catch (err) { if (err.code === 'EEXIST') { // curDir already exists! return curDir; } // To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on Windows. if (err.code === 'ENOENT') { // Throw the original parentDir error on curDir `ENOENT` failure. throw new Error(`EACCES: permission denied, mkdir '${parentDir}'`); } const caughtErr = ['EACCES', 'EPERM', 'EISDIR'].indexOf(err.code) > -1; if (!caughtErr || caughtErr && curDir === path.resolve(targetDir)) { throw err; // Throw if it's just the last created dir. } } return curDir; }, initDir); }
Usage
// Default, make directories relative to current working directory. mkDirByPathSync('path/to/dir'); // Make directories relative to the current script. mkDirByPathSync('path/to/dir', {isRelativeToScript: true}); // Make directories with an absolute path. mkDirByPathSync('/path/to/dir');
Demo
Try It!
Explanations
- This solution handles platform-specific errors like
EISDIR for Mac and EPERM and EACCES for Windows. - This solution handles both relative and absolute paths.
- In the case of relative paths, target directories will be created (resolved) in the current working directory. To Resolve them relative to the current script directory, pass
{isRelativeToScript: true}. - Using
path.sep and path.resolve(), not just / concatenation, to avoid cross-platform issues. - Using
fs.mkdirSync and handling the error with try/catch if thrown to handle race conditions: another process may add the file between the calls to fs.existsSync() and fs.mkdirSync() and causes an exception. - The other way to achieve that could be checking if a file exists then creating it, I.e,
if (!fs.existsSync(curDir) fs.mkdirSync(curDir);. But this is an antipattern that leaves the code vulnerable to race conditions.
- Requires Node.js v6 and newer to support destructuring assignment.