rimraf
Advanced tools
Comparing version 1.0.2 to 1.0.4
{"name":"rimraf" | ||
,"version":"1.0.2" | ||
,"version":"1.0.4" | ||
,"main":"rimraf.js" | ||
@@ -4,0 +4,0 @@ ,"description":"A deep deletion module for node (like `rm -rf`)" |
A `rm -rf` for node. | ||
Install with `npm install rimraf`, or just drop rimraf.js somewhere. | ||
## API | ||
`rimraf(f, [options,] callback)` | ||
The callback will be called with an error if there is one. Certain | ||
errors are handled for you: | ||
* `EBUSY` - rimraf will back off a maximum of opts.maxBusyTries times | ||
before giving up. | ||
* `EMFILE` - If too many file descriptors get opened, rimraf will | ||
patiently wait until more become available. | ||
## Options | ||
The options object is optional. These fields are respected: | ||
* `maxBusyTries` - The number of times to retry a file or folder in the | ||
event of an `EBUSY` error. The default is 3. | ||
* `gently` - If provided a `gently` path, then rimraf will only delete | ||
files and folders that are beneath this path, and only delete symbolic | ||
links that point to a place within this path. (This is very important | ||
to npm's use-case, and shows rimraf's pedigree.) | ||
## rimraf.sync | ||
It can remove stuff synchronously, too. But that's not so good. Use | ||
the async API. It's better. |
module.exports = rimraf | ||
rimraf.sync = rimrafSync | ||
var fs = require("fs") | ||
, path = require("path") | ||
var path = require("path") | ||
, fs | ||
try { | ||
// optional dependency | ||
fs = require("graceful-fs") | ||
} catch (ex) { | ||
fs = require("fs") | ||
} | ||
// for EBUSY handling | ||
var waitBusy = {} | ||
, maxBusyTries = 3 | ||
@@ -15,24 +21,29 @@ // for EMFILE handling | ||
function rimraf (p, cb_) { | ||
rimraf_(p, function cb (er) { | ||
function rimraf (p, opts, cb) { | ||
if (typeof opts === "function") cb = opts, opts = {} | ||
opts.maxBusyTries = opts.maxBusyTries || 3 | ||
rimraf_(p, opts, function (er) { | ||
if (er) { | ||
if (er.message.match(/^EBUSY/)) { | ||
// windows is annoying. | ||
if (!waitBusy.hasOwnProperty(p)) waitBusy[p] = maxBusyTries | ||
if (!waitBusy.hasOwnProperty(p)) waitBusy[p] = opts.maxBusyTries | ||
if (waitBusy[p]) { | ||
waitBusy[p] -- | ||
// give it 100ms more each time | ||
var time = (maxBusyTries - waitBusy[p]) * 100 | ||
return setTimeout(function () { rimraf_(p, cb) }, time) | ||
var time = (opts.maxBusyTries - waitBusy[p]) * 100 | ||
return setTimeout(function () { rimraf_(p, opts, cb) }, time) | ||
} | ||
} | ||
// this one won't happen if graceful-fs is used. | ||
if (er.message.match(/^EMFILE/)) { | ||
setTimeout(function () { | ||
rimraf_(p, cb) | ||
return setTimeout(function () { | ||
rimraf_(p, opts, cb) | ||
}, timeout ++) | ||
return | ||
} | ||
} | ||
timeout = 0 | ||
cb_(er) | ||
cb(er) | ||
}) | ||
@@ -54,14 +65,24 @@ } | ||
function rimraf_ (p, cb) { | ||
function rimraf_ (p, opts, cb) { | ||
fs.lstat(p, function (er, s) { | ||
// if the stat fails, then assume it's already gone. | ||
if (er) return cb() | ||
if (!s.isDirectory()) return fs.unlink(p, cb) | ||
fs.readdir(p, function (er, files) { | ||
// don't delete that don't point actually live in the "gently" path | ||
if (opts.gently) return clobberTest(p, s, opts, cb) | ||
return rm_(p, s, opts, cb) | ||
}) | ||
} | ||
function rm_ (p, s, opts, cb) { | ||
if (!s.isDirectory()) return fs.unlink(p, cb) | ||
fs.readdir(p, function (er, files) { | ||
if (er) return cb(er) | ||
asyncForEach(files.map(function (f) { | ||
return path.join(p, f) | ||
}), function (file, cb) { | ||
rimraf(file, opts, cb) | ||
}, function (er) { | ||
if (er) return cb(er) | ||
asyncForEach(files.map(function (f) { | ||
return path.join(p, f) | ||
}), rimraf, function (er) { | ||
if (er) return cb(er) | ||
fs.rmdir(p, cb) | ||
}) | ||
fs.rmdir(p, cb) | ||
}) | ||
@@ -71,7 +92,32 @@ }) | ||
function clobberTest (p, s, opts, cb) { | ||
var gently = opts.gently | ||
if (!s.isSymbolicLink()) next(null, path.resolve(p)) | ||
else realish(p, next) | ||
function next (er, rp) { | ||
if (er) return rm_(p, s, cb) | ||
if (rp.indexOf(gently) !== 0) return clobberFail(p, gently, cb) | ||
else return rm_(p, s, opts, cb) | ||
} | ||
} | ||
function realish (p, cb) { | ||
fs.readlink(p, function (er, r) { | ||
if (er) return cb(er) | ||
return cb(null, path.resolve(path.dirname(p), r)) | ||
}) | ||
} | ||
function clobberFail (p, g, cb) { | ||
var er = new Error("Refusing to delete: "+p+" not in "+g) | ||
, constants = require("constants") | ||
er.errno = constants.EEXIST | ||
er.code = "EEXIST" | ||
er.path = p | ||
return cb(er) | ||
} | ||
// this looks simpler, but it will fail with big directory trees, | ||
// or on slow stupid awful windows filesystems, | ||
// and it's much slower, since the functional async version will | ||
// actually delete up to 4 things at once, or whatever eio is | ||
// configured to handle. | ||
// or on slow stupid awful windows filesystems | ||
function rimrafSync (p) { | ||
@@ -78,0 +124,0 @@ var s = fs.lstatSync(p) |
6712
118
33