123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- // 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
- }
|