You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
3.1 KiB
156 lines
3.1 KiB
'use strict'; |
|
const fs = require('fs'); |
|
const path = require('path'); |
|
const {promisify} = require('util'); |
|
const semver = require('semver'); |
|
|
|
const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0'); |
|
|
|
// https://github.com/nodejs/node/issues/8987 |
|
// https://github.com/libuv/libuv/pull/1088 |
|
const checkPath = pth => { |
|
if (process.platform === 'win32') { |
|
const pathHasInvalidWinCharacters = /[<>:"|?*]/.test(pth.replace(path.parse(pth).root, '')); |
|
|
|
if (pathHasInvalidWinCharacters) { |
|
const error = new Error(`Path contains invalid characters: ${pth}`); |
|
error.code = 'EINVAL'; |
|
throw error; |
|
} |
|
} |
|
}; |
|
|
|
const processOptions = options => { |
|
// https://github.com/sindresorhus/make-dir/issues/18 |
|
const defaults = { |
|
mode: 0o777, |
|
fs |
|
}; |
|
|
|
return { |
|
...defaults, |
|
...options |
|
}; |
|
}; |
|
|
|
const permissionError = pth => { |
|
// This replicates the exception of `fs.mkdir` with native the |
|
// `recusive` option when run on an invalid drive under Windows. |
|
const error = new Error(`operation not permitted, mkdir '${pth}'`); |
|
error.code = 'EPERM'; |
|
error.errno = -4048; |
|
error.path = pth; |
|
error.syscall = 'mkdir'; |
|
return error; |
|
}; |
|
|
|
const makeDir = async (input, options) => { |
|
checkPath(input); |
|
options = processOptions(options); |
|
|
|
const mkdir = promisify(options.fs.mkdir); |
|
const stat = promisify(options.fs.stat); |
|
|
|
if (useNativeRecursiveOption && options.fs.mkdir === fs.mkdir) { |
|
const pth = path.resolve(input); |
|
|
|
await mkdir(pth, { |
|
mode: options.mode, |
|
recursive: true |
|
}); |
|
|
|
return pth; |
|
} |
|
|
|
const make = async pth => { |
|
try { |
|
await mkdir(pth, options.mode); |
|
|
|
return pth; |
|
} catch (error) { |
|
if (error.code === 'EPERM') { |
|
throw error; |
|
} |
|
|
|
if (error.code === 'ENOENT') { |
|
if (path.dirname(pth) === pth) { |
|
throw permissionError(pth); |
|
} |
|
|
|
if (error.message.includes('null bytes')) { |
|
throw error; |
|
} |
|
|
|
await make(path.dirname(pth)); |
|
|
|
return make(pth); |
|
} |
|
|
|
try { |
|
const stats = await stat(pth); |
|
if (!stats.isDirectory()) { |
|
throw new Error('The path is not a directory'); |
|
} |
|
} catch (_) { |
|
throw error; |
|
} |
|
|
|
return pth; |
|
} |
|
}; |
|
|
|
return make(path.resolve(input)); |
|
}; |
|
|
|
module.exports = makeDir; |
|
|
|
module.exports.sync = (input, options) => { |
|
checkPath(input); |
|
options = processOptions(options); |
|
|
|
if (useNativeRecursiveOption && options.fs.mkdirSync === fs.mkdirSync) { |
|
const pth = path.resolve(input); |
|
|
|
fs.mkdirSync(pth, { |
|
mode: options.mode, |
|
recursive: true |
|
}); |
|
|
|
return pth; |
|
} |
|
|
|
const make = pth => { |
|
try { |
|
options.fs.mkdirSync(pth, options.mode); |
|
} catch (error) { |
|
if (error.code === 'EPERM') { |
|
throw error; |
|
} |
|
|
|
if (error.code === 'ENOENT') { |
|
if (path.dirname(pth) === pth) { |
|
throw permissionError(pth); |
|
} |
|
|
|
if (error.message.includes('null bytes')) { |
|
throw error; |
|
} |
|
|
|
make(path.dirname(pth)); |
|
return make(pth); |
|
} |
|
|
|
try { |
|
if (!options.fs.statSync(pth).isDirectory()) { |
|
throw new Error('The path is not a directory'); |
|
} |
|
} catch (_) { |
|
throw error; |
|
} |
|
} |
|
|
|
return pth; |
|
}; |
|
|
|
return make(path.resolve(input)); |
|
};
|
|
|