index.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // most of this code was written by Andrew Kelley
  2. // licensed under the BSD license: see
  3. // https://github.com/andrewrk/node-mv/blob/master/package.json
  4. // this needs a cleanup
  5. var fs = require('graceful-fs')
  6. var ncp = require('../copy/ncp')
  7. var path = require('path')
  8. var remove = require('../remove').remove
  9. var mkdirp = require('../mkdirs').mkdirs
  10. function mv (source, dest, options, callback) {
  11. if (typeof options === 'function') {
  12. callback = options
  13. options = {}
  14. }
  15. var shouldMkdirp = ('mkdirp' in options) ? options.mkdirp : true
  16. var overwrite = options.overwrite || options.clobber || false
  17. if (shouldMkdirp) {
  18. mkdirs()
  19. } else {
  20. doRename()
  21. }
  22. function mkdirs () {
  23. mkdirp(path.dirname(dest), function (err) {
  24. if (err) return callback(err)
  25. doRename()
  26. })
  27. }
  28. function doRename () {
  29. if (overwrite) {
  30. fs.rename(source, dest, function (err) {
  31. if (!err) return callback()
  32. if (err.code === 'ENOTEMPTY' || err.code === 'EEXIST') {
  33. remove(dest, function (err) {
  34. if (err) return callback(err)
  35. options.overwrite = false // just overwriteed it, no need to do it again
  36. mv(source, dest, options, callback)
  37. })
  38. return
  39. }
  40. // weird Windows shit
  41. if (err.code === 'EPERM') {
  42. setTimeout(function () {
  43. remove(dest, function (err) {
  44. if (err) return callback(err)
  45. options.overwrite = false
  46. mv(source, dest, options, callback)
  47. })
  48. }, 200)
  49. return
  50. }
  51. if (err.code !== 'EXDEV') return callback(err)
  52. moveAcrossDevice(source, dest, overwrite, callback)
  53. })
  54. } else {
  55. fs.link(source, dest, function (err) {
  56. if (err) {
  57. if (err.code === 'EXDEV' || err.code === 'EISDIR' || err.code === 'EPERM') {
  58. moveAcrossDevice(source, dest, overwrite, callback)
  59. return
  60. }
  61. callback(err)
  62. return
  63. }
  64. fs.unlink(source, callback)
  65. })
  66. }
  67. }
  68. }
  69. function moveAcrossDevice (source, dest, overwrite, callback) {
  70. fs.stat(source, function (err, stat) {
  71. if (err) {
  72. callback(err)
  73. return
  74. }
  75. if (stat.isDirectory()) {
  76. moveDirAcrossDevice(source, dest, overwrite, callback)
  77. } else {
  78. moveFileAcrossDevice(source, dest, overwrite, callback)
  79. }
  80. })
  81. }
  82. function moveFileAcrossDevice (source, dest, overwrite, callback) {
  83. var outFlags = overwrite ? 'w' : 'wx'
  84. var ins = fs.createReadStream(source)
  85. var outs = fs.createWriteStream(dest, {flags: outFlags})
  86. ins.on('error', function (err) {
  87. ins.destroy()
  88. outs.destroy()
  89. outs.removeListener('close', onClose)
  90. // may want to create a directory but `out` line above
  91. // creates an empty file for us: See #108
  92. // don't care about error here
  93. fs.unlink(dest, function () {
  94. // note: `err` here is from the input stream errror
  95. if (err.code === 'EISDIR' || err.code === 'EPERM') {
  96. moveDirAcrossDevice(source, dest, overwrite, callback)
  97. } else {
  98. callback(err)
  99. }
  100. })
  101. })
  102. outs.on('error', function (err) {
  103. ins.destroy()
  104. outs.destroy()
  105. outs.removeListener('close', onClose)
  106. callback(err)
  107. })
  108. outs.once('close', onClose)
  109. ins.pipe(outs)
  110. function onClose () {
  111. fs.unlink(source, callback)
  112. }
  113. }
  114. function moveDirAcrossDevice (source, dest, overwrite, callback) {
  115. var options = {
  116. overwrite: false
  117. }
  118. function startNcp () {
  119. ncp(source, dest, options, function (err) {
  120. if (err) return callback(err)
  121. remove(source, callback)
  122. })
  123. }
  124. if (overwrite) {
  125. remove(dest, function (err) {
  126. if (err) return callback(err)
  127. startNcp()
  128. })
  129. } else {
  130. startNcp()
  131. }
  132. }
  133. module.exports = {
  134. move: mv
  135. }