117 lines
4.2 KiB
Plaintext
117 lines
4.2 KiB
Plaintext
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
|
|
|