Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
117 lines (102 sloc) 4.22 KB
fs = require 'fs'
path = require 'path'
{exists, existsSync} = path
Seq = require 'seq'
/**
* Asynchronously and recursively remove files and/or directories.
*
* @param {String|Array<String>} paths Path or paths to remove.
* @param {Object} [options] Options:
* @param {Boolean} [options.verbose=false] Log all errors and print each path
* just before it's removed.
* @param {Boolean} [options.sequential=false] If true, remove the supplied
* paths sequentially, such that an unsuppressed error would short-circuit
* further deletes.
* @param {Boolean} [options.ignoreErrors=false] If false, halt as soon as
* possible after an error occurs and invoke the callback. When operating
* in `sequential` mode, this implies an error removing the first of several
* paths would halt before touching the rest. If set, `ignoreErrors` overrides
* `ignoreMissing`.
* @param {Boolean} [options.ignoreMissing=false] Whether to treat missing
* paths as errors.
* @param {Function} cb Completion callback, invoked with null on success
* and the error on failure.
* @returns {void}
*/
removeAsync = module.exports = exports = \
function removeAsync (paths=[], options, cb)
paths = [paths] if typeof paths is 'string'
if typeof options is 'function'
[cb, options] = [options, {}]
if typeof cb is not 'function'
throw new Error 'Callback must be a function!'
options = {verbose, ignoreErrors, ignoreMissing} =
({-verbose, -sequential, -ignoreErrors, -ignoreMissing} import options)
# TODO: recurisve? maxdepth?
stepMethod = if options.sequential then 'seqEach_' else 'parEach_'
Seq(paths)[stepMethod] (next_path, p) ->
console.log "rm -rv #p" if verbose
Seq()
.seq fs.lstat, p, Seq
.seq (stats) ->
# Files/links can be removed right away, and then move on to the next path
if stats.isSymbolicLink() or not stats.isDirectory()
fs.unlink p, next_path
else
# ...But directories need to be empty
fs.readdir p, this
# Remove children with the same options
.seq (contents) -> removeAsync contents.map(-> path.join p, it), options, this
# Finally, kill it
.seq fs.rmdir, p, Seq
# And terminate the async chain
.seq next_path.ok
# Propagate errors
.catch (err) -> next_path err
# Notify callback!
.seq -> cb null
.catch (err) ->
console.error err if verbose
return @ok() if ignoreErrors or (ignoreMissing and err.code is 'ENOENT')
cb err
void
exports.removeAsync = removeAsync
/**
* Synchronously and recursively remove files and/or directories.
*
* @param {String|Array<String>} paths Path or paths to remove.
* @param {Object} [options] Options:
* @param {Boolean} [options.verbose=false] Log all errors and print each path
* just before it's removed.
* @param {Boolean} [options.ignoreErrors=false] If false, halt as soon as
* possible after an error occurs and invoke the callback. This implies an error
* removing the first of several paths would halt before touching the rest. If
* set, `ignoreErrors` overrides `ignoreMissing`.
* @param {Boolean} [options.ignoreMissing=false] Whether to treat missing
* paths as errors.
* @returns {void}
*/
removeSync = exports.removeSync = \
function removeSync(paths=[], options={})
paths = [paths] if typeof paths is 'string'
options = {verbose, ignoreErrors, ignoreMissing} =
({-verbose, -ignoreErrors, -ignoreMissing} import options)
for p of paths
console.log "rm -rv #p" if verbose
try
stats = fs.lstatSync p
# files can be removed right away
if stats.isSymbolicLink() or not stats.isDirectory()
fs.unlinkSync p
else
# directories need to be empty
fs.readdirSync p .forEach -> removeSync path.join(p,it), options
# finally, kill it
fs.rmdirSync p
catch err
console.error err if verbose
if ignoreErrors or (ignoreMissing and err.code is 'ENOENT')
continue
else
throw err
void