@rockcarver/frodo-cli 0.11.1-2 → 0.12.2-1
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/CHANGELOG.md +43 -2
- package/package.json +3 -15
- package/src/cli/cmd_common.js +13 -0
- package/src/utils/Console.js +480 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,11 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.12.2-1] - 2022-08-27
|
|
11
|
+
|
|
12
|
+
## [0.12.2-0] - 2022-08-27
|
|
13
|
+
|
|
14
|
+
## [0.12.1] - 2022-08-27
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- \#3: `frodo-cli` now uses the new callback based progress indicator and message display framework in `frodo-lib 0.12.0`
|
|
19
|
+
|
|
20
|
+
## [0.12.0] - 2022-08-27 [YANKED]
|
|
21
|
+
|
|
10
22
|
## [0.11.1-2] - 2022-08-21
|
|
11
23
|
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- rockcarver/frodo#389: Exporting of empty scripts now works properly
|
|
27
|
+
|
|
12
28
|
## [0.11.1-1] - 2022-08-21
|
|
13
29
|
|
|
14
|
-
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- Frodo CLI is now effectively using Frodo Library for all functionality except CLI.
|
|
33
|
+
- This changes has no effect on users using frodo binaries except for the download location of those binaries, which has now shifted to the [frodo-cli](https://github.com/rockcarver/frodo-cli) repo [release section](https://github.com/rockcarver/frodo-cli/releases).
|
|
34
|
+
- This change does affect users who run Frodo in `Developer Mode`. The exact effects and required actions are not yet fully documented and understood.
|
|
35
|
+
- This change does not effect the installation/update/usage process of users running the Frodo CLI NPM package. However, under the surface there is a big change in that the [Frodo CLI (@rockcarver/frodo-cli)](https://www.npmjs.com/package/@rockcarver/frodo-cli) package is now built on the new [Frodo Library (@rockcarver/frodo-lib)](https://www.npmjs.com/package/@rockcarver/frodo-lib).
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- The output of `frodo -v` has changed to include all three versions: cli, lib, and node:
|
|
40
|
+
```console
|
|
41
|
+
% frodo -v
|
|
42
|
+
cli: v0.11.1-1
|
|
43
|
+
lib: v0.11.1-6
|
|
44
|
+
node: v18.7.0
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## [0.11.1-0] - 2022-08-19 [YANKED]
|
|
15
48
|
|
|
16
49
|
## [0.10.4] - 2022-08-13
|
|
17
50
|
|
|
@@ -408,7 +441,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
408
441
|
- Fixed problem with adding connection profiles
|
|
409
442
|
- Miscellaneous bug fixes
|
|
410
443
|
|
|
411
|
-
[Unreleased]: https://github.com/rockcarver/frodo-cli/compare/v0.
|
|
444
|
+
[Unreleased]: https://github.com/rockcarver/frodo-cli/compare/v0.12.2-1...HEAD
|
|
445
|
+
|
|
446
|
+
[0.12.2-1]: https://github.com/rockcarver/frodo-cli/compare/v0.12.2-0...v0.12.2-1
|
|
447
|
+
|
|
448
|
+
[0.12.2-0]: https://github.com/rockcarver/frodo-cli/compare/v0.12.1...v0.12.2-0
|
|
449
|
+
|
|
450
|
+
[0.12.1]: https://github.com/rockcarver/frodo-cli/compare/v0.12.0...v0.12.1
|
|
451
|
+
|
|
452
|
+
[0.12.0]: https://github.com/rockcarver/frodo-cli/compare/v0.11.1-2...v0.12.0
|
|
412
453
|
|
|
413
454
|
[0.11.1-2]: https://github.com/rockcarver/frodo-cli/compare/v0.11.1-1...v0.11.1-2
|
|
414
455
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rockcarver/frodo-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.2-1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A command line interface to manage ForgeRock Identity Cloud tenants, ForgeOps deployments, and classic deployments.",
|
|
6
6
|
"keywords": [
|
|
@@ -80,24 +80,12 @@
|
|
|
80
80
|
},
|
|
81
81
|
"dependencies": {
|
|
82
82
|
"@colors/colors": "^1.5.0",
|
|
83
|
-
"@rockcarver/frodo-lib": "
|
|
84
|
-
"axios": "^0.27.2",
|
|
85
|
-
"axios-retry": "^3.3.1",
|
|
83
|
+
"@rockcarver/frodo-lib": "~0.12.1",
|
|
86
84
|
"cli-progress": "^3.11.2",
|
|
87
85
|
"cli-table3": "^0.6.2",
|
|
88
86
|
"commander": "^9.4.0",
|
|
89
|
-
"fs-extra": "^10.0.0",
|
|
90
|
-
"https-proxy-agent": "^5.0.1",
|
|
91
|
-
"lodash": "^4.17.21",
|
|
92
87
|
"nanospinner": "^1.1.0",
|
|
93
|
-
"
|
|
94
|
-
"qs": "^6.10.3",
|
|
95
|
-
"readline-sync": "^1.4.10",
|
|
96
|
-
"replaceall": "^0.1.6",
|
|
97
|
-
"slugify": "^1.6.5",
|
|
98
|
-
"url": "^0.11.0",
|
|
99
|
-
"uuid": "^8.3.2",
|
|
100
|
-
"yesno": "^0.4.0"
|
|
88
|
+
"uuid": "^8.3.2"
|
|
101
89
|
},
|
|
102
90
|
"devDependencies": {
|
|
103
91
|
"@babel/eslint-parser": "^7.18.9",
|
package/src/cli/cmd_common.js
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { Argument, Option } from 'commander';
|
|
2
|
+
import { state } from '@rockcarver/frodo-lib';
|
|
3
|
+
|
|
2
4
|
import * as global from '../storage/StaticStorage.js';
|
|
5
|
+
import {
|
|
6
|
+
printMessage,
|
|
7
|
+
createProgressIndicator,
|
|
8
|
+
updateProgressIndicator,
|
|
9
|
+
stopProgressIndicator,
|
|
10
|
+
} from '../utils/Console.js';
|
|
11
|
+
|
|
12
|
+
state.default.session.setPrintHandler(printMessage);
|
|
13
|
+
state.default.session.setCreateProgressHandler(createProgressIndicator);
|
|
14
|
+
state.default.session.setUpdateProgressHandler(updateProgressIndicator);
|
|
15
|
+
state.default.session.setStopProgressHandler(stopProgressIndicator);
|
|
3
16
|
|
|
4
17
|
const hostArgumentDescription =
|
|
5
18
|
'Access Management base URL, e.g.: https://cdk.iam.example.com/am. To use a connection profile, just specify a unique substring.';
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import { SingleBar, Presets } from 'cli-progress';
|
|
2
|
+
import { createSpinner } from 'nanospinner';
|
|
3
|
+
import Table from 'cli-table3';
|
|
4
|
+
// eslint-disable-next-line no-unused-vars
|
|
5
|
+
import * as colors from '@colors/colors';
|
|
6
|
+
import { state } from '@rockcarver/frodo-lib';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Output a data message
|
|
10
|
+
* @param {Object} message the message
|
|
11
|
+
*/
|
|
12
|
+
function data(message) {
|
|
13
|
+
switch (typeof message) {
|
|
14
|
+
case 'object':
|
|
15
|
+
console.dir(message, { depth: 3 });
|
|
16
|
+
break;
|
|
17
|
+
default:
|
|
18
|
+
console.log(message);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Output a text message
|
|
24
|
+
* @param {Object} message the message
|
|
25
|
+
*/
|
|
26
|
+
function text(message) {
|
|
27
|
+
switch (typeof message) {
|
|
28
|
+
case 'object':
|
|
29
|
+
console.dir(message, { depth: 3 });
|
|
30
|
+
break;
|
|
31
|
+
default:
|
|
32
|
+
console.error(message);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Output an info message
|
|
38
|
+
* @param {Object} message the message
|
|
39
|
+
*/
|
|
40
|
+
function info(message) {
|
|
41
|
+
switch (typeof message) {
|
|
42
|
+
case 'object':
|
|
43
|
+
console.dir(message, { depth: 3 });
|
|
44
|
+
break;
|
|
45
|
+
default:
|
|
46
|
+
console.error(message.brightCyan);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Output a warn message
|
|
52
|
+
* @param {Object} message the message
|
|
53
|
+
*/
|
|
54
|
+
function warn(message) {
|
|
55
|
+
switch (typeof message) {
|
|
56
|
+
case 'object':
|
|
57
|
+
console.dir(message, { depth: 3 });
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
console.error(message.yellow);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Output an error message
|
|
66
|
+
* @param {Object} message the message
|
|
67
|
+
*/
|
|
68
|
+
function error(message) {
|
|
69
|
+
switch (typeof message) {
|
|
70
|
+
case 'object':
|
|
71
|
+
console.dir(message, { depth: 4 });
|
|
72
|
+
break;
|
|
73
|
+
default:
|
|
74
|
+
console.error(message.brightRed);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Prints a string message to console
|
|
80
|
+
*
|
|
81
|
+
* @param {string} message The string message to print
|
|
82
|
+
* @param {string} [type=text] "text", "info", "warn", "error" or "data". All but
|
|
83
|
+
* type="data" will be written to stderr.
|
|
84
|
+
* @param {boolean} [newline=true] Whether to add a new at the end of message
|
|
85
|
+
*
|
|
86
|
+
*/
|
|
87
|
+
export function printMessage(message, type = 'text', newline = true) {
|
|
88
|
+
// if (state.default.session.getItem('scriptFriendly')) {
|
|
89
|
+
switch (type) {
|
|
90
|
+
case 'data':
|
|
91
|
+
if (newline) {
|
|
92
|
+
data(message);
|
|
93
|
+
} else {
|
|
94
|
+
process.stdout.write(message);
|
|
95
|
+
}
|
|
96
|
+
break;
|
|
97
|
+
case 'text':
|
|
98
|
+
if (newline) {
|
|
99
|
+
text(message);
|
|
100
|
+
} else {
|
|
101
|
+
process.stderr.write(message);
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
case 'info':
|
|
105
|
+
if (newline) {
|
|
106
|
+
info(message);
|
|
107
|
+
} else {
|
|
108
|
+
process.stderr.write(message.brightCyan);
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
case 'warn':
|
|
112
|
+
if (newline) {
|
|
113
|
+
warn(message);
|
|
114
|
+
} else {
|
|
115
|
+
process.stderr.write(message.yellow);
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
case 'error':
|
|
119
|
+
if (newline) {
|
|
120
|
+
error(message);
|
|
121
|
+
} else {
|
|
122
|
+
process.stderr.write(message.brightRed);
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
default:
|
|
126
|
+
if (newline) {
|
|
127
|
+
error(message);
|
|
128
|
+
} else {
|
|
129
|
+
process.stderr.write(message.bgBrightRed);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Creates a progress bar on stderr
|
|
136
|
+
*
|
|
137
|
+
* Example:
|
|
138
|
+
* [========================================] 100% | 49/49 | Analyzing journey - transactional_auth
|
|
139
|
+
*
|
|
140
|
+
* @param {Number} total The total number of entries to track progress for
|
|
141
|
+
* @param {String} message optional progress bar message
|
|
142
|
+
* @param {Object} options progress bar configuration options
|
|
143
|
+
*
|
|
144
|
+
*/
|
|
145
|
+
export function createProgressBar(
|
|
146
|
+
total,
|
|
147
|
+
message = null,
|
|
148
|
+
options = {
|
|
149
|
+
format: '[{bar}] {percentage}% | {value}/{total} | {data}',
|
|
150
|
+
noTTYOutput: true,
|
|
151
|
+
}
|
|
152
|
+
) {
|
|
153
|
+
let opt = options;
|
|
154
|
+
if (message == null) {
|
|
155
|
+
opt = {
|
|
156
|
+
format: '[{bar}] {percentage}% | {value}/{total}',
|
|
157
|
+
noTTYOutput: true,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
let pBar = state.default.session.getItem('progressBar');
|
|
161
|
+
if (!pBar) pBar = new SingleBar(opt, Presets.legacy); // create only when needed
|
|
162
|
+
pBar.start(total, 0, {
|
|
163
|
+
data: message,
|
|
164
|
+
});
|
|
165
|
+
state.default.session.setItem('progressBar', pBar);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Updates the progress bar by 1
|
|
170
|
+
* @param {string} message optional message to update the progress bar
|
|
171
|
+
*
|
|
172
|
+
*/
|
|
173
|
+
export function updateProgressBar(message = null) {
|
|
174
|
+
const pBar = state.default.session.getItem('progressBar');
|
|
175
|
+
if (message)
|
|
176
|
+
pBar.increment({
|
|
177
|
+
data: message,
|
|
178
|
+
});
|
|
179
|
+
else pBar.increment();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Stop and hide the progress bar
|
|
184
|
+
* @param {*} message optional message to update the progress bar
|
|
185
|
+
*/
|
|
186
|
+
export function stopProgressBar(message = null) {
|
|
187
|
+
const pBar = state.default.session.getItem('progressBar');
|
|
188
|
+
if (message)
|
|
189
|
+
pBar.update({
|
|
190
|
+
data: message,
|
|
191
|
+
});
|
|
192
|
+
pBar.stop();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Create the spinner
|
|
197
|
+
* @param {String} message optional spinner message
|
|
198
|
+
*/
|
|
199
|
+
export function showSpinner(message) {
|
|
200
|
+
const spinner = createSpinner(message).start();
|
|
201
|
+
state.default.session.setItem('Spinner', spinner);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Stop the spinner
|
|
206
|
+
* @param {String} message optional message to update the spinner
|
|
207
|
+
*/
|
|
208
|
+
export function stopSpinner(message = null) {
|
|
209
|
+
const spinner = state.default.session.getItem('Spinner');
|
|
210
|
+
if (spinner) {
|
|
211
|
+
let options = {};
|
|
212
|
+
if (message) options = { text: message };
|
|
213
|
+
spinner.stop(options);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Succeed the spinner. Stop and render success checkmark with optional message.
|
|
219
|
+
* @param {String} message optional message to update the spinner
|
|
220
|
+
*/
|
|
221
|
+
export function succeedSpinner(message = null) {
|
|
222
|
+
const spinner = state.default.session.getItem('Spinner');
|
|
223
|
+
if (spinner) {
|
|
224
|
+
let options = {};
|
|
225
|
+
if (message) options = { text: message };
|
|
226
|
+
spinner.success(options);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Warn the spinner
|
|
232
|
+
* @param {String} message optional message to update the spinner
|
|
233
|
+
*/
|
|
234
|
+
export function warnSpinner(message = null) {
|
|
235
|
+
const spinner = state.default.session.getItem('Spinner');
|
|
236
|
+
if (spinner) {
|
|
237
|
+
let options = {};
|
|
238
|
+
if (message) options = { text: message };
|
|
239
|
+
spinner.warn(options);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Fail the spinner
|
|
245
|
+
* @param {String} message optional message to update the spinner
|
|
246
|
+
*/
|
|
247
|
+
export function failSpinner(message = null) {
|
|
248
|
+
const spinner = state.default.session.getItem('Spinner');
|
|
249
|
+
if (spinner) {
|
|
250
|
+
let options = {};
|
|
251
|
+
if (message) options = { text: message };
|
|
252
|
+
spinner.error(options);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Spin the spinner
|
|
258
|
+
* @param {String} message optional message to update the spinner
|
|
259
|
+
*/
|
|
260
|
+
export function spinSpinner(message = null) {
|
|
261
|
+
const spinner = state.default.session.getItem('Spinner');
|
|
262
|
+
if (spinner) {
|
|
263
|
+
let options = {};
|
|
264
|
+
if (message) options = { text: message };
|
|
265
|
+
spinner.update(options);
|
|
266
|
+
spinner.spin();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function createProgressIndicator(
|
|
271
|
+
type = 'determinate',
|
|
272
|
+
total = 0,
|
|
273
|
+
message = null
|
|
274
|
+
) {
|
|
275
|
+
if (type === 'determinate') {
|
|
276
|
+
createProgressBar(total, message);
|
|
277
|
+
} else {
|
|
278
|
+
showSpinner(message);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function updateProgressIndicator(message) {
|
|
283
|
+
const progressBar = state.default.session.getItem('progressBar');
|
|
284
|
+
if (!progressBar) {
|
|
285
|
+
spinSpinner(message);
|
|
286
|
+
} else {
|
|
287
|
+
updateProgressBar(message);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function stopProgressIndicator(message, status = 'none') {
|
|
292
|
+
const progressBar = state.default.session.getItem('progressBar');
|
|
293
|
+
if (!progressBar) {
|
|
294
|
+
switch (status) {
|
|
295
|
+
case 'none':
|
|
296
|
+
stopSpinner(message);
|
|
297
|
+
break;
|
|
298
|
+
case 'success':
|
|
299
|
+
succeedSpinner(message);
|
|
300
|
+
break;
|
|
301
|
+
case 'warn':
|
|
302
|
+
warnSpinner(message);
|
|
303
|
+
break;
|
|
304
|
+
case 'fail':
|
|
305
|
+
failSpinner(message);
|
|
306
|
+
break;
|
|
307
|
+
default:
|
|
308
|
+
stopSpinner(message);
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
stopProgressBar(message);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Create an empty table
|
|
318
|
+
* @param {[String]} head header row as an array of strings
|
|
319
|
+
* @returns {CliTable3} an empty table
|
|
320
|
+
*/
|
|
321
|
+
export function createTable(head) {
|
|
322
|
+
return new Table({
|
|
323
|
+
head,
|
|
324
|
+
chars: {
|
|
325
|
+
top: '',
|
|
326
|
+
'top-mid': '',
|
|
327
|
+
'top-left': '',
|
|
328
|
+
'top-right': '',
|
|
329
|
+
bottom: '',
|
|
330
|
+
'bottom-mid': '',
|
|
331
|
+
'bottom-left': '',
|
|
332
|
+
'bottom-right': '',
|
|
333
|
+
left: '',
|
|
334
|
+
'left-mid': '',
|
|
335
|
+
mid: '',
|
|
336
|
+
'mid-mid': '',
|
|
337
|
+
right: '',
|
|
338
|
+
'right-mid': '',
|
|
339
|
+
},
|
|
340
|
+
style: { 'padding-left': 0, 'padding-right': 0, head: ['brightCyan'] },
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Create a new key/value table
|
|
346
|
+
* @returns {CliTable3} an empty key/value table
|
|
347
|
+
*/
|
|
348
|
+
export function createKeyValueTable() {
|
|
349
|
+
return new Table({
|
|
350
|
+
chars: {
|
|
351
|
+
top: '',
|
|
352
|
+
'top-mid': '',
|
|
353
|
+
'top-left': '',
|
|
354
|
+
'top-right': '',
|
|
355
|
+
bottom: '',
|
|
356
|
+
'bottom-mid': '',
|
|
357
|
+
'bottom-left': '',
|
|
358
|
+
'bottom-right': '',
|
|
359
|
+
left: '',
|
|
360
|
+
'left-mid': '',
|
|
361
|
+
mid: '',
|
|
362
|
+
'mid-mid': '',
|
|
363
|
+
right: '',
|
|
364
|
+
'right-mid': '',
|
|
365
|
+
},
|
|
366
|
+
style: { 'padding-left': 0, 'padding-right': 0 },
|
|
367
|
+
wordWrap: true,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Helper function to determine the total depth of an object
|
|
373
|
+
* @param {Object} object input object
|
|
374
|
+
* @returns {Number} total depth of the input object
|
|
375
|
+
*/
|
|
376
|
+
function getObjectDepth(object) {
|
|
377
|
+
return Object(object) === object
|
|
378
|
+
? 1 + Math.max(-1, ...Object.values(object).map(getObjectDepth))
|
|
379
|
+
: 0;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Helper function to determine if an object has values
|
|
384
|
+
* @param {Object} object input object
|
|
385
|
+
* @returns {boolean} true of the object or any of its sub-objects contain values, false otherwise
|
|
386
|
+
*/
|
|
387
|
+
function hasValues(object) {
|
|
388
|
+
let has = false;
|
|
389
|
+
const keys = Object.keys(object);
|
|
390
|
+
for (const key of keys) {
|
|
391
|
+
if (Object(object[key]) !== object[key]) {
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
has = has || hasValues(object[key]);
|
|
395
|
+
}
|
|
396
|
+
return has;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Helper function (recursive) to add rows to an object table
|
|
401
|
+
* @param {Object} object object to render
|
|
402
|
+
* @param {Number} depth total depth of initial object
|
|
403
|
+
* @param {Number} level current level
|
|
404
|
+
* @param {CliTable3} table the object table to add the rows to
|
|
405
|
+
* @returns the updated object table
|
|
406
|
+
*/
|
|
407
|
+
function addRows(object, depth, level, table, keyMap) {
|
|
408
|
+
const space = ' ';
|
|
409
|
+
const keys = Object.keys(object);
|
|
410
|
+
for (const key of keys) {
|
|
411
|
+
if (Object(object[key]) !== object[key]) {
|
|
412
|
+
if (level === 1) {
|
|
413
|
+
table.push([
|
|
414
|
+
keyMap[key] ? keyMap[key].brightCyan : key.brightCyan,
|
|
415
|
+
object[key],
|
|
416
|
+
]);
|
|
417
|
+
} else {
|
|
418
|
+
table.push([
|
|
419
|
+
{
|
|
420
|
+
hAlign: 'right',
|
|
421
|
+
content: keyMap[key] ? keyMap[key].gray : key.gray,
|
|
422
|
+
},
|
|
423
|
+
object[key],
|
|
424
|
+
]);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
for (const key of keys) {
|
|
429
|
+
if (Object(object[key]) === object[key]) {
|
|
430
|
+
// only print header if there are any values below
|
|
431
|
+
if (hasValues(object[key])) {
|
|
432
|
+
let indention = new Array(level).fill(space).join('');
|
|
433
|
+
if (level < 3) indention = `\n${indention}`;
|
|
434
|
+
table.push([
|
|
435
|
+
indention.concat(
|
|
436
|
+
keyMap[key] ? keyMap[key].brightCyan : key.brightCyan
|
|
437
|
+
),
|
|
438
|
+
'',
|
|
439
|
+
]);
|
|
440
|
+
}
|
|
441
|
+
// eslint-disable-next-line no-param-reassign
|
|
442
|
+
table = addRows(object[key], depth, level + 1, table, keyMap);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return table;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Create and populate an object table from any JSON object. Use for describe commands.
|
|
450
|
+
* @param {Object} object JSON object to create
|
|
451
|
+
* @returns {CliTable3} a table that can be printed to the console
|
|
452
|
+
*/
|
|
453
|
+
export function createObjectTable(object, keyMap = {}) {
|
|
454
|
+
// eslint-disable-next-line no-param-reassign
|
|
455
|
+
const depth = getObjectDepth(object);
|
|
456
|
+
// eslint-disable-next-line no-param-reassign
|
|
457
|
+
const level = 0;
|
|
458
|
+
// eslint-disable-next-line no-param-reassign
|
|
459
|
+
const table = new Table({
|
|
460
|
+
chars: {
|
|
461
|
+
top: '',
|
|
462
|
+
'top-mid': '',
|
|
463
|
+
'top-left': '',
|
|
464
|
+
'top-right': '',
|
|
465
|
+
bottom: '',
|
|
466
|
+
'bottom-mid': '',
|
|
467
|
+
'bottom-left': '',
|
|
468
|
+
'bottom-right': '',
|
|
469
|
+
left: '',
|
|
470
|
+
'left-mid': '',
|
|
471
|
+
mid: '',
|
|
472
|
+
'mid-mid': '',
|
|
473
|
+
right: '',
|
|
474
|
+
'right-mid': '',
|
|
475
|
+
},
|
|
476
|
+
style: { 'padding-left': 0, 'padding-right': 0, head: ['brightCyan'] },
|
|
477
|
+
});
|
|
478
|
+
addRows(object, depth, level + 1, table, keyMap);
|
|
479
|
+
return table;
|
|
480
|
+
}
|