mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
3.4 KiB
161 lines
3.4 KiB
module.exports = rimraf
|
|
rimraf.sync = rimrafSync
|
|
|
|
var path = require("path")
|
|
, fs
|
|
|
|
try {
|
|
// optional dependency
|
|
fs = require("graceful-fs")
|
|
} catch (er) {
|
|
fs = require("fs")
|
|
}
|
|
|
|
var lstat = "lstat"
|
|
if (process.platform === "win32") {
|
|
// not reliable on windows prior to 0.7.9
|
|
var v = process.version.replace(/^v/, '').split(/\.|-/).map(Number)
|
|
if (v[0] === 0 && (v[1] < 7 || v[1] == 7 && v[2] < 9)) {
|
|
lstat = "stat"
|
|
}
|
|
}
|
|
if (!fs[lstat]) lstat = "stat"
|
|
var lstatSync = lstat + "Sync"
|
|
|
|
// for EMFILE handling
|
|
var timeout = 0
|
|
exports.EMFILE_MAX = 1000
|
|
exports.BUSYTRIES_MAX = 3
|
|
|
|
function rimraf (p, cb) {
|
|
|
|
if (!cb) throw new Error("No callback passed to rimraf()")
|
|
|
|
var busyTries = 0
|
|
|
|
rimraf_(p, function CB (er) {
|
|
if (er) {
|
|
if (er.code === "EBUSY" && busyTries < exports.BUSYTRIES_MAX) {
|
|
busyTries ++
|
|
var time = busyTries * 100
|
|
// try again, with the same exact callback as this one.
|
|
return setTimeout(function () {
|
|
rimraf_(p, CB)
|
|
}, time)
|
|
}
|
|
|
|
// this one won't happen if graceful-fs is used.
|
|
if (er.code === "EMFILE" && timeout < exports.EMFILE_MAX) {
|
|
return setTimeout(function () {
|
|
rimraf_(p, CB)
|
|
}, timeout ++)
|
|
}
|
|
|
|
// already gone
|
|
if (er.code === "ENOENT") er = null
|
|
}
|
|
|
|
timeout = 0
|
|
cb(er)
|
|
})
|
|
}
|
|
|
|
function rimraf_ (p, cb) {
|
|
fs[lstat](p, function (er, s) {
|
|
if (er) {
|
|
// already gone
|
|
if (er.code === "ENOENT") return cb()
|
|
// some other kind of error, permissions, etc.
|
|
return cb(er)
|
|
}
|
|
|
|
return rm_(p, s, false, cb)
|
|
})
|
|
}
|
|
|
|
|
|
var myGid = function myGid () {
|
|
var g = process.getuid && process.getgid()
|
|
myGid = function myGid () { return g }
|
|
return g
|
|
}
|
|
|
|
var myUid = function myUid () {
|
|
var u = process.getuid && process.getuid()
|
|
myUid = function myUid () { return u }
|
|
return u
|
|
}
|
|
|
|
|
|
function writable (s) {
|
|
var mode = s.mode || 0777
|
|
, uid = myUid()
|
|
, gid = myGid()
|
|
return (mode & 0002)
|
|
|| (gid === s.gid && (mode & 0020))
|
|
|| (uid === s.uid && (mode & 0200))
|
|
}
|
|
|
|
function rm_ (p, s, didWritableCheck, cb) {
|
|
if (!didWritableCheck && !writable(s)) {
|
|
// make file writable
|
|
// user/group/world, doesn't matter at this point
|
|
// since it's about to get nuked.
|
|
return fs.chmod(p, s.mode | 0222, function (er) {
|
|
if (er) return cb(er)
|
|
rm_(p, s, true, cb)
|
|
})
|
|
}
|
|
|
|
if (!s.isDirectory()) {
|
|
return fs.unlink(p, cb)
|
|
}
|
|
|
|
// directory
|
|
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, cb)
|
|
}, function (er) {
|
|
if (er) return cb(er)
|
|
fs.rmdir(p, cb)
|
|
})
|
|
})
|
|
}
|
|
|
|
function asyncForEach (list, fn, cb) {
|
|
if (!list.length) cb()
|
|
var c = list.length
|
|
, errState = null
|
|
list.forEach(function (item, i, list) {
|
|
fn(item, function (er) {
|
|
if (errState) return
|
|
if (er) return cb(errState = er)
|
|
if (-- c === 0) return cb()
|
|
})
|
|
})
|
|
}
|
|
|
|
// this looks simpler, but it will fail with big directory trees,
|
|
// or on slow stupid awful cygwin filesystems
|
|
function rimrafSync (p) {
|
|
try {
|
|
var s = fs[lstatSync](p)
|
|
} catch (er) {
|
|
if (er.code === "ENOENT") return
|
|
throw er
|
|
}
|
|
|
|
if (!writable(s)) {
|
|
fs.chmodSync(p, s.mode | 0222)
|
|
}
|
|
|
|
if (!s.isDirectory()) return fs.unlinkSync(p)
|
|
|
|
fs.readdirSync(p).forEach(function (f) {
|
|
rimrafSync(path.join(p, f))
|
|
})
|
|
fs.rmdirSync(p)
|
|
}
|
|
|