| 1 | const { dirname } = require('path')
|
|---|
| 2 |
|
|---|
| 3 | const fileURLToPath = require('../common/file-url-to-path/index.js')
|
|---|
| 4 | const fs = require('../fs.js')
|
|---|
| 5 |
|
|---|
| 6 | const defaultOptions = {
|
|---|
| 7 | mode: 0o777,
|
|---|
| 8 | recursive: false,
|
|---|
| 9 | }
|
|---|
| 10 |
|
|---|
| 11 | const mkdir = async (path, opts) => {
|
|---|
| 12 | const options = { ...defaultOptions, ...opts }
|
|---|
| 13 |
|
|---|
| 14 | // if we're not in recursive mode, just call the real mkdir with the path and
|
|---|
| 15 | // the mode option only
|
|---|
| 16 | if (!options.recursive) {
|
|---|
| 17 | return fs.mkdir(path, options.mode)
|
|---|
| 18 | }
|
|---|
| 19 |
|
|---|
| 20 | const makeDirectory = async (dir, mode) => {
|
|---|
| 21 | // we can't use dirname directly since these functions support URL
|
|---|
| 22 | // objects with the file: protocol as the path input, so first we get a
|
|---|
| 23 | // string path, then we can call dirname on that
|
|---|
| 24 | const parent = dir != null && dir.href && dir.origin
|
|---|
| 25 | ? dirname(fileURLToPath(dir))
|
|---|
| 26 | : dirname(dir)
|
|---|
| 27 |
|
|---|
| 28 | // if the parent is the dir itself, try to create it. anything but EISDIR
|
|---|
| 29 | // should be rethrown
|
|---|
| 30 | if (parent === dir) {
|
|---|
| 31 | try {
|
|---|
| 32 | await fs.mkdir(dir, opts)
|
|---|
| 33 | } catch (err) {
|
|---|
| 34 | if (err.code !== 'EISDIR') {
|
|---|
| 35 | throw err
|
|---|
| 36 | }
|
|---|
| 37 | }
|
|---|
| 38 | return undefined
|
|---|
| 39 | }
|
|---|
| 40 |
|
|---|
| 41 | try {
|
|---|
| 42 | await fs.mkdir(dir, mode)
|
|---|
| 43 | return dir
|
|---|
| 44 | } catch (err) {
|
|---|
| 45 | // ENOENT means the parent wasn't there, so create that
|
|---|
| 46 | if (err.code === 'ENOENT') {
|
|---|
| 47 | const made = await makeDirectory(parent, mode)
|
|---|
| 48 | await makeDirectory(dir, mode)
|
|---|
| 49 | // return the shallowest path we created, i.e. the result of creating
|
|---|
| 50 | // the parent
|
|---|
| 51 | return made
|
|---|
| 52 | }
|
|---|
| 53 |
|
|---|
| 54 | // an EEXIST means there's already something there
|
|---|
| 55 | // an EROFS means we have a read-only filesystem and can't create a dir
|
|---|
| 56 | // any other error is fatal and we should give up now
|
|---|
| 57 | if (err.code !== 'EEXIST' && err.code !== 'EROFS') {
|
|---|
| 58 | throw err
|
|---|
| 59 | }
|
|---|
| 60 |
|
|---|
| 61 | // stat the directory, if the result is a directory, then we successfully
|
|---|
| 62 | // created this one so return its path. otherwise, we reject with the
|
|---|
| 63 | // original error by ignoring the error in the catch
|
|---|
| 64 | try {
|
|---|
| 65 | const stat = await fs.stat(dir)
|
|---|
| 66 | if (stat.isDirectory()) {
|
|---|
| 67 | // if it already existed, we didn't create anything so return
|
|---|
| 68 | // undefined
|
|---|
| 69 | return undefined
|
|---|
| 70 | }
|
|---|
| 71 | } catch (_) {}
|
|---|
| 72 |
|
|---|
| 73 | // if the thing that's there isn't a directory, then just re-throw
|
|---|
| 74 | throw err
|
|---|
| 75 | }
|
|---|
| 76 | }
|
|---|
| 77 |
|
|---|
| 78 | return makeDirectory(path, options.mode)
|
|---|
| 79 | }
|
|---|
| 80 |
|
|---|
| 81 | module.exports = mkdir
|
|---|