UNPKG

8.98 kBJavaScriptView Raw
1"use strict";
2var __assign = (this && this.__assign) || function () {
3 __assign = Object.assign || function(t) {
4 for (var s, i = 1, n = arguments.length; i < n; i++) {
5 s = arguments[i];
6 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7 t[p] = s[p];
8 }
9 return t;
10 };
11 return __assign.apply(this, arguments);
12};
13var fs = require("fs");
14var path = require("path-posix");
15var symlinkOrCopy = require("symlink-or-copy");
16var Logger = require("heimdalljs-logger");
17var entry_1 = require("./entry");
18var util_1 = require("./util");
19var logger = Logger('fs-tree-diff:');
20var ARBITRARY_START_OF_TIME = 0;
21var DEFAULT_DELEGATE = {
22 unlink: function (inputPath, outputPath, relativePath) {
23 try {
24 fs.unlinkSync(outputPath);
25 }
26 catch (e) {
27 if (typeof e === 'object' && e !== null && e.code === 'ENOENT') {
28 return;
29 }
30 throw e;
31 }
32 },
33 rmdir: function (inputPath, outputPath, relativePath) {
34 fs.rmdirSync(outputPath);
35 },
36 mkdir: function (inputPath, outputPath, relativePath) {
37 fs.mkdirSync(outputPath);
38 },
39 change: function (inputPath, outputPath, relativePath) {
40 // We no-op if the platform can symlink, because we assume the output path
41 // is already linked via a prior create operation.
42 if (symlinkOrCopy.canSymlink) {
43 return;
44 }
45 fs.unlinkSync(outputPath);
46 symlinkOrCopy.sync(inputPath, outputPath);
47 },
48 create: function (inputPath, outputPath, relativePath) {
49 symlinkOrCopy.sync(inputPath, outputPath);
50 }
51};
52var FSTree = /** @class */ (function () {
53 function FSTree(options) {
54 if (options === void 0) { options = {}; }
55 var entries = options.entries || [];
56 if (options.sortAndExpand) {
57 util_1.sortAndExpand(entries);
58 }
59 else {
60 util_1.validateSortedUnique(entries);
61 }
62 this.entries = entries;
63 }
64 FSTree.fromPaths = function (paths, options) {
65 if (options === void 0) { options = {}; }
66 var entries = paths.map(function (path) {
67 return new entry_1.default(path, 0, ARBITRARY_START_OF_TIME);
68 });
69 return new this({
70 entries: entries,
71 sortAndExpand: options.sortAndExpand,
72 });
73 };
74 FSTree.fromEntries = function (entries, options) {
75 if (options === void 0) { options = {}; }
76 return new this({
77 entries: entries,
78 sortAndExpand: options.sortAndExpand,
79 });
80 };
81 Object.defineProperty(FSTree.prototype, "size", {
82 get: function () {
83 return this.entries.length;
84 },
85 enumerable: true,
86 configurable: true
87 });
88 FSTree.prototype.addEntries = function (entries, options) {
89 if (!Array.isArray(entries)) {
90 throw new TypeError('entries must be an array');
91 }
92 if (options !== null && typeof options === 'object' && options.sortAndExpand) {
93 util_1.sortAndExpand(entries);
94 }
95 else {
96 util_1.validateSortedUnique(entries);
97 }
98 var fromIndex = 0;
99 var toIndex = 0;
100 while (fromIndex < entries.length) {
101 while (toIndex < this.entries.length &&
102 this.entries[toIndex].relativePath < entries[fromIndex].relativePath) {
103 toIndex++;
104 }
105 if (toIndex < this.entries.length &&
106 this.entries[toIndex].relativePath === entries[fromIndex].relativePath) {
107 this.entries.splice(toIndex, 1, entries[fromIndex++]);
108 }
109 else {
110 this.entries.splice(toIndex++, 0, entries[fromIndex++]);
111 }
112 }
113 };
114 FSTree.prototype.addPaths = function (paths, options) {
115 var entries = paths.map(function (path) {
116 // TODO:
117 // addPths + a custom isEqual comparator + custom Entry types are actually incompatible
118 // As a addPaths will not abide by the custom Entry type
119 // and will make this.entries be contain a mixture of types.
120 // isEqual's arguments will then be typed incorrectly
121 //
122 // We should likely just deprecate `addPaths` in-favor of addEntries,
123 // which correctly externalizes the creation of entry
124 return new entry_1.default(path, 0, ARBITRARY_START_OF_TIME);
125 });
126 this.addEntries(entries, options);
127 };
128 FSTree.prototype.forEach = function (fn, context) {
129 this.entries.forEach(fn, context);
130 };
131 FSTree.prototype.calculatePatch = function (theirFSTree, isEqual) {
132 if (arguments.length > 1 && typeof isEqual !== 'function') {
133 throw new TypeError('calculatePatch\'s second argument must be a function');
134 }
135 // TODO: the TS here is strange
136 if (typeof isEqual !== 'function') {
137 isEqual = this.constructor.defaultIsEqual;
138 }
139 var ours = this.entries;
140 var theirs = theirFSTree.entries;
141 var additions = [];
142 var removals = [];
143 var i = 0;
144 var j = 0;
145 var command;
146 while (i < ours.length && j < theirs.length) {
147 var x = ours[i];
148 var y = theirs[j];
149 if (x.relativePath < y.relativePath) {
150 // ours
151 i++;
152 removals.push(removeOperation(x));
153 // remove additions
154 }
155 else if (x.relativePath > y.relativePath) {
156 // theirs
157 j++;
158 additions.push(addOperation(y));
159 }
160 else {
161 if (!isEqual(x, y)) {
162 command = updateOperation(y);
163 if (x.isDirectory()) {
164 removals.push(command);
165 }
166 else {
167 additions.push(command);
168 }
169 }
170 // both are the same
171 i++;
172 j++;
173 }
174 }
175 // cleanup ours
176 for (; i < ours.length; i++) {
177 removals.push(removeOperation(ours[i]));
178 }
179 // cleanup theirs
180 for (; j < theirs.length; j++) {
181 additions.push(addOperation(theirs[j]));
182 }
183 return removals.reverse().concat(additions);
184 };
185 FSTree.prototype.calculateAndApplyPatch = function (otherFSTree, input, output, delegate) {
186 this.constructor.applyPatch(input, output, this.calculatePatch(otherFSTree), delegate);
187 };
188 FSTree.defaultIsEqual = function (entryA, entryB) {
189 if (entryA.isDirectory() && entryB.isDirectory()) {
190 // ignore directory changes by default
191 return true;
192 }
193 var equal;
194 if (entryA.size === entryB.size && entryA.mode === entryB.mode) {
195 if (entryA.mtime === entryB.mtime) {
196 equal = true;
197 }
198 else if (entryA.mtime === undefined || entryB.mtime === undefined) {
199 equal = false;
200 }
201 else if (+entryA.mtime === +entryB.mtime) {
202 equal = true;
203 }
204 else {
205 equal = false;
206 }
207 }
208 else {
209 equal = false;
210 }
211 if (equal === false) {
212 logger.info('invalidation reason: \nbefore %o\n entryB %o', entryA, entryB);
213 }
214 return equal;
215 };
216 FSTree.applyPatch = function (input, output, patch, _delegate) {
217 var delegate = __assign({}, DEFAULT_DELEGATE, _delegate);
218 for (var i = 0; i < patch.length; i++) {
219 applyOperation(input, output, patch[i], delegate);
220 }
221 };
222 return FSTree;
223}());
224function applyOperation(input, output, operation, delegate) {
225 var methodName = operation[0];
226 var relativePath = operation[1];
227 var inputPath = path.join(input, relativePath);
228 var outputPath = path.join(output, relativePath);
229 var method = delegate[methodName];
230 if (typeof method === 'function') {
231 method(inputPath, outputPath, relativePath);
232 }
233 else {
234 throw new Error('Unable to apply patch operation: ' + methodName + '. The value of delegate.' + methodName + ' is of type ' + typeof method + ', and not a function. Check the `delegate` argument to `FSTree.prototype.applyPatch`.');
235 }
236}
237function addOperation(entry) {
238 return [
239 entry.isDirectory() ? 'mkdir' : 'create',
240 entry.relativePath,
241 entry
242 ];
243}
244function removeOperation(entry) {
245 return [
246 entry.isDirectory() ? 'rmdir' : 'unlink',
247 entry.relativePath,
248 entry
249 ];
250}
251function updateOperation(entry) {
252 return [
253 'change',
254 entry.relativePath,
255 entry
256 ];
257}
258;
259module.exports = FSTree;