UNPKG

alinex-fs

Version:

Extension of nodes filesystem tools.

324 lines (292 loc) 10.2 kB
/* Move Files ================================================= This will move a single file, complete directory or selection from directory. This is the same as copy the files and remove them afterwards. To select which files to copy and how to work you can use the following options: - `filter` - `Array<Object>|Object` {@link filter.coffee} - `clean` - `Boolean` if set to `true` it will clean old files from target. - `overwrite` - `Boolean` if set to `true` it will not fail if destination file already exists and overwrite it - `ignore` - `Boolean` it will not fail if destination file already exists but skip this and go on with the next file - `noempty` - `Boolean` set to `true to don't create empty directories while no files to copy into` - `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.copy '/tmp/some/directory', '/new/destination', (err) -> return console.error err.message if err console.log "Directory copied!" ``` You may also use options to specify which files within the source directory to move. */ (function() { var async, clean, copy, copyRemove, copyRemoveSync, debug, dirTasks, filter, fs, mkdirs, moveSync, parallel, path, remove; debug = require('debug')('fs:move'); fs = require('fs'); path = require('path'); async = require('async'); mkdirs = require('./mkdirs'); filter = require('../helper/filter'); copy = require('./copy'); remove = require('./remove'); parallel = require('../helper/parallel'); /* @param {String} source path or file to be copied @param {String} target file or directory to copy to @param {Object} [options] specifications for check defining which files to copy @param {function(<Error>)} [cb] callback which is called after done with possible `Èrror` */ module.exports.move = function(source, target, options, cb) { var list; if (options == null) { options = {}; } if (cb == null) { cb = function() {}; } if (typeof options === 'function' || !options) { cb = options != null ? options : function() {}; options = {}; } if (debug.enabled) { debug("move filepath " + source + " to " + target + "."); } list = []; return clean(target, { parallel: parallel(options), clean: options.clean, ignoreErrors: options.ignoreErrors, dereference: options.dereference }, function(err) { var queue; if (err && !options.ignoreErrors) { return cb(err); } 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) { return cb((options != null ? options.ignoreErrors : void 0) ? null : err); } return dirTasks(source, ok, stats, task, queue, function(err) { if (err) { return cb(err); } if (!ok) { return cb(); } target = target + task.source.slice(source.length); return mkdirs.mkdirs(path.dirname(target), function(err) { if (err && !options.ignoreErrors) { return cb(err); } return fs.exists(target, function(exists) { if (exists && !options.overwrite) { return cb(new Error("target file " + target + " already exists")); } return fs.rename(source, target, function(err) { if (!err) { if (debug.enabled) { debug("renamed " + source + " -> " + target); } list.push(target); return cb(); } return copyRemove(task.source, target, { overwrite: options.overwrite, ignore: options.ignore, ignoreErrors: options.ignoreErrors, dereference: options.dereference, parallel: parallel(options) }, function(err) { if (!err) { if (debug.enabled) { debug("copied/removed " + source + " -> " + target); } list.push(target); } return cb(err); }); }); }); }); }); }); }); }); }, Math.sqrt(parallel(options))); 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} source path or file to be copied @param {String} target file or directory to copy to @param {Object} [options] specifications for check defining which files to copy @throws {Error} if anything out of order happened @internal The `depth` parameter is only used internally. @param {Integer} [depth=0] current depth in file tree */ moveSync = module.exports.moveSync = function(source, target, options, depth) { var error, exists, file, i, len, list, ok, ref, stat, stats; if (options == null) { options = {}; } if (depth == null) { depth = 0; } if (debug.enabled) { debug("check " + source); } stat = options.dereference != null ? fs.statSync : fs.lstatSync; list = []; try { stats = stat(source); } catch (error1) { error = error1; if (options.ignoreErrors) { return list; } throw error; } if (options.clean) { remove.removeSync(target, { ignoreErrors: options.ignoreErrors, dereference: options.dereference }); } ok = filter.filterSync(source, depth, options); if (stats.isDirectory() && !ok) { depth++; mkdirs.mkdirsSync(target, stats.mode); if (debug.enabled) { debug("moving directory " + source + " to " + target); } ref = fs.readdirSync(source); for (i = 0, len = ref.length; i < len; i++) { file = ref[i]; list = list.concat(moveSync(path.join(source, file), path.join(target, file), options, depth)); list.sort(); return list; } } else { if (!ok) { return list; } try { exists = fs.existsSync(target); if (exists && !options.overwrite) { throw new Error("target file " + target + " already exists"); } fs.renameSync(source, target); if (debug.enabled) { debug("renamed " + source + " -> " + target); } list.push(target); return list; } catch (error1) { error = error1; if (!options.ignoreErrors) { throw error; } copyRemoveSync(source, target, { overwrite: options.overwrite, ignore: options.ignore, ignoreErrors: options.ignoreErrors, dereference: options.dereference }); if (debug.enabled) { debug("copied/removed " + source + " -> " + target); } list.push(target); return list; } } }; clean = function(source, options, cb) { if (!options.clean) { return cb(); } return remove.remove(source, options, cb); }; dirTasks = function(source, ok, stats, task, queue, cb) { if (!(stats.isDirectory && !ok)) { return cb(); } 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(); }); }; copyRemove = function(source, target, options, cb) { return copy.copy(source, target, options, function(err, list) { if (err) { return cb(err); } list.reverse(); return async.eachLimit(list, options.parallel, function(file, cb) { return remove.remove(file, options, cb); }, cb); }); }; copyRemoveSync = function(source, target, options) { copy.copySync(source, target, options); return remove.removeSync(source, options); }; /* Debugging --------------------------------------------------------- This module uses the {@link debug} module so you may anytime call your app with the environment setting `DEBUG=fs:move` 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:move move filepath test/temp/dir1 to test/temp/dir4. +26ms fs:move check test/temp/dir1 +0ms fs:filter skip because path not included +7ms fs:filter test/temp/dir1 SKIP +0ms fs:move check test/temp/dir1/file11 +0ms fs:filter test/temp/dir1/file11 OK +2ms fs:mkdirs directory /home/alex/github/node-fs/test/temp/dir4? +0ms fs:mkdirs directory /home/alex/github/node-fs/test/temp/dir4 created +0ms fs:move renamed test/temp/dir1 -> test/temp/dir4/file11 +0ms */ }).call(this); //# sourceMappingURL=move.map