1 | var util = require('util');
|
2 |
|
3 | var stream = require('stream-wrapper');
|
4 | var noansi = require('ansi-stripper');
|
5 |
|
6 | require('colors');
|
7 |
|
8 | var PROGRESS_BAR_LENGTH = 50;
|
9 | var TABLE_CELL_PADDING = 2;
|
10 | var OUTPUT_PADDING = 10;
|
11 |
|
12 | var bar = function(percent) {
|
13 | percent = Math.min(percent, 100);
|
14 |
|
15 | var bar = '';
|
16 | var limit = Math.floor(percent / 100 * PROGRESS_BAR_LENGTH);
|
17 | var i;
|
18 |
|
19 | for(i = 0; i < limit; i++) {
|
20 | bar += '=';
|
21 | }
|
22 | if(limit < PROGRESS_BAR_LENGTH) {
|
23 | bar += '>';
|
24 | limit++;
|
25 | }
|
26 | for(i = limit; i < PROGRESS_BAR_LENGTH; i++) {
|
27 | bar += ' ';
|
28 | }
|
29 |
|
30 | return '[' + bar.bold.cyan + ']';
|
31 | };
|
32 |
|
33 | var table = function(table) {
|
34 | var columnLengths = [];
|
35 |
|
36 | var each = function(fn) {
|
37 | for(var i = 0; i < table.length; i++) {
|
38 | var row = table[i];
|
39 |
|
40 | for(var j = 0; j < row.length; j++) {
|
41 | fn(i, j, row[j]);
|
42 | }
|
43 | }
|
44 | };
|
45 | var padding = function(padding) {
|
46 | var result = '';
|
47 |
|
48 | for(var i = 0; i < padding; i++) {
|
49 | result += ' ';
|
50 | }
|
51 |
|
52 | return result;
|
53 | };
|
54 | var length = function(obj) {
|
55 | return noansi(obj.toString()).length;
|
56 | };
|
57 |
|
58 | each(function(i, j, value) {
|
59 | columnLengths[j] = Math.max(columnLengths[j] || 0, length(value));
|
60 | });
|
61 |
|
62 | each(function(i, j, value) {
|
63 | table[i][j] = value + padding(columnLengths[j] - length(value) + TABLE_CELL_PADDING);
|
64 | });
|
65 |
|
66 | return table.map(function(row) {
|
67 | return row.join(padding(TABLE_CELL_PADDING * 2));
|
68 | });
|
69 | };
|
70 |
|
71 | var time = function(time) {
|
72 | var hours = Math.floor(time / 3600);
|
73 | var minutes = Math.floor((time - (hours * 3600)) / 60);
|
74 | var seconds = Math.floor(time - (hours * 3600) - (minutes * 60));
|
75 |
|
76 | var pad = function(n) {
|
77 | if(n < 10) {
|
78 | return '0' + n;
|
79 | }
|
80 |
|
81 | return n;
|
82 | };
|
83 |
|
84 | return pad(hours) + 'h ' + pad(minutes) + 'm ' + pad(seconds) + 's';
|
85 | };
|
86 |
|
87 | var sign = function(number) {
|
88 | if(!number) {
|
89 | return ' 0';
|
90 | }
|
91 |
|
92 | return number < 0 ? number.toString() : ('+' + number);
|
93 | };
|
94 |
|
95 | var capture = function(delta) {
|
96 | var current = Number.MIN_VALUE;
|
97 |
|
98 | return function(v) {
|
99 | if(Math.abs(current - v) > delta) {
|
100 | current = v;
|
101 | return v;
|
102 | }
|
103 |
|
104 | return current;
|
105 | };
|
106 | };
|
107 |
|
108 | var progress = function(options) {
|
109 | var output = [];
|
110 | var eta = capture(30);
|
111 | var speed = capture(10);
|
112 |
|
113 | var outputProgress = function(output, count, total, progress) {
|
114 | output.push('Progress: '.grey + bar(progress) + ' ' + count + '/' + total + ' ' + progress.toFixed(1) + '%');
|
115 | output.push('Patch: '.grey + options.patch);
|
116 | };
|
117 |
|
118 | var outputDiff = function(output, diff) {
|
119 | diff = Object.keys(diff || {}).map(function(key) {
|
120 | return [
|
121 | key,
|
122 | sign(diff[key].added).green,
|
123 | diff[key].updated.toString().yellow,
|
124 | sign(-diff[key].removed).red
|
125 | ];
|
126 | });
|
127 |
|
128 | diff = diff.length ? diff : [[ '(No changes)' ]];
|
129 | diff.forEach(function(row) {
|
130 | row.unshift('');
|
131 | });
|
132 |
|
133 | diff.unshift(['Diff: '.grey, '', 'added'.grey, 'updated'.grey, 'removed'.grey]);
|
134 | Array.prototype.push.apply(output, table(diff));
|
135 | };
|
136 |
|
137 | var outputStats = function(output, modified, count, t, e, s) {
|
138 | var summary = table([
|
139 | ['Summary:'.grey, 'Time', time(t)],
|
140 | ['', 'ETA', time(eta(e)), util.format('(speed %s)', Math.round(speed(s)))],
|
141 | ['', 'Modified', modified, util.format('(rest %s)', count - modified)]
|
142 | ]);
|
143 |
|
144 | Array.prototype.push.apply(output, summary);
|
145 | };
|
146 |
|
147 | var logOutput = function(output) {
|
148 | console.log(output.join('\n'));
|
149 | };
|
150 |
|
151 | outputProgress(output, 0, 0, options.total === 0 ? 100 : 0);
|
152 | logOutput(output);
|
153 |
|
154 | return stream.transform({ objectMode: true }, function(patch, enc, callback) {
|
155 | var progress = patch.progress;
|
156 |
|
157 | process.stdout.moveCursor(0, -output.length);
|
158 | output = [];
|
159 |
|
160 | outputProgress(output, progress.count, progress.total, progress.percentage);
|
161 |
|
162 | output.push('');
|
163 |
|
164 | outputStats(output, progress.modified, progress.count, progress.time, progress.eta, progress.speed);
|
165 | outputDiff(output, progress.diff);
|
166 |
|
167 | if(output.length > process.stdout.rows - OUTPUT_PADDING) {
|
168 | output = output.slice(0, process.stdout.rows - OUTPUT_PADDING);
|
169 | output.push(' ...');
|
170 | }
|
171 |
|
172 | logOutput(output);
|
173 |
|
174 | callback(null, patch);
|
175 | }, function(callback) {
|
176 | console.log('\n DONE'.green);
|
177 | callback();
|
178 | });
|
179 | };
|
180 |
|
181 | module.exports = progress;
|