copy.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /**
  2. * @author Toru Nagashima
  3. * @copyright 2016 Toru Nagashima. All rights reserved.
  4. * See LICENSE file in root directory for full license.
  5. */
  6. "use strict";
  7. var fs = require("fs");
  8. var mkdir = require("mkdirp");
  9. var Queue = require("./queue");
  10. /**
  11. * @param {string} src - A path of the source file.
  12. * @param {string} dst - A path of the destination file.
  13. * @param {function[]} transformFactories - Factory functions for transform streams.
  14. * @param {function} cb - A callback function that called after copied.
  15. * @returns {void}
  16. * @private
  17. */
  18. function copyBody(src, dst, transformFactories, cb) {
  19. var reader = fs.createReadStream(src);
  20. var writer = fs.createWriteStream(dst);
  21. var streams = [reader];
  22. /**
  23. * Clean up.
  24. * @param {Error|undefined} err - An error or undefined.
  25. * @returns {void}
  26. */
  27. function next(err) {
  28. try {
  29. streams.forEach(function (s) {
  30. s.removeListener("error", next);
  31. if (typeof s.destroy === "function") {
  32. s.destroy();
  33. }
  34. });
  35. writer.removeListener("error", next);
  36. writer.removeListener("finish", next);
  37. } catch (cleanupErr) {
  38. cb(err || cleanupErr);
  39. return;
  40. }
  41. cb(err);
  42. }
  43. reader.on("error", next);
  44. writer.on("error", next);
  45. writer.on("finish", next);
  46. try {
  47. transformFactories.reduce(function (input, factory) {
  48. var t = factory(src);
  49. t.on("error", next);
  50. streams.push(t);
  51. return input.pipe(t);
  52. }, reader).pipe(writer);
  53. } catch (err) {
  54. next(err);
  55. }
  56. }
  57. /**
  58. * @param {string} src - A path of the source file.
  59. * @param {string} dst - A path of the destination file.
  60. * @param {object} options - Options.
  61. * @param {function[]} options.transformFactories - Factory functions for transform streams.
  62. * @param {boolean} options.preserve - The flag to copy attributes.
  63. * @param {boolean} options.update - The flag to disallow overwriting.
  64. * @param {function} cb - A callback function that called after copied.
  65. * @returns {void}
  66. * @private
  67. */
  68. module.exports = function copy(src, dst, _ref, cb) {
  69. var transformFactories = _ref.transformFactories;
  70. var preserve = _ref.preserve;
  71. var update = _ref.update;
  72. var q = new Queue();
  73. var stat = null;
  74. q.push(function (next) {
  75. return fs.stat(src, function (err, result) {
  76. if (err) {
  77. cb(err);
  78. } else {
  79. stat = result;
  80. next();
  81. }
  82. });
  83. });
  84. if (update) {
  85. q.push(function (next) {
  86. return fs.stat(dst, function (err, dstStat) {
  87. if (!err && dstStat.mtime.getTime() > stat.mtime.getTime()) {
  88. // Don't overwrite because the file on destination is newer than
  89. // the source file.
  90. cb(null);
  91. } else {
  92. next();
  93. }
  94. });
  95. });
  96. }
  97. q.push(function (next) {
  98. if (stat.isDirectory()) {
  99. mkdir(dst, function (err) {
  100. if (err) {
  101. cb(err);
  102. } else {
  103. next();
  104. }
  105. });
  106. } else {
  107. copyBody(src, dst, transformFactories, function (err) {
  108. if (err) {
  109. cb(err);
  110. } else {
  111. next();
  112. }
  113. });
  114. }
  115. });
  116. q.push(function (next) {
  117. return fs.chmod(dst, stat.mode, function (err) {
  118. if (err) {
  119. cb(err);
  120. } else {
  121. next();
  122. }
  123. });
  124. });
  125. if (preserve) {
  126. q.push(function (next) {
  127. return fs.chown(dst, stat.uid, stat.gid, function (err) {
  128. if (err) {
  129. cb(err);
  130. } else {
  131. next();
  132. }
  133. });
  134. });
  135. q.push(function (next) {
  136. return fs.utimes(dst, stat.atime, stat.mtime, function (err) {
  137. if (err) {
  138. cb(err);
  139. } else {
  140. next();
  141. }
  142. });
  143. });
  144. }
  145. q.push(function (next) {
  146. next();
  147. cb(null);
  148. });
  149. };