index.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. 'use strict'
  2. const fs = require('graceful-fs')
  3. const path = require('path')
  4. const copySync = require('../copy-sync').copySync
  5. const removeSync = require('../remove').removeSync
  6. const mkdirpSync = require('../mkdirs').mkdirsSync
  7. const buffer = require('../util/buffer')
  8. function moveSync (src, dest, options) {
  9. options = options || {}
  10. const overwrite = options.overwrite || options.clobber || false
  11. src = path.resolve(src)
  12. dest = path.resolve(dest)
  13. if (src === dest) return fs.accessSync(src)
  14. if (isSrcSubdir(src, dest)) throw new Error(`Cannot move '${src}' into itself '${dest}'.`)
  15. mkdirpSync(path.dirname(dest))
  16. tryRenameSync()
  17. function tryRenameSync () {
  18. if (overwrite) {
  19. try {
  20. return fs.renameSync(src, dest)
  21. } catch (err) {
  22. if (err.code === 'ENOTEMPTY' || err.code === 'EEXIST' || err.code === 'EPERM') {
  23. removeSync(dest)
  24. options.overwrite = false // just overwriteed it, no need to do it again
  25. return moveSync(src, dest, options)
  26. }
  27. if (err.code !== 'EXDEV') throw err
  28. return moveSyncAcrossDevice(src, dest, overwrite)
  29. }
  30. } else {
  31. try {
  32. fs.linkSync(src, dest)
  33. return fs.unlinkSync(src)
  34. } catch (err) {
  35. if (err.code === 'EXDEV' || err.code === 'EISDIR' || err.code === 'EPERM' || err.code === 'ENOTSUP') {
  36. return moveSyncAcrossDevice(src, dest, overwrite)
  37. }
  38. throw err
  39. }
  40. }
  41. }
  42. }
  43. function moveSyncAcrossDevice (src, dest, overwrite) {
  44. const stat = fs.statSync(src)
  45. if (stat.isDirectory()) {
  46. return moveDirSyncAcrossDevice(src, dest, overwrite)
  47. } else {
  48. return moveFileSyncAcrossDevice(src, dest, overwrite)
  49. }
  50. }
  51. function moveFileSyncAcrossDevice (src, dest, overwrite) {
  52. const BUF_LENGTH = 64 * 1024
  53. const _buff = buffer(BUF_LENGTH)
  54. const flags = overwrite ? 'w' : 'wx'
  55. const fdr = fs.openSync(src, 'r')
  56. const stat = fs.fstatSync(fdr)
  57. const fdw = fs.openSync(dest, flags, stat.mode)
  58. let bytesRead = 1
  59. let pos = 0
  60. while (bytesRead > 0) {
  61. bytesRead = fs.readSync(fdr, _buff, 0, BUF_LENGTH, pos)
  62. fs.writeSync(fdw, _buff, 0, bytesRead)
  63. pos += bytesRead
  64. }
  65. fs.closeSync(fdr)
  66. fs.closeSync(fdw)
  67. return fs.unlinkSync(src)
  68. }
  69. function moveDirSyncAcrossDevice (src, dest, overwrite) {
  70. const options = {
  71. overwrite: false
  72. }
  73. if (overwrite) {
  74. removeSync(dest)
  75. tryCopySync()
  76. } else {
  77. tryCopySync()
  78. }
  79. function tryCopySync () {
  80. copySync(src, dest, options)
  81. return removeSync(src)
  82. }
  83. }
  84. // return true if dest is a subdir of src, otherwise false.
  85. // extract dest base dir and check if that is the same as src basename
  86. function isSrcSubdir (src, dest) {
  87. try {
  88. return fs.statSync(src).isDirectory() &&
  89. src !== dest &&
  90. dest.indexOf(src) > -1 &&
  91. dest.split(path.dirname(src) + path.sep)[1].split(path.sep)[0] === path.basename(src)
  92. } catch (e) {
  93. return false
  94. }
  95. }
  96. module.exports = {
  97. moveSync
  98. }