copyPromise.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.copyPromise = exports.LinkStrategy = void 0;
  4. const tslib_1 = require("tslib");
  5. const fs_1 = tslib_1.__importDefault(require("fs"));
  6. const constants = tslib_1.__importStar(require("../constants"));
  7. const path_1 = require("../path");
  8. const defaultTime = new Date(constants.SAFE_TIME * 1000);
  9. var LinkStrategy;
  10. (function (LinkStrategy) {
  11. LinkStrategy["Allow"] = "allow";
  12. LinkStrategy["ReadOnly"] = "readOnly";
  13. })(LinkStrategy = exports.LinkStrategy || (exports.LinkStrategy = {}));
  14. async function copyPromise(destinationFs, destination, sourceFs, source, opts) {
  15. const normalizedDestination = destinationFs.pathUtils.normalize(destination);
  16. const normalizedSource = sourceFs.pathUtils.normalize(source);
  17. const prelayout = [];
  18. const postlayout = [];
  19. const { atime, mtime } = opts.stableTime
  20. ? { atime: defaultTime, mtime: defaultTime }
  21. : await sourceFs.lstatPromise(normalizedSource);
  22. await destinationFs.mkdirpPromise(destinationFs.pathUtils.dirname(destination), { utimes: [atime, mtime] });
  23. const updateTime = typeof destinationFs.lutimesPromise === `function`
  24. ? destinationFs.lutimesPromise.bind(destinationFs)
  25. : destinationFs.utimesPromise.bind(destinationFs);
  26. await copyImpl(prelayout, postlayout, updateTime, destinationFs, normalizedDestination, sourceFs, normalizedSource, { ...opts, didParentExist: true });
  27. for (const operation of prelayout)
  28. await operation();
  29. await Promise.all(postlayout.map(operation => {
  30. return operation();
  31. }));
  32. }
  33. exports.copyPromise = copyPromise;
  34. async function copyImpl(prelayout, postlayout, updateTime, destinationFs, destination, sourceFs, source, opts) {
  35. var _a, _b;
  36. const destinationStat = opts.didParentExist ? await maybeLStat(destinationFs, destination) : null;
  37. const sourceStat = await sourceFs.lstatPromise(source);
  38. const { atime, mtime } = opts.stableTime
  39. ? { atime: defaultTime, mtime: defaultTime }
  40. : sourceStat;
  41. let updated;
  42. switch (true) {
  43. case sourceStat.isDirectory():
  44. {
  45. updated = await copyFolder(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts);
  46. }
  47. break;
  48. case sourceStat.isFile():
  49. {
  50. updated = await copyFile(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts);
  51. }
  52. break;
  53. case sourceStat.isSymbolicLink():
  54. {
  55. updated = await copySymlink(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts);
  56. }
  57. break;
  58. default:
  59. {
  60. throw new Error(`Unsupported file type (${sourceStat.mode})`);
  61. }
  62. break;
  63. }
  64. if (updated || ((_a = destinationStat === null || destinationStat === void 0 ? void 0 : destinationStat.mtime) === null || _a === void 0 ? void 0 : _a.getTime()) !== mtime.getTime() || ((_b = destinationStat === null || destinationStat === void 0 ? void 0 : destinationStat.atime) === null || _b === void 0 ? void 0 : _b.getTime()) !== atime.getTime()) {
  65. postlayout.push(() => updateTime(destination, atime, mtime));
  66. updated = true;
  67. }
  68. if (destinationStat === null || (destinationStat.mode & 0o777) !== (sourceStat.mode & 0o777)) {
  69. postlayout.push(() => destinationFs.chmodPromise(destination, sourceStat.mode & 0o777));
  70. updated = true;
  71. }
  72. return updated;
  73. }
  74. async function maybeLStat(baseFs, p) {
  75. try {
  76. return await baseFs.lstatPromise(p);
  77. }
  78. catch (e) {
  79. return null;
  80. }
  81. }
  82. async function copyFolder(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) {
  83. if (destinationStat !== null && !destinationStat.isDirectory()) {
  84. if (opts.overwrite) {
  85. prelayout.push(async () => destinationFs.removePromise(destination));
  86. destinationStat = null;
  87. }
  88. else {
  89. return false;
  90. }
  91. }
  92. let updated = false;
  93. if (destinationStat === null) {
  94. prelayout.push(async () => {
  95. try {
  96. await destinationFs.mkdirPromise(destination, { mode: sourceStat.mode });
  97. }
  98. catch (err) {
  99. if (err.code !== `EEXIST`) {
  100. throw err;
  101. }
  102. }
  103. });
  104. updated = true;
  105. }
  106. const entries = await sourceFs.readdirPromise(source);
  107. const nextOpts = opts.didParentExist && !destinationStat ? { ...opts, didParentExist: false } : opts;
  108. if (opts.stableSort) {
  109. for (const entry of entries.sort()) {
  110. if (await copyImpl(prelayout, postlayout, updateTime, destinationFs, destinationFs.pathUtils.join(destination, entry), sourceFs, sourceFs.pathUtils.join(source, entry), nextOpts)) {
  111. updated = true;
  112. }
  113. }
  114. }
  115. else {
  116. const entriesUpdateStatus = await Promise.all(entries.map(async (entry) => {
  117. await copyImpl(prelayout, postlayout, updateTime, destinationFs, destinationFs.pathUtils.join(destination, entry), sourceFs, sourceFs.pathUtils.join(source, entry), nextOpts);
  118. }));
  119. if (entriesUpdateStatus.some(status => status)) {
  120. updated = true;
  121. }
  122. }
  123. return updated;
  124. }
  125. const isCloneSupportedCache = new WeakMap();
  126. function makeLinkOperation(opFs, destination, source, sourceStat, linkStrategy) {
  127. return async () => {
  128. await opFs.linkPromise(source, destination);
  129. if (linkStrategy === LinkStrategy.ReadOnly) {
  130. // We mutate the stat, otherwise it'll be reset by copyImpl
  131. sourceStat.mode &= ~0o222;
  132. await opFs.chmodPromise(destination, sourceStat.mode);
  133. }
  134. };
  135. }
  136. function makeCloneLinkOperation(opFs, destination, source, sourceStat, linkStrategy) {
  137. const isCloneSupported = isCloneSupportedCache.get(opFs);
  138. if (typeof isCloneSupported === `undefined`) {
  139. return async () => {
  140. try {
  141. await opFs.copyFilePromise(source, destination, fs_1.default.constants.COPYFILE_FICLONE_FORCE);
  142. isCloneSupportedCache.set(opFs, true);
  143. }
  144. catch (err) {
  145. if (err.code === `ENOSYS` || err.code === `ENOTSUP`) {
  146. isCloneSupportedCache.set(opFs, false);
  147. await makeLinkOperation(opFs, destination, source, sourceStat, linkStrategy)();
  148. }
  149. else {
  150. throw err;
  151. }
  152. }
  153. };
  154. }
  155. else {
  156. if (isCloneSupported) {
  157. return async () => opFs.copyFilePromise(source, destination, fs_1.default.constants.COPYFILE_FICLONE_FORCE);
  158. }
  159. else {
  160. return makeLinkOperation(opFs, destination, source, sourceStat, linkStrategy);
  161. }
  162. }
  163. }
  164. async function copyFile(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) {
  165. var _a;
  166. if (destinationStat !== null) {
  167. if (opts.overwrite) {
  168. prelayout.push(async () => destinationFs.removePromise(destination));
  169. destinationStat = null;
  170. }
  171. else {
  172. return false;
  173. }
  174. }
  175. const linkStrategy = (_a = opts.linkStrategy) !== null && _a !== void 0 ? _a : null;
  176. const op = destinationFs === sourceFs
  177. ? linkStrategy !== null
  178. ? makeCloneLinkOperation(destinationFs, destination, source, sourceStat, linkStrategy)
  179. : async () => destinationFs.copyFilePromise(source, destination, fs_1.default.constants.COPYFILE_FICLONE)
  180. : linkStrategy !== null
  181. ? makeLinkOperation(destinationFs, destination, source, sourceStat, linkStrategy)
  182. : async () => destinationFs.writeFilePromise(destination, await sourceFs.readFilePromise(source));
  183. prelayout.push(async () => op());
  184. return true;
  185. }
  186. async function copySymlink(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) {
  187. if (destinationStat !== null) {
  188. if (opts.overwrite) {
  189. prelayout.push(async () => destinationFs.removePromise(destination));
  190. destinationStat = null;
  191. }
  192. else {
  193. return false;
  194. }
  195. }
  196. prelayout.push(async () => {
  197. await destinationFs.symlinkPromise((0, path_1.convertPath)(destinationFs.pathUtils, await sourceFs.readlinkPromise(source)), destination);
  198. });
  199. return true;
  200. }