1'use strict' 2module.exports = move 3 4var nodeFs = require('fs') 5var rimraf = require('rimraf') 6var validate = require('aproba') 7var copy = require('copy-concurrently') 8var RunQueue = require('run-queue') 9var extend = Object.assign || require('util')._extend 10 11function promisify (Promise, fn) { 12 return function () { 13 var args = [].slice.call(arguments) 14 return new Promise(function (resolve, reject) { 15 return fn.apply(null, args.concat(function (err, value) { 16 if (err) { 17 reject(err) 18 } else { 19 resolve(value) 20 } 21 })) 22 }) 23 } 24} 25 26function move (from, to, opts) { 27 validate('SSO|SS', arguments) 28 opts = extend({}, opts || {}) 29 30 var Promise = opts.Promise || global.Promise 31 var fs = opts.fs || nodeFs 32 var rimrafAsync = promisify(Promise, rimraf) 33 var renameAsync = promisify(Promise, fs.rename) 34 35 opts.top = from 36 37 var queue = new RunQueue({ 38 maxConcurrency: opts.maxConcurrency, 39 Promise: Promise 40 }) 41 opts.queue = queue 42 opts.recurseWith = rename 43 44 queue.add(0, rename, [from, to, opts]) 45 46 return queue.run().then(function () { 47 return remove(from) 48 }, function (err) { 49 // if the target already exists don't clobber it 50 if (err.code === 'EEXIST' || err.code === 'EPERM') { 51 return passThroughError() 52 } else { 53 return remove(to).then(passThroughError, passThroughError) 54 } 55 function passThroughError () { 56 return Promise.reject(err) 57 } 58 }) 59 60 function remove (target) { 61 var opts = { 62 unlink: fs.unlink, 63 chmod: fs.chmod, 64 stat: fs.stat, 65 lstat: fs.lstat, 66 rmdir: fs.rmdir, 67 readdir: fs.readdir, 68 glob: false 69 } 70 return rimrafAsync(target, opts) 71 } 72 73 function rename (from, to, opts, done) { 74 return renameAsync(from, to).catch(function (err) { 75 if (err.code !== 'EXDEV') { 76 return Promise.reject(err) 77 } else { 78 return remove(to).then(function () { 79 return copy.item(from, to, opts) 80 }) 81 } 82 }) 83 } 84} 85