@bemoje/cli 0.0.2
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 +34 -0
- package/index.cjs.d.ts +1 -0
- package/index.cjs.js +2682 -0
- package/index.esm.js +2558 -0
- package/package.json +36 -0
- package/src/core/CommandBuilder/ArgumentBuilder.d.ts +21 -0
- package/src/core/CommandBuilder/ArgumentReader.d.ts +15 -0
- package/src/core/CommandBuilder/Base.d.ts +5 -0
- package/src/core/CommandBuilder/CommandBuilder.d.ts +80 -0
- package/src/core/CommandBuilder/CommandBuilder.example.d.ts +1 -0
- package/src/core/CommandBuilder/CommandBuilderMetaData.d.ts +17 -0
- package/src/core/CommandBuilder/CommandFeatureSelector.d.ts +26 -0
- package/src/core/CommandBuilder/ErrorParser.d.ts +15 -0
- package/src/core/CommandBuilder/OptionBuilder.d.ts +27 -0
- package/src/core/CommandBuilder/OptionReader.d.ts +24 -0
- package/src/core/CommandBuilder/OutputManager.d.ts +47 -0
- package/src/core/CommandBuilder/assertCommandNameNotReserved.d.ts +1 -0
- package/src/core/CommandBuilder/ensureBackRefToCommandBuilder.d.ts +7 -0
- package/src/core/CommandBuilder/features/action/actionWrapper.d.ts +5 -0
- package/src/core/CommandBuilder/features/action/combineVariadicArgs.d.ts +3 -0
- package/src/core/CommandBuilder/features/action/debugLogArgsOpts.d.ts +4 -0
- package/src/core/CommandBuilder/features/action/deleteOptionsWithDefaultOrNoValue.d.ts +3 -0
- package/src/core/CommandBuilder/features/action/getPresetArgsAndOpts.d.ts +3 -0
- package/src/core/CommandBuilder/features/action/handleError.d.ts +2 -0
- package/src/core/CommandBuilder/features/action/handleOutputOptions.d.ts +2 -0
- package/src/core/CommandBuilder/features/action/optsWithGlobalsParsed.d.ts +2 -0
- package/src/core/CommandBuilder/features/action/padArgsWithUndefinedUntilExpectedLength.d.ts +3 -0
- package/src/core/CommandBuilder/features/action/parseArguments.d.ts +2 -0
- package/src/core/CommandBuilder/features/action/parseOptions.d.ts +6 -0
- package/src/core/CommandBuilder/features/action/parsedValidArgsOptsWithPresets.d.ts +2 -0
- package/src/core/CommandBuilder/features/action/parsedValidArgsWithPresets.d.ts +2 -0
- package/src/core/CommandBuilder/features/action/parsedValidOptsWithPresets.d.ts +3 -0
- package/src/core/CommandBuilder/features/addConfigCommands.d.ts +3 -0
- package/src/core/CommandBuilder/features/addPresetsCommands.d.ts +3 -0
- package/src/core/CommandBuilder/features/addUtilCommands.d.ts +2 -0
- package/src/core/CommandBuilder/features/assertNoDuplicateCommandNames.d.ts +2 -0
- package/src/core/CommandBuilder/features/assertNoDuplicateOptionNames.d.ts +2 -0
- package/src/core/CommandBuilder/features/autoAssignMissingOptionFlags.d.ts +14 -0
- package/src/core/CommandBuilder/features/autoAssignSubCommandAliases.d.ts +13 -0
- package/src/core/CommandBuilder/features/formatTableForTerminal.d.ts +1 -0
- package/src/core/CommandBuilder/features/getClosestNonNativeParent.d.ts +2 -0
- package/src/core/CommandBuilder/getGlobalOptions.d.ts +3 -0
- package/src/core/CommandBuilder/getOwnAndGlobalOptions.d.ts +3 -0
- package/src/core/CommandBuilder/initializeCommand.d.ts +2 -0
- package/src/core/db/AbstractJsonFileSection.d.ts +45 -0
- package/src/core/db/ConfigSection.d.ts +19 -0
- package/src/core/db/JsonDB.d.ts +19 -0
- package/src/core/db/JsonFile.d.ts +25 -0
- package/src/core/db/JsonFileError.d.ts +6 -0
- package/src/core/db/PresetsSection.d.ts +14 -0
- package/src/core/help/configureHelp.d.ts +2 -0
- package/src/core/util/MethodDisabler.d.ts +42 -0
- package/src/core/util/arrAssign.d.ts +1 -0
- package/src/core/util/assertPresetArgsOptional.d.ts +3 -0
- package/src/core/util/assertValidArguments.d.ts +6 -0
- package/src/core/util/assertValidOptions.d.ts +6 -0
- package/src/core/util/assertValidPreset.d.ts +3 -0
- package/src/core/util/commandExists.d.ts +5 -0
- package/src/core/util/commandLocation.d.ts +5 -0
- package/src/core/util/createArrayMerger.d.ts +1 -0
- package/src/core/util/createObjectMerger.d.ts +2 -0
- package/src/core/util/errorToString.d.ts +1 -0
- package/src/core/util/escapeShellCommandArgument.d.ts +5 -0
- package/src/core/util/forEachChildRecursive.d.ts +4 -0
- package/src/core/util/getARGV.d.ts +1 -0
- package/src/core/util/getAncestors.d.ts +7 -0
- package/src/core/util/getChildren.d.ts +4 -0
- package/src/core/util/getJsonFilepath.d.ts +2 -0
- package/src/core/util/getOptionArgumentName.d.ts +5 -0
- package/src/core/util/getRootCommand.d.ts +5 -0
- package/src/core/util/getSiblings.d.ts +5 -0
- package/src/core/util/hasVariadicArguments.d.ts +5 -0
- package/src/core/util/objAssign.d.ts +1 -0
- package/src/core/util/objDestroy.d.ts +2 -0
- package/src/core/util/optHasArgument.d.ts +2 -0
- package/src/core/util/prefixArray.d.ts +5 -0
- package/src/core/util/prefixString.d.ts +5 -0
- package/src/core/util/prefixStringsRecursive.d.ts +5 -0
- package/src/core/util/realizeLazyProperty.d.ts +1 -0
- package/src/core/util/renderOptionFlags.d.ts +6 -0
- package/src/core/util/setOptionLongName.d.ts +6 -0
- package/src/core/util/setOptionShortName.d.ts +6 -0
- package/src/core/util/splitCombinedArgvShorts.d.ts +1 -0
- package/src/core/util/walkAncestors.d.ts +7 -0
- package/src/core/util/walkChildren.d.ts +4 -0
- package/src/core/util/walkSiblings.d.ts +5 -0
- package/src/index.d.ts +113 -0
- package/src/parsers/createBooleanParser.d.ts +7 -0
- package/src/parsers/createTupleListParser.d.ts +11 -0
- package/src/parsers/createTypedListParser.d.ts +11 -0
- package/src/parsers/parseBoolean.d.ts +10 -0
- package/src/parsers/parseInteger.d.ts +1 -0
- package/src/parsers/parseNumber.d.ts +1 -0
- package/src/parsers/parseString.d.ts +1 -0
- package/src/parsers/selector/AbstractStringParserSelector.d.ts +17 -0
- package/src/parsers/selector/ArgumentParserSelector.d.ts +7 -0
- package/src/parsers/selector/OptionArgumentParserSelector.d.ts +7 -0
- package/src/types/IDefinePropertyOptions.d.ts +9 -0
- package/src/types/IPreset.d.ts +14 -0
- package/src/types/IPresets.d.ts +6 -0
- package/src/types/TStringParser.d.ts +1 -0
- package/src/types/TValidator.d.ts +2 -0
- package/src/validators/createLengthValidator.d.ts +7 -0
- package/src/validators/createTypedArrayValidator.d.ts +11 -0
- package/src/validators/isArray.d.ts +1 -0
- package/src/validators/isBoolean.d.ts +1 -0
- package/src/validators/isInteger.d.ts +1 -0
- package/src/validators/isIntegerArray.d.ts +4 -0
- package/src/validators/isNamedFunction.d.ts +5 -0
- package/src/validators/isNamedFunctionArray.d.ts +1 -0
- package/src/validators/isNull.d.ts +1 -0
- package/src/validators/isNumber.d.ts +1 -0
- package/src/validators/isNumberArray.d.ts +4 -0
- package/src/validators/isNumericString.d.ts +1 -0
- package/src/validators/isString.d.ts +1 -0
- package/src/validators/isStringArray.d.ts +4 -0
- package/src/validators/isStringWithNoSpacesOrDashes.d.ts +1 -0
- package/src/validators/selector/ArgumentValidatorSelector.d.ts +6 -0
- package/src/validators/selector/OptionArgumentValidatorSelector.d.ts +6 -0
- package/src/validators/selector/ValidatorSelector.d.ts +17 -0
package/index.esm.js
ADDED
|
@@ -0,0 +1,2558 @@
|
|
|
1
|
+
import { Argument, Help, Command, Option } from 'commander';
|
|
2
|
+
import { assertThat, strFirstCharToUpperCase, funSetName, isValidNumber, arrLast, colors, isPlainObject, isPrimitive, regexEscapeString, isFunction, defaultOpenInEditorCommand, strEnsureStartsWith, arrSome, XtError, promptUserEditJsonInTextEditorSync, isObject, readJsonFileSafeSync, funAsyncRateLimit, writeJsonFileSafe } from '@bemoje/util';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import Table from 'cli-table';
|
|
6
|
+
import { lookpath } from 'lookpath';
|
|
7
|
+
|
|
8
|
+
class Base {
|
|
9
|
+
constructor(){
|
|
10
|
+
var _Base_nextIndex_this_constructor_name;
|
|
11
|
+
this.id = Base.nextIndex[this.constructor.name] = 1 + ((_Base_nextIndex_this_constructor_name = Base.nextIndex[this.constructor.name]) != null ? _Base_nextIndex_this_constructor_name : 0);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
(()=>{
|
|
15
|
+
Base.nextIndex = {};
|
|
16
|
+
})();
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a parser function that parses a delimited string into a list of typed values.
|
|
20
|
+
* The parser function takes a string and returns an array of typed values.
|
|
21
|
+
*
|
|
22
|
+
* @param delimiter - The delimiter used to split the string into individual values.
|
|
23
|
+
* @param parser - The function used to parse each individual value in the string.
|
|
24
|
+
* @returns A function that takes a delimited string and returns an array of typed values.
|
|
25
|
+
* @template T - The type of the values in the list.
|
|
26
|
+
*/ function createTypedListParser(delimiter, parser) {
|
|
27
|
+
return function parseList(string) {
|
|
28
|
+
return string.split(delimiter).map((str)=>str.trim()).map(parser);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseInteger(string) {
|
|
33
|
+
const errMsg = 'Not an integer. Got: ';
|
|
34
|
+
if (string.includes('.')) throw new TypeError(errMsg + string);
|
|
35
|
+
string = string.replace(/[^-0-9]/g, '');
|
|
36
|
+
if (!string) throw new TypeError(errMsg + string);
|
|
37
|
+
const int = parseInt(string);
|
|
38
|
+
if (isNaN(int)) throw new TypeError(errMsg + string);
|
|
39
|
+
return int;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function parseNumber(string) {
|
|
43
|
+
if (string.endsWith('Infinity')) return Number(string);
|
|
44
|
+
string = string.replace(/[^-0-9.]/g, '');
|
|
45
|
+
if (!string) return NaN;
|
|
46
|
+
return Number(string);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function parseString(string) {
|
|
50
|
+
return string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class AbstractStringParserSelector extends Base {
|
|
54
|
+
string() {
|
|
55
|
+
return this.custom(parseString);
|
|
56
|
+
}
|
|
57
|
+
number() {
|
|
58
|
+
return this.custom(parseNumber);
|
|
59
|
+
}
|
|
60
|
+
integer() {
|
|
61
|
+
return this.custom(parseInteger);
|
|
62
|
+
}
|
|
63
|
+
delimitedStrings(delimiter = ',') {
|
|
64
|
+
return this.delimited(delimiter, parseString);
|
|
65
|
+
}
|
|
66
|
+
delimitedNumbers(delimiter = ',') {
|
|
67
|
+
return this.delimited(delimiter, parseNumber);
|
|
68
|
+
}
|
|
69
|
+
delimitedIntegers(delimiter = ',') {
|
|
70
|
+
return this.delimited(delimiter, parseInteger);
|
|
71
|
+
}
|
|
72
|
+
delimited(delimiter = ',', parser) {
|
|
73
|
+
return this.custom(createTypedListParser(delimiter, parser));
|
|
74
|
+
}
|
|
75
|
+
constructor(builder){
|
|
76
|
+
super();
|
|
77
|
+
this.builder = builder;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
class ArgumentParserSelector extends AbstractStringParserSelector {
|
|
82
|
+
custom(parser) {
|
|
83
|
+
this.builder.cmd.meta.argParsers[this.builder.index] = parser;
|
|
84
|
+
return this.builder;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
class ArgumentReader extends Base {
|
|
89
|
+
get $() {
|
|
90
|
+
return this.parent.$;
|
|
91
|
+
}
|
|
92
|
+
get argument() {
|
|
93
|
+
return this.$;
|
|
94
|
+
}
|
|
95
|
+
get name() {
|
|
96
|
+
return this.$.name();
|
|
97
|
+
}
|
|
98
|
+
get choices() {
|
|
99
|
+
return this.$.argChoices;
|
|
100
|
+
}
|
|
101
|
+
get default() {
|
|
102
|
+
return this.$.defaultValue;
|
|
103
|
+
}
|
|
104
|
+
get description() {
|
|
105
|
+
return this.$.description;
|
|
106
|
+
}
|
|
107
|
+
get variadic() {
|
|
108
|
+
return this.$.variadic;
|
|
109
|
+
}
|
|
110
|
+
get required() {
|
|
111
|
+
return this.$.required;
|
|
112
|
+
}
|
|
113
|
+
get optional() {
|
|
114
|
+
return !this.$.required;
|
|
115
|
+
}
|
|
116
|
+
constructor(parent){
|
|
117
|
+
super();
|
|
118
|
+
this.parent = parent;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Checks if the provided value is a named function.
|
|
124
|
+
*/ function isNamedFunction(func) {
|
|
125
|
+
return typeof func === 'function' && !!func.name;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function isNamedFunctionArray(array) {
|
|
129
|
+
return Array.isArray(array) && array.every(isNamedFunction);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Creates a validator function that checks whether the input is an array where all elements are valid according to every validator provided.
|
|
134
|
+
*
|
|
135
|
+
* @param validators - An array of validator functions.
|
|
136
|
+
* @param name - The name of the validator function. If not provided, the name will be derived from the validator functions.
|
|
137
|
+
*
|
|
138
|
+
* @throws TypeError - if no name is provided and not all validators are named functions.
|
|
139
|
+
*/ function createTypedArrayValidator(validators, name) {
|
|
140
|
+
if (!name) {
|
|
141
|
+
assertThat(validators, isNamedFunctionArray);
|
|
142
|
+
name = 'isArrayWhereEachIs' + validators.map((fun)=>strFirstCharToUpperCase(fun.name)).join('And');
|
|
143
|
+
}
|
|
144
|
+
return funSetName(name, function(array) {
|
|
145
|
+
return Array.isArray(array) && array.every((value)=>validators.every((isValid)=>isValid(value)));
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const isInteger = Number.isInteger;
|
|
150
|
+
|
|
151
|
+
function isNumber(value) {
|
|
152
|
+
return isValidNumber(value);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function isString(value) {
|
|
156
|
+
return typeof value === 'string';
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
class AbstractValidatorSelector extends Base {
|
|
160
|
+
isString() {
|
|
161
|
+
return this.custom(isString);
|
|
162
|
+
}
|
|
163
|
+
isNumber() {
|
|
164
|
+
return this.custom(isNumber);
|
|
165
|
+
}
|
|
166
|
+
isInteger() {
|
|
167
|
+
return this.custom(isInteger);
|
|
168
|
+
}
|
|
169
|
+
isStringArray() {
|
|
170
|
+
return this.arrayWhereEach(isString);
|
|
171
|
+
}
|
|
172
|
+
isNumberArray() {
|
|
173
|
+
return this.arrayWhereEach(isNumber);
|
|
174
|
+
}
|
|
175
|
+
isIntegerArray() {
|
|
176
|
+
return this.arrayWhereEach(isInteger);
|
|
177
|
+
}
|
|
178
|
+
arrayWhereEach(...validators) {
|
|
179
|
+
return this.custom(createTypedArrayValidator(validators));
|
|
180
|
+
}
|
|
181
|
+
constructor(builder){
|
|
182
|
+
super();
|
|
183
|
+
this.builder = builder;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
class ArgumentValidatorSelector extends AbstractValidatorSelector {
|
|
188
|
+
custom(validator) {
|
|
189
|
+
arrLast(this.builder.cmd.meta.argValidators).push(validator);
|
|
190
|
+
return this.builder;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function realizeLazyProperty(obj, key, value) {
|
|
195
|
+
Object.defineProperty(obj, key, {
|
|
196
|
+
enumerable: true,
|
|
197
|
+
writable: false,
|
|
198
|
+
configurable: false,
|
|
199
|
+
value
|
|
200
|
+
});
|
|
201
|
+
return value;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Wrapper around the @see Argument class, for more intuitive construction.
|
|
206
|
+
*/ class ArgumentBuilder extends Base {
|
|
207
|
+
description(string) {
|
|
208
|
+
this.$.description = string;
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
default(value, description) {
|
|
212
|
+
if (this.$.required) {
|
|
213
|
+
throw new Error('Cannot set default value on required argument: ' + this.$.name());
|
|
214
|
+
}
|
|
215
|
+
this.$.default(value, description);
|
|
216
|
+
return this;
|
|
217
|
+
}
|
|
218
|
+
choices(values) {
|
|
219
|
+
this.$.choices(values);
|
|
220
|
+
return this;
|
|
221
|
+
}
|
|
222
|
+
get parser() {
|
|
223
|
+
return new ArgumentParserSelector(this);
|
|
224
|
+
}
|
|
225
|
+
get validator() {
|
|
226
|
+
return new ArgumentValidatorSelector(this);
|
|
227
|
+
}
|
|
228
|
+
get get() {
|
|
229
|
+
return realizeLazyProperty(this, 'get', new ArgumentReader(this));
|
|
230
|
+
}
|
|
231
|
+
constructor(cmd, name){
|
|
232
|
+
super();
|
|
233
|
+
this.cmd = cmd;
|
|
234
|
+
this.$ = new Argument(name);
|
|
235
|
+
this.index = cmd.meta.argValidators.length;
|
|
236
|
+
cmd.meta.argValidators[this.index] = [];
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function assertCommandNameNotReserved(name) {
|
|
241
|
+
if (name === 'u' || name === 'util') {
|
|
242
|
+
throw new Error(`Name '${name}' is reserved and is not available as name or alias.`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function _extends() {
|
|
247
|
+
_extends = Object.assign || function assign(target) {
|
|
248
|
+
for(var i = 1; i < arguments.length; i++){
|
|
249
|
+
var source = arguments[i];
|
|
250
|
+
for(var key in source)if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
|
|
251
|
+
}
|
|
252
|
+
return target;
|
|
253
|
+
};
|
|
254
|
+
return _extends.apply(this, arguments);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
class CommandBuilderMetaData extends Base {
|
|
258
|
+
get argParsers() {
|
|
259
|
+
return realizeLazyProperty(this, 'argParsers', []);
|
|
260
|
+
}
|
|
261
|
+
get argValidators() {
|
|
262
|
+
return realizeLazyProperty(this, 'argValidators', []);
|
|
263
|
+
}
|
|
264
|
+
get optParsers() {
|
|
265
|
+
return realizeLazyProperty(this, 'optParsers', {});
|
|
266
|
+
}
|
|
267
|
+
get optValidators() {
|
|
268
|
+
return realizeLazyProperty(this, 'optValidators', {});
|
|
269
|
+
}
|
|
270
|
+
constructor(...args){
|
|
271
|
+
super(...args);
|
|
272
|
+
this.subcommands = [];
|
|
273
|
+
this.globalOptions = [];
|
|
274
|
+
this.hiddenGlobalOptions = new Set();
|
|
275
|
+
this.isNative = false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
*
|
|
281
|
+
*/ class CommandFeatureSelector extends Base {
|
|
282
|
+
debugToggle(info) {
|
|
283
|
+
this.cmd.outputDebugInfo('featureEnabled', ()=>info);
|
|
284
|
+
}
|
|
285
|
+
utils(boolean = true) {
|
|
286
|
+
if (this._utils === boolean) return this;
|
|
287
|
+
if (this.cmd.meta.isNative) throw new Error('Cannot configure utils for native command.');
|
|
288
|
+
this._utils = boolean;
|
|
289
|
+
this.debugToggle({
|
|
290
|
+
utils: boolean
|
|
291
|
+
});
|
|
292
|
+
return this;
|
|
293
|
+
}
|
|
294
|
+
config(boolean = true) {
|
|
295
|
+
if (this._config === boolean) return this;
|
|
296
|
+
if (this.cmd.meta.isNative) throw new Error('Cannot configure config for native command.');
|
|
297
|
+
if (this._config !== boolean) {
|
|
298
|
+
this._config = boolean;
|
|
299
|
+
this.debugToggle({
|
|
300
|
+
config: boolean
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
return this;
|
|
304
|
+
}
|
|
305
|
+
presets(boolean = true) {
|
|
306
|
+
if (this.cmd.meta.isNative) {
|
|
307
|
+
throw new Error('Cannot configure presets for native command.');
|
|
308
|
+
}
|
|
309
|
+
if (this._presets !== boolean) {
|
|
310
|
+
this._presets = boolean;
|
|
311
|
+
this.debugToggle({
|
|
312
|
+
presets: boolean
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
return this;
|
|
316
|
+
}
|
|
317
|
+
autoAssignMissingOptionFlags(boolean = true) {
|
|
318
|
+
if (this._autoAssignMissingOptionFlags !== boolean) {
|
|
319
|
+
this._autoAssignMissingOptionFlags = boolean;
|
|
320
|
+
this.debugToggle({
|
|
321
|
+
autoAssignMissingOptionFlags: boolean
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
return this;
|
|
325
|
+
}
|
|
326
|
+
autoAssignSubCommandAliases(boolean = true) {
|
|
327
|
+
if (this._autoAssignSubCommandAliases !== boolean) {
|
|
328
|
+
this._autoAssignSubCommandAliases = boolean;
|
|
329
|
+
this.debugToggle({
|
|
330
|
+
autoAssignSubCommandAliases: boolean
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
return this;
|
|
334
|
+
}
|
|
335
|
+
all(boolean = true) {
|
|
336
|
+
this.utils(boolean);
|
|
337
|
+
this.config(boolean);
|
|
338
|
+
this.presets(boolean);
|
|
339
|
+
this.autoAssignMissingOptionFlags(boolean);
|
|
340
|
+
this.autoAssignSubCommandAliases(boolean);
|
|
341
|
+
return this.cmd;
|
|
342
|
+
}
|
|
343
|
+
get isUtilsEnabled() {
|
|
344
|
+
if (this.cmd.meta.isNative) return false;
|
|
345
|
+
return this._utils;
|
|
346
|
+
}
|
|
347
|
+
get isConfigEnabled() {
|
|
348
|
+
if (this.cmd.meta.isNative) return false;
|
|
349
|
+
return this._config;
|
|
350
|
+
}
|
|
351
|
+
get isPresetsEnabled() {
|
|
352
|
+
if (this.cmd.meta.isNative) return false;
|
|
353
|
+
return this._presets;
|
|
354
|
+
}
|
|
355
|
+
get isAutoAssignMissingOptionFlagsEnabled() {
|
|
356
|
+
return this._autoAssignMissingOptionFlags;
|
|
357
|
+
}
|
|
358
|
+
get isAutoAssignSubCommandAliasesEnabled() {
|
|
359
|
+
return this._autoAssignSubCommandAliases;
|
|
360
|
+
}
|
|
361
|
+
constructor(cmd){
|
|
362
|
+
var _cmd_parent, _cmd;
|
|
363
|
+
super();
|
|
364
|
+
this.cmd = cmd;
|
|
365
|
+
this._utils = false;
|
|
366
|
+
this._config = false;
|
|
367
|
+
this._presets = false;
|
|
368
|
+
this._autoAssignMissingOptionFlags = false;
|
|
369
|
+
this._autoAssignSubCommandAliases = false;
|
|
370
|
+
const parent = (_cmd = cmd) == null ? void 0 : (_cmd_parent = _cmd.parent) == null ? void 0 : _cmd_parent.features;
|
|
371
|
+
if (parent) {
|
|
372
|
+
if (!this.cmd.meta.isNative) {
|
|
373
|
+
this._utils = parent._utils;
|
|
374
|
+
this._config = parent._config;
|
|
375
|
+
this._presets = parent._presets;
|
|
376
|
+
}
|
|
377
|
+
this._autoAssignMissingOptionFlags = parent._autoAssignMissingOptionFlags;
|
|
378
|
+
this._autoAssignSubCommandAliases = parent._autoAssignSubCommandAliases;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Returns an iterator that walks the command's ancestors, optionally starting with the command itself.
|
|
385
|
+
*/ function* walkAncestors(cmd, options) {
|
|
386
|
+
var _options;
|
|
387
|
+
if ((_options = options) == null ? void 0 : _options.includeSelf) yield cmd;
|
|
388
|
+
let node = cmd.parent;
|
|
389
|
+
while(node){
|
|
390
|
+
yield node;
|
|
391
|
+
node = node.parent;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Get a command's ancestors, optionally starting from the command itself.
|
|
397
|
+
*/ function getAncestors(cmd, options) {
|
|
398
|
+
return [
|
|
399
|
+
...walkAncestors(cmd, options)
|
|
400
|
+
];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function splitCombinedArgvShorts(argv) {
|
|
404
|
+
return argv.map((arg)=>{
|
|
405
|
+
if (arg.length < 3 || !arg.startsWith('-') || arg.startsWith('--') || arg.includes('=')) {
|
|
406
|
+
return arg;
|
|
407
|
+
}
|
|
408
|
+
return Array.from(arg.replace('-', '')).map((s)=>'-' + s);
|
|
409
|
+
}).flat();
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function getARGV(argv) {
|
|
413
|
+
if (argv) ARGV = splitCombinedArgvShorts(argv);
|
|
414
|
+
return ARGV.slice();
|
|
415
|
+
}
|
|
416
|
+
let ARGV = splitCombinedArgvShorts(process.argv.slice(2));
|
|
417
|
+
|
|
418
|
+
function getGlobalOptions(cmd) {
|
|
419
|
+
const result = [];
|
|
420
|
+
const ancestors = getAncestors(cmd, {
|
|
421
|
+
includeSelf: true
|
|
422
|
+
}).reverse();
|
|
423
|
+
for (const anc of ancestors){
|
|
424
|
+
for (const gopt of anc.meta.globalOptions){
|
|
425
|
+
if (!cmd.meta.hiddenGlobalOptions.has(gopt)) {
|
|
426
|
+
result.push(gopt);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return result;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
class ErrorParser {
|
|
434
|
+
get name() {
|
|
435
|
+
return this.error.name;
|
|
436
|
+
}
|
|
437
|
+
get message() {
|
|
438
|
+
return this.error.message;
|
|
439
|
+
}
|
|
440
|
+
get stack() {
|
|
441
|
+
return this.error.stack || '';
|
|
442
|
+
}
|
|
443
|
+
summary() {
|
|
444
|
+
return this.name + ': ' + this.message;
|
|
445
|
+
}
|
|
446
|
+
parseStackFrames() {
|
|
447
|
+
return errParseStack(this.stack);
|
|
448
|
+
}
|
|
449
|
+
prettyStack(parsedStackFrames) {
|
|
450
|
+
return errPrettyStack(this.error, parsedStackFrames);
|
|
451
|
+
}
|
|
452
|
+
toObject() {
|
|
453
|
+
return errToObject(this.error);
|
|
454
|
+
}
|
|
455
|
+
toJSON() {
|
|
456
|
+
return this.toObject();
|
|
457
|
+
}
|
|
458
|
+
constructor(error){
|
|
459
|
+
this.error = error instanceof Error ? error : new Error(String(error));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
function errPrettyStack(error, parsedStackFrames) {
|
|
463
|
+
const frames = parsedStackFrames != null ? parsedStackFrames : errParseStack(error.stack || '');
|
|
464
|
+
// width of the first column = the longest frame.cell string
|
|
465
|
+
const offset = 2 + frames.reduce((acc, frame)=>Math.max(acc, frame[0].length), 0);
|
|
466
|
+
// type and message
|
|
467
|
+
const result = [
|
|
468
|
+
colors.bold(colors.red(error.name)) + ': ' + ' '.repeat(Math.max(0, offset - error.name.length)) + colors.red(error.message)
|
|
469
|
+
];
|
|
470
|
+
// stack trace
|
|
471
|
+
result.push(colors.yellow('stack') + ':');
|
|
472
|
+
result.push(frames.map((frame)=>{
|
|
473
|
+
const [call, file] = frame;
|
|
474
|
+
let s = ' ';
|
|
475
|
+
let fp;
|
|
476
|
+
if (file.startsWith('node:')) {
|
|
477
|
+
s += colors.gray(call);
|
|
478
|
+
fp = colors.gray(file);
|
|
479
|
+
} else if (file.includes('node_modules')) {
|
|
480
|
+
s += call;
|
|
481
|
+
const base = path.basename(file.split(':')[0]);
|
|
482
|
+
fp = file.replace(base, colors.yellow(base));
|
|
483
|
+
} else {
|
|
484
|
+
s += call;
|
|
485
|
+
const base = path.basename(file.split(':')[0]);
|
|
486
|
+
fp = file.replace(base, colors.red(base));
|
|
487
|
+
}
|
|
488
|
+
s += ' '.repeat(offset - call.length);
|
|
489
|
+
s += fp;
|
|
490
|
+
return s;
|
|
491
|
+
}).join('\n'));
|
|
492
|
+
// other properties
|
|
493
|
+
const ignore = [
|
|
494
|
+
'name',
|
|
495
|
+
'message',
|
|
496
|
+
'frames',
|
|
497
|
+
'stack'
|
|
498
|
+
];
|
|
499
|
+
const keys = Object.getOwnPropertyNames(error).filter((key)=>!ignore.includes(key));
|
|
500
|
+
for (const k of keys){
|
|
501
|
+
if (typeof k === 'symbol') continue;
|
|
502
|
+
const key = k;
|
|
503
|
+
let s = colors.yellow(key.toString()) + ': ';
|
|
504
|
+
if (isPlainObject(error[key])) {
|
|
505
|
+
const json = JSON.stringify(error[key], null, 2);
|
|
506
|
+
if (json.length < 350) {
|
|
507
|
+
s += JSON.stringify(error[key], null, 2).split('\n').map((line)=>{
|
|
508
|
+
const arr = line.split('": ');
|
|
509
|
+
arr[0] = colors.gray(arr[0].replace('"', ''));
|
|
510
|
+
return arr.join(': ');
|
|
511
|
+
}).join('\n');
|
|
512
|
+
} else {
|
|
513
|
+
s += JSON.stringify(error[key]);
|
|
514
|
+
}
|
|
515
|
+
} else if (isPrimitive(error[key])) {
|
|
516
|
+
s += String(error[key]);
|
|
517
|
+
} else if (error[key] != null && typeof error[key] === 'object') {
|
|
518
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
519
|
+
s += error[key].toString();
|
|
520
|
+
} else {
|
|
521
|
+
s += JSON.stringify(error[key]);
|
|
522
|
+
}
|
|
523
|
+
result.push(s.trim());
|
|
524
|
+
}
|
|
525
|
+
return '\n' + result.join('\n') + '\n';
|
|
526
|
+
}
|
|
527
|
+
function errParseStack(stack) {
|
|
528
|
+
if (!stack) return [];
|
|
529
|
+
const nodeRe = /^\s*at (?:((?:\[object object\])?[^\\/]+(?: \[as \S+\])?) )?\(?(.*?):(\d+)(?::(\d+))?\)?\s*$/i;
|
|
530
|
+
const recwd = new RegExp('^' + regexEscapeString(process.cwd() + path.sep), 'i');
|
|
531
|
+
const frames = [];
|
|
532
|
+
for (const line of stack.split('\n')){
|
|
533
|
+
const parts = nodeRe.exec(line);
|
|
534
|
+
if (!parts) continue;
|
|
535
|
+
frames.push([
|
|
536
|
+
parts[1] || '<unknown>',
|
|
537
|
+
`${(parts[2] || '').replace(recwd, '').replace(/\\\\?/g, '/')}:${+parts[3]}:${parts[4] ? +parts[4] : null}`
|
|
538
|
+
]);
|
|
539
|
+
}
|
|
540
|
+
return frames;
|
|
541
|
+
}
|
|
542
|
+
function errToObject(error) {
|
|
543
|
+
const own = Object.getOwnPropertyNames(error).filter((key)=>typeof key === 'string');
|
|
544
|
+
const keys = new Set([
|
|
545
|
+
'name',
|
|
546
|
+
'message',
|
|
547
|
+
'frames',
|
|
548
|
+
...own
|
|
549
|
+
]);
|
|
550
|
+
keys.delete('stack');
|
|
551
|
+
const entries = Array.from(keys).filter((key)=>error[key] !== undefined).map((key)=>[
|
|
552
|
+
key,
|
|
553
|
+
error[key]
|
|
554
|
+
]);
|
|
555
|
+
return Object.fromEntries(entries);
|
|
556
|
+
} // try {
|
|
557
|
+
// throw new TypeError('wow ok oops')
|
|
558
|
+
// } catch (error) {
|
|
559
|
+
// const err = new ErrorParser(error)
|
|
560
|
+
// console.log('----------------------')
|
|
561
|
+
// console.log({
|
|
562
|
+
// name: err.name,
|
|
563
|
+
// message: err.message,
|
|
564
|
+
// summary: err.summary,
|
|
565
|
+
// })
|
|
566
|
+
// console.log('----------------------')
|
|
567
|
+
// console.log(err.stack)
|
|
568
|
+
// console.log('----------------------')
|
|
569
|
+
// console.log(err.prettyStack())
|
|
570
|
+
// console.log('----------------------')
|
|
571
|
+
// console.log(err.toObject())
|
|
572
|
+
// console.log('----------------------')
|
|
573
|
+
// }
|
|
574
|
+
|
|
575
|
+
function _class_private_field_loose_base(receiver, privateKey) {
|
|
576
|
+
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
|
|
577
|
+
throw new TypeError("attempted to use private field on non-instance");
|
|
578
|
+
}
|
|
579
|
+
return receiver;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
var id = 0;
|
|
583
|
+
function _class_private_field_loose_key(name) {
|
|
584
|
+
return "__private_" + id++ + "_" + name;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
var _defaultDescriptor = /*#__PURE__*/ _class_private_field_loose_key("_defaultDescriptor"), _noop = /*#__PURE__*/ _class_private_field_loose_key("_noop"), _memoized = /*#__PURE__*/ _class_private_field_loose_key("_memoized"), _obj = /*#__PURE__*/ _class_private_field_loose_key("_obj"), _key = /*#__PURE__*/ _class_private_field_loose_key("_key"), _original = /*#__PURE__*/ _class_private_field_loose_key("_original"), _hasOwn = /*#__PURE__*/ _class_private_field_loose_key("_hasOwn"), _originalDescriptor = /*#__PURE__*/ _class_private_field_loose_key("_originalDescriptor"), _noopDescriptor = /*#__PURE__*/ _class_private_field_loose_key("_noopDescriptor"), _isEnabled = /*#__PURE__*/ _class_private_field_loose_key("_isEnabled");
|
|
588
|
+
/**
|
|
589
|
+
* A class that creates an object with methods for disabling/enabling a given method on a given object.
|
|
590
|
+
*
|
|
591
|
+
* @example
|
|
592
|
+
* const md = new MethodDisabler(process.stdout, 'write')
|
|
593
|
+
*
|
|
594
|
+
* md.disable()
|
|
595
|
+
* assert(!md.isEnabled)
|
|
596
|
+
* console.log('This will not print')
|
|
597
|
+
*
|
|
598
|
+
* md.enable()
|
|
599
|
+
* assert(md.isEnabled)
|
|
600
|
+
* console.log('This will print')
|
|
601
|
+
*
|
|
602
|
+
* assert(md.original === process.stdout.write)
|
|
603
|
+
*/ class MethodDisabler extends Base {
|
|
604
|
+
/**
|
|
605
|
+
* Disable the method.
|
|
606
|
+
*/ disable() {
|
|
607
|
+
if (!this.isEnabled) return;
|
|
608
|
+
Object.defineProperty(_class_private_field_loose_base(this, _obj)[_obj], _class_private_field_loose_base(this, _key)[_key], _class_private_field_loose_base(this, _noopDescriptor)[_noopDescriptor]);
|
|
609
|
+
_class_private_field_loose_base(this, _isEnabled)[_isEnabled] = false;
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Enable the method.
|
|
613
|
+
*/ enable() {
|
|
614
|
+
if (this.isEnabled) return;
|
|
615
|
+
if (_class_private_field_loose_base(this, _hasOwn)[_hasOwn]) Object.defineProperty(_class_private_field_loose_base(this, _obj)[_obj], _class_private_field_loose_base(this, _key)[_key], _class_private_field_loose_base(this, _originalDescriptor)[_originalDescriptor]);
|
|
616
|
+
else delete _class_private_field_loose_base(this, _obj)[_obj][_class_private_field_loose_base(this, _key)[_key]];
|
|
617
|
+
_class_private_field_loose_base(this, _isEnabled)[_isEnabled] = true;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* The original method before it was disabled.
|
|
621
|
+
*/ get original() {
|
|
622
|
+
return _class_private_field_loose_base(this, _original)[_original];
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Whether the method is currently enabled.
|
|
626
|
+
*/ get isEnabled() {
|
|
627
|
+
return _class_private_field_loose_base(this, _isEnabled)[_isEnabled];
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* @param obj - The object on which the method is defined.
|
|
631
|
+
* @param key - The property name of the method.
|
|
632
|
+
*/ constructor(obj, key){
|
|
633
|
+
var _class_private_field_loose_base__memoized_get;
|
|
634
|
+
super();
|
|
635
|
+
Object.defineProperty(this, _obj, {
|
|
636
|
+
writable: true,
|
|
637
|
+
value: void 0
|
|
638
|
+
});
|
|
639
|
+
Object.defineProperty(this, _key, {
|
|
640
|
+
writable: true,
|
|
641
|
+
value: void 0
|
|
642
|
+
});
|
|
643
|
+
Object.defineProperty(this, _original, {
|
|
644
|
+
writable: true,
|
|
645
|
+
value: void 0
|
|
646
|
+
});
|
|
647
|
+
Object.defineProperty(this, _hasOwn, {
|
|
648
|
+
writable: true,
|
|
649
|
+
value: void 0
|
|
650
|
+
});
|
|
651
|
+
Object.defineProperty(this, _originalDescriptor, {
|
|
652
|
+
writable: true,
|
|
653
|
+
value: void 0
|
|
654
|
+
});
|
|
655
|
+
Object.defineProperty(this, _noopDescriptor, {
|
|
656
|
+
writable: true,
|
|
657
|
+
value: void 0
|
|
658
|
+
});
|
|
659
|
+
Object.defineProperty(this, _isEnabled, {
|
|
660
|
+
writable: true,
|
|
661
|
+
value: void 0
|
|
662
|
+
});
|
|
663
|
+
_class_private_field_loose_base(this, _isEnabled)[_isEnabled] = true;
|
|
664
|
+
assertThat(obj[key], isFunction);
|
|
665
|
+
_class_private_field_loose_base(this, _obj)[_obj] = obj;
|
|
666
|
+
_class_private_field_loose_base(this, _key)[_key] = key;
|
|
667
|
+
_class_private_field_loose_base(this, _original)[_original] = obj[key];
|
|
668
|
+
_class_private_field_loose_base(this, _hasOwn)[_hasOwn] = Object.hasOwn(obj, key);
|
|
669
|
+
_class_private_field_loose_base(this, _originalDescriptor)[_originalDescriptor] = _class_private_field_loose_base(this, _hasOwn)[_hasOwn] ? Object.getOwnPropertyDescriptor(obj, key) : _class_private_field_loose_base(MethodDisabler, _defaultDescriptor)[_defaultDescriptor](_class_private_field_loose_base(this, _original)[_original]);
|
|
670
|
+
_class_private_field_loose_base(this, _noopDescriptor)[_noopDescriptor] = Object.assign({}, _class_private_field_loose_base(this, _originalDescriptor)[_originalDescriptor], {
|
|
671
|
+
value: _class_private_field_loose_base(MethodDisabler, _noop)[_noop]
|
|
672
|
+
});
|
|
673
|
+
// return stored if memoized
|
|
674
|
+
if ((_class_private_field_loose_base__memoized_get = _class_private_field_loose_base(MethodDisabler, _memoized)[_memoized].get(obj)) == null ? void 0 : _class_private_field_loose_base__memoized_get.has(key)) {
|
|
675
|
+
var _class_private_field_loose_base__memoized_get1;
|
|
676
|
+
return (_class_private_field_loose_base__memoized_get1 = _class_private_field_loose_base(MethodDisabler, _memoized)[_memoized].get(obj)) == null ? void 0 : _class_private_field_loose_base__memoized_get1.get(key);
|
|
677
|
+
}
|
|
678
|
+
//memoize
|
|
679
|
+
if (!_class_private_field_loose_base(MethodDisabler, _memoized)[_memoized].has(obj)) _class_private_field_loose_base(MethodDisabler, _memoized)[_memoized].set(obj, new Map());
|
|
680
|
+
const methods = _class_private_field_loose_base(MethodDisabler, _memoized)[_memoized].get(obj);
|
|
681
|
+
methods.set(key, this);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
Object.defineProperty(MethodDisabler, _defaultDescriptor, {
|
|
685
|
+
value: defaultDescriptor
|
|
686
|
+
});
|
|
687
|
+
Object.defineProperty(MethodDisabler, _noop, {
|
|
688
|
+
writable: true,
|
|
689
|
+
value: (...args)=>void 0
|
|
690
|
+
});
|
|
691
|
+
Object.defineProperty(MethodDisabler, _memoized, {
|
|
692
|
+
writable: true,
|
|
693
|
+
value: new WeakMap()
|
|
694
|
+
});
|
|
695
|
+
function defaultDescriptor(value) {
|
|
696
|
+
return {
|
|
697
|
+
value,
|
|
698
|
+
writable: true,
|
|
699
|
+
enumerable: true,
|
|
700
|
+
configurable: true
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const stdout = new MethodDisabler(process.stdout, 'write');
|
|
705
|
+
const stderr = new MethodDisabler(process.stderr, 'write');
|
|
706
|
+
const debug = new MethodDisabler(console, 'debug');
|
|
707
|
+
debug.disable();
|
|
708
|
+
/**
|
|
709
|
+
*
|
|
710
|
+
*/ class OutputManager {
|
|
711
|
+
static getInstance() {
|
|
712
|
+
if (!this.instance) {
|
|
713
|
+
this.instance = new OutputManager();
|
|
714
|
+
}
|
|
715
|
+
return this.instance;
|
|
716
|
+
}
|
|
717
|
+
reset() {
|
|
718
|
+
this.colors.enable();
|
|
719
|
+
this.stdout.enable();
|
|
720
|
+
this.stderr.enable();
|
|
721
|
+
this.debug.disable();
|
|
722
|
+
return this;
|
|
723
|
+
}
|
|
724
|
+
outputDebug(fn) {
|
|
725
|
+
if (this.debug.isEnabled) console.debug(fn());
|
|
726
|
+
else this.debugMsgs.push(fn);
|
|
727
|
+
}
|
|
728
|
+
outputDebugMessages() {
|
|
729
|
+
this.debugMsgs.forEach((fn)=>console.debug(fn()));
|
|
730
|
+
this.debugMsgs = [];
|
|
731
|
+
}
|
|
732
|
+
constructor(){
|
|
733
|
+
this.colors = colors;
|
|
734
|
+
this.stdout = stdout;
|
|
735
|
+
this.stderr = stderr;
|
|
736
|
+
this.debug = debug;
|
|
737
|
+
this.debugMsgs = [];
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function handleError(cmd, error) {
|
|
742
|
+
const parsed = new ErrorParser(error);
|
|
743
|
+
if (OutputManager.getInstance().debug.isEnabled) {
|
|
744
|
+
console.error(parsed.prettyStack());
|
|
745
|
+
} else {
|
|
746
|
+
cmd.outputUserError(parsed.summary());
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function handleOutputOptions(cmd) {
|
|
751
|
+
const opts = cmd.$.optsWithGlobals();
|
|
752
|
+
const om = OutputManager.getInstance().reset();
|
|
753
|
+
if (opts['disableColor']) om.colors.disable();
|
|
754
|
+
if (opts['disableStderr']) om.stderr.disable();
|
|
755
|
+
if (opts['disableStdout']) om.stdout.disable();
|
|
756
|
+
if (opts['debug']) {
|
|
757
|
+
om.debug.enable();
|
|
758
|
+
om.outputDebugMessages();
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function debugLogArgsOpts(cb, args, opts, presetArgs, presetOpts, presetOrder) {
|
|
763
|
+
if (opts['debug']) {
|
|
764
|
+
cb.outputDebugInfo('action', ()=>{
|
|
765
|
+
return _extends({}, cb.features.isPresetsEnabled ? {
|
|
766
|
+
presetOrder,
|
|
767
|
+
presetArgs,
|
|
768
|
+
presetOpts
|
|
769
|
+
} : {}, {
|
|
770
|
+
args,
|
|
771
|
+
opts,
|
|
772
|
+
command: [
|
|
773
|
+
cb.root.name,
|
|
774
|
+
...getARGV()
|
|
775
|
+
].join(' ')
|
|
776
|
+
});
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function getPresetArgsAndOpts(cmd) {
|
|
782
|
+
if (!cmd.features.isPresetsEnabled) return [
|
|
783
|
+
[],
|
|
784
|
+
[],
|
|
785
|
+
[]
|
|
786
|
+
];
|
|
787
|
+
const presets = cmd.db.presets.getAll();
|
|
788
|
+
const opts = cmd.$.optsWithGlobals();
|
|
789
|
+
const selectedPresets = Object.keys(presets).filter((name)=>opts[name] === true);
|
|
790
|
+
const order = new Set();
|
|
791
|
+
for (const name of [
|
|
792
|
+
'defaults',
|
|
793
|
+
...selectedPresets
|
|
794
|
+
]){
|
|
795
|
+
for (const n of presets[name].presets.concat(name)){
|
|
796
|
+
if (order.has(n)) order.delete(n);
|
|
797
|
+
order.add(n);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const presetOrder = [
|
|
801
|
+
...order
|
|
802
|
+
];
|
|
803
|
+
const presetArgs = presetOrder.map((name)=>presets[name].args);
|
|
804
|
+
const presetOpts = presetOrder.map((name)=>presets[name].options);
|
|
805
|
+
return [
|
|
806
|
+
presetArgs,
|
|
807
|
+
presetOpts,
|
|
808
|
+
presetOrder
|
|
809
|
+
];
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
function createArrayMerger(predicate) {
|
|
813
|
+
return function arrMerge(target, ...sources) {
|
|
814
|
+
for (const src of sources){
|
|
815
|
+
for(let i = 0; i < src.length; i++){
|
|
816
|
+
if (predicate(src[i], i, src)) {
|
|
817
|
+
target[i] = src[i];
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return target;
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const arrAssign = createArrayMerger((value)=>value != null);
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Validate ALREADY PARSED args using the validators defined in the command builder.
|
|
829
|
+
*/ function assertValidArguments(cmd, parsedArgs) {
|
|
830
|
+
const last = cmd.arguments.length - 1;
|
|
831
|
+
parsedArgs.forEach((arg, i)=>{
|
|
832
|
+
if (arg == null) return;
|
|
833
|
+
const index = i > last ? last : i;
|
|
834
|
+
const validators = cmd.meta.argValidators[index];
|
|
835
|
+
if (!validators) return;
|
|
836
|
+
for (const isValid of validators){
|
|
837
|
+
assertThat(arg, isValid);
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
return parsedArgs;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Returns whether a command's last argument is variadic.
|
|
845
|
+
*/ function hasVariadicArguments(cmd) {
|
|
846
|
+
if (!cmd.arguments.length) return false;
|
|
847
|
+
return arrLast(cmd.arguments).variadic;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
function combineVariadicArgs(cmd, result) {
|
|
851
|
+
if (hasVariadicArguments(cmd) && result.length && !Array.isArray(arrLast(result))) {
|
|
852
|
+
const rest = result.splice(cmd.arguments.length - 1);
|
|
853
|
+
result.push(rest.filter((arg)=>arg != null));
|
|
854
|
+
}
|
|
855
|
+
return result;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
function padArgsWithUndefinedUntilExpectedLength(cmd, args) {
|
|
859
|
+
while(args.length < cmd.arguments.length)args.push(undefined);
|
|
860
|
+
return args;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
function parseArguments(cmd, args) {
|
|
864
|
+
const $ = cmd.$;
|
|
865
|
+
const last = $.registeredArguments.length - 1;
|
|
866
|
+
return args.map((arg, i)=>{
|
|
867
|
+
if (!arg) return arg;
|
|
868
|
+
const parse = cmd.meta.argParsers[i > last ? last : i];
|
|
869
|
+
return parse ? parse(arg) : arg;
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
function parsedValidArgsWithPresets(cmd, presetArgs) {
|
|
874
|
+
const result = arrAssign([], ...presetArgs, parseArguments(cmd, cmd.$.args));
|
|
875
|
+
combineVariadicArgs(cmd, result);
|
|
876
|
+
assertValidArguments(cmd, result);
|
|
877
|
+
return padArgsWithUndefinedUntilExpectedLength(cmd, result);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Validate ALREADY PARSED options using the validators defined in the command builder.
|
|
882
|
+
*/ function assertValidOptions(cb, parsedOptions) {
|
|
883
|
+
for (const [key, value] of Object.entries(parsedOptions)){
|
|
884
|
+
if (!cb.meta.optValidators[key]) continue;
|
|
885
|
+
if (value == null) continue;
|
|
886
|
+
for (const isValid of cb.meta.optValidators[key]){
|
|
887
|
+
assertThat(value, isValid);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return parsedOptions;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function getOwnAndGlobalOptions(cmd) {
|
|
894
|
+
return cmd.options.concat(getGlobalOptions(cmd));
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
function deleteOptionsWithDefaultOrNoValue(cmd, opts) {
|
|
898
|
+
const names = new Set(getOwnAndGlobalOptions(cmd).map((o)=>o.attributeName()));
|
|
899
|
+
for (const [key, value] of Object.entries(opts)){
|
|
900
|
+
if (!names.has(key) || value === false || value == null) {
|
|
901
|
+
delete opts[key];
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
return opts;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Parses (and validates) options using the parsers defined in the command builder.
|
|
909
|
+
*/ function parseOptions(cmd, opts) {
|
|
910
|
+
for (const [key, value] of Object.entries(opts)){
|
|
911
|
+
const parse = cmd.meta.optParsers[key];
|
|
912
|
+
opts[key] = parse ? parse(value) : value;
|
|
913
|
+
}
|
|
914
|
+
return opts;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
function optsWithGlobalsParsed(cmd) {
|
|
918
|
+
const $ = cmd.$;
|
|
919
|
+
return parseOptions(cmd, $.optsWithGlobals());
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function parsedValidOptsWithPresets(cb, presetOpts) {
|
|
923
|
+
const parsed = optsWithGlobalsParsed(cb);
|
|
924
|
+
const opts = presetOpts.length ? Object.assign({}, ...presetOpts, parsed) : parsed;
|
|
925
|
+
deleteOptionsWithDefaultOrNoValue(cb, opts);
|
|
926
|
+
assertValidOptions(cb, opts);
|
|
927
|
+
return opts;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
function parsedValidArgsOptsWithPresets(cmd) {
|
|
931
|
+
const [presetArgs, presetOpts, presetOrder] = getPresetArgsAndOpts(cmd);
|
|
932
|
+
const args = parsedValidArgsWithPresets(cmd, presetArgs);
|
|
933
|
+
const opts = parsedValidOptsWithPresets(cmd, presetOpts);
|
|
934
|
+
debugLogArgsOpts(cmd, args, opts, presetArgs, presetOpts, presetOrder);
|
|
935
|
+
return [
|
|
936
|
+
args,
|
|
937
|
+
opts
|
|
938
|
+
];
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
*
|
|
943
|
+
*/ function actionWrapper(cmd) {
|
|
944
|
+
const meta = cmd.meta;
|
|
945
|
+
cmd.$.action(async ()=>{
|
|
946
|
+
handleOutputOptions(cmd);
|
|
947
|
+
const [args, opts] = parsedValidArgsOptsWithPresets(cmd);
|
|
948
|
+
if (opts['help'] || !meta.actionHandler) return cmd.outputHelp();
|
|
949
|
+
return await meta.actionHandler(...args, opts, cmd).catch((err)=>handleError(cmd, err));
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
function getClosestNonNativeParent(cmd) {
|
|
954
|
+
for (const anc of walkAncestors(cmd, {
|
|
955
|
+
includeSelf: true
|
|
956
|
+
})){
|
|
957
|
+
if (!anc.meta.isNative) return anc;
|
|
958
|
+
}
|
|
959
|
+
return cmd.root;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function addConfigCommands(cmd) {
|
|
963
|
+
cmd.nativeCommand('config', createConfig);
|
|
964
|
+
}
|
|
965
|
+
const createConfig = (()=>{
|
|
966
|
+
return function createConfig(config) {
|
|
967
|
+
config.description('Manage configuration file.');
|
|
968
|
+
config.alias('c');
|
|
969
|
+
config.nativeCommand('edit', createEdit);
|
|
970
|
+
config.nativeCommand('list', createList);
|
|
971
|
+
config.nativeCommand('get', createGet);
|
|
972
|
+
config.nativeCommand('set', createSet);
|
|
973
|
+
config.nativeCommand('reset', createReset);
|
|
974
|
+
};
|
|
975
|
+
function createEdit(edit) {
|
|
976
|
+
edit.description('Edit as JSON in a text editor.');
|
|
977
|
+
edit.option('--editor [cmd]', (e)=>{
|
|
978
|
+
e.description('The command to launch your preferred text editor.');
|
|
979
|
+
e.default(defaultOpenInEditorCommand());
|
|
980
|
+
});
|
|
981
|
+
edit.action(editAction);
|
|
982
|
+
}
|
|
983
|
+
async function editAction(opts, edit) {
|
|
984
|
+
const cmd = getClosestNonNativeParent(edit);
|
|
985
|
+
cmd.db.config.edit(opts.editor);
|
|
986
|
+
console.info(cmd.db.config.getAll());
|
|
987
|
+
}
|
|
988
|
+
function createList(list) {
|
|
989
|
+
list.description('Print entire config with values, descriptions and defaults.');
|
|
990
|
+
list.action(listAction);
|
|
991
|
+
}
|
|
992
|
+
async function listAction(_, list) {
|
|
993
|
+
const cmd = getClosestNonNativeParent(list);
|
|
994
|
+
console.dir(cmd.db.config.keys.map((key)=>{
|
|
995
|
+
return {
|
|
996
|
+
key,
|
|
997
|
+
description: cmd.db.config.descriptions[key],
|
|
998
|
+
value: cmd.db.config.get(key),
|
|
999
|
+
defaultValue: cmd.db.config.defaultValues
|
|
1000
|
+
};
|
|
1001
|
+
}));
|
|
1002
|
+
}
|
|
1003
|
+
function createGet(get) {
|
|
1004
|
+
get.description('Print value(s) from the config.');
|
|
1005
|
+
get.argument('[key]', (a)=>{
|
|
1006
|
+
a.description('The key to print the value of. Omit to print all values.');
|
|
1007
|
+
a.choices(a.cmd.db.config.keys);
|
|
1008
|
+
});
|
|
1009
|
+
get.action(getAction);
|
|
1010
|
+
}
|
|
1011
|
+
async function getAction(key, get) {
|
|
1012
|
+
const cmd = getClosestNonNativeParent(get);
|
|
1013
|
+
if (key) console.log(cmd.db.config.get(key));
|
|
1014
|
+
else console.log(cmd.db.config.getAll());
|
|
1015
|
+
}
|
|
1016
|
+
function createSet(set) {
|
|
1017
|
+
set.description('Set a value in the config.');
|
|
1018
|
+
set.argument('<key>', (a)=>{
|
|
1019
|
+
a.description('The key to set the value of.');
|
|
1020
|
+
a.choices(a.cmd.db.config.keys);
|
|
1021
|
+
});
|
|
1022
|
+
set.argument('<value>', (a)=>a.description('The new value.'));
|
|
1023
|
+
set.action(setAction);
|
|
1024
|
+
}
|
|
1025
|
+
async function setAction(key, value, set) {
|
|
1026
|
+
const cmd = getClosestNonNativeParent(set);
|
|
1027
|
+
const from = cmd.db.config.get(key);
|
|
1028
|
+
const to = cmd.db.config.parsers[key](value);
|
|
1029
|
+
cmd.db.config.set(key, to);
|
|
1030
|
+
console.info({
|
|
1031
|
+
changed: key,
|
|
1032
|
+
from,
|
|
1033
|
+
to
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
function createReset(reset) {
|
|
1037
|
+
reset.description('Reset to defaults.');
|
|
1038
|
+
reset.argument('[key]', (a)=>{
|
|
1039
|
+
a.description('The key for which to reset the value. Omit to reset entire config.');
|
|
1040
|
+
a.choices(a.cmd.db.config.keys);
|
|
1041
|
+
});
|
|
1042
|
+
reset.action(resetAction);
|
|
1043
|
+
}
|
|
1044
|
+
async function resetAction(key, reset) {
|
|
1045
|
+
var _reset_parent;
|
|
1046
|
+
const cmd = (_reset_parent = reset.parent) == null ? void 0 : _reset_parent.parent;
|
|
1047
|
+
if (key) cmd.db.config.reset(key);
|
|
1048
|
+
else cmd.db.config.resetAll();
|
|
1049
|
+
console.info(cmd.db.config.getAll());
|
|
1050
|
+
}
|
|
1051
|
+
})();
|
|
1052
|
+
|
|
1053
|
+
function addPresetsCommands(cmd) {
|
|
1054
|
+
cmd.nativeCommand('presets', createPresets);
|
|
1055
|
+
}
|
|
1056
|
+
const createPresets = (()=>{
|
|
1057
|
+
return function createPresets(presets) {
|
|
1058
|
+
const cmd = getClosestNonNativeParent(presets);
|
|
1059
|
+
for (const [name, pre] of Object.entries(cmd.db.presets.getAll())){
|
|
1060
|
+
if (name === 'defaults') continue;
|
|
1061
|
+
cmd.option(`--${name}`, (opt)=>{
|
|
1062
|
+
opt.description('[Preset]: ' + pre.description);
|
|
1063
|
+
if (pre.presets.length) {
|
|
1064
|
+
opt.implies(pre.presets.reduce((acc, key)=>{
|
|
1065
|
+
acc[key] = true;
|
|
1066
|
+
return acc;
|
|
1067
|
+
}, {}));
|
|
1068
|
+
}
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
presets.alias('p');
|
|
1072
|
+
presets.description('Edit presets in your text editor', '', 'A preset consists of pre-set arguments and/or options for a command.', 'Additionally, a preset can have other presets as dependencies.', 'When running the command, multiple presets can be stacked.', 'Required arguments cannot be pre-set.');
|
|
1073
|
+
presets.nativeCommand('edit', createEdit);
|
|
1074
|
+
presets.nativeCommand('list', createList);
|
|
1075
|
+
};
|
|
1076
|
+
function createEdit(edit) {
|
|
1077
|
+
edit.description('Edit as JSON in a text editor.');
|
|
1078
|
+
edit.option('--editor [cmd]', (e)=>{
|
|
1079
|
+
e.description('The command to launch your preferred text editor.');
|
|
1080
|
+
e.default(defaultOpenInEditorCommand());
|
|
1081
|
+
});
|
|
1082
|
+
edit.action(editAction);
|
|
1083
|
+
}
|
|
1084
|
+
async function editAction(opts, edit) {
|
|
1085
|
+
const cmd = getClosestNonNativeParent(edit);
|
|
1086
|
+
cmd.db.presets.edit(opts.editor);
|
|
1087
|
+
console.info(cmd.db.presets.getAll());
|
|
1088
|
+
}
|
|
1089
|
+
function createList(list) {
|
|
1090
|
+
list.description('List all presets.');
|
|
1091
|
+
list.action(listAction);
|
|
1092
|
+
}
|
|
1093
|
+
async function listAction(_, list) {
|
|
1094
|
+
const cmd = getClosestNonNativeParent(list);
|
|
1095
|
+
console.dir(cmd.db.presets.getAll(), {
|
|
1096
|
+
depth: null
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
})();
|
|
1100
|
+
|
|
1101
|
+
function formatTableForTerminal(rows, headers) {
|
|
1102
|
+
if (!rows.length || !rows[0].length) return '';
|
|
1103
|
+
const table = new Table();
|
|
1104
|
+
if (headers && headers.length) {
|
|
1105
|
+
table.push(headers.map((s)=>colors.bold(colors.yellow(s))));
|
|
1106
|
+
}
|
|
1107
|
+
for (const row of rows)table.push(row);
|
|
1108
|
+
return table.toString();
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/**
|
|
1112
|
+
* Get a commands prefix array based on all its parent/ancestor commands.
|
|
1113
|
+
*/ function prefixArray(cmd) {
|
|
1114
|
+
return getAncestors(cmd, {
|
|
1115
|
+
includeSelf: true
|
|
1116
|
+
}).reverse().map((node)=>node.name);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
function getJsonFilepath(cmd) {
|
|
1120
|
+
return path.join(CommandBuilder.dataDirectory, prefixArray(cmd).join('-')) + '.json';
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* Get a commands prefix string based on all its parent/ancestor commands.
|
|
1125
|
+
*/ function prefixString(cmd) {
|
|
1126
|
+
return prefixArray(cmd).join(' ');
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
function* walkChildren(cmd, options) {
|
|
1130
|
+
var _options;
|
|
1131
|
+
if ((_options = options) == null ? void 0 : _options.includeSelf) yield cmd;
|
|
1132
|
+
for (const sub of cmd.meta.subcommands){
|
|
1133
|
+
yield sub;
|
|
1134
|
+
yield* walkChildren(sub);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/**
|
|
1139
|
+
* Returns a command's and its children's prefix strings.
|
|
1140
|
+
*/ function prefixStringsRecursive(cmd, filter) {
|
|
1141
|
+
const result = [];
|
|
1142
|
+
for (const c of walkChildren(cmd, {
|
|
1143
|
+
includeSelf: true
|
|
1144
|
+
})){
|
|
1145
|
+
const prefix = prefixString(c);
|
|
1146
|
+
if (filter && !filter(prefix, c)) continue;
|
|
1147
|
+
result.push([
|
|
1148
|
+
prefix,
|
|
1149
|
+
c.get.summary
|
|
1150
|
+
]);
|
|
1151
|
+
}
|
|
1152
|
+
return result;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
function addUtilCommands(cmd) {
|
|
1156
|
+
cmd.nativeCommand('util', createUtil);
|
|
1157
|
+
}
|
|
1158
|
+
const createUtil = (()=>{
|
|
1159
|
+
return function createUtil(util) {
|
|
1160
|
+
util.$.alias('u');
|
|
1161
|
+
util.description('Utility commands.');
|
|
1162
|
+
const cmd = getClosestNonNativeParent(util);
|
|
1163
|
+
if (cmd.features.isConfigEnabled) {
|
|
1164
|
+
addConfigCommands(util);
|
|
1165
|
+
}
|
|
1166
|
+
if (cmd.features.isPresetsEnabled && cmd.meta.actionHandler) {
|
|
1167
|
+
addPresetsCommands(util);
|
|
1168
|
+
}
|
|
1169
|
+
if (hasGrandChildren(cmd) || usesJsonFile(cmd)) {
|
|
1170
|
+
util.nativeCommand('tree', createTree);
|
|
1171
|
+
}
|
|
1172
|
+
if (usesJsonFile(cmd)) {
|
|
1173
|
+
util.nativeCommand('filepath', createFilepath);
|
|
1174
|
+
}
|
|
1175
|
+
};
|
|
1176
|
+
function hasGrandChildren(c) {
|
|
1177
|
+
return c.meta.subcommands.some((c)=>!!c.meta.subcommands.length);
|
|
1178
|
+
}
|
|
1179
|
+
function usesJsonFile(c) {
|
|
1180
|
+
return c.features.isConfigEnabled || c.features.isPresetsEnabled;
|
|
1181
|
+
}
|
|
1182
|
+
function createFilepath(fp) {
|
|
1183
|
+
fp.description('Print filepath to JSON file containing user data, eg. config and presets.');
|
|
1184
|
+
fp.action(filepathAction);
|
|
1185
|
+
}
|
|
1186
|
+
async function filepathAction(_, fp) {
|
|
1187
|
+
const cmd = getClosestNonNativeParent(fp);
|
|
1188
|
+
console.log(getJsonFilepath(cmd));
|
|
1189
|
+
}
|
|
1190
|
+
function createTree(tree) {
|
|
1191
|
+
tree.description('List nested subcommands.');
|
|
1192
|
+
tree.action(treeAction);
|
|
1193
|
+
tree.nativeCommand('all', createTreeAll);
|
|
1194
|
+
}
|
|
1195
|
+
async function treeAction(_, tree) {
|
|
1196
|
+
const cmd = getClosestNonNativeParent(tree);
|
|
1197
|
+
const table = prefixStringsRecursive(cmd, (prefix)=>{
|
|
1198
|
+
return !/ (config|presets|util)( .+)?$/gi.test(prefix);
|
|
1199
|
+
});
|
|
1200
|
+
console.log(formatCommandTable(table));
|
|
1201
|
+
}
|
|
1202
|
+
function createTreeAll(all) {
|
|
1203
|
+
all.description('List all subcommands including utility commands.');
|
|
1204
|
+
all.action(treeAllAction);
|
|
1205
|
+
}
|
|
1206
|
+
async function treeAllAction(_, all) {
|
|
1207
|
+
const cmd = getClosestNonNativeParent(all);
|
|
1208
|
+
const table = prefixStringsRecursive(cmd);
|
|
1209
|
+
console.log(formatCommandTable(table));
|
|
1210
|
+
}
|
|
1211
|
+
function formatCommandTable(table) {
|
|
1212
|
+
const ansi = table.map((row)=>{
|
|
1213
|
+
const arr = row[0].split(' ');
|
|
1214
|
+
const last = arr.pop();
|
|
1215
|
+
let col = colors.magenta;
|
|
1216
|
+
if (row[1].startsWith('[Preset]')) {
|
|
1217
|
+
col = colors.green;
|
|
1218
|
+
} else if (/ (util|config|presets) /.test(row[0])) {
|
|
1219
|
+
col = colors.gray;
|
|
1220
|
+
} else if (/ (util|config|presets)/.test(row[0])) {
|
|
1221
|
+
col = colors.dim;
|
|
1222
|
+
}
|
|
1223
|
+
row[0] = arr.map(colors.dim).concat(col(last)).join(' ');
|
|
1224
|
+
return row;
|
|
1225
|
+
});
|
|
1226
|
+
return formatTableForTerminal(ansi, [
|
|
1227
|
+
'Command',
|
|
1228
|
+
'Summary'
|
|
1229
|
+
]);
|
|
1230
|
+
}
|
|
1231
|
+
})();
|
|
1232
|
+
|
|
1233
|
+
function assertNoDuplicateCommandNames(cmd) {
|
|
1234
|
+
const names = cmd.$.commands.map((sub)=>sub.aliases().concat(sub.name())).flat();
|
|
1235
|
+
if (names.length !== new Set(names).size) {
|
|
1236
|
+
throw new Error(`Duplicate subcommand names/aliases found for command, ${cmd.name}: ${names.join(', ')}`);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
function assertNoDuplicateOptionNames(cmd) {
|
|
1241
|
+
const throwErr = (cmd, opt, anc)=>{
|
|
1242
|
+
throw new Error(`Duplicate option names > cmd: ${cmd.name}, ${anc ? `anc: ${anc.name}, ` : ''}opt: ${opt}`);
|
|
1243
|
+
};
|
|
1244
|
+
const set = new Set();
|
|
1245
|
+
for (const opt of cmd.options){
|
|
1246
|
+
if (opt.name() === 'help') continue;
|
|
1247
|
+
if (opt.short) {
|
|
1248
|
+
if (set.has(opt.short)) throwErr(cmd, opt.short);
|
|
1249
|
+
set.add(opt.short);
|
|
1250
|
+
}
|
|
1251
|
+
if (opt.long) {
|
|
1252
|
+
if (set.has(opt.long)) throwErr(cmd, opt.long);
|
|
1253
|
+
set.add(opt.long);
|
|
1254
|
+
}
|
|
1255
|
+
if (opt.attributeName()) {
|
|
1256
|
+
if (set.has(opt.attributeName())) throwErr(cmd, opt.attributeName());
|
|
1257
|
+
set.add(opt.attributeName());
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
for (const anc of walkAncestors(cmd)){
|
|
1261
|
+
for (const opt of anc.$.options){
|
|
1262
|
+
if (opt.short && set.has(opt.short)) {
|
|
1263
|
+
if (opt.short !== 'V') continue;
|
|
1264
|
+
throwErr(cmd, opt.short, anc);
|
|
1265
|
+
}
|
|
1266
|
+
if (opt.long && set.has(opt.long)) {
|
|
1267
|
+
throwErr(cmd, opt.long, anc);
|
|
1268
|
+
}
|
|
1269
|
+
if (opt.attributeName() && set.has(opt.attributeName())) {
|
|
1270
|
+
throwErr(cmd, opt.attributeName(), anc);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
/**
|
|
1277
|
+
* Extract the argument name from an option's 'flags' string.
|
|
1278
|
+
*/ function getOptionArgumentName(opt) {
|
|
1279
|
+
const result = arrLast(opt.flags.trim().split(' '));
|
|
1280
|
+
if (/-/.test(result)) return undefined;
|
|
1281
|
+
return result;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
/**
|
|
1285
|
+
* Update an Option's 'flags' property from its 'short' and 'long' properties.
|
|
1286
|
+
* The flags property is not automatically updated when 'short' or 'long' are changed.
|
|
1287
|
+
*/ function renderOptionFlags(opt) {
|
|
1288
|
+
const shortLong = [];
|
|
1289
|
+
if (opt.short) shortLong.push(opt.short);
|
|
1290
|
+
if (opt.long) shortLong.push(opt.long);
|
|
1291
|
+
const argName = getOptionArgumentName(opt);
|
|
1292
|
+
return shortLong.join(', ') + (argName ? ' ' + argName : '');
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
/**
|
|
1296
|
+
* Set an Option's 'short' name. The 'flags' property is updated accordingly.
|
|
1297
|
+
* The '-' prefix is automatically added if not present.
|
|
1298
|
+
*/ function setOptionShortName(opt, short) {
|
|
1299
|
+
opt.short = strEnsureStartsWith(short, '-').replace(/^-+/, '-');
|
|
1300
|
+
opt.flags = renderOptionFlags(opt);
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* Automatically set 'short' and 'long' names to options that don't have one assigned yet.
|
|
1305
|
+
*
|
|
1306
|
+
* First, it tries to assign a short name based on the first letter of the option's attribute name
|
|
1307
|
+
* Both lower and upper case are tried. If these is not available, the next letter of the option name is tried.
|
|
1308
|
+
*
|
|
1309
|
+
* If none of the letters of the option name are available, the option is skipped until all other
|
|
1310
|
+
* options have had letters from their names attempted assigned.
|
|
1311
|
+
* Those that remain are assigned the first available letter of the alphabet + 0-9.
|
|
1312
|
+
* If there are 64 options for the command and no more alphanumeric characters are available,
|
|
1313
|
+
* the option is not assigned a short name.
|
|
1314
|
+
*/ function autoAssignMissingOptionFlags(cmd) {
|
|
1315
|
+
const taken = new Set();
|
|
1316
|
+
for (const anc of walkAncestors(cmd, {
|
|
1317
|
+
includeSelf: true
|
|
1318
|
+
})){
|
|
1319
|
+
anc.options.forEach((opt)=>{
|
|
1320
|
+
if (!opt.short) return;
|
|
1321
|
+
taken.add(opt.short.replace(/^-/g, ''));
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
const failed = new Set();
|
|
1325
|
+
// assign letter from option name
|
|
1326
|
+
cmd.options.forEach((opt)=>{
|
|
1327
|
+
if (opt.short) return;
|
|
1328
|
+
const name = opt.attributeName();
|
|
1329
|
+
for(let c = 0; c < name.length; c++){
|
|
1330
|
+
let char = name.charAt(c).toLowerCase();
|
|
1331
|
+
if (taken.has(char)) {
|
|
1332
|
+
char = char.toUpperCase();
|
|
1333
|
+
if (taken.has(char)) continue;
|
|
1334
|
+
}
|
|
1335
|
+
setOptionShortName(opt, char);
|
|
1336
|
+
taken.add(char);
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
failed.add(opt);
|
|
1340
|
+
});
|
|
1341
|
+
// assign random alphanumeric character.
|
|
1342
|
+
const name = 'abcdefghijklmnopqrstuvwxyz1234567890';
|
|
1343
|
+
failed.forEach((opt)=>{
|
|
1344
|
+
for(let c = 0; c < name.length; c++){
|
|
1345
|
+
let char = name.charAt(c);
|
|
1346
|
+
if (taken.has(char)) {
|
|
1347
|
+
char = char.toUpperCase();
|
|
1348
|
+
if (taken.has(char)) continue;
|
|
1349
|
+
}
|
|
1350
|
+
setOptionShortName(opt, char);
|
|
1351
|
+
taken.add(char);
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
/**
|
|
1358
|
+
* Iterate sibling CommandBuilder objects.
|
|
1359
|
+
*/ function* walkSiblings(cmd) {
|
|
1360
|
+
if (!cmd.parent) return;
|
|
1361
|
+
for (const sub of cmd.parent.meta.subcommands){
|
|
1362
|
+
if (sub === cmd) continue;
|
|
1363
|
+
yield sub;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
/**
|
|
1368
|
+
* Returns an array of sibling CommandBuilder objects.
|
|
1369
|
+
*/ function getSiblings(cmd) {
|
|
1370
|
+
return [
|
|
1371
|
+
...walkSiblings(cmd)
|
|
1372
|
+
];
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/**
|
|
1376
|
+
* Makes aliases for the command.
|
|
1377
|
+
* The idea is to be able to navigate the command tree by only typing the first letter(s) of the command names.
|
|
1378
|
+
*
|
|
1379
|
+
* Example: A command 'cola' would get these aliases: ['c', 'co', 'col'].
|
|
1380
|
+
* However, if there are namespace clashes with sibling subcommands that start with the same letter,
|
|
1381
|
+
* eg. like 'cola' and 'coal' where the first two letters clash, cola's aliases are reduced to only ['col'] and similarly for 'coal'.
|
|
1382
|
+
*
|
|
1383
|
+
* This method creates the aliases, ensuring there are no clashes with sublings, why it is important that the
|
|
1384
|
+
* entire command tree is built before invoking this method.
|
|
1385
|
+
*/ function autoAssignSubCommandAliases(cmd) {
|
|
1386
|
+
if (cmd.get.alias || cmd.name.length <= 1) return;
|
|
1387
|
+
const sibAliases = getSiblings(cmd).map((sib)=>sib.get.aliases).flat();
|
|
1388
|
+
for(let i = 0; i < cmd.name.length - 1; i++){
|
|
1389
|
+
let cmdAlias = cmd.name.substring(0, i + 1);
|
|
1390
|
+
let isClash = arrSome(sibAliases, (sibAlias)=>{
|
|
1391
|
+
return cmdAlias === sibAlias;
|
|
1392
|
+
});
|
|
1393
|
+
if (isClash && i === 0) {
|
|
1394
|
+
cmdAlias = cmdAlias.charAt(0).toUpperCase();
|
|
1395
|
+
isClash = arrSome(sibAliases, (sibAlias)=>{
|
|
1396
|
+
return cmdAlias === sibAlias;
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
if (isClash) continue;
|
|
1400
|
+
cmd.alias(cmdAlias);
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
function configureHelp(cmd) {
|
|
1406
|
+
const cmdr = cmd.$;
|
|
1407
|
+
cmdr.addHelpCommand('?', 'show help');
|
|
1408
|
+
cmdr.configureHelp(opts);
|
|
1409
|
+
}
|
|
1410
|
+
const opts = (()=>{
|
|
1411
|
+
return {
|
|
1412
|
+
helpWidth: undefined,
|
|
1413
|
+
showGlobalOptions: true,
|
|
1414
|
+
sortSubcommands: false,
|
|
1415
|
+
sortOptions: false,
|
|
1416
|
+
subcommandTerm,
|
|
1417
|
+
argumentTerm,
|
|
1418
|
+
commandUsage,
|
|
1419
|
+
visibleOptions,
|
|
1420
|
+
visibleGlobalOptions,
|
|
1421
|
+
subcommandDescription,
|
|
1422
|
+
optionDescription,
|
|
1423
|
+
argumentDescription,
|
|
1424
|
+
commandDescription,
|
|
1425
|
+
formatHelp
|
|
1426
|
+
};
|
|
1427
|
+
function formatHelp(cmd) {
|
|
1428
|
+
let result = Help.prototype.formatHelp.call(this, cmd, this);
|
|
1429
|
+
result = movePresetOptionsUnderOwnHeader(result);
|
|
1430
|
+
result = rearrangeSections(result);
|
|
1431
|
+
result = fixLinebreaks(result);
|
|
1432
|
+
result = addColors(result);
|
|
1433
|
+
return result;
|
|
1434
|
+
function movePresetOptionsUnderOwnHeader(result) {
|
|
1435
|
+
const lines = result.split('\n');
|
|
1436
|
+
const presetLines = lines.filter((line)=>line.includes('[Preset]:'));
|
|
1437
|
+
const firstPresetIndex = lines.findIndex((line)=>line.includes('[Preset]'));
|
|
1438
|
+
if (firstPresetIndex !== -1) {
|
|
1439
|
+
lines.splice(firstPresetIndex, presetLines.length);
|
|
1440
|
+
lines.push('', 'Preset Options:', ...presetLines);
|
|
1441
|
+
result = lines.join('\n');
|
|
1442
|
+
}
|
|
1443
|
+
return result;
|
|
1444
|
+
}
|
|
1445
|
+
function rearrangeSections(result) {
|
|
1446
|
+
const order = [
|
|
1447
|
+
'Usage:',
|
|
1448
|
+
'Version:',
|
|
1449
|
+
'Description:',
|
|
1450
|
+
'Commands:',
|
|
1451
|
+
'Arguments:',
|
|
1452
|
+
'Options:',
|
|
1453
|
+
'Global Options:',
|
|
1454
|
+
'Preset Options:'
|
|
1455
|
+
];
|
|
1456
|
+
const blocks = {};
|
|
1457
|
+
let lastAddedBlock = 'Description:';
|
|
1458
|
+
result.split(/\n\n+/).forEach((block)=>{
|
|
1459
|
+
const title = block.split(':')[0].trim() + ':';
|
|
1460
|
+
if (/^[A-Z][\w ]+:/.test(title)) {
|
|
1461
|
+
blocks[title] = block;
|
|
1462
|
+
lastAddedBlock = title;
|
|
1463
|
+
if (!order.includes(title)) order.push(title);
|
|
1464
|
+
} else {
|
|
1465
|
+
if (!order.includes(lastAddedBlock)) order.push(lastAddedBlock);
|
|
1466
|
+
blocks[lastAddedBlock] = ((blocks[lastAddedBlock] || '') + '\n\n' + block).trim();
|
|
1467
|
+
}
|
|
1468
|
+
});
|
|
1469
|
+
result = order.map((title)=>blocks[title]).join('\n\n').trim();
|
|
1470
|
+
return result;
|
|
1471
|
+
}
|
|
1472
|
+
function fixLinebreaks(result) {
|
|
1473
|
+
result = '\n' + result.trim() + '\n\n';
|
|
1474
|
+
result = result.replace(/\n\n+/g, '\n\n');
|
|
1475
|
+
return result;
|
|
1476
|
+
}
|
|
1477
|
+
function addColors(result) {
|
|
1478
|
+
result = result.replace(/\|/g, colors.gray(colors.dim('|')));
|
|
1479
|
+
result = result.replace(/^[A-Z][\w ]+:/gm, (s)=>colors.yellow(s));
|
|
1480
|
+
return result;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
function subcommandTerm(cmd) {
|
|
1484
|
+
const args = cmd.registeredArguments.map((arg)=>this.argumentTerm(arg)).join(' ');
|
|
1485
|
+
const parent = cmd.parent || cmd;
|
|
1486
|
+
const padsize = Math.max(1, ...parent.commands.map((c)=>{
|
|
1487
|
+
var _c_alias;
|
|
1488
|
+
return ((_c_alias = c.alias()) == null ? void 0 : _c_alias.length) || 1;
|
|
1489
|
+
}));
|
|
1490
|
+
let alias = cmd.alias() || ' ';
|
|
1491
|
+
alias = alias.padEnd(padsize, ' ') + ' |';
|
|
1492
|
+
return alias + cmd.name() + (args ? ' ' + args : '');
|
|
1493
|
+
}
|
|
1494
|
+
function argumentTerm(arg) {
|
|
1495
|
+
let r = arg.name();
|
|
1496
|
+
const rest = arg.variadic ? '...' : '';
|
|
1497
|
+
if (arg.required) r = '<' + r + rest + '>';
|
|
1498
|
+
else r = '[' + r + rest + ']';
|
|
1499
|
+
return r;
|
|
1500
|
+
}
|
|
1501
|
+
function commandUsage(cmd) {
|
|
1502
|
+
const args = cmd.registeredArguments.map((arg)=>this.argumentTerm(arg)).join(' ');
|
|
1503
|
+
return [
|
|
1504
|
+
prefixString(cmd.builder) + ' ' + (args ? args : '[command]'),
|
|
1505
|
+
'[options]'
|
|
1506
|
+
].join(' ');
|
|
1507
|
+
}
|
|
1508
|
+
function visibleOptions(cmd) {
|
|
1509
|
+
const builder = cmd.builder;
|
|
1510
|
+
const opts = cmd.options.filter((opt)=>!opt.hidden && !builder.meta.globalOptions.includes(opt));
|
|
1511
|
+
const presets = opts.filter((opt)=>opt.description.startsWith('[Preset]'));
|
|
1512
|
+
return opts.filter((opt)=>!presets.includes(opt)).concat(presets);
|
|
1513
|
+
}
|
|
1514
|
+
function visibleGlobalOptions(cmd) {
|
|
1515
|
+
return getGlobalOptions(cmd.builder);
|
|
1516
|
+
}
|
|
1517
|
+
function subcommandDescription(cmd) {
|
|
1518
|
+
return colors.gray(Help.prototype.subcommandDescription.call(this, cmd));
|
|
1519
|
+
}
|
|
1520
|
+
function optionDescription(opt) {
|
|
1521
|
+
return colors.gray(Help.prototype.optionDescription.call(this, opt));
|
|
1522
|
+
}
|
|
1523
|
+
function argumentDescription(arg) {
|
|
1524
|
+
return colors.gray(Help.prototype.argumentDescription.call(this, arg));
|
|
1525
|
+
}
|
|
1526
|
+
function commandDescription(cmd) {
|
|
1527
|
+
const v = cmd.builder.get.version;
|
|
1528
|
+
const version = v ? colors.yellow('Version: ') + v + '\n\n' : '';
|
|
1529
|
+
const description = 'Description:\n' + Help.prototype.commandDescription.call(this, cmd);
|
|
1530
|
+
return version + description;
|
|
1531
|
+
}
|
|
1532
|
+
})();
|
|
1533
|
+
|
|
1534
|
+
function ensureBackRefToCommandBuilder(cmd) {
|
|
1535
|
+
commandToBuilderMap.set(cmd.$, cmd);
|
|
1536
|
+
}
|
|
1537
|
+
const commandToBuilderMap = new WeakMap();
|
|
1538
|
+
Object.defineProperty(Command.prototype, 'builder', {
|
|
1539
|
+
get () {
|
|
1540
|
+
const ins = commandToBuilderMap.get(this);
|
|
1541
|
+
if (!ins) throw new Error(`CommandBuilder not found for command ${this.name()}`);
|
|
1542
|
+
return ins;
|
|
1543
|
+
}
|
|
1544
|
+
});
|
|
1545
|
+
|
|
1546
|
+
function initializeCommand(cmd, callback) {
|
|
1547
|
+
const t0_precb = Date.now();
|
|
1548
|
+
ensureBackRefToCommandBuilder(cmd);
|
|
1549
|
+
actionWrapper(cmd);
|
|
1550
|
+
configureHelp(cmd);
|
|
1551
|
+
inheritBasicSettings(cmd);
|
|
1552
|
+
inheritHiddenGlobalOptions(cmd);
|
|
1553
|
+
const precb = Date.now() - t0_precb;
|
|
1554
|
+
const t0_cb = Date.now();
|
|
1555
|
+
if (callback) callback(cmd);
|
|
1556
|
+
const cb = Date.now() - t0_cb;
|
|
1557
|
+
const t0_postcb = Date.now();
|
|
1558
|
+
if (cmd.features.isUtilsEnabled) addUtilCommands(cmd);
|
|
1559
|
+
if (cmd.features.isAutoAssignSubCommandAliasesEnabled) {
|
|
1560
|
+
autoAssignSubCommandAliases(cmd);
|
|
1561
|
+
}
|
|
1562
|
+
if (cmd.features.isAutoAssignMissingOptionFlagsEnabled) {
|
|
1563
|
+
autoAssignMissingOptionFlags(cmd);
|
|
1564
|
+
}
|
|
1565
|
+
assertNoDuplicateCommandNames(cmd);
|
|
1566
|
+
assertNoDuplicateOptionNames(cmd);
|
|
1567
|
+
const postcb = Date.now() - t0_postcb;
|
|
1568
|
+
if (precb + cb + postcb > 10) cmd.outputDebugInfo('init timer', ()=>({
|
|
1569
|
+
precb,
|
|
1570
|
+
cb,
|
|
1571
|
+
postcb
|
|
1572
|
+
}));
|
|
1573
|
+
return cmd;
|
|
1574
|
+
}
|
|
1575
|
+
function inheritBasicSettings(cmd) {
|
|
1576
|
+
if (!cmd.parent) return;
|
|
1577
|
+
const cmdr = cmd.$;
|
|
1578
|
+
if (cmdr.parent) cmdr.copyInheritedSettings(cmdr.parent);
|
|
1579
|
+
}
|
|
1580
|
+
function inheritHiddenGlobalOptions(cmd) {
|
|
1581
|
+
if (!cmd.parent) return;
|
|
1582
|
+
for (const opt of cmd.parent.meta.hiddenGlobalOptions){
|
|
1583
|
+
cmd.meta.hiddenGlobalOptions.add(opt);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/**
|
|
1588
|
+
* An error class for errors related to the JSON file used as simple database.
|
|
1589
|
+
*/ class JsonFileError extends XtError {
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
/**
|
|
1593
|
+
* A class that represents the a section of the JSON file used as simple database.
|
|
1594
|
+
*/ class AbstractJsonFileSection extends Base {
|
|
1595
|
+
get defaultValues() {
|
|
1596
|
+
return this._defaultValues;
|
|
1597
|
+
}
|
|
1598
|
+
/**
|
|
1599
|
+
* Get the db lookup prefix for this section.
|
|
1600
|
+
* @param key An optional key to append to the prefix.
|
|
1601
|
+
*/ prefix(key) {
|
|
1602
|
+
if (this.hasFixedKeysForUser && key && !Object.hasOwn(this.defaultValues, key)) {
|
|
1603
|
+
throw new JsonFileError(`No config entry with key '${key}'`);
|
|
1604
|
+
}
|
|
1605
|
+
return this.name + (key ? '.' + key : '');
|
|
1606
|
+
}
|
|
1607
|
+
get(key) {
|
|
1608
|
+
this.initialize(false);
|
|
1609
|
+
var _this_file_db_getSafe;
|
|
1610
|
+
return (_this_file_db_getSafe = this.file.db.getSafe(this.prefix(key))) != null ? _this_file_db_getSafe : this.defaultValues[key];
|
|
1611
|
+
}
|
|
1612
|
+
getAll() {
|
|
1613
|
+
this.initialize(false);
|
|
1614
|
+
var _this_file_db_getSafe;
|
|
1615
|
+
return (_this_file_db_getSafe = this.file.db.getSafe(this.prefix())) != null ? _this_file_db_getSafe : this.defaultValues;
|
|
1616
|
+
}
|
|
1617
|
+
count() {
|
|
1618
|
+
return Object.keys(this.file.db.getSafe(this.prefix()) || {}).length;
|
|
1619
|
+
}
|
|
1620
|
+
set(key, value, save = true) {
|
|
1621
|
+
this.initialize();
|
|
1622
|
+
this.assertValid(key, value);
|
|
1623
|
+
this.file.db.set(this.prefix(key), value, save);
|
|
1624
|
+
}
|
|
1625
|
+
setAll(values, save = true) {
|
|
1626
|
+
const original = this.getAll();
|
|
1627
|
+
for (const [key, value] of Object.entries(values)){
|
|
1628
|
+
if (JSON.stringify(value) === JSON.stringify(original[key])) continue;
|
|
1629
|
+
this.set(key, value, false);
|
|
1630
|
+
}
|
|
1631
|
+
if (!this.hasFixedKeysForUser) {
|
|
1632
|
+
for (const name of Object.keys(original)){
|
|
1633
|
+
if (Object.hasOwn(values, name)) continue;
|
|
1634
|
+
this.delete(name, false);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
if (save) this.save();
|
|
1638
|
+
}
|
|
1639
|
+
reset(key, save = true) {
|
|
1640
|
+
this.set(key, this.defaultValues[key], save);
|
|
1641
|
+
}
|
|
1642
|
+
resetAll(save = true) {
|
|
1643
|
+
this.setAll(this.defaultValues, save);
|
|
1644
|
+
}
|
|
1645
|
+
/**
|
|
1646
|
+
* Actions:
|
|
1647
|
+
* - save the database to disk
|
|
1648
|
+
* - verify that the JSON file is valid JSON data
|
|
1649
|
+
* - format as human readable with 2 indents
|
|
1650
|
+
*/ save() {
|
|
1651
|
+
this.file.db.save();
|
|
1652
|
+
}
|
|
1653
|
+
delete(key, save = true) {
|
|
1654
|
+
this.initialize();
|
|
1655
|
+
this.file.db.delete(this.prefix(key), save);
|
|
1656
|
+
}
|
|
1657
|
+
deleteAll(save = true) {
|
|
1658
|
+
for (const key of Object.keys(this.getAll())){
|
|
1659
|
+
this.delete(key, false);
|
|
1660
|
+
}
|
|
1661
|
+
if (save) this.save();
|
|
1662
|
+
}
|
|
1663
|
+
edit(editor) {
|
|
1664
|
+
const original = this.getAll();
|
|
1665
|
+
const parsed = promptUserEditJsonInTextEditorSync(original, editor || defaultOpenInEditorCommand());
|
|
1666
|
+
this.setAll(parsed);
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* @param file The parent JsonFile instance.
|
|
1670
|
+
* @param name The name of the section.
|
|
1671
|
+
*/ constructor(file, name, hasFixedKeysForUser){
|
|
1672
|
+
super();
|
|
1673
|
+
this.file = file;
|
|
1674
|
+
this.name = name;
|
|
1675
|
+
this.hasFixedKeysForUser = hasFixedKeysForUser;
|
|
1676
|
+
this.isInitialized = false;
|
|
1677
|
+
this.isInitializing = false;
|
|
1678
|
+
this._defaultValues = {};
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
/**
|
|
1683
|
+
* A class that represents the user-config section of the JSON file used as simple database.
|
|
1684
|
+
*/ class ConfigSection extends AbstractJsonFileSection {
|
|
1685
|
+
assertValid(key, value) {
|
|
1686
|
+
if (!this.validators[key]) return;
|
|
1687
|
+
assertThat(value, this.validators[key]);
|
|
1688
|
+
}
|
|
1689
|
+
defineProperty(key, options) {
|
|
1690
|
+
const { description, defaultValue, parse, validate } = options;
|
|
1691
|
+
this.defaultValues[key] = defaultValue;
|
|
1692
|
+
this.parsers[key] = parse != null ? parse : parseString;
|
|
1693
|
+
this.validators[key] = validate != null ? validate : isString;
|
|
1694
|
+
this.descriptions[key] = description != null ? description : '';
|
|
1695
|
+
this.assertValid(key, options.defaultValue);
|
|
1696
|
+
}
|
|
1697
|
+
initialize(save = true) {
|
|
1698
|
+
if (this.isInitialized) return;
|
|
1699
|
+
if (!this.file.cmd.features.isConfigEnabled) {
|
|
1700
|
+
throw new JsonFileError('ConfigSection.initialize() called when config is disabled');
|
|
1701
|
+
}
|
|
1702
|
+
var _this_file_db_getSafe;
|
|
1703
|
+
const values = (_this_file_db_getSafe = this.file.db.getSafe(this.prefix())) != null ? _this_file_db_getSafe : this.defaultValues;
|
|
1704
|
+
const config = {};
|
|
1705
|
+
for (const key of Object.keys(this.defaultValues)){
|
|
1706
|
+
if (values[key] != null) {
|
|
1707
|
+
config[key] = values[key];
|
|
1708
|
+
} else {
|
|
1709
|
+
config[key] = this.defaultValues[key];
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
this.file.db.set(this.prefix(), config, save);
|
|
1713
|
+
this.isInitialized = true;
|
|
1714
|
+
}
|
|
1715
|
+
get keys() {
|
|
1716
|
+
return [
|
|
1717
|
+
...Object.keys(this.defaultValues)
|
|
1718
|
+
];
|
|
1719
|
+
}
|
|
1720
|
+
constructor(file, name, hasFixedKeysForUser = true){
|
|
1721
|
+
super(file, name, hasFixedKeysForUser);
|
|
1722
|
+
this.descriptions = {};
|
|
1723
|
+
this.validators = {};
|
|
1724
|
+
this.parsers = {};
|
|
1725
|
+
// if (file.cmd.isRoot) {
|
|
1726
|
+
// this.defineProperty('editor', {
|
|
1727
|
+
// description: 'application launch command for your preferred text editor.',
|
|
1728
|
+
// defaultValue: defaultOpenInEditorCommand() as Val,
|
|
1729
|
+
// parse: parseString,
|
|
1730
|
+
// })
|
|
1731
|
+
// }
|
|
1732
|
+
if (file.cmd.features.isPresetsEnabled) {
|
|
1733
|
+
this.defineProperty('disabledBuiltinPresets', {
|
|
1734
|
+
description: 'Builtin presets that are disabled.',
|
|
1735
|
+
defaultValue: [],
|
|
1736
|
+
parse: createTypedListParser(',', parseString),
|
|
1737
|
+
validate: function isArrayOfPresetNames(value) {
|
|
1738
|
+
if (!Array.isArray(value)) return false;
|
|
1739
|
+
return value.every((name)=>{
|
|
1740
|
+
return Object.hasOwn(file.presets.defaultValues, name);
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
class JsonDB extends Base {
|
|
1749
|
+
get filepath() {
|
|
1750
|
+
return getJsonFilepath(this.file.cmd);
|
|
1751
|
+
}
|
|
1752
|
+
set(prefix, value = {}, save = true) {
|
|
1753
|
+
if (!prefix) {
|
|
1754
|
+
this.data = assertThat(value, isObject);
|
|
1755
|
+
} else {
|
|
1756
|
+
const keys = prefix.split('.');
|
|
1757
|
+
const lastKey = keys.pop();
|
|
1758
|
+
let node = this.data;
|
|
1759
|
+
for (const key of keys){
|
|
1760
|
+
if (!Object.hasOwn(node, key)) {
|
|
1761
|
+
node[key] = {};
|
|
1762
|
+
}
|
|
1763
|
+
node = node[key];
|
|
1764
|
+
}
|
|
1765
|
+
node[lastKey] = this.cloneDeep(value);
|
|
1766
|
+
}
|
|
1767
|
+
if (save) this.save();
|
|
1768
|
+
}
|
|
1769
|
+
get(prefix) {
|
|
1770
|
+
const value = this.getSafe(prefix);
|
|
1771
|
+
if (value === undefined) throw new Error(`No config entry with key '${prefix}'`);
|
|
1772
|
+
return value;
|
|
1773
|
+
}
|
|
1774
|
+
getSafe(prefix) {
|
|
1775
|
+
if (!prefix) return this.cloneDeep(this.data);
|
|
1776
|
+
const keys = prefix.split('.');
|
|
1777
|
+
let node = this.data;
|
|
1778
|
+
for (const key of keys){
|
|
1779
|
+
if (!Object.hasOwn(node, key)) {
|
|
1780
|
+
return undefined;
|
|
1781
|
+
}
|
|
1782
|
+
node = node[key];
|
|
1783
|
+
}
|
|
1784
|
+
return this.cloneDeep(node);
|
|
1785
|
+
}
|
|
1786
|
+
has(prefix) {
|
|
1787
|
+
if (!prefix) return true;
|
|
1788
|
+
return this.getSafe(prefix) !== undefined;
|
|
1789
|
+
}
|
|
1790
|
+
delete(prefix, save = true) {
|
|
1791
|
+
if (!prefix) {
|
|
1792
|
+
this.data = {};
|
|
1793
|
+
} else {
|
|
1794
|
+
const keys = prefix.split('.');
|
|
1795
|
+
const lastKey = keys.pop();
|
|
1796
|
+
let node = this.data;
|
|
1797
|
+
for (const key of keys){
|
|
1798
|
+
if (!Object.hasOwn(node, key)) {
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
node = node[key];
|
|
1802
|
+
}
|
|
1803
|
+
delete node[lastKey];
|
|
1804
|
+
}
|
|
1805
|
+
if (save) this.save();
|
|
1806
|
+
}
|
|
1807
|
+
cloneDeep(obj) {
|
|
1808
|
+
return JSON.parse(JSON.stringify(obj));
|
|
1809
|
+
}
|
|
1810
|
+
/**
|
|
1811
|
+
* @param filepath The path to the JSON file.
|
|
1812
|
+
*/ constructor(file){
|
|
1813
|
+
super();
|
|
1814
|
+
this.file = file;
|
|
1815
|
+
var _readJsonFileSafeSync;
|
|
1816
|
+
this.data = (_readJsonFileSafeSync = readJsonFileSafeSync(this.filepath)) != null ? _readJsonFileSafeSync : {};
|
|
1817
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1818
|
+
const [queue, writeJsonFileSafeLimited] = funAsyncRateLimit(writeJsonFileSafe, {
|
|
1819
|
+
concurrency: 1,
|
|
1820
|
+
autoStart: true,
|
|
1821
|
+
timeout: 3000,
|
|
1822
|
+
throwOnTimeout: true
|
|
1823
|
+
});
|
|
1824
|
+
const opts = {
|
|
1825
|
+
spaces: 2
|
|
1826
|
+
};
|
|
1827
|
+
this.save = ()=>{
|
|
1828
|
+
writeJsonFileSafeLimited(this.filepath, this.data, opts).catch((err)=>{
|
|
1829
|
+
var _err;
|
|
1830
|
+
this.file.cmd.outputUserError(((_err = err) == null ? void 0 : _err.message) || String(err));
|
|
1831
|
+
});
|
|
1832
|
+
};
|
|
1833
|
+
// const cmdr = getCommander(this.file.cmd)
|
|
1834
|
+
// const awaitQueue = async () => await queue.onIdle()
|
|
1835
|
+
// process.on('exit', awaitQueue)
|
|
1836
|
+
// cmdr.exitOverride(awaitQueue)
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
function assertPresetArgsOptional(cmd, args) {
|
|
1841
|
+
args.forEach((arg, i)=>{
|
|
1842
|
+
if (arg != null && i < cmd.arguments.length && cmd.arguments[i].required) {
|
|
1843
|
+
throw new Error(`Cannot preset required arguments.`);
|
|
1844
|
+
}
|
|
1845
|
+
});
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
const isArray = Array.isArray;
|
|
1849
|
+
|
|
1850
|
+
/**
|
|
1851
|
+
* Determine whether the input is a string array.
|
|
1852
|
+
*/ function isStringArray(value) {
|
|
1853
|
+
return Array.isArray(value) && value.every((v)=>isString(v));
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
function isStringWithNoSpacesOrDashes(value) {
|
|
1857
|
+
return isString(value) && /^[^\s-]+$/i.test(value);
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
function assertValidPreset(cmd, key, preset) {
|
|
1861
|
+
const { description, presets, args, options } = preset;
|
|
1862
|
+
assertThat(key, isStringWithNoSpacesOrDashes);
|
|
1863
|
+
assertThat(description, isString);
|
|
1864
|
+
assertThat(presets, isStringArray);
|
|
1865
|
+
assertThat(args, isArray);
|
|
1866
|
+
assertPresetArgsOptional(cmd, args);
|
|
1867
|
+
assertValidArguments(cmd, args);
|
|
1868
|
+
assertThat(options, isObject);
|
|
1869
|
+
assertValidOptions(cmd, options);
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
function createObjectMerger(predicate) {
|
|
1873
|
+
return function objMerge(target, ...sources) {
|
|
1874
|
+
for (const src of sources){
|
|
1875
|
+
for (const [key, value] of Object.entries(src)){
|
|
1876
|
+
if (predicate(value, key, src)) {
|
|
1877
|
+
target[key] = value;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
return target;
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
const objAssign = createObjectMerger((value)=>value != null);
|
|
1886
|
+
|
|
1887
|
+
/**
|
|
1888
|
+
* A class that represents the user-presets section of the JSON file used as simple database.
|
|
1889
|
+
*/ class PresetsSection extends AbstractJsonFileSection {
|
|
1890
|
+
assertValid(key, value) {
|
|
1891
|
+
assertValidPreset(this.file.cmd, key, value);
|
|
1892
|
+
}
|
|
1893
|
+
defineProperty(key, options) {
|
|
1894
|
+
this.defaultValues[key] = options;
|
|
1895
|
+
this.assertValid(key, options);
|
|
1896
|
+
this.isInitialized = false;
|
|
1897
|
+
}
|
|
1898
|
+
initialize(save = true) {
|
|
1899
|
+
if (this.isInitialized) return;
|
|
1900
|
+
const data = this.file.db.getSafe(this.prefix());
|
|
1901
|
+
const hasData = data && Object.keys(data).length;
|
|
1902
|
+
const presets = objAssign({}, JSON.parse(JSON.stringify(this.defaultValues)), data || {});
|
|
1903
|
+
// remove disabled presets
|
|
1904
|
+
const disabled = this.file.db.getSafe('config.disabledBuiltinPresets');
|
|
1905
|
+
for (const name of disabled || []){
|
|
1906
|
+
if (Object.hasOwn(presets, name)) {
|
|
1907
|
+
delete presets[name];
|
|
1908
|
+
save = true;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
// merge data
|
|
1912
|
+
if (hasData) {
|
|
1913
|
+
// presets['defaults'].args = arrAssign([], this.defaultValues['defaults'].args, presets['defaults'].args)
|
|
1914
|
+
// presets['defaults'].options = objAssign({}, this.defaultValues['defaults'].options, presets['defaults'].options)
|
|
1915
|
+
// for (const preset of Object.values(presets)) {
|
|
1916
|
+
// preset.args = arrAssign([], this.defaultValues['defaults'].args, preset.args)
|
|
1917
|
+
// preset.options = objAssign({}, this.defaultValues['defaults'].options, preset.options)
|
|
1918
|
+
// preset.presets = preset.presets || this.defaultValues['defaults'].presets
|
|
1919
|
+
// // objAssign(preset.options, this.defaultValues['defaults'].options)
|
|
1920
|
+
// // preset.options = objFilter(preset.options, (_, key) => Object.hasOwn(this.defaultValues, key))
|
|
1921
|
+
// // preset.presets = preset.presets.filter((key: string) => Object.hasOwn(this.defaultValues, key))
|
|
1922
|
+
// }
|
|
1923
|
+
if (JSON.stringify(data) !== JSON.stringify(presets)) {
|
|
1924
|
+
save = true;
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
this.file.db.set(this.prefix(), presets, save);
|
|
1928
|
+
this.isInitialized = true;
|
|
1929
|
+
}
|
|
1930
|
+
delete(name, save = true) {
|
|
1931
|
+
if (name === 'defaults') throw new JsonFileError('Cannot delete the "defaults" preset.');
|
|
1932
|
+
super.delete(name, save);
|
|
1933
|
+
if (!Object.hasOwn(this.defaultValues, name)) return;
|
|
1934
|
+
const disabled = this.file.config.get('disabledBuiltinPresets');
|
|
1935
|
+
if (disabled.includes(name)) return;
|
|
1936
|
+
this.file.config.set('disabledBuiltinPresets', disabled.concat(name), save);
|
|
1937
|
+
}
|
|
1938
|
+
async setAll(presets, save = true) {
|
|
1939
|
+
if (!presets['defaults']) throw new JsonFileError('Missing "defaults" preset');
|
|
1940
|
+
super.setAll(presets, save);
|
|
1941
|
+
}
|
|
1942
|
+
constructor(file, name, hasFixedKeysForUser = false){
|
|
1943
|
+
super(file, name, hasFixedKeysForUser);
|
|
1944
|
+
var _arg_defaultValue;
|
|
1945
|
+
this.defineProperty('defaults', {
|
|
1946
|
+
description: 'All presets inherit from this preset',
|
|
1947
|
+
presets: [],
|
|
1948
|
+
args: this.file.cmd.arguments.map((arg)=>(_arg_defaultValue = arg.defaultValue) != null ? _arg_defaultValue : null),
|
|
1949
|
+
options: getOwnAndGlobalOptions(this.file.cmd).reduce((acc, opt)=>{
|
|
1950
|
+
var _opt_defaultValue;
|
|
1951
|
+
acc[opt.attributeName()] = (_opt_defaultValue = opt.defaultValue) != null ? _opt_defaultValue : opt.isBoolean() ? false : null;
|
|
1952
|
+
return acc;
|
|
1953
|
+
}, {})
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
/**
|
|
1959
|
+
* A class that represents the JSON file used as a simple database.
|
|
1960
|
+
*/ class JsonFile extends Base {
|
|
1961
|
+
/**
|
|
1962
|
+
* A lazy-loaded instance of the `config` section of the JSON file.
|
|
1963
|
+
* Upon first property access, it is stored as a property on the instance.
|
|
1964
|
+
*/ get config() {
|
|
1965
|
+
return realizeLazyProperty(this, 'config', new ConfigSection(this, 'config'));
|
|
1966
|
+
}
|
|
1967
|
+
/**
|
|
1968
|
+
* A lazy-loaded instance of the `presets` section of the JSON file.
|
|
1969
|
+
*/ get presets() {
|
|
1970
|
+
return realizeLazyProperty(this, 'presets', new PresetsSection(this, 'presets'));
|
|
1971
|
+
}
|
|
1972
|
+
/**
|
|
1973
|
+
* @param cmd The parent CommandBuilder instance.
|
|
1974
|
+
*/ constructor(cmd){
|
|
1975
|
+
super();
|
|
1976
|
+
this.cmd = cmd;
|
|
1977
|
+
this.db = new JsonDB(this);
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
function objDestroy(obj) {
|
|
1982
|
+
for (const key of Reflect.ownKeys(obj)){
|
|
1983
|
+
Reflect.deleteProperty(obj, key);
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
function optHasArgument(opt) {
|
|
1988
|
+
return /[<>[\]]/.test(opt.flags);
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
class OptionArgumentParserSelector extends AbstractStringParserSelector {
|
|
1992
|
+
custom(parser) {
|
|
1993
|
+
const name = this.builder.$.attributeName();
|
|
1994
|
+
this.builder.cmd.meta.optParsers[name] = parser;
|
|
1995
|
+
return this.builder;
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
class OptionArgumentValidatorSelector extends AbstractValidatorSelector {
|
|
2000
|
+
custom(validator) {
|
|
2001
|
+
const name = this.builder.$.attributeName();
|
|
2002
|
+
const obj = this.builder.cmd.meta.optValidators;
|
|
2003
|
+
if (!obj[name]) obj[name] = [];
|
|
2004
|
+
obj[name].push(validator);
|
|
2005
|
+
return this.builder;
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
class OptionReader extends Base {
|
|
2010
|
+
get $() {
|
|
2011
|
+
return this.parent.$;
|
|
2012
|
+
}
|
|
2013
|
+
get option() {
|
|
2014
|
+
return this.parent.$;
|
|
2015
|
+
}
|
|
2016
|
+
get description() {
|
|
2017
|
+
return this.$.description;
|
|
2018
|
+
}
|
|
2019
|
+
get optional() {
|
|
2020
|
+
return this.$.optional;
|
|
2021
|
+
}
|
|
2022
|
+
// get negate() {
|
|
2023
|
+
// return this.$.negate
|
|
2024
|
+
// }
|
|
2025
|
+
get mandatory() {
|
|
2026
|
+
return this.$.mandatory;
|
|
2027
|
+
}
|
|
2028
|
+
get hidden() {
|
|
2029
|
+
return this.$.hidden;
|
|
2030
|
+
}
|
|
2031
|
+
get variadic() {
|
|
2032
|
+
return this.$.variadic;
|
|
2033
|
+
}
|
|
2034
|
+
get short() {
|
|
2035
|
+
return this.$.short;
|
|
2036
|
+
}
|
|
2037
|
+
get long() {
|
|
2038
|
+
return this.$.long;
|
|
2039
|
+
}
|
|
2040
|
+
// get preset() {
|
|
2041
|
+
// return this.$.presetArg
|
|
2042
|
+
// }
|
|
2043
|
+
get default() {
|
|
2044
|
+
return this.$.defaultValue;
|
|
2045
|
+
}
|
|
2046
|
+
get choices() {
|
|
2047
|
+
return this.$.argChoices;
|
|
2048
|
+
}
|
|
2049
|
+
get env() {
|
|
2050
|
+
return this.$.envVar;
|
|
2051
|
+
}
|
|
2052
|
+
get flags() {
|
|
2053
|
+
return this.$.flags;
|
|
2054
|
+
}
|
|
2055
|
+
get name() {
|
|
2056
|
+
return this.$.name();
|
|
2057
|
+
}
|
|
2058
|
+
get attributeName() {
|
|
2059
|
+
return this.$.attributeName();
|
|
2060
|
+
}
|
|
2061
|
+
get fullDescription() {
|
|
2062
|
+
return this.$.fullDescription();
|
|
2063
|
+
}
|
|
2064
|
+
get defaultValueDescription() {
|
|
2065
|
+
return this.$.defaultValueDescription;
|
|
2066
|
+
}
|
|
2067
|
+
get hasArgument() {
|
|
2068
|
+
return optHasArgument(this.parent.$);
|
|
2069
|
+
}
|
|
2070
|
+
constructor(parent){
|
|
2071
|
+
super();
|
|
2072
|
+
this.parent = parent;
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
/**
|
|
2077
|
+
* Wrapper around the @see Option class, for more intuitive construction.
|
|
2078
|
+
* @remarks Options are one of boolean, negated, required argument, or optional argument.
|
|
2079
|
+
*/ class OptionBuilder extends Base {
|
|
2080
|
+
description(string) {
|
|
2081
|
+
this.$.description = string;
|
|
2082
|
+
return this;
|
|
2083
|
+
}
|
|
2084
|
+
// negate(negate = true) {
|
|
2085
|
+
// this.$.negate = negate
|
|
2086
|
+
// return this
|
|
2087
|
+
// }
|
|
2088
|
+
// mandatory(mandatory = true) {
|
|
2089
|
+
// this.$.makeOptionMandatory(mandatory)
|
|
2090
|
+
// return this
|
|
2091
|
+
// }
|
|
2092
|
+
hideHelp(hide = true) {
|
|
2093
|
+
this.$.hideHelp(hide);
|
|
2094
|
+
return this;
|
|
2095
|
+
}
|
|
2096
|
+
hidden(hidden = true) {
|
|
2097
|
+
this.$.hidden = hidden;
|
|
2098
|
+
return this;
|
|
2099
|
+
}
|
|
2100
|
+
// preset(arg: unknown) {
|
|
2101
|
+
// this.$.preset(arg)
|
|
2102
|
+
// return this
|
|
2103
|
+
// }
|
|
2104
|
+
default(value, description) {
|
|
2105
|
+
if (!optHasArgument(this.$)) {
|
|
2106
|
+
throw new Error('Cannot set default value on option without argument: ' + this.$.name());
|
|
2107
|
+
}
|
|
2108
|
+
if (!this.$.optional) {
|
|
2109
|
+
throw new Error('Cannot set default value on required option: ' + this.$.name());
|
|
2110
|
+
}
|
|
2111
|
+
this.$.default(value, description);
|
|
2112
|
+
return this;
|
|
2113
|
+
}
|
|
2114
|
+
choices(values) {
|
|
2115
|
+
this.$.choices(values);
|
|
2116
|
+
return this;
|
|
2117
|
+
}
|
|
2118
|
+
conflicts(names) {
|
|
2119
|
+
this.$.conflicts(names);
|
|
2120
|
+
return this;
|
|
2121
|
+
}
|
|
2122
|
+
implies(optionValues) {
|
|
2123
|
+
this.$.implies(optionValues);
|
|
2124
|
+
return this;
|
|
2125
|
+
}
|
|
2126
|
+
env(name) {
|
|
2127
|
+
this.$.env(name);
|
|
2128
|
+
return this;
|
|
2129
|
+
}
|
|
2130
|
+
short(short) {
|
|
2131
|
+
setOptionShortName(this.$, short);
|
|
2132
|
+
return this;
|
|
2133
|
+
}
|
|
2134
|
+
get parser() {
|
|
2135
|
+
if (this.$.isBoolean()) {
|
|
2136
|
+
throw new Error('Cannot set parser on boolean option: ' + this.$.attributeName());
|
|
2137
|
+
}
|
|
2138
|
+
return new OptionArgumentParserSelector(this);
|
|
2139
|
+
}
|
|
2140
|
+
get validator() {
|
|
2141
|
+
if (this.$.isBoolean()) {
|
|
2142
|
+
throw new Error('Cannot set validator on boolean option: ' + this.$.attributeName());
|
|
2143
|
+
}
|
|
2144
|
+
return new OptionArgumentValidatorSelector(this);
|
|
2145
|
+
}
|
|
2146
|
+
get get() {
|
|
2147
|
+
return realizeLazyProperty(this, 'get', new OptionReader(this));
|
|
2148
|
+
}
|
|
2149
|
+
constructor(cmd, flags){
|
|
2150
|
+
var _this_$_long;
|
|
2151
|
+
super();
|
|
2152
|
+
this.cmd = cmd;
|
|
2153
|
+
this.$ = new Option(flags);
|
|
2154
|
+
if (this.$.isBoolean() && ((_this_$_long = this.$.long) == null ? void 0 : _this_$_long.startsWith('--no-'))) {
|
|
2155
|
+
this.$.default(true);
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
/**
|
|
2161
|
+
* Wrapper around the @see Command class, for more intuitive construction.
|
|
2162
|
+
*/ class CommandBuilder extends Base {
|
|
2163
|
+
get db() {
|
|
2164
|
+
return realizeLazyProperty(this, 'db', new JsonFile(this));
|
|
2165
|
+
}
|
|
2166
|
+
get name() {
|
|
2167
|
+
return this.$.name();
|
|
2168
|
+
}
|
|
2169
|
+
version(string) {
|
|
2170
|
+
this.$.version(string);
|
|
2171
|
+
return this;
|
|
2172
|
+
}
|
|
2173
|
+
alias(alias) {
|
|
2174
|
+
assertCommandNameNotReserved(alias);
|
|
2175
|
+
this.$.alias(alias);
|
|
2176
|
+
return this;
|
|
2177
|
+
}
|
|
2178
|
+
aliases(...aliases) {
|
|
2179
|
+
aliases.forEach((a)=>this.alias(a));
|
|
2180
|
+
return this;
|
|
2181
|
+
}
|
|
2182
|
+
get isRoot() {
|
|
2183
|
+
return !this.parent;
|
|
2184
|
+
}
|
|
2185
|
+
get arguments() {
|
|
2186
|
+
return this.$.registeredArguments;
|
|
2187
|
+
}
|
|
2188
|
+
get options() {
|
|
2189
|
+
return this.$.options;
|
|
2190
|
+
}
|
|
2191
|
+
enableBuiltinOptions(options) {
|
|
2192
|
+
if (!options || options.help) this.globalOption('-h, --help', 'show help');
|
|
2193
|
+
if (!options || options.debug) this.globalOption('-D, --debug', 'Output debugging information.');
|
|
2194
|
+
if (!options || options.disableColor) this.globalOption('-C, --disable-color', 'Disable color in terminal output.');
|
|
2195
|
+
if (!options || options.disableStderr) this.globalOption('-E, --disable-stderr', 'Mute all output to stderr.');
|
|
2196
|
+
if (!options || options.disableStdout) this.globalOption('-O, --disable-stdout', 'Mute all output to stdout.');
|
|
2197
|
+
}
|
|
2198
|
+
outputHelp() {
|
|
2199
|
+
this.$.help();
|
|
2200
|
+
}
|
|
2201
|
+
/**
|
|
2202
|
+
* Display error message and exit (or call exitOverride).
|
|
2203
|
+
*/ outputUserError(message, options) {
|
|
2204
|
+
this.$.error(message, options);
|
|
2205
|
+
}
|
|
2206
|
+
description(...lines) {
|
|
2207
|
+
const description = lines.join('\n');
|
|
2208
|
+
const summary = description.split(/(\. ?|\n|$)/)[0];
|
|
2209
|
+
this.$.summary(summary + '.');
|
|
2210
|
+
this.$.description(description);
|
|
2211
|
+
return this;
|
|
2212
|
+
}
|
|
2213
|
+
allowExcessArguments(bool = true) {
|
|
2214
|
+
this.$.allowExcessArguments(bool);
|
|
2215
|
+
return this;
|
|
2216
|
+
}
|
|
2217
|
+
allowUnknownOption(bool = true) {
|
|
2218
|
+
this.$.allowUnknownOption(bool);
|
|
2219
|
+
return this;
|
|
2220
|
+
}
|
|
2221
|
+
/**
|
|
2222
|
+
* Register callback to use as replacement for calling process.exit.
|
|
2223
|
+
*/ exitOverride(callback) {
|
|
2224
|
+
this.$.exitOverride(callback);
|
|
2225
|
+
return this;
|
|
2226
|
+
}
|
|
2227
|
+
get root() {
|
|
2228
|
+
if (this.isRoot) return this;
|
|
2229
|
+
return getAncestors(this).pop();
|
|
2230
|
+
}
|
|
2231
|
+
/**
|
|
2232
|
+
* Display error message and exit (or call exitOverride).
|
|
2233
|
+
*/ outputDebugInfo(event, getProps = ()=>({})) {
|
|
2234
|
+
OutputManager.getInstance().outputDebug(()=>_extends({
|
|
2235
|
+
cmd: prefixString(this),
|
|
2236
|
+
event
|
|
2237
|
+
}, getProps()));
|
|
2238
|
+
}
|
|
2239
|
+
hideGlobalOptions(...names) {
|
|
2240
|
+
const globals = getGlobalOptions(this);
|
|
2241
|
+
names = names.length ? names : globals.map((opt)=>opt.attributeName());
|
|
2242
|
+
for (const name of names){
|
|
2243
|
+
if (!name) continue;
|
|
2244
|
+
let found = false;
|
|
2245
|
+
for (const opt of globals){
|
|
2246
|
+
if (opt.attributeName() === name) {
|
|
2247
|
+
this.meta.hiddenGlobalOptions.add(opt);
|
|
2248
|
+
found = true;
|
|
2249
|
+
break;
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
if (!found) throw new Error(`Unknown global option name: ${name} for command, ${this.name}`);
|
|
2253
|
+
}
|
|
2254
|
+
return this;
|
|
2255
|
+
}
|
|
2256
|
+
unhideGlobalOptions(...names) {
|
|
2257
|
+
const globals = getGlobalOptions(this);
|
|
2258
|
+
names = names.length ? names : globals.map((opt)=>opt.attributeName());
|
|
2259
|
+
for (const name of names){
|
|
2260
|
+
if (!name) continue;
|
|
2261
|
+
let found = false;
|
|
2262
|
+
for (const opt of globals){
|
|
2263
|
+
if (opt.attributeName() === name) {
|
|
2264
|
+
this.meta.hiddenGlobalOptions.delete(opt);
|
|
2265
|
+
found = true;
|
|
2266
|
+
break;
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
if (!found) throw new Error(`Unknown global option name: ${name} for command, ${this.name}`);
|
|
2270
|
+
}
|
|
2271
|
+
return this;
|
|
2272
|
+
}
|
|
2273
|
+
argument(name, cb) {
|
|
2274
|
+
const ins = new ArgumentBuilder(this, name);
|
|
2275
|
+
this.$.addArgument(ins.$);
|
|
2276
|
+
if (typeof cb === 'function') {
|
|
2277
|
+
cb(ins, this);
|
|
2278
|
+
} else if (typeof cb === 'string') {
|
|
2279
|
+
ins.description(cb);
|
|
2280
|
+
}
|
|
2281
|
+
objDestroy(ins);
|
|
2282
|
+
return this;
|
|
2283
|
+
}
|
|
2284
|
+
option(flags, cb) {
|
|
2285
|
+
const ins = new OptionBuilder(this, flags);
|
|
2286
|
+
this.$.addOption(ins.$);
|
|
2287
|
+
if (typeof cb === 'function') {
|
|
2288
|
+
cb(ins, this);
|
|
2289
|
+
} else if (typeof cb === 'string') {
|
|
2290
|
+
ins.description(cb);
|
|
2291
|
+
}
|
|
2292
|
+
objDestroy(ins);
|
|
2293
|
+
return this;
|
|
2294
|
+
}
|
|
2295
|
+
globalOption(flags, cb) {
|
|
2296
|
+
return this.option(flags, (ins)=>{
|
|
2297
|
+
const opt = ins.get.option;
|
|
2298
|
+
this.meta.globalOptions.push(opt);
|
|
2299
|
+
if (typeof cb === 'function') {
|
|
2300
|
+
cb(ins, this);
|
|
2301
|
+
} else if (typeof cb === 'string') {
|
|
2302
|
+
ins.description(cb);
|
|
2303
|
+
}
|
|
2304
|
+
if (opt.hidden) this.meta.hiddenGlobalOptions.add(opt);
|
|
2305
|
+
});
|
|
2306
|
+
}
|
|
2307
|
+
command(name, cb) {
|
|
2308
|
+
this.meta.subcommands.push(new CommandBuilder(name, cb, this));
|
|
2309
|
+
return this;
|
|
2310
|
+
}
|
|
2311
|
+
nativeCommand(name, cb) {
|
|
2312
|
+
return this.command(name, (ins)=>{
|
|
2313
|
+
ins.meta.isNative = true;
|
|
2314
|
+
if (cb) cb(ins);
|
|
2315
|
+
});
|
|
2316
|
+
}
|
|
2317
|
+
action(fn) {
|
|
2318
|
+
this.meta.actionHandler = fn;
|
|
2319
|
+
return this;
|
|
2320
|
+
}
|
|
2321
|
+
config(key, entry) {
|
|
2322
|
+
this.features.config();
|
|
2323
|
+
this.db.config.defineProperty(key, entry);
|
|
2324
|
+
return this;
|
|
2325
|
+
}
|
|
2326
|
+
preset(name, preset) {
|
|
2327
|
+
this.features.presets();
|
|
2328
|
+
var _preset_presets, _preset_args, _preset_options;
|
|
2329
|
+
this.db.presets.defineProperty(name, {
|
|
2330
|
+
description: preset.description,
|
|
2331
|
+
presets: (_preset_presets = preset.presets) != null ? _preset_presets : [],
|
|
2332
|
+
args: (_preset_args = preset.args) != null ? _preset_args : [],
|
|
2333
|
+
options: (_preset_options = preset.options) != null ? _preset_options : {}
|
|
2334
|
+
});
|
|
2335
|
+
return this;
|
|
2336
|
+
}
|
|
2337
|
+
createMain() {
|
|
2338
|
+
return async (argv = process.argv.slice(2))=>{
|
|
2339
|
+
await this.$.parseAsync(getARGV(argv), {
|
|
2340
|
+
from: 'user'
|
|
2341
|
+
});
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2344
|
+
constructor(name, callback, parent = null){
|
|
2345
|
+
super();
|
|
2346
|
+
this.$ = new Command(name);
|
|
2347
|
+
this.parent = parent;
|
|
2348
|
+
if (this.parent) this.parent.$.addCommand(this.$);
|
|
2349
|
+
this.meta = new CommandBuilderMetaData();
|
|
2350
|
+
this.get = new CommandReader(this);
|
|
2351
|
+
this.features = new CommandFeatureSelector(this);
|
|
2352
|
+
initializeCommand(this, callback);
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
(()=>{
|
|
2356
|
+
CommandBuilder.dataDirectory = path.join(os.homedir(), 'config', 'cli');
|
|
2357
|
+
})();
|
|
2358
|
+
function CLI(name, callback) {
|
|
2359
|
+
return new CommandBuilder(name, callback);
|
|
2360
|
+
}
|
|
2361
|
+
class CommandReader extends Base {
|
|
2362
|
+
get action() {
|
|
2363
|
+
return this.cmd.$.builder.meta.actionHandler;
|
|
2364
|
+
}
|
|
2365
|
+
get description() {
|
|
2366
|
+
return this.cmd.$.description();
|
|
2367
|
+
}
|
|
2368
|
+
get summary() {
|
|
2369
|
+
return this.cmd.$.summary();
|
|
2370
|
+
}
|
|
2371
|
+
get version() {
|
|
2372
|
+
return this.cmd.$.version();
|
|
2373
|
+
}
|
|
2374
|
+
get alias() {
|
|
2375
|
+
return this.cmd.$.alias();
|
|
2376
|
+
}
|
|
2377
|
+
get aliases() {
|
|
2378
|
+
return this.cmd.$.aliases();
|
|
2379
|
+
}
|
|
2380
|
+
get renderHelp() {
|
|
2381
|
+
return this.cmd.$.helpInformation();
|
|
2382
|
+
}
|
|
2383
|
+
constructor(cmd){
|
|
2384
|
+
super();
|
|
2385
|
+
this.cmd = cmd;
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
/**
|
|
2390
|
+
* Check whether a command exists in the PATH.
|
|
2391
|
+
* @param command The command to check for.
|
|
2392
|
+
*/ async function commandExists(command) {
|
|
2393
|
+
return await lookpath(command) !== undefined;
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
/**
|
|
2397
|
+
* Get the absolute file path of given command or undefined it does not exist in the PATH.
|
|
2398
|
+
* @param command The command to check for.
|
|
2399
|
+
*/ async function commandLocation(command) {
|
|
2400
|
+
return await lookpath(command);
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
function errorToString(error) {
|
|
2404
|
+
const name = error instanceof Error ? error.name : 'Error';
|
|
2405
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
2406
|
+
return name + ': ' + msg;
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
/**
|
|
2410
|
+
* Escape a string to be used as a shell command argument.
|
|
2411
|
+
* Wraps in double quotes only if necessary.
|
|
2412
|
+
*/ function escapeShellCommandArgument(command) {
|
|
2413
|
+
if (!/(["'$`\\ ])/.test(command)) return command;
|
|
2414
|
+
return '"' + command.replace(/(["'$`\\])/g, '\\$1') + '"';
|
|
2415
|
+
} /*
|
|
2416
|
+
console.log(escapeShellCommandArgument('21')) ///=> 21
|
|
2417
|
+
console.log(escapeShellCommandArgument('noproblem')) ///=> noproblem
|
|
2418
|
+
console.log(escapeShellCommandArgument('foo bar')) ///=> "foo bar"
|
|
2419
|
+
console.log(escapeShellCommandArgument("'c:\\program files\\node'")) ///=> "\'c:\\program files\\node\'"
|
|
2420
|
+
console.log(escapeShellCommandArgument('^(wow|hi)$')) ///=> "^(wow|hi)\$"
|
|
2421
|
+
console.log(escapeShellCommandArgument('^(wow|hi)$')) ///=> "^(wow|hi)\$"
|
|
2422
|
+
*/
|
|
2423
|
+
|
|
2424
|
+
function forEachChildRecursive(cmd, callback, options) {
|
|
2425
|
+
var _options;
|
|
2426
|
+
if (((_options = options) == null ? void 0 : _options.includeSelf) && callback(cmd)) return true;
|
|
2427
|
+
for (const sub of cmd.meta.subcommands){
|
|
2428
|
+
if (callback(sub) || forEachChildRecursive(sub, callback)) {
|
|
2429
|
+
return true;
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
function getChildren(cmd, options) {
|
|
2435
|
+
return [
|
|
2436
|
+
...walkChildren(cmd, options)
|
|
2437
|
+
];
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
/**
|
|
2441
|
+
* Get the command at the root of the command tree.
|
|
2442
|
+
*/ function getRootCommand(cmd) {
|
|
2443
|
+
if (cmd.isRoot) return cmd;
|
|
2444
|
+
return getAncestors(cmd).pop();
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
/**
|
|
2448
|
+
* Set an Option's 'long' name. The 'flags' property is updated accordingly.
|
|
2449
|
+
* The '--' prefix is automatically added if not present.
|
|
2450
|
+
*/ function setOptionLongName(opt, long) {
|
|
2451
|
+
opt.long = strEnsureStartsWith(long, '--').replace(/^-+/, '--');
|
|
2452
|
+
opt.flags = renderOptionFlags(opt);
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
/**
|
|
2456
|
+
* Creates a parser function that parses a string into a boolean.
|
|
2457
|
+
*
|
|
2458
|
+
* @param trueValues - An array of strings that are considered true. Defaults to ['TRUE', 'T', 'YES', 'Y', '1'].
|
|
2459
|
+
* @param falseValues - An array of strings that are considered false. Defaults to ['', 'FALSE', 'F', 'NO', 'N', '0'].
|
|
2460
|
+
*/ function createBooleanParser(trueValues = [
|
|
2461
|
+
'TRUE',
|
|
2462
|
+
'T',
|
|
2463
|
+
'YES',
|
|
2464
|
+
'Y',
|
|
2465
|
+
'1'
|
|
2466
|
+
], falseValues = [
|
|
2467
|
+
'',
|
|
2468
|
+
'FALSE',
|
|
2469
|
+
'F',
|
|
2470
|
+
'NO',
|
|
2471
|
+
'N',
|
|
2472
|
+
'0'
|
|
2473
|
+
]) {
|
|
2474
|
+
return function parseBoolean(string) {
|
|
2475
|
+
string = string.toUpperCase();
|
|
2476
|
+
if (falseValues.includes(string)) return false;
|
|
2477
|
+
if (trueValues.includes(string)) return true;
|
|
2478
|
+
throw new TypeError(`The value ${string} is not a valid boolean value. Accepted values are: ${[
|
|
2479
|
+
...trueValues,
|
|
2480
|
+
...falseValues
|
|
2481
|
+
].join(', ')}`);
|
|
2482
|
+
};
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
/**
|
|
2486
|
+
* Creates a function that validates if the length of the input is equal to the specified length.
|
|
2487
|
+
* The returned function accepts any value with a 'name' property and is named 'isLengthOf' concatenated with the specified length.
|
|
2488
|
+
* @param length - The length to validate against.
|
|
2489
|
+
* @throws if length is not an integer.
|
|
2490
|
+
*/ function createLengthValidator(length) {
|
|
2491
|
+
assertThat(length, Number.isInteger);
|
|
2492
|
+
return funSetName('isLengthOf' + length, function(input) {
|
|
2493
|
+
return input.length === length;
|
|
2494
|
+
});
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2497
|
+
/**
|
|
2498
|
+
* Creates a parser function that parses a delimited string into a list of typed values.
|
|
2499
|
+
* The parsers array corresponds to the ordering of the expected input values.
|
|
2500
|
+
*
|
|
2501
|
+
* @param delimiter - The delimiter used to split the string into individual values.
|
|
2502
|
+
* @param parsers - An array of functions used to parse each individual value in the string.
|
|
2503
|
+
* @returns A function that takes a delimited string and returns an array of typed values.
|
|
2504
|
+
* @template T - The type of the values in the list.
|
|
2505
|
+
*/ function createTupleListParser(delimiter, parsers) {
|
|
2506
|
+
const isValidLength = createLengthValidator(parsers.length);
|
|
2507
|
+
return function parseList(string) {
|
|
2508
|
+
return assertThat(string.split(delimiter).map((str)=>str.trim()), isValidLength).map((str, i)=>parsers[i](str));
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
/**
|
|
2513
|
+
* Parses a string into a boolean.
|
|
2514
|
+
*
|
|
2515
|
+
* Accepted values (case insensitive):
|
|
2516
|
+
* - true: 'TRUE', 'T', 'YES', 'Y', '1'
|
|
2517
|
+
* - false: 'FALSE', 'F', 'NO', 'N', '0'
|
|
2518
|
+
*
|
|
2519
|
+
* @throws Will throw an error if the input string is not a valid input.
|
|
2520
|
+
*/ const parseBoolean = createBooleanParser([
|
|
2521
|
+
'TRUE',
|
|
2522
|
+
'T',
|
|
2523
|
+
'YES',
|
|
2524
|
+
'Y',
|
|
2525
|
+
'1'
|
|
2526
|
+
], [
|
|
2527
|
+
'FALSE',
|
|
2528
|
+
'F',
|
|
2529
|
+
'NO',
|
|
2530
|
+
'N',
|
|
2531
|
+
'0'
|
|
2532
|
+
]);
|
|
2533
|
+
|
|
2534
|
+
function isBoolean(value) {
|
|
2535
|
+
return typeof value === 'boolean';
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
/**
|
|
2539
|
+
* Determine whether the input is an integer array.
|
|
2540
|
+
*/ function isIntegerArray(value) {
|
|
2541
|
+
return Array.isArray(value) && value.every((v)=>isInteger(v));
|
|
2542
|
+
}
|
|
2543
|
+
|
|
2544
|
+
function isNull(value) {
|
|
2545
|
+
return value === null;
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
/**
|
|
2549
|
+
* Determine whether the input is a number array.
|
|
2550
|
+
*/ function isNumberArray(value) {
|
|
2551
|
+
return Array.isArray(value) && value.every((v)=>isNumber(v));
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
function isNumericString(string) {
|
|
2555
|
+
return /^-?[0-9,.]+$/.test(string.trim());
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
export { AbstractJsonFileSection, AbstractStringParserSelector, AbstractValidatorSelector, ArgumentBuilder, ArgumentParserSelector, ArgumentReader, ArgumentValidatorSelector, Base, CLI, CommandBuilder, CommandBuilderMetaData, CommandFeatureSelector, CommandReader, ConfigSection, ErrorParser, JsonDB, JsonFile, JsonFileError, MethodDisabler, OptionArgumentParserSelector, OptionArgumentValidatorSelector, OptionBuilder, OptionReader, OutputManager, PresetsSection, actionWrapper, addConfigCommands, addPresetsCommands, addUtilCommands, arrAssign, assertCommandNameNotReserved, assertNoDuplicateCommandNames, assertNoDuplicateOptionNames, assertPresetArgsOptional, assertValidArguments, assertValidOptions, assertValidPreset, autoAssignMissingOptionFlags, autoAssignSubCommandAliases, combineVariadicArgs, commandExists, commandLocation, configureHelp, createArrayMerger, createBooleanParser, createConfig, createLengthValidator, createObjectMerger, createPresets, createTupleListParser, createTypedArrayValidator, createTypedListParser, debugLogArgsOpts, deleteOptionsWithDefaultOrNoValue, ensureBackRefToCommandBuilder, errParseStack, errPrettyStack, errToObject, errorToString, escapeShellCommandArgument, forEachChildRecursive, formatTableForTerminal, getARGV, getAncestors, getChildren, getClosestNonNativeParent, getGlobalOptions, getJsonFilepath, getOptionArgumentName, getOwnAndGlobalOptions, getPresetArgsAndOpts, getRootCommand, getSiblings, handleError, handleOutputOptions, hasVariadicArguments, initializeCommand, isArray, isBoolean, isInteger, isIntegerArray, isNamedFunction, isNamedFunctionArray, isNull, isNumber, isNumberArray, isNumericString, isString, isStringArray, isStringWithNoSpacesOrDashes, objAssign, objDestroy, optHasArgument, optsWithGlobalsParsed, padArgsWithUndefinedUntilExpectedLength, parseArguments, parseBoolean, parseInteger, parseNumber, parseOptions, parseString, parsedValidArgsOptsWithPresets, parsedValidArgsWithPresets, parsedValidOptsWithPresets, prefixArray, prefixString, prefixStringsRecursive, realizeLazyProperty, renderOptionFlags, setOptionLongName, setOptionShortName, splitCombinedArgvShorts, walkAncestors, walkChildren, walkSiblings };
|