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

117 lines
2.9 KiB

4 years ago
  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 pos = 0
  59. while (pos < stat.size) {
  60. const bytesRead = fs.readSync(fdr, _buff, 0, BUF_LENGTH, pos)
  61. fs.writeSync(fdw, _buff, 0, bytesRead)
  62. pos += bytesRead
  63. }
  64. fs.closeSync(fdr)
  65. fs.closeSync(fdw)
  66. return fs.unlinkSync(src)
  67. }
  68. function moveDirSyncAcrossDevice (src, dest, overwrite) {
  69. const options = {
  70. overwrite: false
  71. }
  72. if (overwrite) {
  73. removeSync(dest)
  74. tryCopySync()
  75. } else {
  76. tryCopySync()
  77. }
  78. function tryCopySync () {
  79. copySync(src, dest, options)
  80. return removeSync(src)
  81. }
  82. }
  83. // return true if dest is a subdir of src, otherwise false.
  84. // extract dest base dir and check if that is the same as src basename
  85. function isSrcSubdir (src, dest) {
  86. try {
  87. return fs.statSync(src).isDirectory() &&
  88. src !== dest &&
  89. dest.indexOf(src) > -1 &&
  90. dest.split(path.dirname(src) + path.sep)[1].split(path.sep)[0] === path.basename(src)
  91. } catch (e) {
  92. return false
  93. }
  94. }
  95. module.exports = {
  96. moveSync
  97. }