ncp.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // imported from ncp (this is temporary, will rewrite)
  2. var fs = require('graceful-fs')
  3. var path = require('path')
  4. var utimes = require('../util/utimes')
  5. function ncp (source, dest, options, callback) {
  6. if (!callback) {
  7. callback = options
  8. options = {}
  9. }
  10. var basePath = process.cwd()
  11. var currentPath = path.resolve(basePath, source)
  12. var targetPath = path.resolve(basePath, dest)
  13. var filter = options.filter
  14. var transform = options.transform
  15. var clobber = options.clobber !== false
  16. var dereference = options.dereference
  17. var preserveTimestamps = options.preserveTimestamps === true
  18. var errs = null
  19. var started = 0
  20. var finished = 0
  21. var running = 0
  22. // this is pretty useless now that we're using graceful-fs
  23. // consider removing
  24. var limit = options.limit || 512
  25. startCopy(currentPath)
  26. function startCopy (source) {
  27. started++
  28. if (filter) {
  29. if (filter instanceof RegExp) {
  30. if (!filter.test(source)) {
  31. return doneOne(true)
  32. }
  33. } else if (typeof filter === 'function') {
  34. if (!filter(source)) {
  35. return doneOne(true)
  36. }
  37. }
  38. }
  39. return getStats(source)
  40. }
  41. function getStats (source) {
  42. var stat = dereference ? fs.stat : fs.lstat
  43. if (running >= limit) {
  44. return setImmediate(function () {
  45. getStats(source)
  46. })
  47. }
  48. running++
  49. stat(source, function (err, stats) {
  50. if (err) return onError(err)
  51. // We need to get the mode from the stats object and preserve it.
  52. var item = {
  53. name: source,
  54. mode: stats.mode,
  55. mtime: stats.mtime, // modified time
  56. atime: stats.atime, // access time
  57. stats: stats // temporary
  58. }
  59. if (stats.isDirectory()) {
  60. return onDir(item)
  61. } else if (stats.isFile() || stats.isCharacterDevice() || stats.isBlockDevice()) {
  62. return onFile(item)
  63. } else if (stats.isSymbolicLink()) {
  64. // Symlinks don't really need to know about the mode.
  65. return onLink(source)
  66. }
  67. })
  68. }
  69. function onFile (file) {
  70. var target = file.name.replace(currentPath, targetPath)
  71. isWritable(target, function (writable) {
  72. if (writable) {
  73. copyFile(file, target)
  74. } else {
  75. if (clobber) {
  76. rmFile(target, function () {
  77. copyFile(file, target)
  78. })
  79. } else {
  80. doneOne()
  81. }
  82. }
  83. })
  84. }
  85. function copyFile (file, target) {
  86. var readStream = fs.createReadStream(file.name)
  87. var writeStream = fs.createWriteStream(target, { mode: file.mode })
  88. readStream.on('error', onError)
  89. writeStream.on('error', onError)
  90. if (transform) {
  91. transform(readStream, writeStream, file)
  92. } else {
  93. writeStream.on('open', function () {
  94. readStream.pipe(writeStream)
  95. })
  96. }
  97. writeStream.once('finish', function () {
  98. fs.chmod(target, file.mode, function (err) {
  99. if (err) return onError(err)
  100. if (preserveTimestamps) {
  101. utimes.utimesMillis(target, file.atime, file.mtime, function (err) {
  102. if (err) return onError(err)
  103. return doneOne()
  104. })
  105. } else {
  106. doneOne()
  107. }
  108. })
  109. })
  110. }
  111. function rmFile (file, done) {
  112. fs.unlink(file, function (err) {
  113. if (err) return onError(err)
  114. return done()
  115. })
  116. }
  117. function onDir (dir) {
  118. var target = dir.name.replace(currentPath, targetPath)
  119. isWritable(target, function (writable) {
  120. if (writable) {
  121. return mkDir(dir, target)
  122. }
  123. copyDir(dir.name)
  124. })
  125. }
  126. function mkDir (dir, target) {
  127. fs.mkdir(target, dir.mode, function (err) {
  128. if (err) return onError(err)
  129. // despite setting mode in fs.mkdir, doesn't seem to work
  130. // so we set it here.
  131. fs.chmod(target, dir.mode, function (err) {
  132. if (err) return onError(err)
  133. copyDir(dir.name)
  134. })
  135. })
  136. }
  137. function copyDir (dir) {
  138. fs.readdir(dir, function (err, items) {
  139. if (err) return onError(err)
  140. items.forEach(function (item) {
  141. startCopy(path.join(dir, item))
  142. })
  143. return doneOne()
  144. })
  145. }
  146. function onLink (link) {
  147. var target = link.replace(currentPath, targetPath)
  148. fs.readlink(link, function (err, resolvedPath) {
  149. if (err) return onError(err)
  150. checkLink(resolvedPath, target)
  151. })
  152. }
  153. function checkLink (resolvedPath, target) {
  154. if (dereference) {
  155. resolvedPath = path.resolve(basePath, resolvedPath)
  156. }
  157. isWritable(target, function (writable) {
  158. if (writable) {
  159. return makeLink(resolvedPath, target)
  160. }
  161. fs.readlink(target, function (err, targetDest) {
  162. if (err) return onError(err)
  163. if (dereference) {
  164. targetDest = path.resolve(basePath, targetDest)
  165. }
  166. if (targetDest === resolvedPath) {
  167. return doneOne()
  168. }
  169. return rmFile(target, function () {
  170. makeLink(resolvedPath, target)
  171. })
  172. })
  173. })
  174. }
  175. function makeLink (linkPath, target) {
  176. fs.symlink(linkPath, target, function (err) {
  177. if (err) return onError(err)
  178. return doneOne()
  179. })
  180. }
  181. function isWritable (path, done) {
  182. fs.lstat(path, function (err) {
  183. if (err) {
  184. if (err.code === 'ENOENT') return done(true)
  185. return done(false)
  186. }
  187. return done(false)
  188. })
  189. }
  190. function onError (err) {
  191. if (options.stopOnError) {
  192. return callback(err)
  193. } else if (!errs && options.errs) {
  194. errs = fs.createWriteStream(options.errs)
  195. } else if (!errs) {
  196. errs = []
  197. }
  198. if (typeof errs.write === 'undefined') {
  199. errs.push(err)
  200. } else {
  201. errs.write(err.stack + '\n\n')
  202. }
  203. return doneOne()
  204. }
  205. function doneOne (skipped) {
  206. if (!skipped) running--
  207. finished++
  208. if ((started === finished) && (running === 0)) {
  209. if (callback !== undefined) {
  210. return errs ? callback(errs) : callback(null)
  211. }
  212. }
  213. }
  214. }
  215. module.exports = ncp