├── .github ├── CODEOWNERS ├── FUNDING.yml └── workflows │ ├── ci.yml │ ├── commit-if-modified.sh │ ├── copyright-year.sh │ ├── isaacs-makework.yml │ ├── package-json-repo.js │ ├── static.yml │ └── typedoc.yml ├── .gitignore ├── .prettierignore ├── CHANGELOG.md ├── LICENSE ├── fixup.sh ├── package-lock.json ├── package.json ├── readme.markdown ├── src ├── bin.ts ├── find-made.ts ├── index.ts ├── mkdirp-manual.ts ├── mkdirp-native.ts ├── opts-arg.ts ├── path-arg.ts └── use-native.ts ├── tap-snapshots ├── test-cmd.js-TAP.test.js └── test │ └── cmd.ts.test.cjs ├── test ├── cmd.ts ├── find-made.ts ├── index.ts ├── mkdirp-manual.ts ├── mkdirp-native.ts ├── opts-arg.ts ├── path-arg.ts └── use-native.ts ├── tsconfig-base.json ├── tsconfig-esm.json ├── tsconfig.json └── typedoc.json /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * isaacs 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [isaacs, substack] 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | node-version: [16.x, 18.x, 19.x] 11 | platform: 12 | - os: ubuntu-latest 13 | shell: bash 14 | - os: macos-latest 15 | shell: bash 16 | - os: windows-latest 17 | shell: powershell 18 | 19 | runs-on: ${{ matrix.platform.os }} 20 | defaults: 21 | run: 22 | shell: ${{ matrix.platform.shell }} 23 | 24 | steps: 25 | # Checkout the npm/cli repo 26 | - uses: actions/checkout@v3 27 | 28 | # Installs the specific version of Node.js 29 | - name: Use Node.js ${{ matrix.node-version }} 30 | uses: actions/setup-node@v3 31 | with: 32 | node-version: ${{ matrix.node-version }} 33 | cache: npm 34 | 35 | # Run the installer script 36 | - name: Install dependencies 37 | run: npm ci 38 | 39 | # Run the tests 40 | - name: Run Tap tests 41 | run: npm test -- -t0 -c 42 | -------------------------------------------------------------------------------- /.github/workflows/commit-if-modified.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | git config --global user.email "$1" 3 | shift 4 | git config --global user.name "$1" 5 | shift 6 | message="$1" 7 | shift 8 | if [ $(git status --porcelain "$@" | egrep '^ M' | wc -l) -gt 0 ]; then 9 | git add "$@" 10 | git commit -m "$message" 11 | git push || git pull --rebase 12 | git push 13 | fi 14 | -------------------------------------------------------------------------------- /.github/workflows/copyright-year.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | dir=${1:-$PWD} 3 | dates=($(git log --date=format:%Y --pretty=format:'%ad' --reverse | sort | uniq)) 4 | if [ "${#dates[@]}" -eq 1 ]; then 5 | datestr="${dates}" 6 | else 7 | datestr="${dates}-${dates[${#dates[@]}-1]}" 8 | fi 9 | 10 | stripDate='s/^((.*)Copyright\b(.*?))((?:,\s*)?(([0-9]{4}\s*-\s*[0-9]{4})|(([0-9]{4},\s*)*[0-9]{4})))(?:,)?\s*(.*)\n$/$1$9\n/g' 11 | addDate='s/^.*Copyright(?:\s*\(c\))? /Copyright \(c\) '$datestr' /g' 12 | for l in $dir/LICENSE*; do 13 | perl -pi -e "$stripDate" $l 14 | perl -pi -e "$addDate" $l 15 | done 16 | -------------------------------------------------------------------------------- /.github/workflows/isaacs-makework.yml: -------------------------------------------------------------------------------- 1 | name: "various tidying up tasks to silence nagging" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | makework: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | with: 15 | fetch-depth: 0 16 | - name: Use Node.js 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version: 16.x 20 | - name: put repo in package.json 21 | run: node .github/workflows/package-json-repo.js 22 | - name: check in package.json if modified 23 | run: | 24 | bash -x .github/workflows/commit-if-modified.sh \ 25 | "package-json-repo-bot@example.com" \ 26 | "package.json Repo Bot" \ 27 | "chore: add repo to package.json" \ 28 | package.json package-lock.json 29 | - name: put all dates in license copyright line 30 | run: bash .github/workflows/copyright-year.sh 31 | - name: check in licenses if modified 32 | run: | 33 | bash .github/workflows/commit-if-modified.sh \ 34 | "license-year-bot@example.com" \ 35 | "License Year Bot" \ 36 | "chore: add copyright year to license" \ 37 | LICENSE* 38 | -------------------------------------------------------------------------------- /.github/workflows/package-json-repo.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const pf = require.resolve(`${process.cwd()}/package.json`) 4 | const pj = require(pf) 5 | 6 | if (!pj.repository && process.env.GITHUB_REPOSITORY) { 7 | const fs = require('fs') 8 | const server = process.env.GITHUB_SERVER_URL || 'https://github.com' 9 | const repo = `${server}/${process.env.GITHUB_REPOSITORY}` 10 | pj.repository = repo 11 | const json = fs.readFileSync(pf, 'utf8') 12 | const match = json.match(/^\s*\{[\r\n]+([ \t]*)"/) 13 | const indent = match[1] 14 | const output = JSON.stringify(pj, null, indent || 2) + '\n' 15 | fs.writeFileSync(pf, output) 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow one concurrent deployment 19 | concurrency: 20 | group: "pages" 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | # Single deploy job since we're just deploying 25 | deploy: 26 | environment: 27 | name: github-pages 28 | url: ${{ steps.deployment.outputs.page_url }} 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v3 33 | - name: Use Nodejs 34 | uses: actions/setup-node@v3 35 | with: 36 | node-version: 18.x 37 | cache: npm 38 | - name: Install dependencies 39 | run: npm ci 40 | - name: Generate typedocs 41 | run: npm run typedoc 42 | - name: Setup Pages 43 | uses: actions/configure-pages@v3 44 | - name: Upload artifact 45 | uses: actions/upload-pages-artifact@v1 46 | with: 47 | path: './docs' 48 | - name: Deploy to GitHub Pages 49 | id: deployment 50 | uses: actions/deploy-pages@v1 51 | -------------------------------------------------------------------------------- /.github/workflows/typedoc.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow one concurrent deployment 19 | concurrency: 20 | group: "pages" 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | # Single deploy job since we're just deploying 25 | deploy: 26 | environment: 27 | name: github-pages 28 | url: ${{ steps.deployment.outputs.page_url }} 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v3 33 | - name: Use Nodejs 34 | uses: actions/setup-node@v3 35 | with: 36 | node-version: 18.x 37 | cache: npm 38 | - name: Install dependencies 39 | run: npm ci 40 | - name: Generate typedocs 41 | run: npm run typedoc 42 | 43 | - name: Setup Pages 44 | uses: actions/configure-pages@v3 45 | - name: Upload artifact 46 | uses: actions/upload-pages-artifact@v1 47 | with: 48 | path: './docs' 49 | - name: Deploy to GitHub Pages 50 | id: deployment 51 | uses: actions/deploy-pages@v1 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore most things, include some others 2 | /* 3 | /.* 4 | !src/ 5 | !.github 6 | !bin/ 7 | !docs/ 8 | !package.json 9 | !package-lock.json 10 | !README.md 11 | !CONTRIBUTING.md 12 | !LICENSE 13 | !CHANGELOG.md 14 | !example/ 15 | !scripts/ 16 | !tap-snapshots/ 17 | !test/ 18 | !.travis.yml 19 | !.gitignore 20 | !.gitattributes 21 | !/tsconfig*.json 22 | !/fixup.sh 23 | !/.prettierignore 24 | !/typedoc.json 25 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /example 3 | /.github 4 | /dist 5 | .env 6 | /tap-snapshots 7 | /.nyc_output 8 | /coverage 9 | /benchmark 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changers Lorgs! 2 | 3 | ## 3.0 4 | 5 | No default exports, just a named export. 6 | 7 | ## 2.1 8 | 9 | Export CommonJS module without a `.default` dangly wart. (A 10 | synthetic `.default` has been added just in case anyone is already 11 | relying on that from v2.0.) 12 | 13 | ## 2.0 14 | 15 | Export hybrid module with TypeScript types. 16 | 17 | ## 1.0 18 | 19 | Full rewrite. Essentially a brand new module. 20 | 21 | - Return a promise instead of taking a callback. 22 | - Use native `fs.mkdir(path, { recursive: true })` when available. 23 | - Drop support for outdated Node.js versions. (Technically still works on 24 | Node.js v8, but only 10 and above are officially supported.) 25 | 26 | ## 0.x 27 | 28 | Original and most widely used recursive directory creation implementation 29 | in JavaScript, dating back to 2010. 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2023 James Halliday (mail@substack.net) and Isaac Z. Schlueter (i@izs.me) 2 | 3 | This project is free software released under the MIT license: 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /fixup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cat >dist/cjs/package.json <dist/mjs/package.json <=10" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /readme.markdown: -------------------------------------------------------------------------------- 1 | # mkdirp 2 | 3 | Like `mkdir -p`, but in Node.js! 4 | 5 | Now with a modern API and no\* bugs! 6 | 7 | \* may contain some bugs 8 | 9 | # example 10 | 11 | ## pow.js 12 | 13 | ```js 14 | // hybrid module, import or require() both work 15 | import { mkdirp } from 'mkdirp' 16 | // or: 17 | const { mkdirp } = require('mkdirp') 18 | 19 | // return value is a Promise resolving to the first directory created 20 | mkdirp('/tmp/foo/bar/baz').then(made => 21 | console.log(`made directories, starting with ${made}`) 22 | ) 23 | ``` 24 | 25 | Output (where `/tmp/foo` already exists) 26 | 27 | ``` 28 | made directories, starting with /tmp/foo/bar 29 | ``` 30 | 31 | Or, if you don't have time to wait around for promises: 32 | 33 | ```js 34 | import { mkdirp } from 'mkdirp' 35 | 36 | // return value is the first directory created 37 | const made = mkdirp.sync('/tmp/foo/bar/baz') 38 | console.log(`made directories, starting with ${made}`) 39 | ``` 40 | 41 | And now /tmp/foo/bar/baz exists, huzzah! 42 | 43 | # methods 44 | 45 | ```js 46 | import { mkdirp } from 'mkdirp' 47 | ``` 48 | 49 | ## `mkdirp(dir: string, opts?: MkdirpOptions) => Promise` 50 | 51 | Create a new directory and any necessary subdirectories at `dir` 52 | with octal permission string `opts.mode`. If `opts` is a string 53 | or number, it will be treated as the `opts.mode`. 54 | 55 | If `opts.mode` isn't specified, it defaults to `0o777`. 56 | 57 | Promise resolves to first directory `made` that had to be 58 | created, or `undefined` if everything already exists. Promise 59 | rejects if any errors are encountered. Note that, in the case of 60 | promise rejection, some directories _may_ have been created, as 61 | recursive directory creation is not an atomic operation. 62 | 63 | You can optionally pass in an alternate `fs` implementation by 64 | passing in `opts.fs`. Your implementation should have 65 | `opts.fs.mkdir(path, opts, cb)` and `opts.fs.stat(path, cb)`. 66 | 67 | You can also override just one or the other of `mkdir` and `stat` 68 | by passing in `opts.stat` or `opts.mkdir`, or providing an `fs` 69 | option that only overrides one of these. 70 | 71 | ## `mkdirp.sync(dir: string, opts: MkdirpOptions) => string|undefined` 72 | 73 | Synchronously create a new directory and any necessary 74 | subdirectories at `dir` with octal permission string `opts.mode`. 75 | If `opts` is a string or number, it will be treated as the 76 | `opts.mode`. 77 | 78 | If `opts.mode` isn't specified, it defaults to `0o777`. 79 | 80 | Returns the first directory that had to be created, or undefined 81 | if everything already exists. 82 | 83 | You can optionally pass in an alternate `fs` implementation by 84 | passing in `opts.fs`. Your implementation should have 85 | `opts.fs.mkdirSync(path, mode)` and `opts.fs.statSync(path)`. 86 | 87 | You can also override just one or the other of `mkdirSync` and 88 | `statSync` by passing in `opts.statSync` or `opts.mkdirSync`, or 89 | providing an `fs` option that only overrides one of these. 90 | 91 | ## `mkdirp.manual`, `mkdirp.manualSync` 92 | 93 | Use the manual implementation (not the native one). This is the 94 | default when the native implementation is not available or the 95 | stat/mkdir implementation is overridden. 96 | 97 | ## `mkdirp.native`, `mkdirp.nativeSync` 98 | 99 | Use the native implementation (not the manual one). This is the 100 | default when the native implementation is available and 101 | stat/mkdir are not overridden. 102 | 103 | # implementation 104 | 105 | On Node.js v10.12.0 and above, use the native `fs.mkdir(p, 106 | {recursive:true})` option, unless `fs.mkdir`/`fs.mkdirSync` has 107 | been overridden by an option. 108 | 109 | ## native implementation 110 | 111 | - If the path is a root directory, then pass it to the underlying 112 | implementation and return the result/error. (In this case, 113 | it'll either succeed or fail, but we aren't actually creating 114 | any dirs.) 115 | - Walk up the path statting each directory, to find the first 116 | path that will be created, `made`. 117 | - Call `fs.mkdir(path, { recursive: true })` (or `fs.mkdirSync`) 118 | - If error, raise it to the caller. 119 | - Return `made`. 120 | 121 | ## manual implementation 122 | 123 | - Call underlying `fs.mkdir` implementation, with `recursive: 124 | false` 125 | - If error: 126 | - If path is a root directory, raise to the caller and do not 127 | handle it 128 | - If ENOENT, mkdirp parent dir, store result as `made` 129 | - stat(path) 130 | - If error, raise original `mkdir` error 131 | - If directory, return `made` 132 | - Else, raise original `mkdir` error 133 | - else 134 | - return `undefined` if a root dir, or `made` if set, or `path` 135 | 136 | ## windows vs unix caveat 137 | 138 | On Windows file systems, attempts to create a root directory (ie, 139 | a drive letter or root UNC path) will fail. If the root 140 | directory exists, then it will fail with `EPERM`. If the root 141 | directory does not exist, then it will fail with `ENOENT`. 142 | 143 | On posix file systems, attempts to create a root directory (in 144 | recursive mode) will succeed silently, as it is treated like just 145 | another directory that already exists. (In non-recursive mode, 146 | of course, it fails with `EEXIST`.) 147 | 148 | In order to preserve this system-specific behavior (and because 149 | it's not as if we can create the parent of a root directory 150 | anyway), attempts to create a root directory are passed directly 151 | to the `fs` implementation, and any errors encountered are not 152 | handled. 153 | 154 | ## native error caveat 155 | 156 | The native implementation (as of at least Node.js v13.4.0) does 157 | not provide appropriate errors in some cases (see 158 | [nodejs/node#31481](https://github.com/nodejs/node/issues/31481) 159 | and 160 | [nodejs/node#28015](https://github.com/nodejs/node/issues/28015)). 161 | 162 | In order to work around this issue, the native implementation 163 | will fall back to the manual implementation if an `ENOENT` error 164 | is encountered. 165 | 166 | # choosing a recursive mkdir implementation 167 | 168 | There are a few to choose from! Use the one that suits your 169 | needs best :D 170 | 171 | ## use `fs.mkdir(path, {recursive: true}, cb)` if: 172 | 173 | - You wish to optimize performance even at the expense of other 174 | factors. 175 | - You don't need to know the first dir created. 176 | - You are ok with getting `ENOENT` as the error when some other 177 | problem is the actual cause. 178 | - You can limit your platforms to Node.js v10.12 and above. 179 | - You're ok with using callbacks instead of promises. 180 | - You don't need/want a CLI. 181 | - You don't need to override the `fs` methods in use. 182 | 183 | ## use this module (mkdirp 1.x or 2.x) if: 184 | 185 | - You need to know the first directory that was created. 186 | - You wish to use the native implementation if available, but 187 | fall back when it's not. 188 | - You prefer promise-returning APIs to callback-taking APIs. 189 | - You want more useful error messages than the native recursive 190 | mkdir provides (at least as of Node.js v13.4), and are ok with 191 | re-trying on `ENOENT` to achieve this. 192 | - You need (or at least, are ok with) a CLI. 193 | - You need to override the `fs` methods in use. 194 | 195 | ## use [`make-dir`](http://npm.im/make-dir) if: 196 | 197 | - You do not need to know the first dir created (and wish to save 198 | a few `stat` calls when using the native implementation for 199 | this reason). 200 | - You wish to use the native implementation if available, but 201 | fall back when it's not. 202 | - You prefer promise-returning APIs to callback-taking APIs. 203 | - You are ok with occasionally getting `ENOENT` errors for 204 | failures that are actually related to something other than a 205 | missing file system entry. 206 | - You don't need/want a CLI. 207 | - You need to override the `fs` methods in use. 208 | 209 | ## use mkdirp 0.x if: 210 | 211 | - You need to know the first directory that was created. 212 | - You need (or at least, are ok with) a CLI. 213 | - You need to override the `fs` methods in use. 214 | - You're ok with using callbacks instead of promises. 215 | - You are not running on Windows, where the root-level ENOENT 216 | errors can lead to infinite regress. 217 | - You think vinyl just sounds warmer and richer for some weird 218 | reason. 219 | - You are supporting truly ancient Node.js versions, before even 220 | the advent of a `Promise` language primitive. (Please don't. 221 | You deserve better.) 222 | 223 | # cli 224 | 225 | This package also ships with a `mkdirp` command. 226 | 227 | ``` 228 | $ mkdirp -h 229 | 230 | usage: mkdirp [DIR1,DIR2..] {OPTIONS} 231 | 232 | Create each supplied directory including any necessary parent directories 233 | that don't yet exist. 234 | 235 | If the directory already exists, do nothing. 236 | 237 | OPTIONS are: 238 | 239 | -m If a directory needs to be created, set the mode as an octal 240 | --mode= permission string. 241 | 242 | -v --version Print the mkdirp version number 243 | 244 | -h --help Print this helpful banner 245 | 246 | -p --print Print the first directories created for each path provided 247 | 248 | --manual Use manual implementation, even if native is available 249 | ``` 250 | 251 | # install 252 | 253 | With [npm](http://npmjs.org) do: 254 | 255 | ``` 256 | npm install mkdirp 257 | ``` 258 | 259 | to get the library locally, or 260 | 261 | ``` 262 | npm install -g mkdirp 263 | ``` 264 | 265 | to get the command everywhere, or 266 | 267 | ``` 268 | npx mkdirp ... 269 | ``` 270 | 271 | to run the command without installing it globally. 272 | 273 | # platform support 274 | 275 | This module works on node v8, but only v10 and above are officially 276 | supported, as Node v8 reached its LTS end of life 2020-01-01, which is in 277 | the past, as of this writing. 278 | 279 | # license 280 | 281 | MIT 282 | -------------------------------------------------------------------------------- /src/bin.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { version } from '../package.json' 4 | import { MkdirpOptions } from './opts-arg.js' 5 | 6 | const usage = () => ` 7 | usage: mkdirp [DIR1,DIR2..] {OPTIONS} 8 | 9 | Create each supplied directory including any necessary parent directories 10 | that don't yet exist. 11 | 12 | If the directory already exists, do nothing. 13 | 14 | OPTIONS are: 15 | 16 | -m If a directory needs to be created, set the mode as an octal 17 | --mode= permission string. 18 | 19 | -v --version Print the mkdirp version number 20 | 21 | -h --help Print this helpful banner 22 | 23 | -p --print Print the first directories created for each path provided 24 | 25 | --manual Use manual implementation, even if native is available 26 | ` 27 | 28 | const dirs: string[] = [] 29 | const opts: MkdirpOptions = {} 30 | let doPrint: boolean = false 31 | let dashdash = false 32 | let manual = false 33 | for (const arg of process.argv.slice(2)) { 34 | if (dashdash) dirs.push(arg) 35 | else if (arg === '--') dashdash = true 36 | else if (arg === '--manual') manual = true 37 | else if (/^-h/.test(arg) || /^--help/.test(arg)) { 38 | console.log(usage()) 39 | process.exit(0) 40 | } else if (arg === '-v' || arg === '--version') { 41 | console.log(version) 42 | process.exit(0) 43 | } else if (arg === '-p' || arg === '--print') { 44 | doPrint = true 45 | } else if (/^-m/.test(arg) || /^--mode=/.test(arg)) { 46 | // these don't get covered in CI, but work locally 47 | // weird because the tests below show as passing in the output. 48 | /* c8 ignore start */ 49 | const mode = parseInt(arg.replace(/^(-m|--mode=)/, ''), 8) 50 | if (isNaN(mode)) { 51 | console.error(`invalid mode argument: ${arg}\nMust be an octal number.`) 52 | process.exit(1) 53 | } 54 | /* c8 ignore stop */ 55 | opts.mode = mode 56 | } else dirs.push(arg) 57 | } 58 | 59 | import { mkdirp } from './index.js' 60 | const impl = manual ? mkdirp.manual : mkdirp 61 | if (dirs.length === 0) { 62 | console.error(usage()) 63 | } 64 | 65 | // these don't get covered in CI, but work locally 66 | /* c8 ignore start */ 67 | Promise.all(dirs.map(dir => impl(dir, opts))) 68 | .then(made => (doPrint ? made.forEach(m => m && console.log(m)) : null)) 69 | .catch(er => { 70 | console.error(er.message) 71 | if (er.code) console.error(' code: ' + er.code) 72 | process.exit(1) 73 | }) 74 | /* c8 ignore stop */ 75 | -------------------------------------------------------------------------------- /src/find-made.ts: -------------------------------------------------------------------------------- 1 | import { dirname } from 'path' 2 | import { MkdirpOptionsResolved } from './opts-arg.js' 3 | 4 | export const findMade = async ( 5 | opts: MkdirpOptionsResolved, 6 | parent: string, 7 | path?: string 8 | ): Promise => { 9 | // we never want the 'made' return value to be a root directory 10 | if (path === parent) { 11 | return 12 | } 13 | 14 | return opts.statAsync(parent).then( 15 | st => (st.isDirectory() ? path : undefined), // will fail later 16 | er => { 17 | const fer = er as NodeJS.ErrnoException 18 | return fer && fer.code === 'ENOENT' 19 | ? findMade(opts, dirname(parent), parent) 20 | : undefined 21 | } 22 | ) 23 | } 24 | 25 | export const findMadeSync = ( 26 | opts: MkdirpOptionsResolved, 27 | parent: string, 28 | path?: string 29 | ): undefined | string => { 30 | if (path === parent) { 31 | return undefined 32 | } 33 | 34 | try { 35 | return opts.statSync(parent).isDirectory() ? path : undefined 36 | } catch (er) { 37 | const fer = er as NodeJS.ErrnoException 38 | return fer && fer.code === 'ENOENT' 39 | ? findMadeSync(opts, dirname(parent), parent) 40 | : undefined 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { mkdirpManual, mkdirpManualSync } from './mkdirp-manual.js' 2 | import { mkdirpNative, mkdirpNativeSync } from './mkdirp-native.js' 3 | import { MkdirpOptions, optsArg } from './opts-arg.js' 4 | import { pathArg } from './path-arg.js' 5 | import { useNative, useNativeSync } from './use-native.js' 6 | /* c8 ignore start */ 7 | export { mkdirpManual, mkdirpManualSync } from './mkdirp-manual.js' 8 | export { mkdirpNative, mkdirpNativeSync } from './mkdirp-native.js' 9 | export { useNative, useNativeSync } from './use-native.js' 10 | /* c8 ignore stop */ 11 | 12 | export const mkdirpSync = (path: string, opts?: MkdirpOptions) => { 13 | path = pathArg(path) 14 | const resolved = optsArg(opts) 15 | return useNativeSync(resolved) 16 | ? mkdirpNativeSync(path, resolved) 17 | : mkdirpManualSync(path, resolved) 18 | } 19 | 20 | export const sync = mkdirpSync 21 | export const manual = mkdirpManual 22 | export const manualSync = mkdirpManualSync 23 | export const native = mkdirpNative 24 | export const nativeSync = mkdirpNativeSync 25 | export const mkdirp = Object.assign( 26 | async (path: string, opts?: MkdirpOptions) => { 27 | path = pathArg(path) 28 | const resolved = optsArg(opts) 29 | return useNative(resolved) 30 | ? mkdirpNative(path, resolved) 31 | : mkdirpManual(path, resolved) 32 | }, 33 | { 34 | mkdirpSync, 35 | mkdirpNative, 36 | mkdirpNativeSync, 37 | mkdirpManual, 38 | mkdirpManualSync, 39 | 40 | sync: mkdirpSync, 41 | native: mkdirpNative, 42 | nativeSync: mkdirpNativeSync, 43 | manual: mkdirpManual, 44 | manualSync: mkdirpManualSync, 45 | useNative, 46 | useNativeSync, 47 | } 48 | ) 49 | -------------------------------------------------------------------------------- /src/mkdirp-manual.ts: -------------------------------------------------------------------------------- 1 | import { dirname } from 'path' 2 | import { MkdirpOptions, optsArg } from './opts-arg.js' 3 | 4 | export const mkdirpManualSync = ( 5 | path: string, 6 | options?: MkdirpOptions, 7 | made?: string | undefined | void 8 | ): string | undefined | void => { 9 | const parent = dirname(path) 10 | const opts = { ...optsArg(options), recursive: false } 11 | 12 | if (parent === path) { 13 | try { 14 | return opts.mkdirSync(path, opts) 15 | } catch (er) { 16 | // swallowed by recursive implementation on posix systems 17 | // any other error is a failure 18 | const fer = er as NodeJS.ErrnoException 19 | if (fer && fer.code !== 'EISDIR') { 20 | throw er 21 | } 22 | return 23 | } 24 | } 25 | 26 | try { 27 | opts.mkdirSync(path, opts) 28 | return made || path 29 | } catch (er) { 30 | const fer = er as NodeJS.ErrnoException 31 | if (fer && fer.code === 'ENOENT') { 32 | return mkdirpManualSync(path, opts, mkdirpManualSync(parent, opts, made)) 33 | } 34 | if (fer && fer.code !== 'EEXIST' && fer && fer.code !== 'EROFS') { 35 | throw er 36 | } 37 | try { 38 | if (!opts.statSync(path).isDirectory()) throw er 39 | } catch (_) { 40 | throw er 41 | } 42 | } 43 | } 44 | 45 | export const mkdirpManual = Object.assign( 46 | async ( 47 | path: string, 48 | options?: MkdirpOptions, 49 | made?: string | undefined | void 50 | ): Promise => { 51 | const opts = optsArg(options) 52 | opts.recursive = false 53 | const parent = dirname(path) 54 | if (parent === path) { 55 | return opts.mkdirAsync(path, opts).catch(er => { 56 | // swallowed by recursive implementation on posix systems 57 | // any other error is a failure 58 | const fer = er as NodeJS.ErrnoException 59 | if (fer && fer.code !== 'EISDIR') { 60 | throw er 61 | } 62 | }) 63 | } 64 | 65 | return opts.mkdirAsync(path, opts).then( 66 | () => made || path, 67 | async er => { 68 | const fer = er as NodeJS.ErrnoException 69 | if (fer && fer.code === 'ENOENT') { 70 | return mkdirpManual(parent, opts).then( 71 | (made?: string | undefined | void) => mkdirpManual(path, opts, made) 72 | ) 73 | } 74 | if (fer && fer.code !== 'EEXIST' && fer.code !== 'EROFS') { 75 | throw er 76 | } 77 | return opts.statAsync(path).then( 78 | st => { 79 | if (st.isDirectory()) { 80 | return made 81 | } else { 82 | throw er 83 | } 84 | }, 85 | () => { 86 | throw er 87 | } 88 | ) 89 | } 90 | ) 91 | }, 92 | { sync: mkdirpManualSync } 93 | ) 94 | -------------------------------------------------------------------------------- /src/mkdirp-native.ts: -------------------------------------------------------------------------------- 1 | import { dirname } from 'path' 2 | import { findMade, findMadeSync } from './find-made.js' 3 | import { mkdirpManual, mkdirpManualSync } from './mkdirp-manual.js' 4 | import { MkdirpOptions, optsArg } from './opts-arg.js' 5 | 6 | export const mkdirpNativeSync = ( 7 | path: string, 8 | options?: MkdirpOptions 9 | ): string | void | undefined => { 10 | const opts = optsArg(options) 11 | opts.recursive = true 12 | const parent = dirname(path) 13 | if (parent === path) { 14 | return opts.mkdirSync(path, opts) 15 | } 16 | 17 | const made = findMadeSync(opts, path) 18 | try { 19 | opts.mkdirSync(path, opts) 20 | return made 21 | } catch (er) { 22 | const fer = er as NodeJS.ErrnoException 23 | if (fer && fer.code === 'ENOENT') { 24 | return mkdirpManualSync(path, opts) 25 | } else { 26 | throw er 27 | } 28 | } 29 | } 30 | 31 | export const mkdirpNative = Object.assign( 32 | async ( 33 | path: string, 34 | options?: MkdirpOptions 35 | ): Promise => { 36 | const opts = { ...optsArg(options), recursive: true } 37 | const parent = dirname(path) 38 | if (parent === path) { 39 | return await opts.mkdirAsync(path, opts) 40 | } 41 | 42 | return findMade(opts, path).then((made?: string | undefined) => 43 | opts 44 | .mkdirAsync(path, opts) 45 | .then(m => made || m) 46 | .catch(er => { 47 | const fer = er as NodeJS.ErrnoException 48 | if (fer && fer.code === 'ENOENT') { 49 | return mkdirpManual(path, opts) 50 | } else { 51 | throw er 52 | } 53 | }) 54 | ) 55 | }, 56 | { sync: mkdirpNativeSync } 57 | ) 58 | -------------------------------------------------------------------------------- /src/opts-arg.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MakeDirectoryOptions, 3 | mkdir, 4 | mkdirSync, 5 | stat, 6 | Stats, 7 | statSync, 8 | } from 'fs' 9 | 10 | export interface FsProvider { 11 | stat?: ( 12 | path: string, 13 | callback: (err: NodeJS.ErrnoException | null, stats: Stats) => any 14 | ) => any 15 | mkdir?: ( 16 | path: string, 17 | opts: MakeDirectoryOptions & { recursive?: boolean }, 18 | callback: (err: NodeJS.ErrnoException | null, made?: string) => any 19 | ) => any 20 | statSync?: (path: string) => Stats 21 | mkdirSync?: ( 22 | path: string, 23 | opts: MakeDirectoryOptions & { recursive?: boolean } 24 | ) => string | undefined 25 | } 26 | 27 | interface Options extends FsProvider { 28 | mode?: number | string 29 | fs?: FsProvider 30 | mkdirAsync?: ( 31 | path: string, 32 | opts: MakeDirectoryOptions & { recursive?: boolean } 33 | ) => Promise 34 | statAsync?: (path: string) => Promise 35 | } 36 | 37 | export type MkdirpOptions = Options | number | string 38 | 39 | export interface MkdirpOptionsResolved { 40 | mode: number 41 | fs: FsProvider 42 | mkdirAsync: ( 43 | path: string, 44 | opts: MakeDirectoryOptions & { recursive?: boolean } 45 | ) => Promise 46 | statAsync: (path: string) => Promise 47 | stat: ( 48 | path: string, 49 | callback: (err: NodeJS.ErrnoException | null, stats: Stats) => any 50 | ) => any 51 | mkdir: ( 52 | path: string, 53 | opts: MakeDirectoryOptions & { recursive?: boolean }, 54 | callback: (err: NodeJS.ErrnoException | null, made?: string) => any 55 | ) => any 56 | statSync: (path: string) => Stats 57 | mkdirSync: ( 58 | path: string, 59 | opts: MakeDirectoryOptions & { recursive?: boolean } 60 | ) => string | undefined 61 | recursive?: boolean 62 | } 63 | 64 | export const optsArg = (opts?: MkdirpOptions): MkdirpOptionsResolved => { 65 | if (!opts) { 66 | opts = { mode: 0o777 } 67 | } else if (typeof opts === 'object') { 68 | opts = { mode: 0o777, ...opts } 69 | } else if (typeof opts === 'number') { 70 | opts = { mode: opts } 71 | } else if (typeof opts === 'string') { 72 | opts = { mode: parseInt(opts, 8) } 73 | } else { 74 | throw new TypeError('invalid options argument') 75 | } 76 | 77 | const resolved = opts as MkdirpOptionsResolved 78 | const optsFs = opts.fs || {} 79 | 80 | opts.mkdir = opts.mkdir || optsFs.mkdir || mkdir 81 | 82 | opts.mkdirAsync = opts.mkdirAsync 83 | ? opts.mkdirAsync 84 | : async ( 85 | path: string, 86 | options: MakeDirectoryOptions & { recursive?: boolean } 87 | ): Promise => { 88 | return new Promise((res, rej) => 89 | resolved.mkdir(path, options, (er, made) => 90 | er ? rej(er) : res(made) 91 | ) 92 | ) 93 | } 94 | 95 | opts.stat = opts.stat || optsFs.stat || stat 96 | opts.statAsync = opts.statAsync 97 | ? opts.statAsync 98 | : async (path: string) => 99 | new Promise((res, rej) => 100 | resolved.stat(path, (err, stats) => (err ? rej(err) : res(stats))) 101 | ) 102 | 103 | opts.statSync = opts.statSync || optsFs.statSync || statSync 104 | opts.mkdirSync = opts.mkdirSync || optsFs.mkdirSync || mkdirSync 105 | 106 | return resolved 107 | } 108 | -------------------------------------------------------------------------------- /src/path-arg.ts: -------------------------------------------------------------------------------- 1 | const platform = process.env.__TESTING_MKDIRP_PLATFORM__ || process.platform 2 | import { parse, resolve } from 'path' 3 | export const pathArg = (path: string) => { 4 | if (/\0/.test(path)) { 5 | // simulate same failure that node raises 6 | throw Object.assign( 7 | new TypeError('path must be a string without null bytes'), 8 | { 9 | path, 10 | code: 'ERR_INVALID_ARG_VALUE', 11 | } 12 | ) 13 | } 14 | 15 | path = resolve(path) 16 | if (platform === 'win32') { 17 | const badWinChars = /[*|"<>?:]/ 18 | const { root } = parse(path) 19 | if (badWinChars.test(path.substring(root.length))) { 20 | throw Object.assign(new Error('Illegal characters in path.'), { 21 | path, 22 | code: 'EINVAL', 23 | }) 24 | } 25 | } 26 | 27 | return path 28 | } 29 | -------------------------------------------------------------------------------- /src/use-native.ts: -------------------------------------------------------------------------------- 1 | import { mkdir, mkdirSync } from 'fs' 2 | import { MkdirpOptions, optsArg } from './opts-arg.js' 3 | 4 | const version = process.env.__TESTING_MKDIRP_NODE_VERSION__ || process.version 5 | const versArr = version.replace(/^v/, '').split('.') 6 | const hasNative = +versArr[0] > 10 || (+versArr[0] === 10 && +versArr[1] >= 12) 7 | 8 | export const useNativeSync = !hasNative 9 | ? () => false 10 | : (opts?: MkdirpOptions) => optsArg(opts).mkdirSync === mkdirSync 11 | 12 | export const useNative = Object.assign( 13 | !hasNative 14 | ? () => false 15 | : (opts?: MkdirpOptions) => optsArg(opts).mkdir === mkdir, 16 | { 17 | sync: useNativeSync, 18 | } 19 | ) 20 | -------------------------------------------------------------------------------- /tap-snapshots/test-cmd.js-TAP.test.js: -------------------------------------------------------------------------------- 1 | /* IMPORTANT 2 | * This snapshot file is auto-generated, but designed for humans. 3 | * It should be checked into source control and tracked carefully. 4 | * Re-generate by setting TAP_SNAPSHOT=1 and running tests. 5 | * Make sure to inspect the output below. Do not ignore changes! 6 | */ 7 | 'use strict' 8 | exports[`test/cmd.js TAP -h --help prints usage > --help output 1`] = ` 9 | Object { 10 | "code": 0, 11 | "signal": null, 12 | "stderr": "", 13 | "stdout": "\\nusage: mkdirp [DIR1,DIR2..] {OPTIONS}\\n\\n Create each supplied directory including any necessary parent directories\\n that don't yet exist.\\n\\n If the directory already exists, do nothing.\\n\\nOPTIONS are:\\n\\n -m If a directory needs to be created, set the mode as an octal\\n --mode= permission string.\\n\\n -v --version Print the mkdirp version number\\n\\n -h --help Print this helpful banner\\n\\n -p --print Print the first directories created for each path provided\\n\\n --manual Use manual implementation, even if native is available\\n\\n", 14 | } 15 | ` 16 | 17 | exports[`test/cmd.js TAP -v --version prints version > --version output 1`] = ` 18 | Object { 19 | "code": 0, 20 | "signal": null, 21 | "stderr": "", 22 | "stdout": "4.2.0-69.lol\\n", 23 | } 24 | ` 25 | 26 | exports[`test/cmd.js TAP failures > expect resolving Promise 1`] = ` 27 | Array [ 28 | Object { 29 | "code": 1, 30 | "signal": null, 31 | "stderr": "nope\\n", 32 | "stdout": "", 33 | }, 34 | Object { 35 | "code": 1, 36 | "signal": null, 37 | "stderr": "fail\\n code: EFAIL\\n", 38 | "stdout": "", 39 | }, 40 | ] 41 | ` 42 | 43 | exports[`test/cmd.js TAP invalid mode > expect resolving Promise 1`] = ` 44 | Object { 45 | "code": 1, 46 | "signal": null, 47 | "stderr": "invalid mode argument: --mode=XYZ\\nMust be an octal number.\\n", 48 | "stdout": "", 49 | } 50 | ` 51 | 52 | exports[`test/cmd.js TAP make dir named --help > expect resolving Promise 1`] = ` 53 | Object { 54 | "code": 0, 55 | "signal": null, 56 | "stderr": "", 57 | "stdout": "--help 0\\n", 58 | } 59 | ` 60 | 61 | exports[`test/cmd.js TAP making dirs > expect resolving Promise 1`] = ` 62 | Object { 63 | "code": 0, 64 | "signal": null, 65 | "stderr": "", 66 | "stdout": "", 67 | } 68 | ` 69 | 70 | exports[`test/cmd.js TAP manual > expect resolving Promise 1`] = ` 71 | Object { 72 | "code": 0, 73 | "signal": null, 74 | "stderr": "", 75 | "stdout": "MANUAL a 0\\nMANUAL b/c/d 0\\n", 76 | } 77 | ` 78 | 79 | exports[`test/cmd.js TAP no dirs -> stderr usage > expect resolving Promise 1`] = ` 80 | Object { 81 | "code": 0, 82 | "signal": null, 83 | "stderr": "\\nusage: mkdirp [DIR1,DIR2..] {OPTIONS}\\n\\n Create each supplied directory including any necessary parent directories\\n that don't yet exist.\\n\\n If the directory already exists, do nothing.\\n\\nOPTIONS are:\\n\\n -m If a directory needs to be created, set the mode as an octal\\n --mode= permission string.\\n\\n -v --version Print the mkdirp version number\\n\\n -h --help Print this helpful banner\\n\\n -p --print Print the first directories created for each path provided\\n\\n --manual Use manual implementation, even if native is available\\n\\n", 84 | "stdout": "", 85 | } 86 | ` 87 | 88 | exports[`test/cmd.js TAP noisily > expect resolving Promise 1`] = ` 89 | Object { 90 | "code": 0, 91 | "signal": null, 92 | "stderr": "", 93 | "stdout": "a 0\\nb/c/d 0\\n", 94 | } 95 | ` 96 | 97 | exports[`test/cmd.js TAP print modes > expect resolving Promise 1`] = ` 98 | Object { 99 | "code": 0, 100 | "signal": null, 101 | "stderr": "", 102 | "stdout": "a 509\\n", 103 | } 104 | ` 105 | -------------------------------------------------------------------------------- /tap-snapshots/test/cmd.ts.test.cjs: -------------------------------------------------------------------------------- 1 | /* IMPORTANT 2 | * This snapshot file is auto-generated, but designed for humans. 3 | * It should be checked into source control and tracked carefully. 4 | * Re-generate by setting TAP_SNAPSHOT=1 and running tests. 5 | * Make sure to inspect the output below. Do not ignore changes! 6 | */ 7 | 'use strict' 8 | exports[`test/cmd.ts TAP -h --help prints usage > --help output 1`] = ` 9 | Object { 10 | "code": 0, 11 | "signal": null, 12 | "stderr": "", 13 | "stdout": String( 14 | 15 | usage: mkdirp [DIR1,DIR2..] {OPTIONS} 16 | 17 | Create each supplied directory including any necessary parent directories 18 | that don't yet exist. 19 | 20 | If the directory already exists, do nothing. 21 | 22 | OPTIONS are: 23 | 24 | -m If a directory needs to be created, set the mode as an octal 25 | --mode= permission string. 26 | 27 | -v --version Print the mkdirp version number 28 | 29 | -h --help Print this helpful banner 30 | 31 | -p --print Print the first directories created for each path provided 32 | 33 | --manual Use manual implementation, even if native is available 34 | 35 | 36 | ), 37 | } 38 | ` 39 | 40 | exports[`test/cmd.ts TAP -v --version prints version > --version output 1`] = ` 41 | Object { 42 | "code": 0, 43 | "signal": null, 44 | "stderr": "", 45 | "stdout": "4.2.0-69.lol\\n", 46 | } 47 | ` 48 | 49 | exports[`test/cmd.ts TAP failures > expect resolving Promise 1`] = ` 50 | Array [ 51 | Object { 52 | "code": 1, 53 | "signal": null, 54 | "stderr": "nope\\n", 55 | "stdout": "", 56 | }, 57 | Object { 58 | "code": 1, 59 | "signal": null, 60 | "stderr": String( 61 | fail 62 | code: EFAIL 63 | 64 | ), 65 | "stdout": "", 66 | }, 67 | ] 68 | ` 69 | 70 | exports[`test/cmd.ts TAP invalid mode > expect resolving Promise 1`] = ` 71 | Object { 72 | "code": 1, 73 | "signal": null, 74 | "stderr": String( 75 | invalid mode argument: --mode=XYZ 76 | Must be an octal number. 77 | 78 | ), 79 | "stdout": "", 80 | } 81 | ` 82 | 83 | exports[`test/cmd.ts TAP make dir named --help > expect resolving Promise 1`] = ` 84 | Object { 85 | "code": 0, 86 | "signal": null, 87 | "stderr": "", 88 | "stdout": "--help 0\\n", 89 | } 90 | ` 91 | 92 | exports[`test/cmd.ts TAP making dirs > expect resolving Promise 1`] = ` 93 | Object { 94 | "code": 0, 95 | "signal": null, 96 | "stderr": "", 97 | "stdout": "", 98 | } 99 | ` 100 | 101 | exports[`test/cmd.ts TAP manual > expect resolving Promise 1`] = ` 102 | Object { 103 | "code": 0, 104 | "signal": null, 105 | "stderr": "", 106 | "stdout": String( 107 | MANUAL a 0 108 | MANUAL b/c/d 0 109 | 110 | ), 111 | } 112 | ` 113 | 114 | exports[`test/cmd.ts TAP no dirs -> stderr usage > expect resolving Promise 1`] = ` 115 | Object { 116 | "code": 0, 117 | "signal": null, 118 | "stderr": String( 119 | 120 | usage: mkdirp [DIR1,DIR2..] {OPTIONS} 121 | 122 | Create each supplied directory including any necessary parent directories 123 | that don't yet exist. 124 | 125 | If the directory already exists, do nothing. 126 | 127 | OPTIONS are: 128 | 129 | -m If a directory needs to be created, set the mode as an octal 130 | --mode= permission string. 131 | 132 | -v --version Print the mkdirp version number 133 | 134 | -h --help Print this helpful banner 135 | 136 | -p --print Print the first directories created for each path provided 137 | 138 | --manual Use manual implementation, even if native is available 139 | 140 | 141 | ), 142 | "stdout": "", 143 | } 144 | ` 145 | 146 | exports[`test/cmd.ts TAP noisily > expect resolving Promise 1`] = ` 147 | Object { 148 | "code": 0, 149 | "signal": null, 150 | "stderr": "", 151 | "stdout": String( 152 | a 0 153 | b/c/d 0 154 | 155 | ), 156 | } 157 | ` 158 | 159 | exports[`test/cmd.ts TAP print modes > expect resolving Promise 1`] = ` 160 | Object { 161 | "code": 0, 162 | "signal": null, 163 | "stderr": "", 164 | "stdout": "a 509\\n", 165 | } 166 | ` 167 | -------------------------------------------------------------------------------- /test/cmd.ts: -------------------------------------------------------------------------------- 1 | const cmd = require.resolve('../dist/cjs/src/bin.js') 2 | import t from 'tap' 3 | import { MkdirpOptions } from '../src/opts-arg' 4 | 5 | import { spawn } from 'child_process' 6 | import { basename } from 'path' 7 | 8 | const fakeMkdirp = (path: string, opts: MkdirpOptions) => 9 | basename(path) === 'ERROR' 10 | ? Promise.reject(new Error('nope')) 11 | : basename(path) === 'EFAIL' 12 | ? Promise.reject(Object.assign(new Error('fail'), { code: 'EFAIL' })) 13 | : opts && typeof opts === 'object' 14 | ? Promise.resolve(`${path} ${opts.mode || 0}`) 15 | : Promise.reject('wtf') 16 | 17 | fakeMkdirp.manual = (path: string, opts: MkdirpOptions) => 18 | fakeMkdirp(`MANUAL ${path}`, opts) 19 | fakeMkdirp.mkdirp = fakeMkdirp 20 | 21 | if (process.argv[2] === 'RUN') { 22 | process.argv = [process.execPath, cmd, ...process.argv.slice(3)] 23 | t.mock(cmd, { 24 | '../dist/cjs/src/index.js': { mkdirp: fakeMkdirp }, 25 | '../dist/cjs/package.json': { 26 | version: '4.2.0-69.lol', 27 | }, 28 | }) 29 | } else { 30 | const run = (...args: string[]) => 31 | new Promise(res => { 32 | const proc = spawn(process.execPath, [ 33 | ...process.execArgv, 34 | __filename, 35 | 'RUN', 36 | ...args, 37 | ]) 38 | const out: Buffer[] = [] 39 | const err: Buffer[] = [] 40 | proc.stdout.on('data', c => out.push(c)) 41 | proc.stderr.on('data', c => err.push(c)) 42 | proc.on('close', (code, signal) => { 43 | res({ 44 | code, 45 | signal, 46 | stdout: Buffer.concat(out).toString('utf8'), 47 | stderr: Buffer.concat(err).toString('utf8'), 48 | }) 49 | }) 50 | }) 51 | 52 | t.test('-h --help prints usage', t => 53 | Promise.all([run('-h'), run('--help')]).then(res => { 54 | t.strictSame(res[0], res[1], 'same for -h and --help') 55 | t.matchSnapshot(res[0], '--help output') 56 | }) 57 | ) 58 | 59 | t.test('no dirs -> stderr usage', t => t.resolveMatchSnapshot(run())) 60 | 61 | t.test('-v --version prints version', t => 62 | Promise.all([run('-v'), run('--version')]).then(res => { 63 | t.strictSame(res[0], res[1], 'same for -v and --version') 64 | t.matchSnapshot(res[0], '--version output') 65 | }) 66 | ) 67 | 68 | t.test('making dirs', t => t.resolveMatchSnapshot(run('a', 'b/c/d', 'e'))) 69 | t.test('noisily', t => t.resolveMatchSnapshot(run('a', 'b/c/d', '--print'))) 70 | t.test('manual', t => 71 | t.resolveMatchSnapshot(run('a', 'b/c/d', '-p', '--manual')) 72 | ) 73 | t.test('print modes', t => t.resolveMatchSnapshot(run('a', '-m775', '-p'))) 74 | t.test('invalid mode', t => t.resolveMatchSnapshot(run('--mode=XYZ'))) 75 | t.test('make dir named --help', t => 76 | t.resolveMatchSnapshot(run('-p', '--', '--help')) 77 | ) 78 | t.test('failures', t => 79 | t.resolveMatchSnapshot(Promise.all([run('x/ERROR'), run('x/EFAIL')])) 80 | ) 81 | } 82 | -------------------------------------------------------------------------------- /test/find-made.ts: -------------------------------------------------------------------------------- 1 | import t from 'tap' 2 | 3 | import * as fs from 'fs' 4 | import { basename, posix } from 'path' 5 | import { promisify } from 'util' 6 | 7 | const statAsync = (path: string) => 8 | basename(path) === 'error' 9 | ? Promise.reject(new Error('not a real error')) 10 | : promisify(fs.stat)(path) 11 | 12 | const statSync = (path: string) => { 13 | if (basename(path) === 'error') { 14 | throw new Error('not a real error') 15 | } else { 16 | return fs.statSync(path) 17 | } 18 | } 19 | 20 | const { findMade, findMadeSync } = t.mock('../dist/cjs/src/find-made.js', { 21 | path: posix, 22 | }) 23 | 24 | t.test('find what dir will be made', async t => { 25 | const dir = t.testdir({ 26 | file: 'txt', 27 | subdir: {}, 28 | }) 29 | 30 | const o = { statAsync, statSync } 31 | 32 | t.equal(findMadeSync(o, `${dir}/subdir/x/y/z`), `${dir}/subdir/x`) 33 | t.equal(findMadeSync(o, `${dir}/subdir`), undefined) 34 | t.equal(findMadeSync(o, `${dir}/file/x/y/z`), undefined) 35 | t.equal(findMadeSync(o, `${dir}/file`, `${dir}/file/x`), undefined) 36 | t.equal(findMadeSync(o, `${dir}/subdir/error`), undefined) 37 | t.equal(findMadeSync(o, '/', '/'), undefined) 38 | return Promise.all([ 39 | findMade(o, `${dir}/subdir/x/y/z`), 40 | findMade(o, `${dir}/subdir`), 41 | findMade(o, `${dir}/file/x/y/z`), 42 | findMade(o, `${dir}/file`, `${dir}/file/x`), 43 | findMade(o, `${dir}/subdir/error`), 44 | findMade(o, '/', '/'), 45 | ]).then(made => 46 | t.strictSame(made, [ 47 | `${dir}/subdir/x`, 48 | undefined, 49 | undefined, 50 | undefined, 51 | undefined, 52 | undefined, 53 | ]) 54 | ) 55 | }) 56 | -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | import { mkdir, mkdirSync, statSync } from 'fs' 2 | import t from 'tap' 3 | import { mkdirp } from '../dist/cjs/src/index.js' 4 | import { 5 | MkdirpOptions, 6 | MkdirpOptionsResolved, 7 | } from '../dist/cjs/src/opts-arg.js' 8 | 9 | const s = (s: any) => (typeof s !== 'string' ? s : s.replace(/\\/g, '/')) 10 | 11 | // node before 10.13 didn't native recursive mkdir 12 | const doNative = !/^v([0-8]\.|10.([0-9]\.|10\.|11\.([0-9]|1[01])$))/.test( 13 | process.version 14 | ) 15 | 16 | t.test('module shape', t => { 17 | t.type(mkdirp, Function) 18 | t.type(mkdirp.sync, Function) 19 | t.type(mkdirp.manual, Function) 20 | t.type(mkdirp.manualSync, Function) 21 | if (doNative) { 22 | t.type(mkdirp.native, Function) 23 | t.type(mkdirp.nativeSync, Function) 24 | } 25 | t.end() 26 | }) 27 | 28 | t.test('basic making of dirs should work', async t => { 29 | const dir = t.testdir({ a: {} }) 30 | const check = (d: string) => t.ok(statSync(d).isDirectory()) 31 | t.equal(s(mkdirp.sync(`${dir}/a/sync`)), s(`${dir}/a/sync`)) 32 | check(`${dir}/a/sync`) 33 | t.equal(mkdirp.sync(`${dir}/a/sync`), undefined) 34 | 35 | t.equal( 36 | s(mkdirp.manualSync(`${dir}/a/manual-sync`)), 37 | s(`${dir}/a/manual-sync`) 38 | ) 39 | check(`${dir}/a/manual-sync`) 40 | t.equal(s(mkdirp.manualSync(`${dir}/a/manual-sync`)), undefined) 41 | 42 | if (doNative) { 43 | t.equal( 44 | s(mkdirp.nativeSync(`${dir}/a/native-sync`)), 45 | s(`${dir}/a/native-sync`) 46 | ) 47 | check(`${dir}/a/native-sync`) 48 | t.equal(mkdirp.nativeSync(`${dir}/a/native-sync`), undefined) 49 | } 50 | 51 | // override to force the manual option 52 | const myMkdir = ( 53 | path: string, 54 | opts: MkdirpOptionsResolved, 55 | cb: (er: NodeJS.ErrnoException, made: string | undefined | void) => void 56 | //@ts-ignore 57 | ) => mkdir(path, opts, cb) 58 | const myMkdirSync = (path: string, opts: MkdirpOptions) => 59 | mkdirSync(path, opts) 60 | const opts = { mkdir: myMkdir, mkdirSync: myMkdirSync } 61 | //@ts-ignore 62 | t.equal(s(mkdirp.sync(`${dir}/a/custom-sync`, opts)), s(`${dir}/a/custom-sync`)) 63 | check(`${dir}/a/custom-sync`) 64 | //@ts-ignore 65 | t.equal(s(mkdirp.sync(`${dir}/a/custom-sync`, opts)), undefined) 66 | 67 | return Promise.all([ 68 | mkdirp(`${dir}/a/async`), 69 | mkdirp.manual(`${dir}/a/manual-async`), 70 | doNative && mkdirp.native(`${dir}/a/native-async`), 71 | //@ts-ignore 72 | mkdirp(`${dir}/a/custom-async`, opts), 73 | ]) 74 | .then(made => { 75 | t.strictSame( 76 | made.map(m => s(m)), 77 | [ 78 | `${dir}/a/async`, 79 | `${dir}/a/manual-async`, 80 | doNative && `${dir}/a/native-async`, 81 | `${dir}/a/custom-async`, 82 | ].map(m => s(m)) 83 | ) 84 | check(`${dir}/a/async`) 85 | check(`${dir}/a/manual-async`) 86 | doNative && check(`${dir}/a/native-async`) 87 | check(`${dir}/a/custom-async`) 88 | return Promise.all([ 89 | mkdirp(`${dir}/a/async`), 90 | mkdirp.manual(`${dir}/a/manual-async`), 91 | doNative ? mkdirp.native(`${dir}/a/native-async`) : undefined, 92 | //@ts-ignore 93 | mkdirp(`${dir}/a/custom-async`, opts), 94 | ]) 95 | }) 96 | .then(made => 97 | t.strictSame(made, [undefined, undefined, undefined, undefined]) 98 | ) 99 | }) 100 | -------------------------------------------------------------------------------- /test/mkdirp-manual.ts: -------------------------------------------------------------------------------- 1 | import t from 'tap' 2 | import { promisify } from 'util' 3 | 4 | import { mkdir, mkdirSync, stat, statSync } from 'fs' 5 | const statAsync = promisify(stat) 6 | const mkdirAsync = promisify(mkdir) 7 | 8 | import { posix as path } from 'path' 9 | 10 | const { mkdirpManual, mkdirpManualSync } = t.mock( 11 | '../dist/cjs/src/mkdirp-manual.js', 12 | { 13 | path, 14 | } 15 | ) 16 | 17 | t.test('mkdirpManual / just calls implementation', t => { 18 | t.test('success is fine, of course', t => { 19 | const opt = { 20 | mkdirAsync: () => Promise.resolve('mkdirAsync impl'), 21 | mkdirSync: () => 'mkdirSync impl', 22 | recursive: true, 23 | } 24 | t.equal(mkdirpManualSync('/', opt), 'mkdirSync impl') 25 | //@ts-ignore 26 | t.equal(opt.recursive, true) 27 | return mkdirpManual('/', opt).then((res: string) => { 28 | t.equal(res, 'mkdirAsync impl') 29 | //@ts-ignore 30 | t.equal(opt.recursive, true) 31 | }) 32 | }) 33 | 34 | t.test('EISDIR is expected and ignored', t => { 35 | const opt = { 36 | mkdirAsync: () => 37 | Promise.reject(Object.assign(new Error('is dir'), { code: 'EISDIR' })), 38 | mkdirSync: () => { 39 | throw Object.assign(new Error('is dir'), { code: 'EISDIR' }) 40 | }, 41 | recursive: true, 42 | } 43 | t.equal(mkdirpManualSync('/', opt), undefined) 44 | t.equal(opt.recursive, true) 45 | opt.recursive = true 46 | return mkdirpManual('/', opt).then((made: string) => { 47 | t.equal(made, undefined) 48 | t.equal(opt.recursive, true) 49 | }) 50 | }) 51 | 52 | t.test('other failures are failures', t => { 53 | const opt = { 54 | mkdirAsync: () => 55 | Promise.reject(Object.assign(new Error('grolb'), { code: 'blorg' })), 56 | mkdirSync: () => { 57 | throw Object.assign(new Error('grolb'), { code: 'blorg' }) 58 | }, 59 | recursive: true, 60 | } 61 | t.throws(() => mkdirpManualSync('/', opt), { code: 'blorg' }) 62 | return t.rejects(mkdirpManual('/', opt), { code: 'blorg' }) 63 | }) 64 | 65 | t.end() 66 | }) 67 | 68 | t.test('read-only file system, still succeed if dir exists', t => { 69 | const dir = t.testdir({ foo: {} }) 70 | const opt = { 71 | stat, 72 | statAsync, 73 | statSync, 74 | mkdir, 75 | mkdirAsync: () => 76 | Promise.reject( 77 | Object.assign(new Error('EROFS'), { 78 | code: 'EROFS', 79 | }) 80 | ), 81 | mkdirSync: () => { 82 | throw Object.assign(new Error('EROFS'), { 83 | code: 'EROFS', 84 | }) 85 | }, 86 | } 87 | t.equal(mkdirpManualSync(`${dir}/foo`, opt), undefined) 88 | return mkdirpManual(`${dir}/foo`, opt).then((made: string) => 89 | t.equal(made, undefined) 90 | ) 91 | }) 92 | 93 | t.test('recurse and return first dir made', t => { 94 | const dir = t.testdir() 95 | const opt = { 96 | stat, 97 | statAsync, 98 | statSync, 99 | mkdir, 100 | mkdirAsync, 101 | mkdirSync, 102 | } 103 | 104 | t.equal(mkdirpManualSync(`${dir}/sync/a/b`, opt), `${dir}/sync`) 105 | t.equal(statSync(`${dir}/sync/a/b`).isDirectory(), true, 'made dir') 106 | t.equal(mkdirpManualSync(`${dir}/sync/a/b`, opt), undefined) 107 | 108 | return mkdirpManual(`${dir}/async/a/b`, opt) 109 | .then((made: string) => { 110 | t.equal(made, `${dir}/async`) 111 | return mkdirpManual(`${dir}/async/a/b`, opt) 112 | }) 113 | .then((made: string | undefined) => t.equal(made, undefined)) 114 | }) 115 | 116 | t.test('unknown failure types are failures', t => { 117 | const opt = { 118 | mkdirAsync: () => 119 | Promise.reject(Object.assign(new Error('grolb'), { code: 'blorg' })), 120 | mkdirSync: () => { 121 | throw Object.assign(new Error('grolb'), { code: 'blorg' }) 122 | }, 123 | // ensure it gets reset 124 | recursive: true, 125 | } 126 | t.throws(() => mkdirpManualSync('/x/y/z', opt), { code: 'blorg' }) 127 | return t.rejects(mkdirpManual('/x/y/z', opt), { code: 'blorg' }) 128 | }) 129 | 130 | t.test('cannot make dir over a file', t => { 131 | const dir = t.testdir({ file: 'txt' }) 132 | const opt = { 133 | stat, 134 | statAsync, 135 | statSync, 136 | mkdir, 137 | mkdirAsync, 138 | mkdirSync, 139 | } 140 | 141 | t.throws(() => mkdirpManualSync(`${dir}/file`, opt), { code: 'EEXIST' }) 142 | return t.rejects(mkdirpManual(`${dir}/file`, opt), { code: 'EEXIST' }) 143 | }) 144 | 145 | t.test('try to overwrite a file, then fail to stat it', t => { 146 | const dir = t.testdir({ file: 'txt' }) 147 | const file = `${dir}/file` 148 | const er = Object.assign(new Error('nope'), { code: 'grob' }) 149 | const opt = { 150 | statAsync: (path: string) => 151 | path === file ? Promise.reject(er) : statAsync(path), 152 | statSync: (path: string) => { 153 | if (path === file) throw er 154 | else return statSync(path) 155 | }, 156 | mkdirAsync, 157 | mkdirSync, 158 | } 159 | 160 | t.throws(() => mkdirpManualSync(`${dir}/file`, opt), { code: 'EEXIST' }) 161 | return t.rejects(mkdirpManual(`${dir}/file`, opt), { code: 'EEXIST' }) 162 | }) 163 | -------------------------------------------------------------------------------- /test/mkdirp-native.ts: -------------------------------------------------------------------------------- 1 | import { stat, statSync } from 'fs' 2 | import t from 'tap' 3 | import { promisify } from 'util' 4 | const statAsync = promisify(stat) 5 | 6 | import { posix as path } from 'path' 7 | const { mkdirpNative, mkdirpNativeSync } = t.mock( 8 | '../dist/cjs/src/mkdirp-native.js', 9 | { 10 | // just return an indicator that it was called 11 | '../dist/cjs/src/mkdirp-manual.js': { 12 | mkdirpManual: () => 'mkdirpManual', 13 | mkdirpManualSync: () => 'mkdirpManualSync', 14 | }, 15 | path, 16 | } 17 | ) 18 | 19 | t.test('mkdirpNative / just calls implementation', async t => { 20 | const opt = { 21 | mkdirAsync: () => 'mkdirAsync impl', 22 | mkdirSync: () => 'mkdirSync impl', 23 | } 24 | t.equal(await mkdirpNative('/', opt), 'mkdirAsync impl') 25 | //@ts-ignore 26 | t.equal(opt.recursive, undefined) 27 | t.equal(mkdirpNativeSync('/', opt), 'mkdirSync impl') 28 | //@ts-ignore 29 | t.equal(opt.recursive, undefined) 30 | }) 31 | 32 | t.test('mkdirpNative calls impl and returns findMade', t => { 33 | const opt = { 34 | mkdirAsync: () => Promise.resolve(), 35 | mkdirSync: () => undefined, 36 | statAsync, 37 | statSync, 38 | } 39 | 40 | const dir = t.testdir() 41 | t.equal(mkdirpNativeSync(`${dir}/sync/a/b/c`, opt), `${dir}/sync`) 42 | return mkdirpNative(`${dir}/async/a/b/c`, opt).then((made: string) => 43 | t.equal(made, `${dir}/async`) 44 | ) 45 | }) 46 | 47 | t.test('ENOENT error falls back to manual', t => { 48 | const opt = { 49 | mkdirAsync: () => 50 | Promise.reject(Object.assign(new Error('poo'), { code: 'ENOENT' })), 51 | mkdirSync: () => { 52 | throw Object.assign(new Error('poo'), { code: 'ENOENT' }) 53 | }, 54 | statAsync, 55 | statSync, 56 | } 57 | 58 | const dir = t.testdir() 59 | t.equal(mkdirpNativeSync(`${dir}/sync/a/b/c`, opt), 'mkdirpManualSync') 60 | return mkdirpNative(`${dir}/async/a/b/c`, opt).then((made: string) => 61 | t.equal(made, 'mkdirpManual') 62 | ) 63 | }) 64 | 65 | t.test('other errors are raised to caller', t => { 66 | const opt = { 67 | mkdirAsync: () => 68 | Promise.reject(Object.assign(new Error('poo'), { code: 'blorg' })), 69 | mkdirSync: () => { 70 | throw Object.assign(new Error('poo'), { code: 'blorg' }) 71 | }, 72 | statAsync, 73 | statSync, 74 | } 75 | 76 | t.throws(() => mkdirpNativeSync('anything', opt), { code: 'blorg' }) 77 | return t.rejects(mkdirpNative('at/all', opt), { code: 'blorg' }) 78 | }) 79 | -------------------------------------------------------------------------------- /test/opts-arg.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs' 2 | import t from 'tap' 3 | import { optsArg } from '../dist/cjs/src/opts-arg.js' 4 | const mode = 0o777 5 | 6 | const defFs = { 7 | mkdir: fs.mkdir, 8 | mkdirSync: fs.mkdirSync, 9 | stat: fs.stat, 10 | statSync: fs.statSync, 11 | } 12 | 13 | const stat = () => {} 14 | stat.fake = true 15 | const statSync = () => {} 16 | statSync.fake = true 17 | 18 | // arg, expect 19 | const cases = { 20 | null: [null, { mode, ...defFs }], 21 | false: [false, { mode, ...defFs }], 22 | undefined: [undefined, { mode, ...defFs }], 23 | 'empty object': [{}, { mode, ...defFs }], 24 | 'numeric mode': [0o775, { mode: 0o775, ...defFs }], 25 | 'string mode': ['775', { mode: 0o775, ...defFs }], 26 | 'empty custom fs': [{ fs: {} }, { mode, ...defFs, fs: {} }], 27 | 'custom stat/statSync': [ 28 | { stat, statSync }, 29 | { mode, ...defFs, stat, statSync }, 30 | ], 31 | 'custom fs with stat/statSync': [ 32 | { fs: { stat, statSync } }, 33 | { mode, ...defFs, fs: { stat, statSync }, stat, statSync }, 34 | ], 35 | } 36 | 37 | for (const [name, c] of Object.entries(cases)) { 38 | const [arg, expect] = c 39 | //@ts-ignore 40 | t.match(optsArg(arg), expect, name) 41 | } 42 | 43 | //@ts-ignore 44 | t.throws(() => optsArg(() => {}), TypeError('invalid options argument')) 45 | -------------------------------------------------------------------------------- /test/path-arg.ts: -------------------------------------------------------------------------------- 1 | import t from 'tap' 2 | 3 | if (!process.env.__TESTING_MKDIRP_PLATFORM__) { 4 | const fake = process.platform === 'win32' ? 'posix' : 'win32' 5 | //@ts-ignore 6 | t.spawn(process.execPath, [...process.execArgv, __filename], { 7 | env: { 8 | ...process.env, 9 | __TESTING_MKDIRP_PLATFORM__: fake, 10 | }, 11 | }) 12 | } 13 | 14 | const platform = process.env.__TESTING_MKDIRP_PLATFORM__ || process.platform 15 | const path = require('path').platform || require('path') 16 | const { pathArg } = t.mock('../dist/cjs/src/path-arg.js', { 17 | path, 18 | }) 19 | const { resolve } = path 20 | 21 | t.equal(pathArg('a/b/c'), resolve('a/b/c')) 22 | t.throws( 23 | () => pathArg('a\0b'), 24 | Error('path must be a string without null bytes') 25 | ) 26 | if (platform === 'win32') { 27 | const badPaths = [ 28 | 'c:\\a\\b:c', 29 | 'c:\\a\\b*c', 30 | 'c:\\a\\b?c', 31 | 'c:\\a\\bc', 33 | 'c:\\a\\b|c', 34 | 'c:\\a\\b"c', 35 | ] 36 | for (const path of badPaths) { 37 | const er = Object.assign(new Error('Illegal characters in path'), { 38 | path, 39 | code: 'EINVAL', 40 | }) 41 | t.throws(() => pathArg(path), er) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/use-native.ts: -------------------------------------------------------------------------------- 1 | import { mkdir, mkdirSync } from 'fs' 2 | import t from 'tap' 3 | import { useNative, useNativeSync } from '../dist/cjs/src/use-native.js' 4 | // node before 10.13 didn't native recursive mkdir 5 | if (/^v([0-8]\.|10.([0-9]\.|10\.|11\.([0-9]|1[01])$))/.test(process.version)) { 6 | t.plan(0, 'no native recursive mkdirp in this node version') 7 | process.exit(0) 8 | } 9 | 10 | if (!process.env.__TESTING_MKDIRP_NODE_VERSION__) { 11 | //@ts-ignore 12 | t.spawn(process.execPath, [...process.execArgv, __filename], { 13 | env: { 14 | ...process.env, 15 | __TESTING_MKDIRP_NODE_VERSION__: 'v10.11.12', 16 | }, 17 | }) 18 | 19 | //@ts-ignore 20 | t.spawn(process.execPath, [...process.execArgv, __filename], { 21 | env: { 22 | ...process.env, 23 | __TESTING_MKDIRP_NODE_VERSION__: 'v8.9.10', 24 | }, 25 | }) 26 | 27 | // this one has the native impl 28 | t.equal(useNative({ mkdir }), true) 29 | //@ts-ignore 30 | t.equal(useNative({ mkdir: 1243 }), false) 31 | t.equal(useNativeSync({ mkdirSync }), true) 32 | //@ts-ignore 33 | t.equal(useNativeSync({ mkdirSync: 1243 }), false) 34 | } else { 35 | t.equal(useNative({ mkdir }), false) 36 | //@ts-ignore 37 | t.equal(useNative({ mkdir: 1243 }), false) 38 | t.equal(useNativeSync({ mkdirSync }), false) 39 | //@ts-ignore 40 | t.equal(useNativeSync({ mkdirSync: 1243 }), false) 41 | } 42 | -------------------------------------------------------------------------------- /tsconfig-base.json: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": ["./test", "./tap-snapshots"], 3 | "include": ["src/**/*.ts"], 4 | "compilerOptions": { 5 | "allowSyntheticDefaultImports": true, 6 | "declaration": true, 7 | "inlineSources": true, 8 | "declarationMap": true, 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "isolatedModules": true, 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "skipLibCheck": true, 15 | "sourceMap": true, 16 | "strict": true, 17 | "target": "es2022" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig-esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig-base.json", 3 | "exclude": ["./test", "./tap-snapshots", "src/bin.ts"], 4 | "compilerOptions": { 5 | "module": "esnext", 6 | "outDir": "dist/mjs" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig-base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "dist/cjs", 6 | "moduleResolution": "Node" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationLinks": { 3 | "GitHub": "https://github.com/isaacs/node-mkdirp", 4 | "isaacs projects": "https://isaacs.github.io/" 5 | } 6 | } 7 | --------------------------------------------------------------------------------