1 | "use strict";
|
2 | var __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 | };
|
13 | var fs = require("fs");
|
14 | var path = require("path-posix");
|
15 | var symlinkOrCopy = require("symlink-or-copy");
|
16 | var Logger = require("heimdalljs-logger");
|
17 | var entry_1 = require("./entry");
|
18 | var util_1 = require("./util");
|
19 | var logger = Logger('fs-tree-diff:');
|
20 | var ARBITRARY_START_OF_TIME = 0;
|
21 | var 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 |
|
41 |
|
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 | };
|
52 | var FSTree = (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 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
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 |
|
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 |
|
151 | i++;
|
152 | removals.push(removeOperation(x));
|
153 |
|
154 | }
|
155 | else if (x.relativePath > y.relativePath) {
|
156 |
|
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 |
|
171 | i++;
|
172 | j++;
|
173 | }
|
174 | }
|
175 |
|
176 | for (; i < ours.length; i++) {
|
177 | removals.push(removeOperation(ours[i]));
|
178 | }
|
179 |
|
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 |
|
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 | }());
|
224 | function 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 | }
|
237 | function addOperation(entry) {
|
238 | return [
|
239 | entry.isDirectory() ? 'mkdir' : 'create',
|
240 | entry.relativePath,
|
241 | entry
|
242 | ];
|
243 | }
|
244 | function removeOperation(entry) {
|
245 | return [
|
246 | entry.isDirectory() ? 'rmdir' : 'unlink',
|
247 | entry.relativePath,
|
248 | entry
|
249 | ];
|
250 | }
|
251 | function updateOperation(entry) {
|
252 | return [
|
253 | 'change',
|
254 | entry.relativePath,
|
255 | entry
|
256 | ];
|
257 | }
|
258 | ;
|
259 | module.exports = FSTree;
|