UNPKG

7.53 kBJavaScriptView Raw
1'use strict';
2
3/* eslint-disable no-console */
4
5const chalk = require('chalk');
6const inquirer = require('inquirer');
7const Spinner = require('node-spinner');
8const readline = require('readline');
9const { Bar: Progress } = require('cli-progress');
10
11const MultiBar = require('./MultiBar');
12const is = require('./is');
13const pad = require('./pad');
14const unicode = require('./unicode');
15
16const prettyJson = require('./prettyJson');
17
18let characters = unicode[is.utf() ? 'utf8' : 'ascii'];
19
20let interval;
21let stopSpinner;
22
23const progressBarTheme = {
24 format: `{text}|${chalk.green('{bar}')}| ${chalk.grey('{percentage}% | ETA: {eta}s | {value}/{total}')}`,
25 barCompleteChar: '\u2588',
26 barIncompleteChar: ' ',
27};
28
29const 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 /* eslint-disable no-use-before-define */
42 if (spinnerNeedsRestart)
43 konzola.wait();
44 /* eslint-enable no-use-before-define */
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 /* eslint-disable no-use-before-define */
63 if (spinnerNeedsRestart)
64 konzola.wait();
65 /* eslint-enable no-use-before-define */
66
67 return result;
68 };
69 },
70
71 skipIfQuiet(fn) {
72 return (...args) => {
73 /* eslint-disable no-use-before-define */
74 if (is.quiet())
75 return konzola;
76 /* eslint-enable no-use-before-define */
77
78 return fn(...args);
79 };
80 },
81
82 skipIfNotVerbose(fn) {
83 return (...args) => {
84 /* eslint-disable no-use-before-define */
85 if (!is.verbose())
86 return konzola;
87 /* eslint-enable no-use-before-define */
88 return fn(...args);
89 };
90 },
91};
92
93const 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 // process.stderr.write(`\r${prefix} cha✓${text}\r`);
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 // process.stdout.clearLine();
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; // eslint-disable-line
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 /* eslint-disable no-process-exit */
357 process.exit(code);
358 /* eslint-enable no-process-exit */
359 },
360};
361
362module.exports = konzola;