You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
3.2 KiB
JavaScript

// most of this code was written by Andrew Kelley
// licensed under the BSD license: see
// https://github.com/andrewrk/node-mv/blob/master/package.json
// this needs a cleanup
var fs = require('graceful-fs')
var ncp = require('./_copy').ncp
var path = require('path')
var rimraf = require('rimraf')
var mkdirp = require('./mkdir').mkdirs
function mv(source, dest, options, callback){
if (typeof options === 'function') {
callback = 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 callback(err)
doRename()
})
}
function doRename() {
if (clobber) {
fs.rename(source, dest, function(err) {
if (!err) return callback()
if (err.code === 'ENOTEMPTY') {
rimraf(dest, function(err) {
if (err) return callback(err)
options.clobber = false // just clobbered it, no need to do it again
mv(source, dest, options, callback)
})
return
}
if (err.code !== 'EXDEV') return callback(err)
moveFileAcrossDevice(source, dest, clobber, limit, callback)
})
} else {
fs.link(source, dest, function(err) {
if (err) {
if (err.code === 'EXDEV') {
moveFileAcrossDevice(source, dest, clobber, limit, callback)
return
}
if (err.code === 'EISDIR' || err.code === 'EPERM') {
moveDirAcrossDevice(source, dest, clobber, limit, callback)
return
}
callback(err)
return
}
fs.unlink(source, callback)
})
}
}
}
function moveFileAcrossDevice(source, dest, clobber, limit, callback) {
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)
// may want to create a directory but `out` line above
// creates an empty file for us: See #108
// don't care about error here
fs.unlink(dest, function() {
// note: `err` here is from the input stream errror
if (err.code === 'EISDIR' || err.code === 'EPERM') {
moveDirAcrossDevice(source, dest, clobber, limit, callback)
} else {
callback(err)
}
})
})
outs.on('error', function(err) {
ins.destroy()
outs.destroy()
outs.removeListener('close', onClose)
callback(err)
})
outs.once('close', onClose)
ins.pipe(outs)
function onClose() {
fs.unlink(source, callback)
}
}
function moveDirAcrossDevice(source, dest, clobber, limit, callback) {
var options = {
stopOnErr: true,
clobber: false,
limit: limit,
}
function startNcp() {
ncp(source, dest, options, function(errList) {
if (errList) return callback(errList[0])
rimraf(source, callback)
})
}
if (clobber) {
rimraf(dest, function(err) {
if (err) return callback(err)
startNcp()
})
} else {
startNcp()
}
}
module.exports = mv