1'use strict'; 2const fs = require('fs'); 3const path = require('path'); 4const pify = require('pify'); 5 6const defaults = { 7 mode: 0o777 & (~process.umask()), 8 fs 9}; 10 11// https://github.com/nodejs/node/issues/8987 12// https://github.com/libuv/libuv/pull/1088 13const checkPath = pth => { 14 if (process.platform === 'win32') { 15 const pathHasInvalidWinCharacters = /[<>:"|?*]/.test(pth.replace(path.parse(pth).root, '')); 16 17 if (pathHasInvalidWinCharacters) { 18 const err = new Error(`Path contains invalid characters: ${pth}`); 19 err.code = 'EINVAL'; 20 throw err; 21 } 22 } 23}; 24 25module.exports = (input, opts) => Promise.resolve().then(() => { 26 checkPath(input); 27 opts = Object.assign({}, defaults, opts); 28 29 const mkdir = pify(opts.fs.mkdir); 30 const stat = pify(opts.fs.stat); 31 32 const make = pth => { 33 return mkdir(pth, opts.mode) 34 .then(() => pth) 35 .catch(err => { 36 if (err.code === 'ENOENT') { 37 if (err.message.includes('null bytes') || path.dirname(pth) === pth) { 38 throw err; 39 } 40 41 return make(path.dirname(pth)).then(() => make(pth)); 42 } 43 44 return stat(pth) 45 .then(stats => stats.isDirectory() ? pth : Promise.reject()) 46 .catch(() => { 47 throw err; 48 }); 49 }); 50 }; 51 52 return make(path.resolve(input)); 53}); 54 55module.exports.sync = (input, opts) => { 56 checkPath(input); 57 opts = Object.assign({}, defaults, opts); 58 59 const make = pth => { 60 try { 61 opts.fs.mkdirSync(pth, opts.mode); 62 } catch (err) { 63 if (err.code === 'ENOENT') { 64 if (err.message.includes('null bytes') || path.dirname(pth) === pth) { 65 throw err; 66 } 67 68 make(path.dirname(pth)); 69 return make(pth); 70 } 71 72 try { 73 if (!opts.fs.statSync(pth).isDirectory()) { 74 throw new Error('The path is not a directory'); 75 } 76 } catch (_) { 77 throw err; 78 } 79 } 80 81 return pth; 82 }; 83 84 return make(path.resolve(input)); 85}; 86