var fs = require('fs'); var ncp = require('ncp').ncp; var path = require('path'); var rimraf = require('rimraf'); var mkdirp = require('mkdirp'); module.exports = mv; function mv(source, dest, options, cb){ if (typeof options === 'function') { cb = options; options = {}; } var shouldMkdirp = !!options.mkdirp; var clobber = options.clobber !== false; var limit = options.limit || 16; if (shouldMkdirp) { mkdirs(); } else { doRename(); } function mkdirs() { mkdirp(path.dirname(dest), function(err) { if (err) return cb(err); doRename(); }); } function doRename() { if (clobber) { fs.rename(source, dest, function(err) { if (!err) return cb(); if (err.code !== 'EXDEV') return cb(err); moveFileAcrossDevice(source, dest, clobber, limit, cb); }); } else { fs.link(source, dest, function(err) { if (err) { if (err.code === 'EXDEV') { moveFileAcrossDevice(source, dest, clobber, limit, cb); return; } if (err.code === 'EISDIR' || err.code === 'EPERM') { moveDirAcrossDevice(source, dest, clobber, limit, cb); return; } cb(err); return; } fs.unlink(source, cb); }); } } } function moveFileAcrossDevice(source, dest, clobber, limit, cb) { var outFlags = clobber ? 'w' : 'wx'; var ins = fs.createReadStream(source); var outs = fs.createWriteStream(dest, {flags: outFlags}); ins.on('error', function(err){ ins.destroy(); outs.destroy(); outs.removeListener('close', onClose); if (err.code === 'EISDIR' || err.code === 'EPERM') { moveDirAcrossDevice(source, dest, clobber, limit, cb); } else { cb(err); } }); outs.on('error', function(err){ ins.destroy(); outs.destroy(); outs.removeListener('close', onClose); cb(err); }); outs.once('close', onClose); ins.pipe(outs); function onClose(){ fs.unlink(source, cb); } } function moveDirAcrossDevice(source, dest, clobber, limit, cb) { var options = { stopOnErr: true, clobber: false, limit: limit, }; if (clobber) { rimraf(dest, { disableGlob: true }, function(err) { if (err) return cb(err); startNcp(); }); } else { startNcp(); } function startNcp() { ncp(source, dest, options, function(errList) { if (errList) return cb(errList[0]); rimraf(source, { disableGlob: true }, cb); }); } }