1 | 'use strict';
|
2 |
|
3 |
|
4 |
|
5 | const chalk = require('chalk');
|
6 | const inquirer = require('inquirer');
|
7 | const Spinner = require('node-spinner');
|
8 | const readline = require('readline');
|
9 | const { Bar: Progress } = require('cli-progress');
|
10 |
|
11 | const MultiBar = require('./MultiBar');
|
12 | const is = require('./is');
|
13 | const pad = require('./pad');
|
14 | const unicode = require('./unicode');
|
15 |
|
16 | const prettyJson = require('./prettyJson');
|
17 |
|
18 | let characters = unicode[is.utf() ? 'utf8' : 'ascii'];
|
19 |
|
20 | let interval;
|
21 | let stopSpinner;
|
22 |
|
23 | const progressBarTheme = {
|
24 | format: `{text}|${chalk.green('{bar}')}| ${chalk.grey('{percentage}% | ETA: {eta}s | {value}/{total}')}`,
|
25 | barCompleteChar: '\u2588',
|
26 | barIncompleteChar: ' ',
|
27 | };
|
28 |
|
29 | const decorators = {
|
30 | pauseSpinner(fn) {
|
31 | return (...args) => {
|
32 | let spinnerNeedsRestart = false;
|
33 |
|
34 | if (stopSpinner) {
|
35 | stopSpinner();
|
36 | spinnerNeedsRestart = true;
|
37 | }
|
38 |
|
39 | const result = fn(...args);
|
40 |
|
41 |
|
42 | if (spinnerNeedsRestart)
|
43 | konzola.wait();
|
44 |
|
45 |
|
46 |
|
47 | return result;
|
48 | };
|
49 | },
|
50 |
|
51 | pauseSpinnerAsync(fn) {
|
52 | return async (...args) => {
|
53 | let spinnerNeedsRestart = false;
|
54 |
|
55 | if (stopSpinner) {
|
56 | stopSpinner();
|
57 | spinnerNeedsRestart = true;
|
58 | }
|
59 |
|
60 | const result = await fn(...args);
|
61 |
|
62 |
|
63 | if (spinnerNeedsRestart)
|
64 | konzola.wait();
|
65 |
|
66 |
|
67 | return result;
|
68 | };
|
69 | },
|
70 |
|
71 | skipIfQuiet(fn) {
|
72 | return (...args) => {
|
73 |
|
74 | if (is.quiet())
|
75 | return konzola;
|
76 |
|
77 |
|
78 | return fn(...args);
|
79 | };
|
80 | },
|
81 |
|
82 | skipIfNotVerbose(fn) {
|
83 | return (...args) => {
|
84 |
|
85 | if (!is.verbose())
|
86 | return konzola;
|
87 |
|
88 | return fn(...args);
|
89 | };
|
90 | },
|
91 | };
|
92 |
|
93 | const konzola = {
|
94 | forceColor() {
|
95 | chalk.enabled = true;
|
96 | },
|
97 |
|
98 | noColor() {
|
99 | chalk.enabled = false;
|
100 | },
|
101 |
|
102 | forceUtf() {
|
103 | characters = unicode.utf8;
|
104 | },
|
105 |
|
106 | noUtf() {
|
107 | characters = unicode.ascii;
|
108 | },
|
109 |
|
110 | error: decorators.pauseSpinner((message, { prefix = characters.crossMark } = {}) => {
|
111 | console.error(chalk.red.bold(`${prefix} ${String(message)}`));
|
112 |
|
113 | return konzola;
|
114 | }),
|
115 |
|
116 | warn: decorators.pauseSpinner((message, { prefix = characters.rightPointingPointer } = {}) => {
|
117 | console.error(chalk.yellow.bold(`${prefix} ${String(message)}`));
|
118 |
|
119 | return konzola;
|
120 | }),
|
121 |
|
122 | success: decorators.skipIfQuiet(decorators.pauseSpinner((message, { prefix = characters.checkMark } = {}) => {
|
123 | console.log(chalk.green.bold(`${prefix} ${String(message)}`));
|
124 |
|
125 | return konzola;
|
126 | })),
|
127 |
|
128 | info: decorators.skipIfQuiet(decorators.pauseSpinner((message, { prefix = ' ' } = {}) => {
|
129 | console.log(chalk.white(`${prefix} ${String(message)}`));
|
130 |
|
131 | return konzola;
|
132 | })),
|
133 |
|
134 | verbose: decorators.skipIfQuiet(decorators.skipIfNotVerbose(decorators.pauseSpinner((message, { prefix = ' ' } = {}) => {
|
135 | console.log(chalk.gray(`${prefix} ${String(message)}`));
|
136 |
|
137 | return konzola;
|
138 | }))),
|
139 |
|
140 | line: decorators.skipIfQuiet(decorators.pauseSpinner(() => {
|
141 | console.log(chalk.gray('\u2500'.repeat(process.stdout.columns || 80)));
|
142 |
|
143 | return konzola;
|
144 | })),
|
145 |
|
146 | header(headline, { prefix = characters.rightPointingPointer } = {}) {
|
147 | konzola.newLine();
|
148 | konzola.info(headline, { prefix });
|
149 | konzola.line();
|
150 |
|
151 | return konzola;
|
152 | },
|
153 |
|
154 | list(message, { prefix = characters.multiplicationDot, indent = 0 } = {}) {
|
155 | const width = indent * (prefix.length + 1);
|
156 |
|
157 | prefix = new Array(width + 1).join(' ') + prefix;
|
158 |
|
159 | konzola.info(message, { prefix });
|
160 |
|
161 | return konzola;
|
162 | },
|
163 |
|
164 | json(obj, { prefix = '', indent = 0 } = {}) {
|
165 | const width = indent * (prefix.length + 1);
|
166 | prefix = new Array(width + 1).join(' ') + prefix;
|
167 | konzola.info(prettyJson.render(obj, indent, { colors: chalk }), { prefix });
|
168 | },
|
169 |
|
170 | newLine: decorators.skipIfQuiet(decorators.pauseSpinner(() => {
|
171 | console.log();
|
172 |
|
173 | return konzola;
|
174 | })),
|
175 |
|
176 | table(rows) {
|
177 | if (!rows)
|
178 | throw new Error('Rows are missing.');
|
179 |
|
180 |
|
181 | const widths = [];
|
182 |
|
183 | rows.forEach((row) => {
|
184 | row.forEach((value, columnIndex) => {
|
185 | widths[columnIndex] = Math.max(widths[columnIndex] || 0, String(value).length);
|
186 | });
|
187 | });
|
188 |
|
189 | rows.forEach((row) => {
|
190 | const line = [];
|
191 |
|
192 | if (row.length > 0)
|
193 | row.forEach((value, columnIndex) => {
|
194 | line.push(pad(value, widths[columnIndex]));
|
195 | });
|
196 | else
|
197 | widths.forEach((width) => {
|
198 | line.push(new Array(width + 1).join(characters.boxDrawingsLightHorizontal));
|
199 | });
|
200 |
|
201 |
|
202 | konzola.info(line.join(' '));
|
203 | });
|
204 |
|
205 | return konzola;
|
206 | },
|
207 |
|
208 | passThrough: decorators.pauseSpinner((message, { prefix = ' ', target = 'stdout' } = {}) => {
|
209 | if (is.quiet() && target === 'stdout')
|
210 | return konzola;
|
211 |
|
212 |
|
213 | process[target].write(`${prefix || ' '} ${String(message)}`);
|
214 |
|
215 | return konzola;
|
216 | }),
|
217 |
|
218 | wait({ text = '', prefix = ' ' } = {}) {
|
219 | if (is.quiet() || !is.interactiveMode())
|
220 | return () => {};
|
221 |
|
222 | if (stopSpinner)
|
223 | return null;
|
224 |
|
225 | if (text)
|
226 | text = ` ${text}`;
|
227 |
|
228 | const spinner = new Spinner();
|
229 |
|
230 | interval = setInterval(() => {
|
231 | process.stderr.write(`\r${prefix} ${spinner.next()}${text}`);
|
232 | }, 50);
|
233 |
|
234 | stopSpinner = () => {
|
235 | stopSpinner = undefined;
|
236 |
|
237 | process.stderr.write('\r \r');
|
238 | clearInterval(interval);
|
239 | };
|
240 |
|
241 | return stopSpinner;
|
242 | },
|
243 |
|
244 | progress({ text = '', prefix = '', total = 100 } = {}) {
|
245 | const bar = new Progress({}, progressBarTheme);
|
246 |
|
247 | if (text)
|
248 | text = `${text} `;
|
249 |
|
250 | if (prefix)
|
251 | text = `${prefix} ${text}`;
|
252 |
|
253 | bar.start(total, 0, { text });
|
254 |
|
255 | bar.done = (doneText) => {
|
256 | bar.stop();
|
257 |
|
258 | if (!doneText)
|
259 | return;
|
260 |
|
261 | readline.cursorTo(process.stdout, 0, null);
|
262 | readline.moveCursor(process.stdout, 0, -1);
|
263 | readline.clearLine(process.stdout);
|
264 |
|
265 |
|
266 | konzola.success(doneText);
|
267 | };
|
268 | return bar;
|
269 | },
|
270 |
|
271 | ask: decorators.pauseSpinnerAsync(async (question, options = {}) => {
|
272 | if (!question)
|
273 | throw new Error('Question is missing.');
|
274 |
|
275 |
|
276 | let defaultValue;
|
277 | let mask;
|
278 |
|
279 | if (options instanceof RegExp) {
|
280 | defaultValue = undefined;
|
281 | mask = options;
|
282 | } else if (typeof options === 'string') {
|
283 | defaultValue = options;
|
284 | mask = undefined;
|
285 | } else {
|
286 | defaultValue = options.default;
|
287 | mask = options.mask;
|
288 | }
|
289 |
|
290 | const { answer } = await inquirer.prompt([
|
291 | {
|
292 | type: 'input',
|
293 | name: 'answer',
|
294 | message: question,
|
295 | default: defaultValue,
|
296 | validate(value) {
|
297 | if (mask && !mask.test(value))
|
298 | return 'Malformed input, please retry.';
|
299 |
|
300 |
|
301 | return true;
|
302 | },
|
303 | },
|
304 | ]);
|
305 |
|
306 | return answer;
|
307 | }),
|
308 |
|
309 | confirm: decorators.pauseSpinnerAsync(async (message, value = true) => {
|
310 | if (!message)
|
311 | throw new Error('Message is missing.');
|
312 |
|
313 |
|
314 | const { isConfirmed } = await inquirer.prompt([
|
315 | {
|
316 | type: 'confirm',
|
317 | name: 'isConfirmed',
|
318 | message,
|
319 | default: value,
|
320 | },
|
321 | ]);
|
322 |
|
323 | return isConfirmed;
|
324 | }),
|
325 |
|
326 | select: decorators.pauseSpinnerAsync(async (question, choices) => {
|
327 | if (!question)
|
328 | throw new Error('Question is missing.');
|
329 |
|
330 | if (!choices)
|
331 | throw new Error('Choices are missing.');
|
332 |
|
333 |
|
334 | const { selection } = await inquirer.prompt([
|
335 | {
|
336 | type: 'list',
|
337 | name: 'selection',
|
338 | message: question,
|
339 | choices,
|
340 | },
|
341 | ]);
|
342 |
|
343 | return selection;
|
344 | }),
|
345 |
|
346 | chalk,
|
347 | characters,
|
348 |
|
349 | multiBar: new MultiBar(),
|
350 |
|
351 | exit(code = 0) {
|
352 | if (stopSpinner)
|
353 | stopSpinner();
|
354 |
|
355 |
|
356 |
|
357 | process.exit(code);
|
358 |
|
359 | },
|
360 | };
|
361 |
|
362 | module.exports = konzola;
|