UNPKG

alinex-fs

Version:

Extension of nodes filesystem tools.

278 lines (251 loc) 8.45 kB
/* Remove Files ================================================= This method will remove the given `path` entry and if it is a directory it will also remove any containing data or only the selection of files. The option `maxdepth` is only supported in the search, but if a directory is matched everything within will be deleted. This method will remove the given `path` entry and if it is a directory it will also remove any containing data. If some filter given you can also delete# selectively. To select which files to remove the following options may be used: - `filter` - `Array<Object>|Object` {@link filter.coffee} - `dereference` - `Boolean` dereference symbolic links and go into them - `ìgnoreErrors` - `Boolean` go on and ignore IO errors - `parallel` - `Integer` number of maximum parallel calls in asynchronous run (defaults to half of open files limit per process on the system) __Example:__ ``` coffee fs = require 'alinex-fs' fs.remove '/tmp/some/directory', (err, removed) -> return console.error err if err if removed console.log "Directory '"+removed+"' was removed with all it's contents." console.log "Directory no longer exists!" ``` */ (function() { var PARALLEL, async, debug, filter, fs, path, posix, removeFile, removeSync; debug = require('debug')('fs:remove'); path = require('path'); async = require('async'); fs = require('fs'); posix = require('posix'); filter = require('../helper/filter'); PARALLEL = Math.floor(posix.getrlimit('nofile').soft / 2); /* @param {String} source directory or file to be deleted @param {Object} [options] specifications for check defining which files to remove @param {function(Error, Array<String>)} [cb] callback which is called after done with possible `Èrror` or with the files/directories been deleted */ module.exports.remove = function(source, options, cb) { var list, queue, ref; if (cb == null) { cb = function() {}; } if (typeof options === 'function' || !options) { cb = options != null ? options : function() {}; options = {}; } list = []; queue = async.queue(function(task, cb) { if (debug.enabled) { debug("check " + task.source); } return async.setImmediate(function() { return filter.filter(task.source, task.depth, options, function(ok) { var stat; if (ok === void 0) { return cb(); } stat = options.dereference != null ? fs.stat : fs.lstat; return stat(task.source, function(err, stats) { if (err && (err.code === 'ENOENT' || options.ignoreErrors)) { return cb(); } if (err) { return cb((options != null ? options.ignoreErrors : void 0) ? null : err); } if (ok) { removeFile(task.source, options, function(err) { if (err && (err.code === 'ENOENT' || options.ignoreErrors)) { return cb(); } return cb(err); }); return list.push(task.source); } if (!stats.isDirectory()) { return cb(null, list); } debug("going deeper into " + task.source + " directory"); task.depth++; return fs.readdir(task.source, function(err, files) { var file, i, len; if (err) { return cb(err); } for (i = 0, len = files.length; i < len; i++) { file = files[i]; queue.push({ source: task.source + "/" + file, depth: task.depth }); } return cb(); }); }); }); }); }, (ref = options.parallel) != null ? ref : PARALLEL); queue.push({ source: source, depth: 0 }); queue.drain = function() { list.sort(); return cb(null, list); }; return queue.error = function(err) { queue.kill(); cb(err); return cb = function() {}; }; }; /* @param {String} path directory or file to be deleted @param {Object} [options] specifications for check defining which files to remove @return {Array<String>} the files or directories been deleted @throws {Error} if domething went wrong @internal The `depth` parameter is only used internally. @param {Integer} [depth=0] current depth in file tree */ removeSync = module.exports.removeSync = function(file, options, depth) { var dir, error, files, i, len, list, ok, stat, stats; if (options == null) { options = {}; } if (depth == null) { depth = 0; } stat = options.dereference != null ? fs.statSync : fs.lstatSync; list = []; try { stats = stat(file); } catch (error1) { error = error1; if (error.code === 'ENOENT' || options.ignoreErrors) { return list; } throw error; } ok = filter.filterSync(file, depth, options); if (ok) { list.push(file); } if (stats.isFile()) { if (!ok) { return list; } if (debug.enabled) { debug("removing file " + file); } fs.unlinkSync(file); } else if (stats.isSymbolicLink()) { if (!ok) { return list; } if (debug.enabled) { debug("removing link " + file); } fs.unlinkSync(file); } else if (stats.isDirectory()) { dir = file; depth++; if (debug.enabled) { debug("removing directory " + file); } if (ok) { options = {}; } files = fs.readdirSync(file); for (i = 0, len = files.length; i < len; i++) { file = files[i]; list = list.concat(removeSync(path.join(dir, file), options, depth)); } if (!ok) { return; } try { fs.rmdirSync(dir); } catch (error1) {} try { fs.unlinkSync(dir); } catch (error1) {} } else { throw new Error("Entry '" + file + "' is no directory, file or symbolic link."); } list.sort(); return list; }; removeFile = function(source, options, cb) { var stat; stat = options.dereference != null ? fs.stat : fs.lstat; return stat(source, function(err, stats) { if (err && (err.code === 'ENOENT' || options.ignoreErrors)) { return cb(); } if (stats.isFile()) { if (debug.enabled) { debug("removing " + source + " (file)"); } return fs.unlink(source, cb); } else if (stats.isSymbolicLink()) { if (debug.enabled) { debug("removing " + source + " (link)"); } return fs.unlink(source, cb); } else if (stats.isDirectory()) { if (debug.enabled) { debug("removing " + source + " (directory)"); } return fs.readdir(source, function(err, files) { var ref; if (err) { return cb(err); } return async.eachLimit(files, (ref = options.parallel) != null ? ref : PARALLEL, function(file, cb) { return removeFile(source + "/" + file, options, cb); }, function(err) { if (err) { return cb(err); } return fs.rmdir(source, function(err) { if ((err != null ? err.code : void 0) === 'ENOTDIR') { return fs.unlink(source, cb); } return cb(err); }); }); }); } else { return cb(new Error("Entry '" + source + "' is no directory, file or symbolic link.")); } }); }; /* Debugging --------------------------------------------------------- This module uses the {@link debug} module so you may anytime call your app with the environment setting `DEBUG=fs:remove` for the output of this method only. Because there are `mkdirs` subcalls here you see the output of `DEBUG=fs:*` while removeing a small directory: fs:remove check test/temp/dir1 +29ms fs:filter skip test/temp/dir1 because not in specified depth +1ms fs:remove going deeper into test/temp/dir1 directory +0ms fs:remove check test/temp/dir1/file11 +0ms fs:remove removing test/temp/dir1/file11 (file) +0ms */ }).call(this); //# sourceMappingURL=remove.map