Update gitignore (sorry)

This commit is contained in:
olcxja 2026-05-10 14:02:17 +02:00
commit cca8b02fea
6604 changed files with 1219661 additions and 4 deletions

View file

@ -0,0 +1 @@
../mkdirp/bin/cmd.js

View file

@ -0,0 +1 @@
../rimraf/bin.js

View file

@ -0,0 +1,15 @@
# Changers Lorgs!
## 1.0
Full rewrite. Essentially a brand new module.
- Return a promise instead of taking a callback.
- Use native `fs.mkdir(path, { recursive: true })` when available.
- Drop support for outdated Node.js versions. (Technically still works on
Node.js v8, but only 10 and above are officially supported.)
## 0.x
Original and most widely used recursive directory creation implementation
in JavaScript, dating back to 2010.

View file

@ -0,0 +1,21 @@
Copyright James Halliday (mail@substack.net) and Isaac Z. Schlueter (i@izs.me)
This project is free software released under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,68 @@
#!/usr/bin/env node
const usage = () => `
usage: mkdirp [DIR1,DIR2..] {OPTIONS}
Create each supplied directory including any necessary parent directories
that don't yet exist.
If the directory already exists, do nothing.
OPTIONS are:
-m<mode> If a directory needs to be created, set the mode as an octal
--mode=<mode> permission string.
-v --version Print the mkdirp version number
-h --help Print this helpful banner
-p --print Print the first directories created for each path provided
--manual Use manual implementation, even if native is available
`
const dirs = []
const opts = {}
let print = false
let dashdash = false
let manual = false
for (const arg of process.argv.slice(2)) {
if (dashdash)
dirs.push(arg)
else if (arg === '--')
dashdash = true
else if (arg === '--manual')
manual = true
else if (/^-h/.test(arg) || /^--help/.test(arg)) {
console.log(usage())
process.exit(0)
} else if (arg === '-v' || arg === '--version') {
console.log(require('../package.json').version)
process.exit(0)
} else if (arg === '-p' || arg === '--print') {
print = true
} else if (/^-m/.test(arg) || /^--mode=/.test(arg)) {
const mode = parseInt(arg.replace(/^(-m|--mode=)/, ''), 8)
if (isNaN(mode)) {
console.error(`invalid mode argument: ${arg}\nMust be an octal number.`)
process.exit(1)
}
opts.mode = mode
} else
dirs.push(arg)
}
const mkdirp = require('../')
const impl = manual ? mkdirp.manual : mkdirp
if (dirs.length === 0)
console.error(usage())
Promise.all(dirs.map(dir => impl(dir, opts)))
.then(made => print ? made.forEach(m => m && console.log(m)) : null)
.catch(er => {
console.error(er.message)
if (er.code)
console.error(' code: ' + er.code)
process.exit(1)
})

View file

@ -0,0 +1,31 @@
const optsArg = require('./lib/opts-arg.js')
const pathArg = require('./lib/path-arg.js')
const {mkdirpNative, mkdirpNativeSync} = require('./lib/mkdirp-native.js')
const {mkdirpManual, mkdirpManualSync} = require('./lib/mkdirp-manual.js')
const {useNative, useNativeSync} = require('./lib/use-native.js')
const mkdirp = (path, opts) => {
path = pathArg(path)
opts = optsArg(opts)
return useNative(opts)
? mkdirpNative(path, opts)
: mkdirpManual(path, opts)
}
const mkdirpSync = (path, opts) => {
path = pathArg(path)
opts = optsArg(opts)
return useNativeSync(opts)
? mkdirpNativeSync(path, opts)
: mkdirpManualSync(path, opts)
}
mkdirp.sync = mkdirpSync
mkdirp.native = (path, opts) => mkdirpNative(pathArg(path), optsArg(opts))
mkdirp.manual = (path, opts) => mkdirpManual(pathArg(path), optsArg(opts))
mkdirp.nativeSync = (path, opts) => mkdirpNativeSync(pathArg(path), optsArg(opts))
mkdirp.manualSync = (path, opts) => mkdirpManualSync(pathArg(path), optsArg(opts))
module.exports = mkdirp

View file

@ -0,0 +1,29 @@
const {dirname} = require('path')
const findMade = (opts, parent, path = undefined) => {
// we never want the 'made' return value to be a root directory
if (path === parent)
return Promise.resolve()
return opts.statAsync(parent).then(
st => st.isDirectory() ? path : undefined, // will fail later
er => er.code === 'ENOENT'
? findMade(opts, dirname(parent), parent)
: undefined
)
}
const findMadeSync = (opts, parent, path = undefined) => {
if (path === parent)
return undefined
try {
return opts.statSync(parent).isDirectory() ? path : undefined
} catch (er) {
return er.code === 'ENOENT'
? findMadeSync(opts, dirname(parent), parent)
: undefined
}
}
module.exports = {findMade, findMadeSync}

View file

@ -0,0 +1,64 @@
const {dirname} = require('path')
const mkdirpManual = (path, opts, made) => {
opts.recursive = false
const parent = dirname(path)
if (parent === path) {
return opts.mkdirAsync(path, opts).catch(er => {
// swallowed by recursive implementation on posix systems
// any other error is a failure
if (er.code !== 'EISDIR')
throw er
})
}
return opts.mkdirAsync(path, opts).then(() => made || path, er => {
if (er.code === 'ENOENT')
return mkdirpManual(parent, opts)
.then(made => mkdirpManual(path, opts, made))
if (er.code !== 'EEXIST' && er.code !== 'EROFS')
throw er
return opts.statAsync(path).then(st => {
if (st.isDirectory())
return made
else
throw er
}, () => { throw er })
})
}
const mkdirpManualSync = (path, opts, made) => {
const parent = dirname(path)
opts.recursive = false
if (parent === path) {
try {
return opts.mkdirSync(path, opts)
} catch (er) {
// swallowed by recursive implementation on posix systems
// any other error is a failure
if (er.code !== 'EISDIR')
throw er
else
return
}
}
try {
opts.mkdirSync(path, opts)
return made || path
} catch (er) {
if (er.code === 'ENOENT')
return mkdirpManualSync(path, opts, mkdirpManualSync(parent, opts, made))
if (er.code !== 'EEXIST' && er.code !== 'EROFS')
throw er
try {
if (!opts.statSync(path).isDirectory())
throw er
} catch (_) {
throw er
}
}
}
module.exports = {mkdirpManual, mkdirpManualSync}

View file

@ -0,0 +1,39 @@
const {dirname} = require('path')
const {findMade, findMadeSync} = require('./find-made.js')
const {mkdirpManual, mkdirpManualSync} = require('./mkdirp-manual.js')
const mkdirpNative = (path, opts) => {
opts.recursive = true
const parent = dirname(path)
if (parent === path)
return opts.mkdirAsync(path, opts)
return findMade(opts, path).then(made =>
opts.mkdirAsync(path, opts).then(() => made)
.catch(er => {
if (er.code === 'ENOENT')
return mkdirpManual(path, opts)
else
throw er
}))
}
const mkdirpNativeSync = (path, opts) => {
opts.recursive = true
const parent = dirname(path)
if (parent === path)
return opts.mkdirSync(path, opts)
const made = findMadeSync(opts, path)
try {
opts.mkdirSync(path, opts)
return made
} catch (er) {
if (er.code === 'ENOENT')
return mkdirpManualSync(path, opts)
else
throw er
}
}
module.exports = {mkdirpNative, mkdirpNativeSync}

View file

@ -0,0 +1,23 @@
const { promisify } = require('util')
const fs = require('fs')
const optsArg = opts => {
if (!opts)
opts = { mode: 0o777, fs }
else if (typeof opts === 'object')
opts = { mode: 0o777, fs, ...opts }
else if (typeof opts === 'number')
opts = { mode: opts, fs }
else if (typeof opts === 'string')
opts = { mode: parseInt(opts, 8), fs }
else
throw new TypeError('invalid options argument')
opts.mkdir = opts.mkdir || opts.fs.mkdir || fs.mkdir
opts.mkdirAsync = promisify(opts.mkdir)
opts.stat = opts.stat || opts.fs.stat || fs.stat
opts.statAsync = promisify(opts.stat)
opts.statSync = opts.statSync || opts.fs.statSync || fs.statSync
opts.mkdirSync = opts.mkdirSync || opts.fs.mkdirSync || fs.mkdirSync
return opts
}
module.exports = optsArg

View file

@ -0,0 +1,29 @@
const platform = process.env.__TESTING_MKDIRP_PLATFORM__ || process.platform
const { resolve, parse } = require('path')
const pathArg = path => {
if (/\0/.test(path)) {
// simulate same failure that node raises
throw Object.assign(
new TypeError('path must be a string without null bytes'),
{
path,
code: 'ERR_INVALID_ARG_VALUE',
}
)
}
path = resolve(path)
if (platform === 'win32') {
const badWinChars = /[*|"<>?:]/
const {root} = parse(path)
if (badWinChars.test(path.substr(root.length))) {
throw Object.assign(new Error('Illegal characters in path.'), {
path,
code: 'EINVAL',
})
}
}
return path
}
module.exports = pathArg

View file

@ -0,0 +1,10 @@
const fs = require('fs')
const version = process.env.__TESTING_MKDIRP_NODE_VERSION__ || process.version
const versArr = version.replace(/^v/, '').split('.')
const hasNative = +versArr[0] > 10 || +versArr[0] === 10 && +versArr[1] >= 12
const useNative = !hasNative ? () => false : opts => opts.mkdir === fs.mkdir
const useNativeSync = !hasNative ? () => false : opts => opts.mkdirSync === fs.mkdirSync
module.exports = {useNative, useNativeSync}

View file

@ -0,0 +1,44 @@
{
"name": "mkdirp",
"description": "Recursively mkdir, like `mkdir -p`",
"version": "1.0.4",
"main": "index.js",
"keywords": [
"mkdir",
"directory",
"make dir",
"make",
"dir",
"recursive",
"native"
],
"repository": {
"type": "git",
"url": "https://github.com/isaacs/node-mkdirp.git"
},
"scripts": {
"test": "tap",
"snap": "tap",
"preversion": "npm test",
"postversion": "npm publish",
"postpublish": "git push origin --follow-tags"
},
"tap": {
"check-coverage": true,
"coverage-map": "map.js"
},
"devDependencies": {
"require-inject": "^1.4.4",
"tap": "^14.10.7"
},
"bin": "bin/cmd.js",
"license": "MIT",
"engines": {
"node": ">=10"
},
"files": [
"bin",
"lib",
"index.js"
]
}

View file

@ -0,0 +1,266 @@
# mkdirp
Like `mkdir -p`, but in Node.js!
Now with a modern API and no\* bugs!
<small>\* may contain some bugs</small>
# example
## pow.js
```js
const mkdirp = require('mkdirp')
// return value is a Promise resolving to the first directory created
mkdirp('/tmp/foo/bar/baz').then(made =>
console.log(`made directories, starting with ${made}`))
```
Output (where `/tmp/foo` already exists)
```
made directories, starting with /tmp/foo/bar
```
Or, if you don't have time to wait around for promises:
```js
const mkdirp = require('mkdirp')
// return value is the first directory created
const made = mkdirp.sync('/tmp/foo/bar/baz')
console.log(`made directories, starting with ${made}`)
```
And now /tmp/foo/bar/baz exists, huzzah!
# methods
```js
const mkdirp = require('mkdirp')
```
## mkdirp(dir, [opts]) -> Promise<String | undefined>
Create a new directory and any necessary subdirectories at `dir` with octal
permission string `opts.mode`. If `opts` is a string or number, it will be
treated as the `opts.mode`.
If `opts.mode` isn't specified, it defaults to `0o777 &
(~process.umask())`.
Promise resolves to first directory `made` that had to be created, or
`undefined` if everything already exists. Promise rejects if any errors
are encountered. Note that, in the case of promise rejection, some
directories _may_ have been created, as recursive directory creation is not
an atomic operation.
You can optionally pass in an alternate `fs` implementation by passing in
`opts.fs`. Your implementation should have `opts.fs.mkdir(path, opts, cb)`
and `opts.fs.stat(path, cb)`.
You can also override just one or the other of `mkdir` and `stat` by
passing in `opts.stat` or `opts.mkdir`, or providing an `fs` option that
only overrides one of these.
## mkdirp.sync(dir, opts) -> String|null
Synchronously create a new directory and any necessary subdirectories at
`dir` with octal permission string `opts.mode`. If `opts` is a string or
number, it will be treated as the `opts.mode`.
If `opts.mode` isn't specified, it defaults to `0o777 &
(~process.umask())`.
Returns the first directory that had to be created, or undefined if
everything already exists.
You can optionally pass in an alternate `fs` implementation by passing in
`opts.fs`. Your implementation should have `opts.fs.mkdirSync(path, mode)`
and `opts.fs.statSync(path)`.
You can also override just one or the other of `mkdirSync` and `statSync`
by passing in `opts.statSync` or `opts.mkdirSync`, or providing an `fs`
option that only overrides one of these.
## mkdirp.manual, mkdirp.manualSync
Use the manual implementation (not the native one). This is the default
when the native implementation is not available or the stat/mkdir
implementation is overridden.
## mkdirp.native, mkdirp.nativeSync
Use the native implementation (not the manual one). This is the default
when the native implementation is available and stat/mkdir are not
overridden.
# implementation
On Node.js v10.12.0 and above, use the native `fs.mkdir(p,
{recursive:true})` option, unless `fs.mkdir`/`fs.mkdirSync` has been
overridden by an option.
## native implementation
- If the path is a root directory, then pass it to the underlying
implementation and return the result/error. (In this case, it'll either
succeed or fail, but we aren't actually creating any dirs.)
- Walk up the path statting each directory, to find the first path that
will be created, `made`.
- Call `fs.mkdir(path, { recursive: true })` (or `fs.mkdirSync`)
- If error, raise it to the caller.
- Return `made`.
## manual implementation
- Call underlying `fs.mkdir` implementation, with `recursive: false`
- If error:
- If path is a root directory, raise to the caller and do not handle it
- If ENOENT, mkdirp parent dir, store result as `made`
- stat(path)
- If error, raise original `mkdir` error
- If directory, return `made`
- Else, raise original `mkdir` error
- else
- return `undefined` if a root dir, or `made` if set, or `path`
## windows vs unix caveat
On Windows file systems, attempts to create a root directory (ie, a drive
letter or root UNC path) will fail. If the root directory exists, then it
will fail with `EPERM`. If the root directory does not exist, then it will
fail with `ENOENT`.
On posix file systems, attempts to create a root directory (in recursive
mode) will succeed silently, as it is treated like just another directory
that already exists. (In non-recursive mode, of course, it fails with
`EEXIST`.)
In order to preserve this system-specific behavior (and because it's not as
if we can create the parent of a root directory anyway), attempts to create
a root directory are passed directly to the `fs` implementation, and any
errors encountered are not handled.
## native error caveat
The native implementation (as of at least Node.js v13.4.0) does not provide
appropriate errors in some cases (see
[nodejs/node#31481](https://github.com/nodejs/node/issues/31481) and
[nodejs/node#28015](https://github.com/nodejs/node/issues/28015)).
In order to work around this issue, the native implementation will fall
back to the manual implementation if an `ENOENT` error is encountered.
# choosing a recursive mkdir implementation
There are a few to choose from! Use the one that suits your needs best :D
## use `fs.mkdir(path, {recursive: true}, cb)` if:
- You wish to optimize performance even at the expense of other factors.
- You don't need to know the first dir created.
- You are ok with getting `ENOENT` as the error when some other problem is
the actual cause.
- You can limit your platforms to Node.js v10.12 and above.
- You're ok with using callbacks instead of promises.
- You don't need/want a CLI.
- You don't need to override the `fs` methods in use.
## use this module (mkdirp 1.x) if:
- You need to know the first directory that was created.
- You wish to use the native implementation if available, but fall back
when it's not.
- You prefer promise-returning APIs to callback-taking APIs.
- You want more useful error messages than the native recursive mkdir
provides (at least as of Node.js v13.4), and are ok with re-trying on
`ENOENT` to achieve this.
- You need (or at least, are ok with) a CLI.
- You need to override the `fs` methods in use.
## use [`make-dir`](http://npm.im/make-dir) if:
- You do not need to know the first dir created (and wish to save a few
`stat` calls when using the native implementation for this reason).
- You wish to use the native implementation if available, but fall back
when it's not.
- You prefer promise-returning APIs to callback-taking APIs.
- You are ok with occasionally getting `ENOENT` errors for failures that
are actually related to something other than a missing file system entry.
- You don't need/want a CLI.
- You need to override the `fs` methods in use.
## use mkdirp 0.x if:
- You need to know the first directory that was created.
- You need (or at least, are ok with) a CLI.
- You need to override the `fs` methods in use.
- You're ok with using callbacks instead of promises.
- You are not running on Windows, where the root-level ENOENT errors can
lead to infinite regress.
- You think vinyl just sounds warmer and richer for some weird reason.
- You are supporting truly ancient Node.js versions, before even the advent
of a `Promise` language primitive. (Please don't. You deserve better.)
# cli
This package also ships with a `mkdirp` command.
```
$ mkdirp -h
usage: mkdirp [DIR1,DIR2..] {OPTIONS}
Create each supplied directory including any necessary parent directories
that don't yet exist.
If the directory already exists, do nothing.
OPTIONS are:
-m<mode> If a directory needs to be created, set the mode as an octal
--mode=<mode> permission string.
-v --version Print the mkdirp version number
-h --help Print this helpful banner
-p --print Print the first directories created for each path provided
--manual Use manual implementation, even if native is available
```
# install
With [npm](http://npmjs.org) do:
```
npm install mkdirp
```
to get the library locally, or
```
npm install -g mkdirp
```
to get the command everywhere, or
```
npx mkdirp ...
```
to run the command without installing it globally.
# platform support
This module works on node v8, but only v10 and above are officially
supported, as Node v8 reached its LTS end of life 2020-01-01, which is in
the past, as of this writing.
# license
MIT

View file

@ -0,0 +1,65 @@
# v3.0
- Add `--preserve-root` option to executable (default true)
- Drop support for Node.js below version 6
# v2.7
- Make `glob` an optional dependency
# 2.6
- Retry on EBUSY on non-windows platforms as well
- Make `rimraf.sync` 10000% more reliable on Windows
# 2.5
- Handle Windows EPERM when lstat-ing read-only dirs
- Add glob option to pass options to glob
# 2.4
- Add EPERM to delay/retry loop
- Add `disableGlob` option
# 2.3
- Make maxBusyTries and emfileWait configurable
- Handle weird SunOS unlink-dir issue
- Glob the CLI arg for better Windows support
# 2.2
- Handle ENOENT properly on Windows
- Allow overriding fs methods
- Treat EPERM as indicative of non-empty dir
- Remove optional graceful-fs dep
- Consistently return null error instead of undefined on success
- win32: Treat ENOTEMPTY the same as EBUSY
- Add `rimraf` binary
# 2.1
- Fix SunOS error code for a non-empty directory
- Try rmdir before readdir
- Treat EISDIR like EPERM
- Remove chmod
- Remove lstat polyfill, node 0.7 is not supported
# 2.0
- Fix myGid call to check process.getgid
- Simplify the EBUSY backoff logic.
- Use fs.lstat in node >= 0.7.9
- Remove gently option
- remove fiber implementation
- Delete files that are marked read-only
# 1.0
- Allow ENOENT in sync method
- Throw when no callback is provided
- Make opts.gently an absolute path
- use 'stat' if 'lstat' is not available
- Consistent error naming, and rethrow non-ENOENT stat errors
- add fiber implementation

View file

@ -0,0 +1,15 @@
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View file

@ -0,0 +1,101 @@
[![Build Status](https://travis-ci.org/isaacs/rimraf.svg?branch=master)](https://travis-ci.org/isaacs/rimraf) [![Dependency Status](https://david-dm.org/isaacs/rimraf.svg)](https://david-dm.org/isaacs/rimraf) [![devDependency Status](https://david-dm.org/isaacs/rimraf/dev-status.svg)](https://david-dm.org/isaacs/rimraf#info=devDependencies)
The [UNIX command](http://en.wikipedia.org/wiki/Rm_(Unix)) `rm -rf` for node.
Install with `npm install rimraf`, or just drop rimraf.js somewhere.
## API
`rimraf(f, [opts], callback)`
The first parameter will be interpreted as a globbing pattern for files. If you
want to disable globbing you can do so with `opts.disableGlob` (defaults to
`false`). This might be handy, for instance, if you have filenames that contain
globbing wildcard characters.
The callback will be called with an error if there is one. Certain
errors are handled for you:
* Windows: `EBUSY` and `ENOTEMPTY` - rimraf will back off a maximum of
`opts.maxBusyTries` times before giving up, adding 100ms of wait
between each attempt. The default `maxBusyTries` is 3.
* `ENOENT` - If the file doesn't exist, rimraf will return
successfully, since your desired outcome is already the case.
* `EMFILE` - Since `readdir` requires opening a file descriptor, it's
possible to hit `EMFILE` if too many file descriptors are in use.
In the sync case, there's nothing to be done for this. But in the
async case, rimraf will gradually back off with timeouts up to
`opts.emfileWait` ms, which defaults to 1000.
## options
* unlink, chmod, stat, lstat, rmdir, readdir,
unlinkSync, chmodSync, statSync, lstatSync, rmdirSync, readdirSync
In order to use a custom file system library, you can override
specific fs functions on the options object.
If any of these functions are present on the options object, then
the supplied function will be used instead of the default fs
method.
Sync methods are only relevant for `rimraf.sync()`, of course.
For example:
```javascript
var myCustomFS = require('some-custom-fs')
rimraf('some-thing', myCustomFS, callback)
```
* maxBusyTries
If an `EBUSY`, `ENOTEMPTY`, or `EPERM` error code is encountered
on Windows systems, then rimraf will retry with a linear backoff
wait of 100ms longer on each try. The default maxBusyTries is 3.
Only relevant for async usage.
* emfileWait
If an `EMFILE` error is encountered, then rimraf will retry
repeatedly with a linear backoff of 1ms longer on each try, until
the timeout counter hits this max. The default limit is 1000.
If you repeatedly encounter `EMFILE` errors, then consider using
[graceful-fs](http://npm.im/graceful-fs) in your program.
Only relevant for async usage.
* glob
Set to `false` to disable [glob](http://npm.im/glob) pattern
matching.
Set to an object to pass options to the glob module. The default
glob options are `{ nosort: true, silent: true }`.
Glob version 6 is used in this module.
Relevant for both sync and async usage.
* disableGlob
Set to any non-falsey value to disable globbing entirely.
(Equivalent to setting `glob: false`.)
## rimraf.sync
It can remove stuff synchronously, too. But that's not so good. Use
the async API. It's better.
## CLI
If installed with `npm install rimraf -g` it can be used as a global
command `rimraf <path> [<path> ...]` which is useful for cross platform support.
## mkdirp
If you need to create a directory recursively, check out
[mkdirp](https://github.com/substack/node-mkdirp).

View file

@ -0,0 +1,68 @@
#!/usr/bin/env node
const rimraf = require('./')
const path = require('path')
const isRoot = arg => /^(\/|[a-zA-Z]:\\)$/.test(path.resolve(arg))
const filterOutRoot = arg => {
const ok = preserveRoot === false || !isRoot(arg)
if (!ok) {
console.error(`refusing to remove ${arg}`)
console.error('Set --no-preserve-root to allow this')
}
return ok
}
let help = false
let dashdash = false
let noglob = false
let preserveRoot = true
const args = process.argv.slice(2).filter(arg => {
if (dashdash)
return !!arg
else if (arg === '--')
dashdash = true
else if (arg === '--no-glob' || arg === '-G')
noglob = true
else if (arg === '--glob' || arg === '-g')
noglob = false
else if (arg.match(/^(-+|\/)(h(elp)?|\?)$/))
help = true
else if (arg === '--preserve-root')
preserveRoot = true
else if (arg === '--no-preserve-root')
preserveRoot = false
else
return !!arg
}).filter(arg => !preserveRoot || filterOutRoot(arg))
const go = n => {
if (n >= args.length)
return
const options = noglob ? { glob: false } : {}
rimraf(args[n], options, er => {
if (er)
throw er
go(n+1)
})
}
if (help || args.length === 0) {
// If they didn't ask for help, then this is not a "success"
const log = help ? console.log : console.error
log('Usage: rimraf <path> [<path> ...]')
log('')
log(' Deletes all files and folders at "path" recursively.')
log('')
log('Options:')
log('')
log(' -h, --help Display this usage info')
log(' -G, --no-glob Do not expand glob patterns in arguments')
log(' -g, --glob Expand glob patterns in arguments (default)')
log(' --preserve-root Do not remove \'/\' (default)')
log(' --no-preserve-root Do not treat \'/\' specially')
log(' -- Stop parsing flags')
process.exit(help ? 0 : 1)
} else
go(0)

View file

@ -0,0 +1,32 @@
{
"name": "rimraf",
"version": "3.0.2",
"main": "rimraf.js",
"description": "A deep deletion module for node (like `rm -rf`)",
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
"license": "ISC",
"repository": "git://github.com/isaacs/rimraf.git",
"scripts": {
"preversion": "npm test",
"postversion": "npm publish",
"postpublish": "git push origin --follow-tags",
"test": "tap test/*.js"
},
"bin": "./bin.js",
"dependencies": {
"glob": "^7.1.3"
},
"files": [
"LICENSE",
"README.md",
"bin.js",
"rimraf.js"
],
"devDependencies": {
"mkdirp": "^0.5.1",
"tap": "^12.1.1"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
}

View file

@ -0,0 +1,360 @@
const assert = require("assert")
const path = require("path")
const fs = require("fs")
let glob = undefined
try {
glob = require("glob")
} catch (_err) {
// treat glob as optional.
}
const defaultGlobOpts = {
nosort: true,
silent: true
}
// for EMFILE handling
let timeout = 0
const isWindows = (process.platform === "win32")
const defaults = options => {
const methods = [
'unlink',
'chmod',
'stat',
'lstat',
'rmdir',
'readdir'
]
methods.forEach(m => {
options[m] = options[m] || fs[m]
m = m + 'Sync'
options[m] = options[m] || fs[m]
})
options.maxBusyTries = options.maxBusyTries || 3
options.emfileWait = options.emfileWait || 1000
if (options.glob === false) {
options.disableGlob = true
}
if (options.disableGlob !== true && glob === undefined) {
throw Error('glob dependency not found, set `options.disableGlob = true` if intentional')
}
options.disableGlob = options.disableGlob || false
options.glob = options.glob || defaultGlobOpts
}
const rimraf = (p, options, cb) => {
if (typeof options === 'function') {
cb = options
options = {}
}
assert(p, 'rimraf: missing path')
assert.equal(typeof p, 'string', 'rimraf: path should be a string')
assert.equal(typeof cb, 'function', 'rimraf: callback function required')
assert(options, 'rimraf: invalid options argument provided')
assert.equal(typeof options, 'object', 'rimraf: options should be object')
defaults(options)
let busyTries = 0
let errState = null
let n = 0
const next = (er) => {
errState = errState || er
if (--n === 0)
cb(errState)
}
const afterGlob = (er, results) => {
if (er)
return cb(er)
n = results.length
if (n === 0)
return cb()
results.forEach(p => {
const CB = (er) => {
if (er) {
if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") &&
busyTries < options.maxBusyTries) {
busyTries ++
// try again, with the same exact callback as this one.
return setTimeout(() => rimraf_(p, options, CB), busyTries * 100)
}
// this one won't happen if graceful-fs is used.
if (er.code === "EMFILE" && timeout < options.emfileWait) {
return setTimeout(() => rimraf_(p, options, CB), timeout ++)
}
// already gone
if (er.code === "ENOENT") er = null
}
timeout = 0
next(er)
}
rimraf_(p, options, CB)
})
}
if (options.disableGlob || !glob.hasMagic(p))
return afterGlob(null, [p])
options.lstat(p, (er, stat) => {
if (!er)
return afterGlob(null, [p])
glob(p, options.glob, afterGlob)
})
}
// Two possible strategies.
// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR
// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR
//
// Both result in an extra syscall when you guess wrong. However, there
// are likely far more normal files in the world than directories. This
// is based on the assumption that a the average number of files per
// directory is >= 1.
//
// If anyone ever complains about this, then I guess the strategy could
// be made configurable somehow. But until then, YAGNI.
const rimraf_ = (p, options, cb) => {
assert(p)
assert(options)
assert(typeof cb === 'function')
// sunos lets the root user unlink directories, which is... weird.
// so we have to lstat here and make sure it's not a dir.
options.lstat(p, (er, st) => {
if (er && er.code === "ENOENT")
return cb(null)
// Windows can EPERM on stat. Life is suffering.
if (er && er.code === "EPERM" && isWindows)
fixWinEPERM(p, options, er, cb)
if (st && st.isDirectory())
return rmdir(p, options, er, cb)
options.unlink(p, er => {
if (er) {
if (er.code === "ENOENT")
return cb(null)
if (er.code === "EPERM")
return (isWindows)
? fixWinEPERM(p, options, er, cb)
: rmdir(p, options, er, cb)
if (er.code === "EISDIR")
return rmdir(p, options, er, cb)
}
return cb(er)
})
})
}
const fixWinEPERM = (p, options, er, cb) => {
assert(p)
assert(options)
assert(typeof cb === 'function')
options.chmod(p, 0o666, er2 => {
if (er2)
cb(er2.code === "ENOENT" ? null : er)
else
options.stat(p, (er3, stats) => {
if (er3)
cb(er3.code === "ENOENT" ? null : er)
else if (stats.isDirectory())
rmdir(p, options, er, cb)
else
options.unlink(p, cb)
})
})
}
const fixWinEPERMSync = (p, options, er) => {
assert(p)
assert(options)
try {
options.chmodSync(p, 0o666)
} catch (er2) {
if (er2.code === "ENOENT")
return
else
throw er
}
let stats
try {
stats = options.statSync(p)
} catch (er3) {
if (er3.code === "ENOENT")
return
else
throw er
}
if (stats.isDirectory())
rmdirSync(p, options, er)
else
options.unlinkSync(p)
}
const rmdir = (p, options, originalEr, cb) => {
assert(p)
assert(options)
assert(typeof cb === 'function')
// try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
// if we guessed wrong, and it's not a directory, then
// raise the original error.
options.rmdir(p, er => {
if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM"))
rmkids(p, options, cb)
else if (er && er.code === "ENOTDIR")
cb(originalEr)
else
cb(er)
})
}
const rmkids = (p, options, cb) => {
assert(p)
assert(options)
assert(typeof cb === 'function')
options.readdir(p, (er, files) => {
if (er)
return cb(er)
let n = files.length
if (n === 0)
return options.rmdir(p, cb)
let errState
files.forEach(f => {
rimraf(path.join(p, f), options, er => {
if (errState)
return
if (er)
return cb(errState = er)
if (--n === 0)
options.rmdir(p, cb)
})
})
})
}
// this looks simpler, and is strictly *faster*, but will
// tie up the JavaScript thread and fail on excessively
// deep directory trees.
const rimrafSync = (p, options) => {
options = options || {}
defaults(options)
assert(p, 'rimraf: missing path')
assert.equal(typeof p, 'string', 'rimraf: path should be a string')
assert(options, 'rimraf: missing options')
assert.equal(typeof options, 'object', 'rimraf: options should be object')
let results
if (options.disableGlob || !glob.hasMagic(p)) {
results = [p]
} else {
try {
options.lstatSync(p)
results = [p]
} catch (er) {
results = glob.sync(p, options.glob)
}
}
if (!results.length)
return
for (let i = 0; i < results.length; i++) {
const p = results[i]
let st
try {
st = options.lstatSync(p)
} catch (er) {
if (er.code === "ENOENT")
return
// Windows can EPERM on stat. Life is suffering.
if (er.code === "EPERM" && isWindows)
fixWinEPERMSync(p, options, er)
}
try {
// sunos lets the root user unlink directories, which is... weird.
if (st && st.isDirectory())
rmdirSync(p, options, null)
else
options.unlinkSync(p)
} catch (er) {
if (er.code === "ENOENT")
return
if (er.code === "EPERM")
return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
if (er.code !== "EISDIR")
throw er
rmdirSync(p, options, er)
}
}
}
const rmdirSync = (p, options, originalEr) => {
assert(p)
assert(options)
try {
options.rmdirSync(p)
} catch (er) {
if (er.code === "ENOENT")
return
if (er.code === "ENOTDIR")
throw originalEr
if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")
rmkidsSync(p, options)
}
}
const rmkidsSync = (p, options) => {
assert(p)
assert(options)
options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options))
// We only end up here once we got ENOTEMPTY at least once, and
// at this point, we are guaranteed to have removed all the kids.
// So, we know that it won't be ENOENT or ENOTDIR or anything else.
// try really hard to delete stuff on windows, because it has a
// PROFOUNDLY annoying habit of not closing handles promptly when
// files are deleted, resulting in spurious ENOTEMPTY errors.
const retries = isWindows ? 100 : 1
let i = 0
do {
let threw = true
try {
const ret = options.rmdirSync(p, options)
threw = false
return ret
} finally {
if (++i < retries && threw)
continue
}
} while (true)
}
module.exports = rimraf
rimraf.sync = rimrafSync