UNPKG

5.51 kBJavaScriptView Raw
1var util = require('util');
2var tty = require('tty');
3
4var stream = require('stream-wrapper');
5var noansi = require('ansi-stripper');
6
7require('colors');
8
9// Is stdout a tty
10var isTty = tty.isatty(1);
11var noopCallback = function(callback) {
12 if (callback) {
13 callback();
14 }
15};
16
17var PROGRESS_BAR_LENGTH = 50;
18var TABLE_CELL_PADDING = 2;
19var OUTPUT_PADDING = 10;
20
21var error = function(err) {
22 console.error((err.message || err).red);
23
24 if (err.patch) {
25 var patch = err.patch;
26
27 console.error(JSON.stringify({
28 modified: patch.modified,
29 before: patch.before,
30 after: patch.after,
31 modifier: patch.modifier,
32 diff: patch.diff
33 }, null, 4));
34 }
35 if (err.stack) {
36 console.error(err.stack);
37 }
38};
39
40var cursor = function() {
41 var ttyCursor = {
42 hide: function(callback) {
43 process.stdout.write('\x1B[?25l', callback);
44 },
45 show: function(callback) {
46 process.stdout.write('\x1B[?25h', callback);
47 }
48 };
49
50 var noopCursor = {
51 hide: noopCallback,
52 show: noopCallback
53 };
54
55 return isTty ? ttyCursor : noopCursor;
56};
57
58var bar = function(percent) {
59 percent = Math.min(percent, 100);
60
61 var bar = '';
62 var limit = Math.floor(percent / 100 * PROGRESS_BAR_LENGTH);
63 var i;
64
65 for (i = 0; i < limit; i++) {
66 bar += '=';
67 }
68 if (limit < PROGRESS_BAR_LENGTH) {
69 bar += '>';
70 limit++;
71 }
72 for (i = limit; i < PROGRESS_BAR_LENGTH; i++) {
73 bar += ' ';
74 }
75
76 return '[' + bar.bold.cyan + ']';
77};
78
79var table = function(table) {
80 var columnLengths = [];
81
82 var each = function(fn) {
83 for (var i = 0; i < table.length; i++) {
84 var row = table[i];
85
86 for (var j = 0; j < row.length; j++) {
87 fn(i, j, row[j]);
88 }
89 }
90 };
91 var padding = function(padding) {
92 var result = '';
93
94 for (var i = 0; i < padding; i++) {
95 result += ' ';
96 }
97
98 return result;
99 };
100 var length = function(obj) {
101 return noansi(obj.toString()).length;
102 };
103
104 each(function(i, j, value) {
105 columnLengths[j] = Math.max(columnLengths[j] || 0, length(value));
106 });
107
108 each(function(i, j, value) {
109 table[i][j] = value + padding(columnLengths[j] - length(value) + TABLE_CELL_PADDING);
110 });
111
112 return table.map(function(row) {
113 return row.join(padding(TABLE_CELL_PADDING * 2));
114 });
115};
116
117var time = function(time) {
118 var hours = Math.floor(time / 3600);
119 var minutes = Math.floor((time - (hours * 3600)) / 60);
120 var seconds = Math.floor(time - (hours * 3600) - (minutes * 60));
121
122 var pad = function(n) {
123 if (n < 10) {
124 return '0' + n;
125 }
126
127 return n;
128 };
129
130 return pad(hours) + 'h ' + pad(minutes) + 'm ' + pad(seconds) + 's';
131};
132
133var sign = function(number) {
134 if (!number) {
135 return ' 0';
136 }
137
138 return number < 0 ? number.toString() : ('+' + number);
139};
140
141var capture = function(delta) {
142 var current = Number.MIN_VALUE;
143
144 return function(v) {
145 if (Math.abs(current - v) > delta) {
146 current = v;
147 return v;
148 }
149
150 return current;
151 };
152};
153
154var progress = function(patchId) {
155 var output = [];
156 var eta = capture(30);
157 var speed = capture(10);
158
159 var outputProgress = function(progress) {
160 output.push('Progress: '.grey + bar(progress.percentage) + ' ' + progress.count + '/' + progress.total + ' ' + progress.percentage.toFixed(1) + '%');
161 output.push('Patch: '.grey + patchId);
162 };
163
164 var outputStats = function(progress) {
165 var summary = table([
166 ['Summary:'.grey, 'Time', time(progress.time)],
167 ['', 'ETA', time(eta(progress.eta)), util.format('(speed %s)', Math.round(speed(progress.speed)))],
168 ['', 'Modified', progress.modified, util.format('(rest %s)', progress.count - progress.modified)],
169 ['', 'Skipped', progress.skipped]
170 ]);
171
172 Array.prototype.push.apply(output, summary);
173 };
174
175 var outputDiff = function(progress) {
176 var diff = progress.diff;
177
178 diff = Object.keys(diff || {}).map(function(key) {
179 return [
180 key,
181 sign(diff[key].added).green,
182 diff[key].updated.toString().yellow,
183 sign(-diff[key].removed).red
184 ];
185 });
186
187 diff = diff.length ? diff : [[ '(No changes)' ]];
188 diff.forEach(function(row) {
189 row.unshift('');
190 });
191
192 diff.unshift(['Diff: '.grey, '', 'added'.grey, 'updated'.grey, 'removed'.grey]);
193 Array.prototype.push.apply(output, table(diff));
194 };
195
196 var outputInterface = function(progress) {
197 output = [];
198
199 outputProgress(progress);
200
201 output.push('');
202
203 outputStats(progress);
204 outputDiff(progress);
205 };
206
207 var formatOutput = function() {
208 return output.join('\n');
209 };
210
211 var formatDone = function() {
212 return '\n DONE'.green;
213 };
214
215 var ttyProgress = function() {
216 outputProgress({ count: 0, total: 0, percentage: 0 });
217 console.log(formatOutput());
218
219 return stream.transform({ objectMode: true }, function(patch, enc, callback) {
220 process.stdout.moveCursor(0, -output.length);
221 outputInterface(patch.progress);
222
223 if (output.length > process.stdout.rows - OUTPUT_PADDING) {
224 output = output.slice(0, process.stdout.rows - OUTPUT_PADDING);
225 output.push(' ...');
226 }
227
228 console.log(formatOutput());
229
230 callback(null, patch);
231 }, function(callback) {
232 console.log(formatDone());
233 callback();
234 });
235 };
236
237 var endProgress = function() {
238 var last;
239
240 return stream.transform({ objectMode: true }, function(patch, enc, callback) {
241 last = patch;
242 callback(null, patch);
243 }, function(callback) {
244 if (last) {
245 outputInterface(last.progress);
246
247 console.log(noansi(formatOutput()));
248 console.log(noansi(formatDone()));
249 }
250
251 callback();
252 });
253 };
254
255 return isTty ? ttyProgress() : endProgress();
256};
257
258exports.cursor = cursor();
259exports.error = error;
260exports.progress = progress;