documentit/node_modules/fs-extra/lib/move/index.js
2018-01-05 10:48:13 -08:00

162 lines
3.9 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('../mkdirs').mkdirs
function mv (source, dest, options, callback) {
if (typeof options === 'function') {
callback = options
options = {}
}
var shouldMkdirp = ('mkdirp' in options) ? options.mkdirp : true
var clobber = ('clobber' in options) ? 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' || err.code === 'EEXIST') {
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
}
// weird Windows shit
if (err.code === 'EPERM') {
setTimeout(function () {
rimraf(dest, function (err) {
if (err) return callback(err)
options.clobber = false
mv(source, dest, options, callback)
})
}, 200)
return
}
if (err.code !== 'EXDEV') return callback(err)
moveAcrossDevice(source, dest, clobber, limit, callback)
})
} else {
fs.link(source, dest, function (err) {
if (err) {
if (err.code === 'EXDEV' || err.code === 'EISDIR' || err.code === 'EPERM') {
moveAcrossDevice(source, dest, clobber, limit, callback)
return
}
callback(err)
return
}
fs.unlink(source, callback)
})
}
}
}
function moveAcrossDevice (source, dest, clobber, limit, callback) {
fs.stat(source, function (err, stat) {
if (err) {
callback(err)
return
}
if (stat.isDirectory()) {
moveDirAcrossDevice(source, dest, clobber, limit, callback)
} else {
moveFileAcrossDevice(source, dest, clobber, limit, 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 = {
move: mv
}