pixl-cli 1.0.16 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -27
- package/cli.js +83 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,14 +43,14 @@ This module provides utilities for creating command-line Node.js apps. Features
|
|
|
43
43
|
|
|
44
44
|
Use [npm](https://www.npmjs.com/) to install the module:
|
|
45
45
|
|
|
46
|
-
```
|
|
46
|
+
```sh
|
|
47
47
|
npm install pixl-cli
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
Then use `require()` to load it in your code:
|
|
51
51
|
|
|
52
|
-
```
|
|
53
|
-
|
|
52
|
+
```js
|
|
53
|
+
const cli = require('pixl-cli');
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
## Basic Tools
|
|
@@ -62,7 +62,7 @@ Here are some of the more basic functions provided by the package.
|
|
|
62
62
|
A very simple `cli.print()` method is provided, which prints any string to [STDOUT](https://nodejs.org/api/process.html#process_process_stdout). This can be shortened to just `print()` if you import everything into the global namespace (see [Importing Into Global](#importing-into-global) below). Example:
|
|
63
63
|
|
|
64
64
|
```js
|
|
65
|
-
|
|
65
|
+
const cli = require('pixl-cli');
|
|
66
66
|
cli.global();
|
|
67
67
|
|
|
68
68
|
print("Hello world!\n");
|
|
@@ -75,7 +75,7 @@ Note that `print()` will be silent if `--quiet` mode is enabled. See [Quiet Mod
|
|
|
75
75
|
An alternate function is provided for printing verbose output, called `cli.verbose()` (or just `verbose()` if [imported into global](#importing-into-global)). This function also prints a string to [STDOUT](https://nodejs.org/api/process.html#process_process_stdout), but only does so if `--verbose` mode is enabled. Example:
|
|
76
76
|
|
|
77
77
|
```js
|
|
78
|
-
|
|
78
|
+
const cli = require('pixl-cli');
|
|
79
79
|
cli.global();
|
|
80
80
|
|
|
81
81
|
verbose("This will only be printed in verbose mode.\n");
|
|
@@ -88,7 +88,7 @@ See [Verbose Mode](#verbose-mode) below for details.
|
|
|
88
88
|
To print something to [STDERR](https://nodejs.org/api/process.html#process_process_stderr) you can use the `cli.warn()` method. This works similarly as `cli.print()` in that it honors [Quiet Mode](#quiet-mode), does not include a trailing EOL, and gets imported to the global namespace if `cli.global()` is called. Example:
|
|
89
89
|
|
|
90
90
|
```js
|
|
91
|
-
|
|
91
|
+
const cli = require('pixl-cli');
|
|
92
92
|
cli.global();
|
|
93
93
|
|
|
94
94
|
warn("This will be printed to STDERR.\n");
|
|
@@ -99,7 +99,7 @@ warn("This will be printed to STDERR.\n");
|
|
|
99
99
|
To print something to [STDERR](https://nodejs.org/api/process.html#process_process_stderr) and exit immediately afterward, you can call `cli.die()` and pass in a message. This will exit with a non-zero code indicating that the process "failed". This method also gets imported to the global namespace if `cli.global()` is called. Example:
|
|
100
100
|
|
|
101
101
|
```js
|
|
102
|
-
|
|
102
|
+
const cli = require('pixl-cli');
|
|
103
103
|
cli.global();
|
|
104
104
|
|
|
105
105
|
die("A fatal error occurred.\n");
|
|
@@ -110,7 +110,7 @@ die("A fatal error occurred.\n");
|
|
|
110
110
|
To enable logging mode, so all calls to `print()`, `verbose()`, `warn()` and `die()` also get logged to a file, call `cli.setLogFile()` and pass in a path. The file need not exist, but the directory should. Example:
|
|
111
111
|
|
|
112
112
|
```js
|
|
113
|
-
cli.setLogFile( "/
|
|
113
|
+
cli.setLogFile( "/let/log/myscript.log" );
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
Note that once the log file is set, everything printed is logged, even if quiet mode is enabled. Additionally, all calls to `cli.verbose()` are also logged, even if `--verbose` mode is not enabled.
|
|
@@ -129,9 +129,9 @@ You can also call `cli.log()` to log something directly without also printing it
|
|
|
129
129
|
Simple methods are provided to load and save files to/from strings. These are both synchronous calls. They are `loadFile()` which accepts a file path and returns the contents as a string, and `saveFile()` which accepts a file path and contents as a string. `saveFile()` writes to the specified file, replacing any existing content, and creating the file if necessary. Example of both functions:
|
|
130
130
|
|
|
131
131
|
```js
|
|
132
|
-
|
|
132
|
+
const cli = require('pixl-cli');
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
let contents = cli.loadFile( "my-file.txt" );
|
|
135
135
|
cli.print( "File contents: " + contents + "\n" );
|
|
136
136
|
|
|
137
137
|
contents = "Replacing with new content!";
|
|
@@ -162,7 +162,7 @@ Specifically, this queries the [STDOUT](https://nodejs.org/api/process.html#proc
|
|
|
162
162
|
To detect the width of the current TTY (user's terminal), you can call `cli.width()`. If a TTY is attached, this will return the current number of characters that will fit in one single line. Example:
|
|
163
163
|
|
|
164
164
|
```js
|
|
165
|
-
|
|
165
|
+
let width = cli.width();
|
|
166
166
|
cli.print("Your terminal is " + width + " characters wide.\n");
|
|
167
167
|
```
|
|
168
168
|
|
|
@@ -188,13 +188,13 @@ In fact, the entire [pixl-tools](https://www.npmjs.com/package/pixl-tools) modul
|
|
|
188
188
|
|
|
189
189
|
Any arguments passed to your script on the command-line are parsed using the [pixl-args](https://www.npmjs.com/package/pixl-args) module, and provided as simple key/value pairs in `cli.args`. Example:
|
|
190
190
|
|
|
191
|
-
```
|
|
191
|
+
```sh
|
|
192
192
|
node my-script.js --name "Joseph Huckaby" --city San\ Mateo
|
|
193
193
|
```
|
|
194
194
|
|
|
195
195
|
Then `cli.args` will contain:
|
|
196
196
|
|
|
197
|
-
```
|
|
197
|
+
```json
|
|
198
198
|
{
|
|
199
199
|
"name": "Joseph Huckaby",
|
|
200
200
|
"city": "San Mateo"
|
|
@@ -203,13 +203,13 @@ Then `cli.args` will contain:
|
|
|
203
203
|
|
|
204
204
|
Several different kinds of arguments are available. You can use single or double-dashes, strings which appear to be integers or floats are parsed as such, any switch without a value is set to `true`, any repeated switches are converted to an array of values, and any values provided without a switch are appended to a special `other` array, which can come before or after all the switches. Here is an example of all these things:
|
|
205
205
|
|
|
206
|
-
```
|
|
206
|
+
```sh
|
|
207
207
|
node my-script.js file1.txt file2.txt --name "Joe" --amount 50 --freq 0.5 -z 1 --verbose --add thing1 --add thing2
|
|
208
208
|
```
|
|
209
209
|
|
|
210
210
|
Then `cli.args` will contain:
|
|
211
211
|
|
|
212
|
-
```
|
|
212
|
+
```json
|
|
213
213
|
{
|
|
214
214
|
"other": ["file1.txt", "file2.txt"],
|
|
215
215
|
"name": "Joe",
|
|
@@ -223,12 +223,25 @@ Then `cli.args` will contain:
|
|
|
223
223
|
|
|
224
224
|
Please see the [pixl-args](https://www.npmjs.com/package/pixl-args) module documentation for more details.
|
|
225
225
|
|
|
226
|
+
### Argument Aliases
|
|
227
|
+
|
|
228
|
+
You can support argument aliases (e.g. `-v` for `--verbose`) by providing a map to the `mapArgs()` function. Example:
|
|
229
|
+
|
|
230
|
+
```js
|
|
231
|
+
const cli = require('pixl-cli');
|
|
232
|
+
cli.mapArgs({
|
|
233
|
+
'v': 'verbose',
|
|
234
|
+
'q': 'quiet',
|
|
235
|
+
'd': 'debug'
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
226
239
|
### Verbose Mode
|
|
227
240
|
|
|
228
241
|
The `--verbose` switch has a special meaning. It is used by the `cli.verbose()` function, and will control whether it outputs anything or not. `cli.verbose()` works just like `cli.print()` except that it only prints if the `--verbose` switch is present on the command-line. Example use:
|
|
229
242
|
|
|
230
243
|
```js
|
|
231
|
-
|
|
244
|
+
const cli = require('pixl-cli');
|
|
232
245
|
cli.verbose("This will only be printed in verbose mode.\n");
|
|
233
246
|
```
|
|
234
247
|
|
|
@@ -241,7 +254,7 @@ The `--quiet` switch also has a special meaning. If passed on the command-line,
|
|
|
241
254
|
To prompt the user for input, you can call `cli.prompt()`. Pass in a string to prompt them with, a default answer, and a callback function which will be fired and passed their answer. This is an asynchronous operation, so beware of code flow. Example:
|
|
242
255
|
|
|
243
256
|
```js
|
|
244
|
-
|
|
257
|
+
const cli = require('pixl-cli');
|
|
245
258
|
|
|
246
259
|
cli.prompt("What is your name?", "", function(name) {
|
|
247
260
|
cli.print("Hello " + name + "!\n");
|
|
@@ -269,8 +282,8 @@ Note that if your script is running without a TTY (i.e. without an attached term
|
|
|
269
282
|
|
|
270
283
|
Call `cli.box()` to render a string (or paragraph) of text surrounded by an ASCII art border. Example:
|
|
271
284
|
|
|
272
|
-
```
|
|
273
|
-
|
|
285
|
+
```js
|
|
286
|
+
const cli = require('pixl-cli');
|
|
274
287
|
|
|
275
288
|
cli.print(
|
|
276
289
|
cli.box("The quick brown fox jumped over the lazy dog.") + "\n"
|
|
@@ -298,7 +311,7 @@ Absent any options, the box will be sized to fit your text string, with exactly
|
|
|
298
311
|
Example:
|
|
299
312
|
|
|
300
313
|
```js
|
|
301
|
-
|
|
314
|
+
let text = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
|
|
302
315
|
|
|
303
316
|
cli.print(
|
|
304
317
|
cli.box( text, {
|
|
@@ -390,9 +403,9 @@ id est laborum.
|
|
|
390
403
|
Calling `cli.table()` produces a nicely formatted ASCII table with a grid of data. It expects an array of rows, and each element should be an array of columns. The first row will be rendered as the "header" and use a different style. Example:
|
|
391
404
|
|
|
392
405
|
```js
|
|
393
|
-
|
|
406
|
+
const cli = require('pixl-cli');
|
|
394
407
|
|
|
395
|
-
|
|
408
|
+
let rows = [
|
|
396
409
|
[ "Username", "Full Name", "Email Address", "Status" ],
|
|
397
410
|
[ "jhuckaby", "Joseph Huckaby", "jhuckaby@test.com", "Administrator" ],
|
|
398
411
|
[ "tsmith", "Tom Smith", "smith@email.com", "Active" ],
|
|
@@ -424,6 +437,7 @@ Of course, if your Terminal supports ANSI color and font styles, the header woul
|
|
|
424
437
|
| `textStyles` | An array of [chalk](https://www.npmjs.com/package/chalk) styles or functions to apply to the table cell text. |
|
|
425
438
|
| `borderStyles` | An array of [chalk](https://www.npmjs.com/package/chalk) styles or functions to apply to the border graphics. |
|
|
426
439
|
| `indent` | Number of characters to indent the table by (defaults to `0`). |
|
|
440
|
+
| `autoFit` | Automatically "fit" table into the available terminal width, if it is too wide. This will add ellipsis to longer columns as required. |
|
|
427
441
|
|
|
428
442
|
Here is an example specifying all the possible options:
|
|
429
443
|
|
|
@@ -433,7 +447,8 @@ cli.print(
|
|
|
433
447
|
headerStyles: ["bold", "yellow"],
|
|
434
448
|
textStyles: ["cyan", function( text ) { return text.toUpperCase() }],
|
|
435
449
|
borderStyles: ["gray"],
|
|
436
|
-
indent: 0
|
|
450
|
+
indent: 0,
|
|
451
|
+
autoFit: true
|
|
437
452
|
}) + "\n"
|
|
438
453
|
);
|
|
439
454
|
```
|
|
@@ -451,9 +466,9 @@ To display a graphical ASCII progress bar, first call `cli.progress.start()` to
|
|
|
451
466
|
Here is a simple example:
|
|
452
467
|
|
|
453
468
|
```js
|
|
454
|
-
|
|
469
|
+
const cli = require('pixl-cli');
|
|
455
470
|
|
|
456
|
-
|
|
471
|
+
let amount = 0;
|
|
457
472
|
cli.progress.start();
|
|
458
473
|
|
|
459
474
|
setInterval( function() {
|
|
@@ -631,7 +646,7 @@ cli.progress.start({
|
|
|
631
646
|
All the style methods from the wonderful [chalk](https://www.npmjs.com/package/chalk) module are automatically imported, so you can use them like this:
|
|
632
647
|
|
|
633
648
|
```js
|
|
634
|
-
|
|
649
|
+
const cli = require('pixl-cli');
|
|
635
650
|
cli.print( cli.bold.red("This is bold and red!") + "\n" );
|
|
636
651
|
```
|
|
637
652
|
|
|
@@ -640,7 +655,7 @@ cli.print( cli.bold.red("This is bold and red!") + "\n" );
|
|
|
640
655
|
You can optionally import some of the most commonly used methods into the global namespace, so you can use them from anywhere without having to prefix them. The method list includes `print()`, `verbose()`, a bunch of others (see below), as well as all the [chalk](https://www.npmjs.com/package/chalk) style methods. Example:
|
|
641
656
|
|
|
642
657
|
```js
|
|
643
|
-
|
|
658
|
+
const cli = require('pixl-cli');
|
|
644
659
|
cli.global();
|
|
645
660
|
|
|
646
661
|
print( box( bold.red("This is bold, red and in a box!") ) + "\n" );
|
package/cli.js
CHANGED
|
@@ -33,6 +33,17 @@ var cli = module.exports = {
|
|
|
33
33
|
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'
|
|
34
34
|
].join('|'), 'g'),
|
|
35
35
|
|
|
36
|
+
mapArgs: function(aliases) {
|
|
37
|
+
// apply alias lookup to a set of args
|
|
38
|
+
// e.g. { 'q':'quiet', 'v':'verbose' }
|
|
39
|
+
for (var key in aliases) {
|
|
40
|
+
if (key in this.args) {
|
|
41
|
+
this.args[ aliases[key] ] = this.args[key];
|
|
42
|
+
delete this.args[key];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
|
|
36
47
|
tty: function() {
|
|
37
48
|
// return true if stdout is connected to a TTY,
|
|
38
49
|
// i.e. so we can ask the user things
|
|
@@ -234,6 +245,76 @@ var cli = module.exports = {
|
|
|
234
245
|
return output.length ? output.join("\n") : "";
|
|
235
246
|
},
|
|
236
247
|
|
|
248
|
+
autoFitTableRows: function(rows, args) {
|
|
249
|
+
// add ellipsis to rows as needed to fit entire table into horiz terminal width
|
|
250
|
+
var self = this;
|
|
251
|
+
var avail_width = (this.width() - (stringWidth(args.indent) * 2));
|
|
252
|
+
if (avail_width < 1) return;
|
|
253
|
+
|
|
254
|
+
var max_col_widths = [];
|
|
255
|
+
rows.forEach( function(cols, idx) {
|
|
256
|
+
cols.forEach( function(col, idy) {
|
|
257
|
+
max_col_widths[idy] = Math.max( max_col_widths[idy] || 0, stringWidth(''+col) );
|
|
258
|
+
} );
|
|
259
|
+
} );
|
|
260
|
+
|
|
261
|
+
var measureTableWidth = function() {
|
|
262
|
+
// measure table width with current settings and max_col_widths "virtually" applied
|
|
263
|
+
var widestCols = [];
|
|
264
|
+
rows.forEach( function(cols, idx) {
|
|
265
|
+
cols.forEach( function(col, idy) {
|
|
266
|
+
var sw = Math.min( stringWidth(''+col), max_col_widths[idy] );
|
|
267
|
+
widestCols[idy] = Math.max( widestCols[idy] || 0, sw + 2 );
|
|
268
|
+
} );
|
|
269
|
+
} );
|
|
270
|
+
|
|
271
|
+
var numCols = widestCols.length;
|
|
272
|
+
var line = "┌";
|
|
273
|
+
widestCols.forEach( function(num, idx) {
|
|
274
|
+
line += self.repeat("─", num);
|
|
275
|
+
if (idx < numCols - 1) line += "┬";
|
|
276
|
+
} );
|
|
277
|
+
line += "┐";
|
|
278
|
+
|
|
279
|
+
return line.length;
|
|
280
|
+
}; // measureTableWidth
|
|
281
|
+
|
|
282
|
+
// now keep chopping down max_col_widths until we fit
|
|
283
|
+
while (measureTableWidth() > avail_width) {
|
|
284
|
+
// find largest max_col_widths and decrement it by 1
|
|
285
|
+
var longest_col_width = Math.max.apply( Math, max_col_widths );
|
|
286
|
+
if (longest_col_width < 2) return; // e-brake
|
|
287
|
+
|
|
288
|
+
var longest_col_idx = max_col_widths.indexOf(longest_col_width);
|
|
289
|
+
if (longest_col_idx == -1) return; // sanity
|
|
290
|
+
|
|
291
|
+
max_col_widths[longest_col_idx]--;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// finally prune affected columns, trying to preserve ANSI color inside column value
|
|
295
|
+
rows.forEach( function(cols, idx) {
|
|
296
|
+
cols.forEach( function(col, idy) {
|
|
297
|
+
col = '' + col;
|
|
298
|
+
if (stringWidth(col) > max_col_widths[idy]) {
|
|
299
|
+
var suffix = '';
|
|
300
|
+
var prefix = '';
|
|
301
|
+
|
|
302
|
+
while (col.match(/^(\u001b\[[^m]*?m)/)) {
|
|
303
|
+
prefix += RegExp.$1;
|
|
304
|
+
col = col.replace(/^(\u001b\[[^m]*?m)/, '');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
while (col.match(/(\u001b\[[^m]*?m)$/)) {
|
|
308
|
+
suffix = RegExp.$1 + suffix;
|
|
309
|
+
col = col.replace(/(\u001b\[[^m]*?m)$/, '');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
cols[idy] = prefix + col.substring(0, max_col_widths[idy] - 1) + '…' + suffix;
|
|
313
|
+
} // too wide
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
},
|
|
317
|
+
|
|
237
318
|
table: function(rows, args) {
|
|
238
319
|
// render table of cols/rows with unicode borders
|
|
239
320
|
// rows should be an array of arrays (columns), with row 0 being the header
|
|
@@ -247,6 +328,8 @@ var cli = module.exports = {
|
|
|
247
328
|
args.indent = args.indent || "";
|
|
248
329
|
if (typeof(args.indent) == 'number') args.indent = cli.space(args.indent);
|
|
249
330
|
|
|
331
|
+
if (args.autoFit) this.autoFitTableRows(rows, args);
|
|
332
|
+
|
|
250
333
|
// calculate widest columns (+1spc of hpadding)
|
|
251
334
|
var widestCols = [];
|
|
252
335
|
rows.forEach( function(cols, idx) {
|
package/package.json
CHANGED