@bemoje/cli 0.0.2 → 0.0.3
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/index.cjs.js +2338 -1764
- package/index.esm.js +2243 -1659
- package/package.json +5 -3
- package/src/{core/CommandBuilder → arg}/ArgumentBuilder.d.ts +5 -6
- package/src/arg/ArgumentParserSelector.d.ts +8 -0
- package/src/{core/CommandBuilder → arg}/ArgumentReader.d.ts +1 -2
- package/src/arg/ArgumentValidatorSelector.d.ts +7 -0
- package/src/cmd/CLI.d.ts +2 -0
- package/src/cmd/CommandBuilder.d.ts +238 -0
- package/src/cmd/CommandBuilderMetaData.d.ts +23 -0
- package/src/cmd/CommandFeatureSelector.d.ts +79 -0
- package/src/cmd/DefaultHelpConfig.d.ts +2 -0
- package/src/core/OutputManager.d.ts +63 -0
- package/src/{parsers/selector/AbstractStringParserSelector.d.ts → core/ParserSelector.d.ts} +5 -6
- package/src/core/ValidatorSelector.d.ts +16 -0
- package/src/core/counter.d.ts +3 -0
- package/src/db/AbstractJsonFileSection.d.ts +136 -0
- package/src/db/AppDataSection.d.ts +29 -0
- package/src/db/ConfigSection.d.ts +47 -0
- package/src/db/JsonFile.d.ts +35 -0
- package/src/db/PresetsSection.d.ts +44 -0
- package/src/index.d.ts +102 -112
- package/src/opt/OptionArgumentParserSelector.d.ts +7 -0
- package/src/opt/OptionArgumentValidatorSelector.d.ts +7 -0
- package/src/{core/CommandBuilder → opt}/OptionBuilder.d.ts +6 -5
- package/src/opt/OptionHelpers.d.ts +32 -0
- package/src/{core/CommandBuilder → opt}/OptionReader.d.ts +2 -3
- package/src/proto/overrideCommanderPrototype.d.ts +8 -0
- package/src/types/IConfig.d.ts +21 -0
- package/src/types/IPreset.d.ts +6 -7
- package/src/util/array/arrLast.d.ts +12 -0
- package/src/util/array/arrSome.d.ts +15 -0
- package/src/util/array/types/ArrayPredicate.d.ts +10 -0
- package/src/util/db/JsonDB.d.ts +65 -0
- package/src/util/fs/promptUserEditInTextEditor/IGetUserInputFromEditorOptions.d.ts +15 -0
- package/src/util/fs/promptUserEditInTextEditor/promptUserEditInTextEditorSync.d.ts +9 -0
- package/src/util/fs/promptUserEditInTextEditor/promptUserEditJsonInTextEditorSync.d.ts +13 -0
- package/src/util/fs/readFile/readFileSafeSync.d.ts +13 -0
- package/src/util/fs/readFile/readFileSync.d.ts +10 -0
- package/src/util/fs/readJsonFile/readJsonFileSafeSync.d.ts +9 -0
- package/src/util/fs/removeFile/removeFile.d.ts +1 -0
- package/src/util/fs/tempFile/tempFileSync.d.ts +7 -0
- package/src/util/fs/types/IReadJsonFileOptions.d.ts +1 -0
- package/src/util/fs/writeFile/writeFileSafeSync.d.ts +1 -0
- package/src/util/fs/writeFile/writeFileSync.d.ts +1 -0
- package/src/util/fs/writeJsonFile/writeJsonFileSafe.d.ts +1 -0
- package/src/{core/util → util/function}/MethodDisabler.d.ts +4 -3
- package/src/util/function/funSetName.d.ts +13 -0
- package/src/util/node/execInherit.d.ts +1 -0
- package/src/{core/util → util/object}/createArrayMerger.d.ts +3 -0
- package/src/{core/util → util/object}/createObjectMerger.d.ts +4 -1
- package/src/util/object/objUpdatePropertyDescriptors.d.ts +16 -0
- package/src/util/object/setNonEnumerable.d.ts +15 -0
- package/src/util/os/defaultOpenInEditorCommand.d.ts +5 -0
- package/src/util/os/isOSX.d.ts +6 -0
- package/src/util/os/isVsCodeInstalled.d.ts +5 -0
- package/src/util/os/isWindows.d.ts +8 -0
- package/src/util/path/getTempDataPath.d.ts +6 -0
- package/src/util/regex/regexEscapeString.d.ts +11 -0
- package/src/util/string/strEnsureStartsWith.d.ts +12 -0
- package/src/util/string/strFirstCharToUpperCase.d.ts +9 -0
- package/src/util/string/strIsLowerCase.d.ts +11 -0
- package/src/util/string/strIsUpperCase.d.ts +11 -0
- package/src/util/string/strSplitCamelCase.d.ts +11 -0
- package/src/util/types/Any.d.ts +1 -0
- package/src/util/types/DeepArray.d.ts +10 -0
- package/src/util/types/DeepObject.d.ts +14 -0
- package/src/util/types/JsonArray.d.ts +6 -0
- package/src/util/types/JsonDefinedPrimitive.d.ts +4 -0
- package/src/util/types/JsonObject.d.ts +6 -0
- package/src/util/types/JsonRawPrimitive.d.ts +5 -0
- package/src/util/types/JsonValue.d.ts +7 -0
- package/src/util/types/ObjectKey.d.ts +1 -0
- package/src/util/types/TConstructor.d.ts +5 -0
- package/src/util/types/TFunction.d.ts +4 -0
- package/src/util/types/TFunctionNoNew.d.ts +2 -0
- package/src/util/types/TPlainObject.d.ts +2 -0
- package/src/util/types/TPrimitive.d.ts +1 -0
- package/src/util/types/TValidator.d.ts +1 -0
- package/src/{validators → util/validation}/createTypedArrayValidator.d.ts +1 -3
- package/src/util/validation/ensureThat.d.ts +6 -0
- package/src/util/validation/isFunction.d.ts +11 -0
- package/src/{validators → util/validation}/isNamedFunction.d.ts +1 -1
- package/src/util/validation/isObject.d.ts +15 -0
- package/src/util/validation/isPlainObject.d.ts +6 -0
- package/src/util/validation/isPrimitive.d.ts +12 -0
- package/src/util/validation/numbers/isInteger.d.ts +13 -0
- package/src/util/validation/numbers/isValidNumber.d.ts +15 -0
- package/src/core/CommandBuilder/Base.d.ts +0 -5
- package/src/core/CommandBuilder/CommandBuilder.d.ts +0 -80
- package/src/core/CommandBuilder/CommandBuilder.example.d.ts +0 -1
- package/src/core/CommandBuilder/CommandBuilderMetaData.d.ts +0 -17
- package/src/core/CommandBuilder/CommandFeatureSelector.d.ts +0 -26
- package/src/core/CommandBuilder/OutputManager.d.ts +0 -47
- package/src/core/CommandBuilder/assertCommandNameNotReserved.d.ts +0 -1
- package/src/core/CommandBuilder/ensureBackRefToCommandBuilder.d.ts +0 -7
- package/src/core/CommandBuilder/features/action/actionWrapper.d.ts +0 -5
- package/src/core/CommandBuilder/features/action/combineVariadicArgs.d.ts +0 -3
- package/src/core/CommandBuilder/features/action/debugLogArgsOpts.d.ts +0 -4
- package/src/core/CommandBuilder/features/action/deleteOptionsWithDefaultOrNoValue.d.ts +0 -3
- package/src/core/CommandBuilder/features/action/getPresetArgsAndOpts.d.ts +0 -3
- package/src/core/CommandBuilder/features/action/handleError.d.ts +0 -2
- package/src/core/CommandBuilder/features/action/handleOutputOptions.d.ts +0 -2
- package/src/core/CommandBuilder/features/action/optsWithGlobalsParsed.d.ts +0 -2
- package/src/core/CommandBuilder/features/action/padArgsWithUndefinedUntilExpectedLength.d.ts +0 -3
- package/src/core/CommandBuilder/features/action/parseArguments.d.ts +0 -2
- package/src/core/CommandBuilder/features/action/parseOptions.d.ts +0 -6
- package/src/core/CommandBuilder/features/action/parsedValidArgsOptsWithPresets.d.ts +0 -2
- package/src/core/CommandBuilder/features/action/parsedValidArgsWithPresets.d.ts +0 -2
- package/src/core/CommandBuilder/features/action/parsedValidOptsWithPresets.d.ts +0 -3
- package/src/core/CommandBuilder/features/addConfigCommands.d.ts +0 -3
- package/src/core/CommandBuilder/features/addPresetsCommands.d.ts +0 -3
- package/src/core/CommandBuilder/features/addUtilCommands.d.ts +0 -2
- package/src/core/CommandBuilder/features/assertNoDuplicateCommandNames.d.ts +0 -2
- package/src/core/CommandBuilder/features/assertNoDuplicateOptionNames.d.ts +0 -2
- package/src/core/CommandBuilder/features/autoAssignMissingOptionFlags.d.ts +0 -14
- package/src/core/CommandBuilder/features/autoAssignSubCommandAliases.d.ts +0 -13
- package/src/core/CommandBuilder/features/getClosestNonNativeParent.d.ts +0 -2
- package/src/core/CommandBuilder/getGlobalOptions.d.ts +0 -3
- package/src/core/CommandBuilder/getOwnAndGlobalOptions.d.ts +0 -3
- package/src/core/CommandBuilder/initializeCommand.d.ts +0 -2
- package/src/core/db/AbstractJsonFileSection.d.ts +0 -45
- package/src/core/db/ConfigSection.d.ts +0 -19
- package/src/core/db/JsonDB.d.ts +0 -19
- package/src/core/db/JsonFile.d.ts +0 -25
- package/src/core/db/JsonFileError.d.ts +0 -6
- package/src/core/db/PresetsSection.d.ts +0 -14
- package/src/core/help/configureHelp.d.ts +0 -2
- package/src/core/util/assertPresetArgsOptional.d.ts +0 -3
- package/src/core/util/assertValidArguments.d.ts +0 -6
- package/src/core/util/assertValidOptions.d.ts +0 -6
- package/src/core/util/assertValidPreset.d.ts +0 -3
- package/src/core/util/commandExists.d.ts +0 -5
- package/src/core/util/commandLocation.d.ts +0 -5
- package/src/core/util/errorToString.d.ts +0 -1
- package/src/core/util/escapeShellCommandArgument.d.ts +0 -5
- package/src/core/util/forEachChildRecursive.d.ts +0 -4
- package/src/core/util/getARGV.d.ts +0 -1
- package/src/core/util/getAncestors.d.ts +0 -7
- package/src/core/util/getChildren.d.ts +0 -4
- package/src/core/util/getJsonFilepath.d.ts +0 -2
- package/src/core/util/getOptionArgumentName.d.ts +0 -5
- package/src/core/util/getRootCommand.d.ts +0 -5
- package/src/core/util/getSiblings.d.ts +0 -5
- package/src/core/util/hasVariadicArguments.d.ts +0 -5
- package/src/core/util/objDestroy.d.ts +0 -2
- package/src/core/util/optHasArgument.d.ts +0 -2
- package/src/core/util/prefixArray.d.ts +0 -5
- package/src/core/util/prefixString.d.ts +0 -5
- package/src/core/util/prefixStringsRecursive.d.ts +0 -5
- package/src/core/util/renderOptionFlags.d.ts +0 -6
- package/src/core/util/setOptionLongName.d.ts +0 -6
- package/src/core/util/setOptionShortName.d.ts +0 -6
- package/src/core/util/walkAncestors.d.ts +0 -7
- package/src/core/util/walkChildren.d.ts +0 -4
- package/src/core/util/walkSiblings.d.ts +0 -5
- package/src/parsers/createTupleListParser.d.ts +0 -11
- package/src/parsers/selector/ArgumentParserSelector.d.ts +0 -7
- package/src/parsers/selector/OptionArgumentParserSelector.d.ts +0 -7
- package/src/types/IDefinePropertyOptions.d.ts +0 -9
- package/src/types/IPresets.d.ts +0 -6
- package/src/types/TValidator.d.ts +0 -2
- package/src/validators/createLengthValidator.d.ts +0 -7
- package/src/validators/isInteger.d.ts +0 -1
- package/src/validators/isIntegerArray.d.ts +0 -4
- package/src/validators/isNull.d.ts +0 -1
- package/src/validators/isNumber.d.ts +0 -1
- package/src/validators/isNumberArray.d.ts +0 -4
- package/src/validators/isNumericString.d.ts +0 -1
- package/src/validators/selector/ArgumentValidatorSelector.d.ts +0 -6
- package/src/validators/selector/OptionArgumentValidatorSelector.d.ts +0 -6
- package/src/validators/selector/ValidatorSelector.d.ts +0 -17
- /package/src/core/{util/splitCombinedArgvShorts.d.ts → splitCombinedArgvShorts.d.ts} +0 -0
- /package/src/{core/CommandBuilder → util/errors}/ErrorParser.d.ts +0 -0
- /package/src/{core/CommandBuilder/features → util/node}/formatTableForTerminal.d.ts +0 -0
- /package/src/{core/util → util/object}/arrAssign.d.ts +0 -0
- /package/src/{core/util → util/object}/objAssign.d.ts +0 -0
- /package/src/{core/util → util/object}/realizeLazyProperty.d.ts +0 -0
- /package/src/{parsers → util/string-parsers}/createBooleanParser.d.ts +0 -0
- /package/src/{parsers → util/string-parsers}/createTypedListParser.d.ts +0 -0
- /package/src/{parsers → util/string-parsers}/parseBoolean.d.ts +0 -0
- /package/src/{parsers → util/string-parsers}/parseInteger.d.ts +0 -0
- /package/src/{parsers → util/string-parsers}/parseNumber.d.ts +0 -0
- /package/src/{parsers → util/string-parsers}/parseString.d.ts +0 -0
- /package/src/{types → util/types}/TStringParser.d.ts +0 -0
- /package/src/{validators → util/validation}/isArray.d.ts +0 -0
- /package/src/{validators → util/validation}/isBoolean.d.ts +0 -0
- /package/src/{validators → util/validation}/isNamedFunctionArray.d.ts +0 -0
- /package/src/{validators → util/validation}/isString.d.ts +0 -0
- /package/src/{validators → util/validation}/isStringArray.d.ts +0 -0
- /package/src/{validators → util/validation}/isStringWithNoSpacesOrDashes.d.ts +0 -0
package/index.esm.js
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
import { Argument,
|
|
2
|
-
|
|
1
|
+
import { Argument, Command, Help, Option, CommanderError, InvalidArgumentError } from 'commander';
|
|
2
|
+
export * from 'commander';
|
|
3
|
+
import colors from 'ansi-colors';
|
|
4
|
+
import fs, { writeFileSync, outputJson, remove } from 'fs-extra';
|
|
5
|
+
export { remove as removeFile, outputFileSync as writeFileSafeSync, writeFileSync, outputJson as writeJsonFileSafe } from 'fs-extra';
|
|
6
|
+
import isAsyncFunction from 'is-async-function';
|
|
3
7
|
import os from 'os';
|
|
4
8
|
import path from 'path';
|
|
5
9
|
import Table from 'cli-table';
|
|
6
|
-
import {
|
|
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
|
-
})();
|
|
10
|
+
import { execSync } from 'child_process';
|
|
17
11
|
|
|
18
12
|
/**
|
|
19
13
|
* Creates a parser function that parses a delimited string into a list of typed values.
|
|
@@ -50,7 +44,10 @@ function parseString(string) {
|
|
|
50
44
|
return string;
|
|
51
45
|
}
|
|
52
46
|
|
|
53
|
-
class
|
|
47
|
+
class ParserSelector {
|
|
48
|
+
constructor(builder){
|
|
49
|
+
this.builder = builder;
|
|
50
|
+
}
|
|
54
51
|
string() {
|
|
55
52
|
return this.custom(parseString);
|
|
56
53
|
}
|
|
@@ -72,20 +69,33 @@ class AbstractStringParserSelector extends Base {
|
|
|
72
69
|
delimited(delimiter = ',', parser) {
|
|
73
70
|
return this.custom(createTypedListParser(delimiter, parser));
|
|
74
71
|
}
|
|
75
|
-
constructor(builder){
|
|
76
|
-
super();
|
|
77
|
-
this.builder = builder;
|
|
78
|
-
}
|
|
79
72
|
}
|
|
80
73
|
|
|
81
|
-
class ArgumentParserSelector extends
|
|
74
|
+
class ArgumentParserSelector extends ParserSelector {
|
|
75
|
+
constructor(builder){
|
|
76
|
+
super(builder);
|
|
77
|
+
}
|
|
82
78
|
custom(parser) {
|
|
83
79
|
this.builder.cmd.meta.argParsers[this.builder.index] = parser;
|
|
84
80
|
return this.builder;
|
|
85
81
|
}
|
|
86
82
|
}
|
|
87
83
|
|
|
88
|
-
|
|
84
|
+
const counts = new Map();
|
|
85
|
+
function countInstance(ctor) {
|
|
86
|
+
const name = ctor.name;
|
|
87
|
+
const count = counts.get(name) ?? 0;
|
|
88
|
+
counts.set(name, count + 1);
|
|
89
|
+
}
|
|
90
|
+
function printCounts() {
|
|
91
|
+
console.log(counts);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
class ArgumentReader {
|
|
95
|
+
constructor(parent){
|
|
96
|
+
this.parent = parent;
|
|
97
|
+
countInstance(ArgumentBuilder);
|
|
98
|
+
}
|
|
89
99
|
get $() {
|
|
90
100
|
return this.parent.$;
|
|
91
101
|
}
|
|
@@ -113,10 +123,49 @@ class ArgumentReader extends Base {
|
|
|
113
123
|
get optional() {
|
|
114
124
|
return !this.$.required;
|
|
115
125
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Returns the last element of an array.
|
|
130
|
+
* Throws an error if the array is empty.
|
|
131
|
+
* @template T The type of elements in the array.
|
|
132
|
+
* @param array The array to get the last element from.
|
|
133
|
+
* @returns The last element of the array.
|
|
134
|
+
* @throws If the array is empty.
|
|
135
|
+
* @example const numbers = [1, 2, 3, 4, 5];
|
|
136
|
+
* const lastNumber = arrLast(numbers);
|
|
137
|
+
* //=> 5
|
|
138
|
+
*/ function arrLast(array) {
|
|
139
|
+
if (!array.length) throw new Error('Cannot get last element of empty array.');
|
|
140
|
+
return array[array.length - 1];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function ensureThat(value, validator, options = {}) {
|
|
144
|
+
const result = validator(value, ...options.args ?? []);
|
|
145
|
+
if (result === true) return value;
|
|
146
|
+
const message = typeof result === 'string' ? `${result}. Got: ${value}` : `Expected '${validator.name}'. Got: ${value}`;
|
|
147
|
+
throw new (options.Err ?? Error)(message);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* This function sets the name of a function and returns the function with the new name.
|
|
152
|
+
* @template T - The type of the function.
|
|
153
|
+
* @param name The new name to be set for the function.
|
|
154
|
+
* @param fun The function whose name is to be set.
|
|
155
|
+
* @returns The function with the new name.
|
|
156
|
+
* @example ```ts
|
|
157
|
+
* const myFun = () => 'Hello World';
|
|
158
|
+
* funSetName('newFun', myFun).name;;
|
|
159
|
+
* //=> 'newFun'
|
|
160
|
+
* ```
|
|
161
|
+
*/ function funSetName(name, fun) {
|
|
162
|
+
Object.defineProperty(fun, 'name', {
|
|
163
|
+
value: name,
|
|
164
|
+
configurable: true,
|
|
165
|
+
writable: true,
|
|
166
|
+
enumerable: false
|
|
167
|
+
});
|
|
168
|
+
return fun;
|
|
120
169
|
}
|
|
121
170
|
|
|
122
171
|
/**
|
|
@@ -129,6 +178,17 @@ function isNamedFunctionArray(array) {
|
|
|
129
178
|
return Array.isArray(array) && array.every(isNamedFunction);
|
|
130
179
|
}
|
|
131
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Converts the first character of a string to uppercase.
|
|
183
|
+
* @param string The string to be converted.
|
|
184
|
+
* @example ```ts
|
|
185
|
+
* strFirstCharToUpperCase('hello');
|
|
186
|
+
* //=> 'Hello'
|
|
187
|
+
* ```
|
|
188
|
+
*/ function strFirstCharToUpperCase(string) {
|
|
189
|
+
return string.charAt(0).toUpperCase() + string.substring(1);
|
|
190
|
+
}
|
|
191
|
+
|
|
132
192
|
/**
|
|
133
193
|
* Creates a validator function that checks whether the input is an array where all elements are valid according to every validator provided.
|
|
134
194
|
*
|
|
@@ -138,30 +198,57 @@ function isNamedFunctionArray(array) {
|
|
|
138
198
|
* @throws TypeError - if no name is provided and not all validators are named functions.
|
|
139
199
|
*/ function createTypedArrayValidator(validators, name) {
|
|
140
200
|
if (!name) {
|
|
141
|
-
|
|
142
|
-
name = '
|
|
201
|
+
ensureThat(validators, isNamedFunctionArray);
|
|
202
|
+
name = 'isArrayWhereEach' + validators.map((fun)=>strFirstCharToUpperCase(fun.name)).join('And');
|
|
143
203
|
}
|
|
144
204
|
return funSetName(name, function(array) {
|
|
145
205
|
return Array.isArray(array) && array.every((value)=>validators.every((isValid)=>isValid(value)));
|
|
146
206
|
});
|
|
147
207
|
}
|
|
148
208
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
function
|
|
152
|
-
|
|
153
|
-
|
|
209
|
+
/**
|
|
210
|
+
* Checks if the provided number is an integer.
|
|
211
|
+
* @remarks This function uses the built-in `Number.isInteger` method.
|
|
212
|
+
* @param int The number to check.
|
|
213
|
+
* @returns A boolean indicating whether the provided number is an integer.
|
|
214
|
+
* @example ```ts
|
|
215
|
+
* isInteger(5);
|
|
216
|
+
* //=> true
|
|
217
|
+
* isInteger(5.5);
|
|
218
|
+
* //=> false
|
|
219
|
+
* ```
|
|
220
|
+
*/ const isInteger = Number.isInteger;
|
|
154
221
|
|
|
155
222
|
function isString(value) {
|
|
156
223
|
return typeof value === 'string';
|
|
157
224
|
}
|
|
158
225
|
|
|
159
|
-
|
|
226
|
+
/**
|
|
227
|
+
* Checks if the provided value is a valid number.
|
|
228
|
+
* @remarks This function checks if the provided value is a finite number and not NaN.
|
|
229
|
+
* @param number The value to check.
|
|
230
|
+
* @returns A boolean indicating whether the provided value is a valid number.
|
|
231
|
+
* @example ```ts
|
|
232
|
+
* isValidNumber(123);
|
|
233
|
+
* //=> true
|
|
234
|
+
* isValidNumber(NaN);
|
|
235
|
+
* //=> false
|
|
236
|
+
* isValidNumber(Infinity);
|
|
237
|
+
* //=> false
|
|
238
|
+
* ```
|
|
239
|
+
*/ function isValidNumber(number) {
|
|
240
|
+
return isFinite(number) && !isNaN(number);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
class ValidatorSelector {
|
|
244
|
+
constructor(builder){
|
|
245
|
+
this.builder = builder;
|
|
246
|
+
}
|
|
160
247
|
isString() {
|
|
161
248
|
return this.custom(isString);
|
|
162
249
|
}
|
|
163
250
|
isNumber() {
|
|
164
|
-
return this.custom(
|
|
251
|
+
return this.custom(isValidNumber);
|
|
165
252
|
}
|
|
166
253
|
isInteger() {
|
|
167
254
|
return this.custom(isInteger);
|
|
@@ -170,7 +257,7 @@ class AbstractValidatorSelector extends Base {
|
|
|
170
257
|
return this.arrayWhereEach(isString);
|
|
171
258
|
}
|
|
172
259
|
isNumberArray() {
|
|
173
|
-
return this.arrayWhereEach(
|
|
260
|
+
return this.arrayWhereEach(isValidNumber);
|
|
174
261
|
}
|
|
175
262
|
isIntegerArray() {
|
|
176
263
|
return this.arrayWhereEach(isInteger);
|
|
@@ -178,13 +265,12 @@ class AbstractValidatorSelector extends Base {
|
|
|
178
265
|
arrayWhereEach(...validators) {
|
|
179
266
|
return this.custom(createTypedArrayValidator(validators));
|
|
180
267
|
}
|
|
181
|
-
constructor(builder){
|
|
182
|
-
super();
|
|
183
|
-
this.builder = builder;
|
|
184
|
-
}
|
|
185
268
|
}
|
|
186
269
|
|
|
187
|
-
class ArgumentValidatorSelector extends
|
|
270
|
+
class ArgumentValidatorSelector extends ValidatorSelector {
|
|
271
|
+
constructor(builder){
|
|
272
|
+
super(builder);
|
|
273
|
+
}
|
|
188
274
|
custom(validator) {
|
|
189
275
|
arrLast(this.builder.cmd.meta.argValidators).push(validator);
|
|
190
276
|
return this.builder;
|
|
@@ -202,16 +288,20 @@ function realizeLazyProperty(obj, key, value) {
|
|
|
202
288
|
}
|
|
203
289
|
|
|
204
290
|
/**
|
|
205
|
-
* Wrapper around the @see Argument class
|
|
206
|
-
*/ class ArgumentBuilder
|
|
291
|
+
* Wrapper around the @see Argument class from 'commander'.
|
|
292
|
+
*/ class ArgumentBuilder {
|
|
293
|
+
constructor(cmd, name){
|
|
294
|
+
this.cmd = cmd;
|
|
295
|
+
countInstance(ArgumentBuilder);
|
|
296
|
+
this.$ = new Argument(name);
|
|
297
|
+
this.index = cmd.meta.argValidators.length;
|
|
298
|
+
cmd.meta.argValidators[this.index] = [];
|
|
299
|
+
}
|
|
207
300
|
description(string) {
|
|
208
301
|
this.$.description = string;
|
|
209
302
|
return this;
|
|
210
303
|
}
|
|
211
304
|
default(value, description) {
|
|
212
|
-
if (this.$.required) {
|
|
213
|
-
throw new Error('Cannot set default value on required argument: ' + this.$.name());
|
|
214
|
-
}
|
|
215
305
|
this.$.default(value, description);
|
|
216
306
|
return this;
|
|
217
307
|
}
|
|
@@ -228,209 +318,87 @@ function realizeLazyProperty(obj, key, value) {
|
|
|
228
318
|
get get() {
|
|
229
319
|
return realizeLazyProperty(this, 'get', new ArgumentReader(this));
|
|
230
320
|
}
|
|
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
321
|
}
|
|
245
322
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
323
|
+
/**
|
|
324
|
+
* Creates a function that merges arrays based on a predicate function.
|
|
325
|
+
*/ function createArrayMerger(predicate) {
|
|
326
|
+
return function arrMerge(target, ...sources) {
|
|
327
|
+
for (const src of sources){
|
|
328
|
+
for(let i = 0; i < src.length; i++){
|
|
329
|
+
if (predicate(src[i], i, src)) {
|
|
330
|
+
target[i] = src[i];
|
|
331
|
+
}
|
|
332
|
+
}
|
|
251
333
|
}
|
|
252
334
|
return target;
|
|
253
335
|
};
|
|
254
|
-
return _extends.apply(this, arguments);
|
|
255
336
|
}
|
|
256
337
|
|
|
257
|
-
|
|
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
|
-
}
|
|
338
|
+
const arrAssign = createArrayMerger((value)=>value != null);
|
|
278
339
|
|
|
279
340
|
/**
|
|
280
|
-
*
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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;
|
|
341
|
+
* Checks if at least one element in the array satisfies the provided predicate.
|
|
342
|
+
* @param predicate The predicate function to apply to each element.
|
|
343
|
+
* @template T The type of elements in the input array.
|
|
344
|
+
* @returns Returns `true` if at least one element in the array passes the test implemented by the provided function, otherwise `false`.
|
|
345
|
+
* @param input The array to check.
|
|
346
|
+
* @example ```ts
|
|
347
|
+
* const numbers = [1, 2, 3, 4, 5];
|
|
348
|
+
* const isEven = (num) => num % 2 === 0;
|
|
349
|
+
* arrSome(numbers, isEven);
|
|
350
|
+
* //=> true
|
|
351
|
+
* ```
|
|
352
|
+
*/ function arrSome(input, predicate) {
|
|
353
|
+
for(let i = 0, len = input.length; i < len; i++){
|
|
354
|
+
if (predicate(input[i], i, input) === true) {
|
|
355
|
+
return true;
|
|
379
356
|
}
|
|
380
357
|
}
|
|
358
|
+
return false;
|
|
381
359
|
}
|
|
382
360
|
|
|
383
361
|
/**
|
|
384
|
-
*
|
|
385
|
-
*/ function
|
|
386
|
-
|
|
387
|
-
if (
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
node = node.parent;
|
|
392
|
-
}
|
|
362
|
+
* Checks if the provided value is a plain object, i.e. an object created by the native base `Object` constructor.
|
|
363
|
+
*/ function isPlainObject(value) {
|
|
364
|
+
if (Object.prototype.toString.call(value) !== '[object Object]') return false;
|
|
365
|
+
if (!value?.constructor) return true;
|
|
366
|
+
if (Object.prototype.toString.call(value.constructor.prototype) !== '[object Object]') return false;
|
|
367
|
+
if (!Object.prototype.hasOwnProperty.call(value.constructor.prototype, 'isPrototypeOf')) return false;
|
|
368
|
+
return true;
|
|
393
369
|
}
|
|
394
370
|
|
|
395
371
|
/**
|
|
396
|
-
*
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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();
|
|
372
|
+
* Checks if the provided value is a primitive type (null, undefined, bigint, boolean, number, string or symbol).
|
|
373
|
+
* @param value The value to check.
|
|
374
|
+
* @returns A boolean indicating whether the provided value is a primitive type.
|
|
375
|
+
* @example
|
|
376
|
+
* isPrimitive(123);
|
|
377
|
+
* //=> true
|
|
378
|
+
* isPrimitive({});
|
|
379
|
+
* //=> false
|
|
380
|
+
*/ function isPrimitive(value) {
|
|
381
|
+
return typeof value !== 'object' && typeof value !== 'function' || value === null;
|
|
415
382
|
}
|
|
416
|
-
let ARGV = splitCombinedArgvShorts(process.argv.slice(2));
|
|
417
383
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
return result;
|
|
384
|
+
/**
|
|
385
|
+
* Escapes special characters in a string to be used in a regular expression.
|
|
386
|
+
* @param str The input string to escape.
|
|
387
|
+
* @returns The escaped string.
|
|
388
|
+
* @example ```ts
|
|
389
|
+
* const input = 'Hello, world!';
|
|
390
|
+
* regexEscapeString(input);;
|
|
391
|
+
* //=> 'Hello, world!'
|
|
392
|
+
* ```
|
|
393
|
+
*/ function regexEscapeString(str) {
|
|
394
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // $& means the whole matched string
|
|
395
|
+
;
|
|
431
396
|
}
|
|
432
397
|
|
|
433
398
|
class ErrorParser {
|
|
399
|
+
constructor(error){
|
|
400
|
+
this.error = error instanceof Error ? error : new Error(String(error));
|
|
401
|
+
}
|
|
434
402
|
get name() {
|
|
435
403
|
return this.error.name;
|
|
436
404
|
}
|
|
@@ -455,12 +423,9 @@ class ErrorParser {
|
|
|
455
423
|
toJSON() {
|
|
456
424
|
return this.toObject();
|
|
457
425
|
}
|
|
458
|
-
constructor(error){
|
|
459
|
-
this.error = error instanceof Error ? error : new Error(String(error));
|
|
460
|
-
}
|
|
461
426
|
}
|
|
462
427
|
function errPrettyStack(error, parsedStackFrames) {
|
|
463
|
-
const frames = parsedStackFrames
|
|
428
|
+
const frames = parsedStackFrames ?? errParseStack(error.stack || '');
|
|
464
429
|
// width of the first column = the longest frame.cell string
|
|
465
430
|
const offset = 2 + frames.reduce((acc, frame)=>Math.max(acc, frame[0].length), 0);
|
|
466
431
|
// type and message
|
|
@@ -515,7 +480,6 @@ function errPrettyStack(error, parsedStackFrames) {
|
|
|
515
480
|
} else if (isPrimitive(error[key])) {
|
|
516
481
|
s += String(error[key]);
|
|
517
482
|
} else if (error[key] != null && typeof error[key] === 'object') {
|
|
518
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
519
483
|
s += error[key].toString();
|
|
520
484
|
} else {
|
|
521
485
|
s += JSON.stringify(error[key]);
|
|
@@ -572,19 +536,18 @@ function errToObject(error) {
|
|
|
572
536
|
// console.log('----------------------')
|
|
573
537
|
// }
|
|
574
538
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
function
|
|
584
|
-
return
|
|
539
|
+
/**
|
|
540
|
+
* Checks if the given value is a function.
|
|
541
|
+
*
|
|
542
|
+
* @example ```ts
|
|
543
|
+
* isFunction(class {}); //=> true
|
|
544
|
+
* isFunction(function () {}); //=> true
|
|
545
|
+
* isFunction(() => {}); //=> true
|
|
546
|
+
* ```
|
|
547
|
+
*/ function isFunction(value) {
|
|
548
|
+
return typeof value === 'function' && value !== Function.prototype;
|
|
585
549
|
}
|
|
586
550
|
|
|
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
551
|
/**
|
|
589
552
|
* A class that creates an object with methods for disabling/enabling a given method on a given object.
|
|
590
553
|
*
|
|
@@ -600,503 +563,441 @@ var _defaultDescriptor = /*#__PURE__*/ _class_private_field_loose_key("_defaultD
|
|
|
600
563
|
* console.log('This will print')
|
|
601
564
|
*
|
|
602
565
|
* assert(md.original === process.stdout.write)
|
|
603
|
-
*/ class MethodDisabler
|
|
566
|
+
*/ class MethodDisabler {
|
|
567
|
+
static #defaultDescriptor(value) {
|
|
568
|
+
return {
|
|
569
|
+
value,
|
|
570
|
+
writable: true,
|
|
571
|
+
enumerable: true,
|
|
572
|
+
configurable: true
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
static #noop = (...args)=>void 0;
|
|
576
|
+
static #memoized = new WeakMap();
|
|
577
|
+
#obj;
|
|
578
|
+
#key;
|
|
579
|
+
#original;
|
|
580
|
+
#hasOwn;
|
|
581
|
+
#originalDescriptor;
|
|
582
|
+
#noopDescriptor;
|
|
583
|
+
#isEnabled;
|
|
584
|
+
/**
|
|
585
|
+
* @param obj - The object on which the method is defined.
|
|
586
|
+
* @param key - The property name of the method.
|
|
587
|
+
*/ constructor(obj, key){
|
|
588
|
+
this.#isEnabled = true;
|
|
589
|
+
ensureThat(obj[key], isFunction);
|
|
590
|
+
this.#obj = obj;
|
|
591
|
+
this.#key = key;
|
|
592
|
+
this.#original = obj[key];
|
|
593
|
+
this.#hasOwn = Object.hasOwn(obj, key);
|
|
594
|
+
this.#originalDescriptor = this.#hasOwn ? Object.getOwnPropertyDescriptor(obj, key) : MethodDisabler.#defaultDescriptor(this.#original);
|
|
595
|
+
this.#noopDescriptor = Object.assign({}, this.#originalDescriptor, {
|
|
596
|
+
value: MethodDisabler.#noop
|
|
597
|
+
});
|
|
598
|
+
// return stored if memoized
|
|
599
|
+
if (MethodDisabler.#memoized.get(obj)?.has(key)) {
|
|
600
|
+
return MethodDisabler.#memoized.get(obj)?.get(key);
|
|
601
|
+
}
|
|
602
|
+
//memoize
|
|
603
|
+
if (!MethodDisabler.#memoized.has(obj)) MethodDisabler.#memoized.set(obj, new Map());
|
|
604
|
+
const methods = MethodDisabler.#memoized.get(obj);
|
|
605
|
+
methods.set(key, this);
|
|
606
|
+
}
|
|
604
607
|
/**
|
|
605
608
|
* Disable the method.
|
|
606
609
|
*/ disable() {
|
|
607
610
|
if (!this.isEnabled) return;
|
|
608
|
-
Object.defineProperty(
|
|
609
|
-
|
|
611
|
+
Object.defineProperty(this.#obj, this.#key, this.#noopDescriptor);
|
|
612
|
+
this.#isEnabled = false;
|
|
610
613
|
}
|
|
611
614
|
/**
|
|
612
615
|
* Enable the method.
|
|
613
616
|
*/ enable() {
|
|
614
617
|
if (this.isEnabled) return;
|
|
615
|
-
if (
|
|
616
|
-
else delete
|
|
617
|
-
|
|
618
|
+
if (this.#hasOwn) Object.defineProperty(this.#obj, this.#key, this.#originalDescriptor);
|
|
619
|
+
else delete this.#obj[this.#key];
|
|
620
|
+
this.#isEnabled = true;
|
|
618
621
|
}
|
|
619
622
|
/**
|
|
620
623
|
* The original method before it was disabled.
|
|
621
624
|
*/ get original() {
|
|
622
|
-
return
|
|
625
|
+
return this.#original;
|
|
623
626
|
}
|
|
624
627
|
/**
|
|
625
628
|
* Whether the method is currently enabled.
|
|
626
629
|
*/ get isEnabled() {
|
|
627
|
-
return
|
|
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);
|
|
630
|
+
return this.#isEnabled;
|
|
682
631
|
}
|
|
683
632
|
}
|
|
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
633
|
|
|
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
634
|
/**
|
|
709
|
-
*
|
|
635
|
+
* The OutputManager class manages the output of debug messages to the console.
|
|
710
636
|
*/ class OutputManager {
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
637
|
+
/**
|
|
638
|
+
* Returns the singleton instance of the OutputManager class.
|
|
639
|
+
* If the instance does not exist, it creates a new one.
|
|
640
|
+
*/ static getInstance() {
|
|
641
|
+
if (!this.instance) this.instance = new OutputManager();
|
|
715
642
|
return this.instance;
|
|
716
643
|
}
|
|
717
|
-
|
|
718
|
-
|
|
644
|
+
/**
|
|
645
|
+
* Constructs a new instance of the OutputManager class.
|
|
646
|
+
* console.debug is disabled by default.
|
|
647
|
+
*/ constructor(){
|
|
648
|
+
/**
|
|
649
|
+
* Ansi-colors object
|
|
650
|
+
*/ this.colors = colors;
|
|
651
|
+
/**
|
|
652
|
+
* A MethodDisabler instance for disabling the write method of the process.stdout object.
|
|
653
|
+
*/ this.stdout = new MethodDisabler(process.stdout, 'write');
|
|
654
|
+
/**
|
|
655
|
+
* A MethodDisabler instance for disabling the write method of the process.stderr object.
|
|
656
|
+
*/ this.stderr = new MethodDisabler(process.stderr, 'write');
|
|
657
|
+
/**
|
|
658
|
+
* A MethodDisabler instance for disabling the debug method of the console object.
|
|
659
|
+
*/ this.debug = new MethodDisabler(console, 'debug');
|
|
660
|
+
/**
|
|
661
|
+
* An array that holds queued debug messages.
|
|
662
|
+
*/ this.debugMsgQueue = [];
|
|
663
|
+
countInstance(OutputManager);
|
|
664
|
+
this.debug.disable();
|
|
665
|
+
colors.enabled = (()=>{
|
|
666
|
+
const { FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM } = process.env;
|
|
667
|
+
return !NODE_DISABLE_COLORS && NO_COLOR == null && TERM !== 'dumb' && (FORCE_COLOR != null && FORCE_COLOR !== '0' || process.stdout && process.stdout.isTTY);
|
|
668
|
+
})();
|
|
669
|
+
}
|
|
670
|
+
get queueSize() {
|
|
671
|
+
return this.debugMsgQueue.length;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Resets the OutputManager to its default state.
|
|
675
|
+
* - Enables colors.
|
|
676
|
+
* - Enables the write method of the process.stdout object.
|
|
677
|
+
* - Enables the write method of the process.stderr object.
|
|
678
|
+
* - Disables the debug method of the console object.
|
|
679
|
+
* @returns The OutputManager instance.
|
|
680
|
+
*/ reset() {
|
|
681
|
+
this.colors.enabled = true;
|
|
719
682
|
this.stdout.enable();
|
|
720
683
|
this.stderr.enable();
|
|
721
684
|
this.debug.disable();
|
|
722
685
|
return this;
|
|
723
686
|
}
|
|
724
|
-
|
|
687
|
+
/**
|
|
688
|
+
* Outputs a debug message to the console.
|
|
689
|
+
* If the debug method is enabled, the message is immediately logged to the console.
|
|
690
|
+
* Otherwise, the message is added to the debug message queue.
|
|
691
|
+
* @param fn - A function that returns the debug message.
|
|
692
|
+
*/ outputDebug(fn) {
|
|
725
693
|
if (this.debug.isEnabled) console.debug(fn());
|
|
726
|
-
else this.
|
|
694
|
+
else this.debugMsgQueue.push(fn);
|
|
727
695
|
}
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
this.colors = colors;
|
|
734
|
-
this.stdout = stdout;
|
|
735
|
-
this.stderr = stderr;
|
|
736
|
-
this.debug = debug;
|
|
737
|
-
this.debugMsgs = [];
|
|
696
|
+
/**
|
|
697
|
+
* Drains the debug message queue by logging all the messages to the console.
|
|
698
|
+
*/ drainDebugMessageQueue() {
|
|
699
|
+
this.debugMsgQueue.forEach((fn)=>console.debug(fn()));
|
|
700
|
+
this.debugMsgQueue.splice(0, this.debugMsgQueue.length);
|
|
738
701
|
}
|
|
739
702
|
}
|
|
740
703
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
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
|
-
});
|
|
704
|
+
class CommandBuilderMetaData {
|
|
705
|
+
constructor(){
|
|
706
|
+
this.subcommands = [];
|
|
707
|
+
this.globalOptions = [];
|
|
708
|
+
this.hiddenGlobalOptions = new Set();
|
|
709
|
+
this.presetOptionKeys = [];
|
|
710
|
+
this.argParsers = [];
|
|
711
|
+
this.argValidators = [];
|
|
712
|
+
this.optParsers = {};
|
|
713
|
+
this.optValidators = {};
|
|
714
|
+
this.rawArgs = [];
|
|
715
|
+
this.isNative = false;
|
|
716
|
+
this.isInitialized = false;
|
|
717
|
+
countInstance(CommandBuilderMetaData);
|
|
778
718
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
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
|
-
}
|
|
719
|
+
get actionHandler() {
|
|
720
|
+
return async function defaultActionHandler() {
|
|
721
|
+
this.outputHelp();
|
|
722
|
+
};
|
|
799
723
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
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
|
-
}
|
|
724
|
+
get errorHandler() {
|
|
725
|
+
return function defaultErrorHandler(error) {
|
|
726
|
+
const parsed = new ErrorParser(error);
|
|
727
|
+
if (OutputManager.getInstance().debug.isEnabled) {
|
|
728
|
+
console.error(parsed.prettyStack());
|
|
729
|
+
this.outputError(parsed.summary());
|
|
730
|
+
} else {
|
|
731
|
+
this.outputError(parsed.summary());
|
|
819
732
|
}
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
get hasCustomActionHandler() {
|
|
736
|
+
return Object.hasOwn(this, 'actionHandler');
|
|
737
|
+
}
|
|
738
|
+
get hasCustomErrorHandler() {
|
|
739
|
+
return Object.hasOwn(this, 'errorHandler');
|
|
740
|
+
}
|
|
823
741
|
}
|
|
824
742
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
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);
|
|
743
|
+
function splitCombinedArgvShorts(argv) {
|
|
744
|
+
return argv.map((arg)=>{
|
|
745
|
+
if (arg.length < 3 || !arg.startsWith('-') || arg.startsWith('--') || arg.includes('=')) {
|
|
746
|
+
return arg;
|
|
838
747
|
}
|
|
839
|
-
|
|
840
|
-
|
|
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;
|
|
748
|
+
return Array.from(arg.replace('-', '')).map((s)=>'-' + s);
|
|
749
|
+
}).flat();
|
|
848
750
|
}
|
|
849
751
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
752
|
+
const commanderBackRefs = new WeakMap();
|
|
753
|
+
const oldParse = Command.prototype.parse;
|
|
754
|
+
Command.prototype.parse = function parse(argv, options) {
|
|
755
|
+
if (argv) {
|
|
756
|
+
argv = splitCombinedArgvShorts(argv.slice());
|
|
757
|
+
this.builder.meta.rawArgs = argv.slice(options?.from === 'user' ? 0 : 2);
|
|
758
|
+
} else {
|
|
759
|
+
this.builder.meta.rawArgs = process.argv.slice(2);
|
|
760
|
+
}
|
|
761
|
+
return oldParse.call(this, argv, options);
|
|
762
|
+
};
|
|
763
|
+
const oldParseAsync = Command.prototype.parseAsync;
|
|
764
|
+
Command.prototype.parseAsync = async function(argv, options) {
|
|
765
|
+
if (argv) {
|
|
766
|
+
argv = splitCombinedArgvShorts(argv.slice());
|
|
767
|
+
this.builder.meta.rawArgs = argv.slice(options?.from === 'user' ? 0 : 2);
|
|
768
|
+
} else {
|
|
769
|
+
this.builder.meta.rawArgs = process.argv.slice(2);
|
|
854
770
|
}
|
|
855
|
-
return
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
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
|
-
}
|
|
771
|
+
return await oldParseAsync.call(this, argv, options);
|
|
772
|
+
};
|
|
773
|
+
Object.defineProperty(Command.prototype, 'builder', {
|
|
774
|
+
get () {
|
|
775
|
+
const ins = commanderBackRefs.get(this);
|
|
776
|
+
if (!ins) throw new Error(`CommandBuilder not found for command ${this.name()}`);
|
|
777
|
+
return ins;
|
|
778
|
+
}
|
|
779
|
+
});
|
|
879
780
|
|
|
880
781
|
/**
|
|
881
|
-
*
|
|
882
|
-
*/
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
782
|
+
* Represents a selector for enabling or disabling command features.
|
|
783
|
+
*/ class CommandFeatureSelector {
|
|
784
|
+
/**
|
|
785
|
+
* Creates a new instance of the CommandFeatureSelector class.
|
|
786
|
+
* @param cmd - The command builder instance.
|
|
787
|
+
*/ constructor(cmd){
|
|
788
|
+
this.cmd = cmd;
|
|
789
|
+
this./**
|
|
790
|
+
* Indicates whether the app data feature is enabled.
|
|
791
|
+
*/ isAppDataEnabled = false;
|
|
792
|
+
this./**
|
|
793
|
+
* Indicates whether the config feature is enabled.
|
|
794
|
+
*/ isConfigEnabled = false;
|
|
795
|
+
this./**
|
|
796
|
+
* Indicates whether the presets feature is enabled.
|
|
797
|
+
*/ isPresetsEnabled = false;
|
|
798
|
+
this./**
|
|
799
|
+
* Indicates whether the auto assign missing option flags feature is enabled.
|
|
800
|
+
*/ isAutoAssignMissingOptionFlagsEnabled = false;
|
|
801
|
+
this./**
|
|
802
|
+
* Indicates whether the auto assign sub command aliases feature is enabled.
|
|
803
|
+
*/ isAutoAssignSubCommandAliasesEnabled = false;
|
|
804
|
+
countInstance(CommandFeatureSelector);
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Inherits the feature settings from a parent CommandFeatureSelector instance.
|
|
808
|
+
* @param parentFeatures - The parent CommandFeatureSelector instance to inherit from.
|
|
809
|
+
*/ inheritFrom(parentFeatures) {
|
|
810
|
+
if (!parentFeatures) return;
|
|
811
|
+
if (this.cmd.meta.isNative) this.isAutoAssignMissingOptionFlagsEnabled = true;
|
|
812
|
+
else this.isAutoAssignMissingOptionFlagsEnabled = parentFeatures.isAutoAssignMissingOptionFlagsEnabled;
|
|
813
|
+
this.isAutoAssignSubCommandAliasesEnabled = parentFeatures.isAutoAssignSubCommandAliasesEnabled;
|
|
814
|
+
this.isPresetsEnabled = parentFeatures.isPresetsEnabled;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Enables or disables the app data feature.
|
|
818
|
+
* @param boolean - Indicates whether the app data feature should be enabled or disabled.
|
|
819
|
+
* @returns The current CommandFeatureSelector instance.
|
|
820
|
+
* @throws Error if the command is native and the app data feature is being configured.
|
|
821
|
+
*/ appData(boolean = true) {
|
|
822
|
+
return this.setProperty(boolean, 'isAppDataEnabled', 'appData', true);
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Enables or disables the config feature.
|
|
826
|
+
* @param boolean - Indicates whether the config feature should be enabled or disabled.
|
|
827
|
+
* @returns The current CommandFeatureSelector instance.
|
|
828
|
+
* @throws Error if the command is native and the config feature is being configured.
|
|
829
|
+
*/ config(boolean = true) {
|
|
830
|
+
return this.setProperty(boolean, 'isConfigEnabled', 'config', true);
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Enables or disables the presets feature.
|
|
834
|
+
* @param boolean - Indicates whether the presets feature should be enabled or disabled.
|
|
835
|
+
* @returns The current CommandFeatureSelector instance.
|
|
836
|
+
* @throws Error if the command is native and the presets feature is being configured.
|
|
837
|
+
*/ presets(boolean = true) {
|
|
838
|
+
return this.setProperty(boolean, 'isPresetsEnabled', 'presets', true);
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Enables or disables the auto assign missing option flags feature.
|
|
842
|
+
* @param boolean - Indicates whether the auto assign missing option flags feature should be enabled or disabled.
|
|
843
|
+
* @returns The current CommandFeatureSelector instance.
|
|
844
|
+
*/ autoAssignMissingOptionFlags(boolean = true) {
|
|
845
|
+
return this.setProperty(boolean, 'isAutoAssignMissingOptionFlagsEnabled', 'autoAssignMissingOptionFlags', false);
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Enables or disables the auto assign sub command aliases feature.
|
|
849
|
+
* @param boolean - Indicates whether the auto assign sub command aliases feature should be enabled or disabled.
|
|
850
|
+
* @returns The current CommandFeatureSelector instance.
|
|
851
|
+
*/ autoAssignSubCommandAliases(boolean = true) {
|
|
852
|
+
return this.setProperty(boolean, 'isAutoAssignSubCommandAliasesEnabled', 'autoAssignSubCommandAliases', false);
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Helper method to enable or disable a feature.
|
|
856
|
+
* @param boolean - Indicates whether the auto assign sub command aliases feature should be enabled or disabled.
|
|
857
|
+
* @param prop - The property name to set.
|
|
858
|
+
* @param method - The method name to output in the debug message.
|
|
859
|
+
* @param throwIfNative - Indicates whether an error should be thrown if the command is native.
|
|
860
|
+
* @returns The current CommandFeatureSelector instance.
|
|
861
|
+
*/ setProperty(boolean = true, prop, method, throwIfNative) {
|
|
862
|
+
if (this[prop] === boolean) return this;
|
|
863
|
+
if (throwIfNative && this.cmd.meta.isNative) throw new Error(`Cannot configure ${method} for native command.`);
|
|
864
|
+
Object.defineProperty(this, prop, {
|
|
865
|
+
value: boolean,
|
|
866
|
+
configurable: true,
|
|
867
|
+
writable: true,
|
|
868
|
+
enumerable: true
|
|
869
|
+
});
|
|
870
|
+
this.cmd.outputDebugMessage('featureEnabled', ()=>({
|
|
871
|
+
[method]: boolean
|
|
872
|
+
}));
|
|
873
|
+
return this;
|
|
889
874
|
}
|
|
890
|
-
return parsedOptions;
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
function getOwnAndGlobalOptions(cmd) {
|
|
894
|
-
return cmd.options.concat(getGlobalOptions(cmd));
|
|
895
875
|
}
|
|
896
876
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
877
|
+
const DefaultHelpConfig = {
|
|
878
|
+
helpWidth: undefined,
|
|
879
|
+
showGlobalOptions: true,
|
|
880
|
+
sortSubcommands: false,
|
|
881
|
+
sortOptions: false,
|
|
882
|
+
subcommandTerm,
|
|
883
|
+
argumentTerm,
|
|
884
|
+
commandUsage,
|
|
885
|
+
visibleOptions,
|
|
886
|
+
visibleGlobalOptions,
|
|
887
|
+
subcommandDescription,
|
|
888
|
+
optionDescription,
|
|
889
|
+
argumentDescription,
|
|
890
|
+
commandDescription,
|
|
891
|
+
formatHelp
|
|
892
|
+
};
|
|
893
|
+
function formatHelp(cmd) {
|
|
894
|
+
let result = Help.prototype.formatHelp.call(this, cmd, this);
|
|
895
|
+
result = movePresetOptionsUnderOwnHeader(result);
|
|
896
|
+
result = rearrangeSections(result);
|
|
897
|
+
result = fixLinebreaks(result);
|
|
898
|
+
result = addColors(result);
|
|
899
|
+
return result;
|
|
900
|
+
function movePresetOptionsUnderOwnHeader(result) {
|
|
901
|
+
const lines = result.split('\n');
|
|
902
|
+
const presetLines = lines.filter((line)=>line.includes('[Preset]:'));
|
|
903
|
+
const firstPresetIndex = lines.findIndex((line)=>line.includes('[Preset]'));
|
|
904
|
+
if (firstPresetIndex !== -1) {
|
|
905
|
+
lines.splice(firstPresetIndex, presetLines.length);
|
|
906
|
+
lines.push('', 'Preset Options:', ...presetLines);
|
|
907
|
+
result = lines.join('\n');
|
|
902
908
|
}
|
|
909
|
+
return result;
|
|
903
910
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
911
|
+
function rearrangeSections(result) {
|
|
912
|
+
const order = [
|
|
913
|
+
'Usage:',
|
|
914
|
+
'Version:',
|
|
915
|
+
'Description:',
|
|
916
|
+
'Commands:',
|
|
917
|
+
'Arguments:',
|
|
918
|
+
'Options:',
|
|
919
|
+
'Global Options:',
|
|
920
|
+
'Preset Options:'
|
|
921
|
+
];
|
|
922
|
+
const blocks = {};
|
|
923
|
+
let lastAddedBlock = 'Description:';
|
|
924
|
+
result.split(/\n\n+/).forEach((block)=>{
|
|
925
|
+
const title = block.split(':')[0].trim() + ':';
|
|
926
|
+
if (/^[A-Z][\w ]+:/.test(title)) {
|
|
927
|
+
blocks[title] = block;
|
|
928
|
+
lastAddedBlock = title;
|
|
929
|
+
if (!order.includes(title)) order.push(title);
|
|
930
|
+
} else {
|
|
931
|
+
if (!order.includes(lastAddedBlock)) order.push(lastAddedBlock);
|
|
932
|
+
blocks[lastAddedBlock] = ((blocks[lastAddedBlock] || '') + '\n\n' + block).trim();
|
|
933
|
+
}
|
|
934
|
+
});
|
|
935
|
+
result = order.map((title)=>blocks[title]).join('\n\n').trim();
|
|
936
|
+
return result;
|
|
937
|
+
}
|
|
938
|
+
function fixLinebreaks(result) {
|
|
939
|
+
result = '\n' + result.trim() + '\n\n';
|
|
940
|
+
result = result.replace(/\n\n+/g, '\n\n');
|
|
941
|
+
return result;
|
|
942
|
+
}
|
|
943
|
+
function addColors(result) {
|
|
944
|
+
result = result.replace(/\|/g, colors.gray(colors.dim('|')));
|
|
945
|
+
result = result.replace(/^[A-Z][\w ]+:/gm, (s)=>colors.yellow(s));
|
|
946
|
+
return result;
|
|
913
947
|
}
|
|
914
|
-
return opts;
|
|
915
948
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
const
|
|
919
|
-
|
|
949
|
+
function subcommandTerm(cmd) {
|
|
950
|
+
const args = cmd.registeredArguments.map((arg)=>this.argumentTerm(arg)).join(' ');
|
|
951
|
+
const parent = cmd.parent || cmd;
|
|
952
|
+
const hasAliases = !cmd.parent || parent.commands.some((c)=>!!c.alias());
|
|
953
|
+
if (!hasAliases) return cmd.name() + (args ? ' ' + args : '');
|
|
954
|
+
const padsize = Math.max(1, ...parent.commands.map((c)=>c.alias()?.length || 1));
|
|
955
|
+
let alias = cmd.alias() || ' ';
|
|
956
|
+
alias = alias.padEnd(padsize, ' ') + ' |';
|
|
957
|
+
return alias + cmd.name() + (args ? ' ' + args : '');
|
|
958
|
+
}
|
|
959
|
+
function argumentTerm(arg) {
|
|
960
|
+
let r = arg.name();
|
|
961
|
+
const rest = arg.variadic ? '...' : '';
|
|
962
|
+
if (arg.required) r = '<' + r + rest + '>';
|
|
963
|
+
else r = '[' + r + rest + ']';
|
|
964
|
+
return r;
|
|
965
|
+
}
|
|
966
|
+
function commandUsage(cmd) {
|
|
967
|
+
const args = cmd.registeredArguments.map((arg)=>this.argumentTerm(arg)).join(' ');
|
|
968
|
+
return [
|
|
969
|
+
cmd.builder.getPrefixString() + ' ' + (args ? args : '[command]'),
|
|
970
|
+
'[options]'
|
|
971
|
+
].join(' ');
|
|
920
972
|
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
const
|
|
924
|
-
const
|
|
925
|
-
|
|
926
|
-
assertValidOptions(cb, opts);
|
|
927
|
-
return opts;
|
|
973
|
+
function visibleOptions(cmd) {
|
|
974
|
+
const builder = cmd.builder;
|
|
975
|
+
const opts = cmd.options.filter((opt)=>!opt.hidden && !builder.meta.globalOptions.includes(opt));
|
|
976
|
+
const presets = opts.filter((opt)=>opt.description.startsWith('[Preset]'));
|
|
977
|
+
return opts.filter((opt)=>!presets.includes(opt)).concat(presets);
|
|
928
978
|
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
const
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
return [
|
|
936
|
-
args,
|
|
937
|
-
opts
|
|
938
|
-
];
|
|
979
|
+
function visibleGlobalOptions(cmd) {
|
|
980
|
+
const gopts = cmd.builder.getGlobalOptions();
|
|
981
|
+
if (gopts.find((opt)=>opt.long === '--help')) return gopts;
|
|
982
|
+
const help = cmd.options.find((opt)=>opt.long === '--help');
|
|
983
|
+
if (help) gopts.push(help);
|
|
984
|
+
return gopts;
|
|
939
985
|
}
|
|
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
|
-
});
|
|
986
|
+
function subcommandDescription(cmd) {
|
|
987
|
+
return colors.gray(Help.prototype.subcommandDescription.call(this, cmd));
|
|
951
988
|
}
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
for (const anc of walkAncestors(cmd, {
|
|
955
|
-
includeSelf: true
|
|
956
|
-
})){
|
|
957
|
-
if (!anc.meta.isNative) return anc;
|
|
958
|
-
}
|
|
959
|
-
return cmd.root;
|
|
989
|
+
function optionDescription(opt) {
|
|
990
|
+
return colors.gray(Help.prototype.optionDescription.call(this, opt));
|
|
960
991
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
992
|
+
function argumentDescription(arg) {
|
|
993
|
+
return colors.gray(Help.prototype.argumentDescription.call(this, arg));
|
|
994
|
+
}
|
|
995
|
+
function commandDescription(cmd) {
|
|
996
|
+
const v = cmd.builder.getVersion();
|
|
997
|
+
const version = v ? colors.yellow('Version: ') + v + '\n\n' : '';
|
|
998
|
+
const description = 'Description:\n' + Help.prototype.commandDescription.call(this, cmd);
|
|
999
|
+
return version + description;
|
|
964
1000
|
}
|
|
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
1001
|
|
|
1101
1002
|
function formatTableForTerminal(rows, headers) {
|
|
1102
1003
|
if (!rows.length || !rows[0].length) return '';
|
|
@@ -1108,527 +1009,264 @@ function formatTableForTerminal(rows, headers) {
|
|
|
1108
1009
|
return table.toString();
|
|
1109
1010
|
}
|
|
1110
1011
|
|
|
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
|
-
}
|
|
1012
|
+
const isArray = Array.isArray;
|
|
1118
1013
|
|
|
1119
|
-
|
|
1120
|
-
|
|
1014
|
+
/**
|
|
1015
|
+
* Checks if the provided value is an object (null, arrays and functions not included).
|
|
1016
|
+
* @template T - The type of the value to check.
|
|
1017
|
+
* @param value The value to check.
|
|
1018
|
+
* @returns A boolean indicating whether the provided value is an object.
|
|
1019
|
+
* @example ```ts
|
|
1020
|
+
* isObject({});;
|
|
1021
|
+
* //=> true
|
|
1022
|
+
* isObject([1]);;
|
|
1023
|
+
* //=> false
|
|
1024
|
+
* isObject(123);;
|
|
1025
|
+
* //=> false
|
|
1026
|
+
* ```
|
|
1027
|
+
*/ function isObject(value) {
|
|
1028
|
+
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
1121
1029
|
}
|
|
1122
1030
|
|
|
1123
1031
|
/**
|
|
1124
|
-
*
|
|
1125
|
-
*/ function
|
|
1126
|
-
return
|
|
1032
|
+
* Determine whether the input is a string array.
|
|
1033
|
+
*/ function isStringArray(value) {
|
|
1034
|
+
return Array.isArray(value) && value.every((v)=>isString(v));
|
|
1127
1035
|
}
|
|
1128
1036
|
|
|
1129
|
-
function
|
|
1130
|
-
|
|
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
|
-
}
|
|
1037
|
+
function isStringWithNoSpacesOrDashes(value) {
|
|
1038
|
+
return isString(value) && /^[^\s-]+$/i.test(value);
|
|
1136
1039
|
}
|
|
1137
1040
|
|
|
1138
1041
|
/**
|
|
1139
|
-
*
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
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;
|
|
1042
|
+
* Checks if the current platform is OSX.
|
|
1043
|
+
* It checks the 'process' object and the 'platform' property to determine if the platform is 'darwin'.
|
|
1044
|
+
* @returns A boolean indicating whether the current platform is OSX.
|
|
1045
|
+
*/ function isOSX() {
|
|
1046
|
+
return process.platform === 'darwin';
|
|
1153
1047
|
}
|
|
1154
1048
|
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
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(', ')}`);
|
|
1049
|
+
/**
|
|
1050
|
+
* Returns whether Visual Studio Code is installed on the system.
|
|
1051
|
+
* @example isVsCodeInstalled() //=> true
|
|
1052
|
+
*/ function isVsCodeInstalled() {
|
|
1053
|
+
try {
|
|
1054
|
+
const stdout = execSync('code --help').toString();
|
|
1055
|
+
return stdout.startsWith('Visual Studio Code') && stdout.includes('-w --wait');
|
|
1056
|
+
} catch (e) {
|
|
1057
|
+
return false;
|
|
1237
1058
|
}
|
|
1238
1059
|
}
|
|
1239
1060
|
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
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
|
-
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Checks if the current platform is Windows.
|
|
1063
|
+
* @remarks
|
|
1064
|
+
* It checks the 'process' object and the 'platform' property to determine if the platform is 'win32'.
|
|
1065
|
+
* It also checks the 'OSTYPE' environment variable to see if it matches 'msys' or 'cygwin'.
|
|
1066
|
+
* @returns A boolean indicating whether the current platform is Windows.
|
|
1067
|
+
*/ function isWindows() {
|
|
1068
|
+
return isWin;
|
|
1274
1069
|
}
|
|
1070
|
+
const isWin = process && (process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env['OSTYPE'] || ''));
|
|
1275
1071
|
|
|
1276
1072
|
/**
|
|
1277
|
-
*
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
if (
|
|
1281
|
-
return
|
|
1073
|
+
* Get the default command to open a file in in a text editor.
|
|
1074
|
+
* If VSCode is installed, this is used. Otherwise, the default text editor of the OS is used.
|
|
1075
|
+
*/ function defaultOpenInEditorCommand() {
|
|
1076
|
+
if (!COMMAND) COMMAND = isVsCodeInstalled() ? 'code -w' : isWindows() ? 'notepad' : isOSX() ? 'open vi' : 'xdg-open';
|
|
1077
|
+
return COMMAND;
|
|
1282
1078
|
}
|
|
1079
|
+
let COMMAND = '';
|
|
1283
1080
|
|
|
1284
1081
|
/**
|
|
1285
|
-
*
|
|
1286
|
-
*
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
return
|
|
1082
|
+
* Reads a file and returns the file's contents.
|
|
1083
|
+
* Identical to fs.readFileSync, except that it uses utf8 encoding by default.
|
|
1084
|
+
*
|
|
1085
|
+
* @param filepath - The path to the file.
|
|
1086
|
+
* @param encoding - The encoding to use when reading the file.
|
|
1087
|
+
* @returns The file's contents.
|
|
1088
|
+
*/ function readFileSync(filepath, encoding = 'utf8') {
|
|
1089
|
+
return fs.readFileSync(filepath, encoding);
|
|
1293
1090
|
}
|
|
1294
1091
|
|
|
1295
1092
|
/**
|
|
1296
|
-
*
|
|
1297
|
-
*
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1093
|
+
* Ensures that a string starts with a specified substring. If the string already starts with the specified substring, it is returned as is. Otherwise, the substring is appended to the end of the string.
|
|
1094
|
+
* @param string The string to be processed.
|
|
1095
|
+
* @param startsWith The substring that the string should end with.
|
|
1096
|
+
* @example ```ts
|
|
1097
|
+
* strEnsureStartsWith('json', '.');
|
|
1098
|
+
* //=> '.json'
|
|
1099
|
+
* strEnsureStartsWith('.json', '.');
|
|
1100
|
+
* //=> '.json'
|
|
1101
|
+
* ```
|
|
1102
|
+
*/ function strEnsureStartsWith(string, startsWith) {
|
|
1103
|
+
return string.startsWith(startsWith) ? string : startsWith + string;
|
|
1301
1104
|
}
|
|
1302
1105
|
|
|
1303
1106
|
/**
|
|
1304
|
-
*
|
|
1107
|
+
* Returns a path to the os tmpdir location.
|
|
1305
1108
|
*
|
|
1306
|
-
*
|
|
1307
|
-
|
|
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
|
-
});
|
|
1109
|
+
* @param paths - The paths to join to the os tmpdir location.
|
|
1110
|
+
*/ function getTempDataPath(...paths) {
|
|
1111
|
+
return path.join(tempdir, ...paths);
|
|
1355
1112
|
}
|
|
1113
|
+
const tempdir = fs.realpathSync(os.tmpdir());
|
|
1356
1114
|
|
|
1357
1115
|
/**
|
|
1358
|
-
*
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1116
|
+
* Syncrhonously creates a temporary file and deletes it after the callback has finished.
|
|
1117
|
+
* @param fileExtension - The file extension to use for the temporary file.
|
|
1118
|
+
* @param callback - The callback to execute with the temporary file path. The callback can return a promise and the temporary file will not be deleted until the promise has resolved or rejected.
|
|
1119
|
+
*/ function tempFileSync(fileExtension, callback) {
|
|
1120
|
+
const fpath = getTempDataPath('temp', Date.now() + strEnsureStartsWith(fileExtension, '.'));
|
|
1121
|
+
fs.ensureFileSync(fpath);
|
|
1122
|
+
try {
|
|
1123
|
+
const retval = callback(fpath);
|
|
1124
|
+
fs.remove(fpath);
|
|
1125
|
+
return retval;
|
|
1126
|
+
} catch (error) {
|
|
1127
|
+
fs.remove(fpath);
|
|
1364
1128
|
}
|
|
1365
1129
|
}
|
|
1366
1130
|
|
|
1367
1131
|
/**
|
|
1368
|
-
*
|
|
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.
|
|
1132
|
+
* Prompts the user to edit a string in the user's text editor.
|
|
1378
1133
|
*
|
|
1379
|
-
*
|
|
1380
|
-
*
|
|
1381
|
-
*
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
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;
|
|
1134
|
+
* @example ```ts
|
|
1135
|
+
* promptUserEditInTextEditor({ editor: 'notepad' })
|
|
1136
|
+
* ```
|
|
1137
|
+
*/ function promptUserEditInTextEditorSync(options) {
|
|
1138
|
+
const { editor, content, extension } = applyDefaults(options);
|
|
1139
|
+
return tempFileSync(extension, (tempfile)=>{
|
|
1140
|
+
writeFileSync(tempfile, content);
|
|
1141
|
+
execSync(`${editor} ${tempfile}`, {
|
|
1142
|
+
stdio: 'inherit'
|
|
1392
1143
|
});
|
|
1393
|
-
|
|
1394
|
-
|
|
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);
|
|
1144
|
+
return readFileSync(tempfile);
|
|
1145
|
+
});
|
|
1409
1146
|
}
|
|
1410
|
-
|
|
1147
|
+
function applyDefaults(options = {}) {
|
|
1148
|
+
const content = options.content ?? '';
|
|
1149
|
+
const extension = strEnsureStartsWith(options.extension ?? '.txt', '.');
|
|
1150
|
+
const editor = options.editor ?? defaultOpenInEditorCommand();
|
|
1411
1151
|
return {
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
sortOptions: false,
|
|
1416
|
-
subcommandTerm,
|
|
1417
|
-
argumentTerm,
|
|
1418
|
-
commandUsage,
|
|
1419
|
-
visibleOptions,
|
|
1420
|
-
visibleGlobalOptions,
|
|
1421
|
-
subcommandDescription,
|
|
1422
|
-
optionDescription,
|
|
1423
|
-
argumentDescription,
|
|
1424
|
-
commandDescription,
|
|
1425
|
-
formatHelp
|
|
1152
|
+
content,
|
|
1153
|
+
extension,
|
|
1154
|
+
editor
|
|
1426
1155
|
};
|
|
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
1156
|
}
|
|
1586
1157
|
|
|
1587
1158
|
/**
|
|
1588
|
-
*
|
|
1589
|
-
|
|
1159
|
+
* Edit a JSON-stringify-compatible value in the user's editor and return the (JSON.parse'd) result.
|
|
1160
|
+
*
|
|
1161
|
+
* @param value - The value to edit (NOT a json string). Defaults to an empty object.
|
|
1162
|
+
* @param editor - Launch command to start your editor. Defaults to VSCode: 'code -w' (if installed).
|
|
1163
|
+
* - Otherwise this logic: isWindows() ? 'notepad' : isOSX() ? 'open vi' : 'xdg-open'
|
|
1164
|
+
*
|
|
1165
|
+
* @example ```ts
|
|
1166
|
+
* promptUserEditJsonInTextEditorSync([1, 2])
|
|
1167
|
+
* ```
|
|
1168
|
+
*/ function promptUserEditJsonInTextEditorSync(value = {}, editor) {
|
|
1169
|
+
const json = promptUserEditInTextEditorSync({
|
|
1170
|
+
editor,
|
|
1171
|
+
content: JSON.stringify(value, null, 2),
|
|
1172
|
+
extension: '.json'
|
|
1173
|
+
});
|
|
1174
|
+
return JSON.parse(json);
|
|
1590
1175
|
}
|
|
1591
1176
|
|
|
1592
1177
|
/**
|
|
1593
|
-
* A class that represents
|
|
1594
|
-
*/ class AbstractJsonFileSection
|
|
1595
|
-
|
|
1596
|
-
|
|
1178
|
+
* A class that represents a section of the JSON file used as a simple database.
|
|
1179
|
+
*/ class AbstractJsonFileSection {
|
|
1180
|
+
/**
|
|
1181
|
+
* Creates an instance of AbstractJsonFileSection.
|
|
1182
|
+
* @param file - The parent JsonFile instance.
|
|
1183
|
+
* @param name - The name of the section.
|
|
1184
|
+
* @param keysAreFixed - Indicates whether the keys in the section are fixed.
|
|
1185
|
+
*/ constructor(file, name, keysAreFixed){
|
|
1186
|
+
this.file = file;
|
|
1187
|
+
this.name = name;
|
|
1188
|
+
this.keysAreFixed = keysAreFixed;
|
|
1189
|
+
this.isInitialized = false;
|
|
1190
|
+
this.defaultValues = {};
|
|
1191
|
+
this.prefixBaseString = file.cmd.getPrefixArray().join('_');
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* The JsonDB associated with the section.
|
|
1195
|
+
*/ get db() {
|
|
1196
|
+
return this.file.db;
|
|
1197
|
+
}
|
|
1198
|
+
/**
|
|
1199
|
+
* The CommandBuilder associated with the section.
|
|
1200
|
+
*/ get cmd() {
|
|
1201
|
+
return this.file.cmd;
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Saves the section.
|
|
1205
|
+
* @returns A promise that resolves when the section is saved.
|
|
1206
|
+
*/ async save() {
|
|
1207
|
+
return await this.db.save();
|
|
1597
1208
|
}
|
|
1598
1209
|
/**
|
|
1599
|
-
*
|
|
1600
|
-
* @param key An optional key to append to the prefix.
|
|
1210
|
+
* Gets the object path prefix as dot-separated keys for this section of the JSON file database.
|
|
1211
|
+
* @param key - An optional key to append to the prefix path.
|
|
1212
|
+
* @returns The object path prefix.
|
|
1213
|
+
* @throws An error if the keys are fixed and the specified key does not exist in the default values.
|
|
1601
1214
|
*/ prefix(key) {
|
|
1602
|
-
if (this.
|
|
1603
|
-
throw new
|
|
1215
|
+
if (this.keysAreFixed && key && !Object.hasOwn(this.defaultValues, key)) {
|
|
1216
|
+
throw new Error(`No entry with key '${key}'`);
|
|
1604
1217
|
}
|
|
1605
|
-
return this.name + (key ? '.' + key : '');
|
|
1218
|
+
return this.prefixBaseString + '.' + this.name + (key ? '.' + key : '');
|
|
1606
1219
|
}
|
|
1607
|
-
|
|
1220
|
+
/**
|
|
1221
|
+
* Gets the value associated with the specified key.
|
|
1222
|
+
* @param key - The key to get the value for.
|
|
1223
|
+
* @returns The value associated with the key.
|
|
1224
|
+
*/ get(key) {
|
|
1608
1225
|
this.initialize(false);
|
|
1609
|
-
|
|
1610
|
-
return (_this_file_db_getSafe = this.file.db.getSafe(this.prefix(key))) != null ? _this_file_db_getSafe : this.defaultValues[key];
|
|
1226
|
+
return this.db.getSafe(this.prefix(key)) ?? this.defaultValues[key];
|
|
1611
1227
|
}
|
|
1612
|
-
|
|
1228
|
+
/**
|
|
1229
|
+
* Gets all the values in the section.
|
|
1230
|
+
* @returns All the values in the section.
|
|
1231
|
+
*/ getAll() {
|
|
1613
1232
|
this.initialize(false);
|
|
1614
|
-
|
|
1615
|
-
|
|
1233
|
+
return this.db.getSafe(this.prefix()) ?? JSON.parse(JSON.stringify(this.defaultValues));
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Gets the keys in the section.
|
|
1237
|
+
* @returns The keys in the section.
|
|
1238
|
+
*/ get keys() {
|
|
1239
|
+
// if (this.keysAreFixed) return Object.keys(this.defaultValues)
|
|
1240
|
+
return Object.keys(this.getAll());
|
|
1616
1241
|
}
|
|
1617
|
-
|
|
1618
|
-
|
|
1242
|
+
/**
|
|
1243
|
+
* Gets the number of keys in the section.
|
|
1244
|
+
* @returns The number of keys in the section.
|
|
1245
|
+
*/ count() {
|
|
1246
|
+
return this.keys.length;
|
|
1619
1247
|
}
|
|
1620
|
-
|
|
1248
|
+
/**
|
|
1249
|
+
* Sets the value associated with the specified key.
|
|
1250
|
+
* @param key - The key to set the value for.
|
|
1251
|
+
* @param value - The value to set.
|
|
1252
|
+
* @param save - Indicates whether to save the section after setting the value.
|
|
1253
|
+
*/ set(key, value, save = true) {
|
|
1621
1254
|
this.initialize();
|
|
1622
1255
|
this.assertValid(key, value);
|
|
1623
|
-
this.
|
|
1256
|
+
this.db.set(this.prefix(key), value, save);
|
|
1624
1257
|
}
|
|
1625
|
-
|
|
1258
|
+
/**
|
|
1259
|
+
* Sets all the values in the section.
|
|
1260
|
+
* @param values - The values to set.
|
|
1261
|
+
* @param save - Indicates whether to save the section after setting the values.
|
|
1262
|
+
*/ setAll(values, save = true) {
|
|
1626
1263
|
const original = this.getAll();
|
|
1627
1264
|
for (const [key, value] of Object.entries(values)){
|
|
1265
|
+
if (value === original[key]) continue;
|
|
1628
1266
|
if (JSON.stringify(value) === JSON.stringify(original[key])) continue;
|
|
1629
1267
|
this.set(key, value, false);
|
|
1630
1268
|
}
|
|
1631
|
-
if (!this.
|
|
1269
|
+
if (!this.keysAreFixed) {
|
|
1632
1270
|
for (const name of Object.keys(original)){
|
|
1633
1271
|
if (Object.hasOwn(values, name)) continue;
|
|
1634
1272
|
this.delete(name, false);
|
|
@@ -1636,122 +1274,208 @@ function inheritHiddenGlobalOptions(cmd) {
|
|
|
1636
1274
|
}
|
|
1637
1275
|
if (save) this.save();
|
|
1638
1276
|
}
|
|
1639
|
-
|
|
1277
|
+
/**
|
|
1278
|
+
* Updates the value associated with the specified key.
|
|
1279
|
+
* @param key - The key to set the value for.
|
|
1280
|
+
* @param callback - The callback function that returns the new value.
|
|
1281
|
+
* @param save - Indicates whether to save the section after setting the value.
|
|
1282
|
+
*/ update(key, callback, save = true) {
|
|
1283
|
+
this.set(key, callback(this.get(key), key), save);
|
|
1284
|
+
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Resets the value associated with the specified key to its default value.
|
|
1287
|
+
* @param key - The key to reset.
|
|
1288
|
+
* @param save - Indicates whether to save the section after resetting the value.
|
|
1289
|
+
*/ reset(key, save = true) {
|
|
1640
1290
|
this.set(key, this.defaultValues[key], save);
|
|
1641
1291
|
}
|
|
1642
|
-
|
|
1292
|
+
/**
|
|
1293
|
+
* Resets all the values in the section to their default values.
|
|
1294
|
+
* @param save - Indicates whether to save the section after resetting the values.
|
|
1295
|
+
*/ resetAll(save = true) {
|
|
1643
1296
|
this.setAll(this.defaultValues, save);
|
|
1644
1297
|
}
|
|
1645
1298
|
/**
|
|
1646
|
-
*
|
|
1647
|
-
* -
|
|
1648
|
-
* -
|
|
1649
|
-
|
|
1650
|
-
*/ save() {
|
|
1651
|
-
this.file.db.save();
|
|
1652
|
-
}
|
|
1653
|
-
delete(key, save = true) {
|
|
1299
|
+
* Deletes the value associated with the specified key.
|
|
1300
|
+
* @param key - The key to delete.
|
|
1301
|
+
* @param save - Indicates whether to save the section after deleting the value.
|
|
1302
|
+
*/ delete(key, save = true) {
|
|
1654
1303
|
this.initialize();
|
|
1655
|
-
this.
|
|
1304
|
+
this.db.delete(this.prefix(key), save);
|
|
1656
1305
|
}
|
|
1657
|
-
|
|
1658
|
-
|
|
1306
|
+
/**
|
|
1307
|
+
* Deletes all the values in the section.
|
|
1308
|
+
* @param save - Indicates whether to save the section after deleting the values.
|
|
1309
|
+
*/ deleteAll(save = true) {
|
|
1310
|
+
for (const key of this.keys){
|
|
1659
1311
|
this.delete(key, false);
|
|
1660
1312
|
}
|
|
1661
1313
|
if (save) this.save();
|
|
1662
1314
|
}
|
|
1663
|
-
|
|
1315
|
+
/**
|
|
1316
|
+
* Edits the values in the section using a text editor.
|
|
1317
|
+
* @param editor - The text editor to use. If not specified, the default text editor command will be used.
|
|
1318
|
+
*/ edit(editor) {
|
|
1664
1319
|
const original = this.getAll();
|
|
1665
1320
|
const parsed = promptUserEditJsonInTextEditorSync(original, editor || defaultOpenInEditorCommand());
|
|
1666
1321
|
this.setAll(parsed);
|
|
1667
1322
|
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/**
|
|
1326
|
+
* A class that represents the appdata section of the JSON file used as simple database.
|
|
1327
|
+
*/ class AppDataSection extends AbstractJsonFileSection {
|
|
1328
|
+
/**
|
|
1329
|
+
* Creates an instance of AppDataSection.
|
|
1330
|
+
* @param file - The parent JsonFile instance.
|
|
1331
|
+
* @param name - The name of the section.
|
|
1332
|
+
*/ constructor(file, name){
|
|
1333
|
+
super(file, name, false);
|
|
1334
|
+
countInstance(AppDataSection);
|
|
1335
|
+
}
|
|
1668
1336
|
/**
|
|
1669
|
-
*
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1337
|
+
* Does nothing
|
|
1338
|
+
*/ assertValid() {
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Defines a property for the section.
|
|
1343
|
+
* @param key - The key of the property.
|
|
1344
|
+
*/ defineProperty(key, value) {
|
|
1345
|
+
if (typeof value === 'object') value = JSON.parse(JSON.stringify(value));
|
|
1346
|
+
this.defaultValues[key] = value;
|
|
1676
1347
|
this.isInitialized = false;
|
|
1677
|
-
this.isInitializing = false;
|
|
1678
|
-
this._defaultValues = {};
|
|
1679
1348
|
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Initializes the section.
|
|
1351
|
+
* @param save - Indicates whether to save the section after initialization.
|
|
1352
|
+
* @returns A string if an error occurred during initialization, otherwise void.
|
|
1353
|
+
*/ initialize(save = false) {
|
|
1354
|
+
if (this.isInitialized) return;
|
|
1355
|
+
const data = this.db.getSafe(this.prefix());
|
|
1356
|
+
if (!data) this.db.set(this.prefix(), this.defaultValues, save);
|
|
1357
|
+
this.isInitialized = true;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
/**
|
|
1362
|
+
* Creates a function that merges objects based on a predicate function.
|
|
1363
|
+
*/ function createObjectMerger(predicate) {
|
|
1364
|
+
return function objMerge(target, ...sources) {
|
|
1365
|
+
for (const src of sources){
|
|
1366
|
+
for (const [key, value] of Object.entries(src)){
|
|
1367
|
+
if (predicate(value, key, src)) {
|
|
1368
|
+
target[key] = value;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
return target;
|
|
1373
|
+
};
|
|
1680
1374
|
}
|
|
1681
1375
|
|
|
1376
|
+
const objAssign = createObjectMerger((value)=>value != null);
|
|
1377
|
+
|
|
1682
1378
|
/**
|
|
1683
1379
|
* A class that represents the user-config section of the JSON file used as simple database.
|
|
1684
1380
|
*/ class ConfigSection extends AbstractJsonFileSection {
|
|
1685
|
-
|
|
1381
|
+
/**
|
|
1382
|
+
* Creates an instance of AbstractJsonFileSection.
|
|
1383
|
+
* @param file - The parent JsonFile instance.
|
|
1384
|
+
* @param name - The name of the section.
|
|
1385
|
+
*/ constructor(file, name){
|
|
1386
|
+
super(file, name, true);
|
|
1387
|
+
/**
|
|
1388
|
+
* String parsers for when editing config from command-line.
|
|
1389
|
+
*/ this.parsers = {};
|
|
1390
|
+
/**
|
|
1391
|
+
* Descriptions for each property key.
|
|
1392
|
+
*/ this.descriptions = {};
|
|
1393
|
+
/**
|
|
1394
|
+
* Validators for each property key.
|
|
1395
|
+
*/ this.validators = {};
|
|
1396
|
+
countInstance(ConfigSection);
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* Asserts that a key-value pair is valid.
|
|
1400
|
+
* @param key - The key to assert.
|
|
1401
|
+
* @param value - The value to assert.
|
|
1402
|
+
*/ assertValid(key, value) {
|
|
1686
1403
|
if (!this.validators[key]) return;
|
|
1687
|
-
|
|
1404
|
+
ensureThat(value, this.validators[key]);
|
|
1688
1405
|
}
|
|
1689
|
-
|
|
1406
|
+
/**
|
|
1407
|
+
* Defines a property for the section.
|
|
1408
|
+
* @param key - The key of the property.
|
|
1409
|
+
* @param options - The options for the property.
|
|
1410
|
+
*/ defineProperty(key, options) {
|
|
1690
1411
|
const { description, defaultValue, parse, validate } = options;
|
|
1691
|
-
this.
|
|
1692
|
-
this.
|
|
1693
|
-
this.
|
|
1694
|
-
this.
|
|
1412
|
+
this.descriptions[key] = description;
|
|
1413
|
+
this.defaultValues[key] = typeof defaultValue === 'object' ? JSON.parse(JSON.stringify(defaultValue)) : defaultValue;
|
|
1414
|
+
if (parse) this.parsers[key] = parse;
|
|
1415
|
+
if (validate) this.validators[key] = validate;
|
|
1695
1416
|
this.assertValid(key, options.defaultValue);
|
|
1696
1417
|
}
|
|
1697
|
-
|
|
1418
|
+
/**
|
|
1419
|
+
* Initializes the section.
|
|
1420
|
+
* @param save - Indicates whether to save the section after initialization.
|
|
1421
|
+
* @returns A string if an error occurred during initialization, otherwise void.
|
|
1422
|
+
*/ initialize(save = false) {
|
|
1698
1423
|
if (this.isInitialized) return;
|
|
1699
|
-
|
|
1700
|
-
|
|
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);
|
|
1424
|
+
const data = this.db.getSafe(this.prefix());
|
|
1425
|
+
const result = objAssign({}, JSON.parse(JSON.stringify(this.defaultValues)), data || {});
|
|
1426
|
+
this.db.set(this.prefix(), result, save);
|
|
1713
1427
|
this.isInitialized = true;
|
|
1714
1428
|
}
|
|
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
1429
|
}
|
|
1747
1430
|
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1431
|
+
/**
|
|
1432
|
+
* Reads a JSON file and then parses it into an object.
|
|
1433
|
+
* If an error occurs, it returns undefined.
|
|
1434
|
+
*
|
|
1435
|
+
* @param filepath - The path to the JSON file.
|
|
1436
|
+
* @param options - Options for reading the JSON file.
|
|
1437
|
+
*/ function readJsonFileSafeSync(filepath, options) {
|
|
1438
|
+
try {
|
|
1439
|
+
return fs.readJsonSync(filepath, options);
|
|
1440
|
+
} catch (error) {
|
|
1441
|
+
return undefined;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
/**
|
|
1446
|
+
* Represents a simple JSON file database.
|
|
1447
|
+
*/ class JsonDB {
|
|
1448
|
+
/**
|
|
1449
|
+
* Creates a new instance of the JsonDB class.
|
|
1450
|
+
* @param filepath - The filepath where the data file is stored or to be stored.
|
|
1451
|
+
*/ constructor(filepath, indents = 0){
|
|
1452
|
+
this.filepath = filepath;
|
|
1453
|
+
this.indents = indents;
|
|
1454
|
+
this.data = readJsonFileSafeSync(this.filepath) ?? {};
|
|
1455
|
+
}
|
|
1456
|
+
/**
|
|
1457
|
+
* Saves the data to the JSON file.
|
|
1458
|
+
*/ async save(indents = this.indents) {
|
|
1459
|
+
return await outputJson(this.filepath, this.data, {
|
|
1460
|
+
spaces: indents
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
/**
|
|
1464
|
+
* Sets the filepath of the JSON file.
|
|
1465
|
+
* @param filepath - The new filepath.
|
|
1466
|
+
*/ setFilepath(filepath, save = true) {
|
|
1467
|
+
if (this.filepath === filepath) return;
|
|
1468
|
+
this.filepath = filepath;
|
|
1469
|
+
if (save) this.save();
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Sets a value in the JSON database.
|
|
1473
|
+
* @param prefix - Object path prefix as dot-separated keys.
|
|
1474
|
+
* @param value - The value to set.
|
|
1475
|
+
* @param save - Whether to save the data to the JSON file.
|
|
1476
|
+
*/ set(prefix, value = {}, save = true) {
|
|
1753
1477
|
if (!prefix) {
|
|
1754
|
-
this.data =
|
|
1478
|
+
this.data = ensureThat(value, isObject);
|
|
1755
1479
|
} else {
|
|
1756
1480
|
const keys = prefix.split('.');
|
|
1757
1481
|
const lastKey = keys.pop();
|
|
@@ -1766,12 +1490,21 @@ class JsonDB extends Base {
|
|
|
1766
1490
|
}
|
|
1767
1491
|
if (save) this.save();
|
|
1768
1492
|
}
|
|
1769
|
-
|
|
1493
|
+
/**
|
|
1494
|
+
* Gets a value from the JSON database.
|
|
1495
|
+
* @param prefix - Object path prefix as dot-separated keys.
|
|
1496
|
+
* @returns The value associated with the key.
|
|
1497
|
+
* @throws An error if no entry is found at the specified key.
|
|
1498
|
+
*/ get(prefix) {
|
|
1770
1499
|
const value = this.getSafe(prefix);
|
|
1771
|
-
if (value === undefined) throw new Error(`No
|
|
1772
|
-
return value;
|
|
1500
|
+
if (value === undefined) throw new Error(`No entry at '${prefix}'`);
|
|
1501
|
+
return this.cloneDeep(value);
|
|
1773
1502
|
}
|
|
1774
|
-
|
|
1503
|
+
/**
|
|
1504
|
+
* Gets a value from the JSON database safely.
|
|
1505
|
+
* @param prefix - Object path prefix as dot-separated keys.
|
|
1506
|
+
* @returns The value associated with the key, or undefined if no entry is found.
|
|
1507
|
+
*/ getSafe(prefix) {
|
|
1775
1508
|
if (!prefix) return this.cloneDeep(this.data);
|
|
1776
1509
|
const keys = prefix.split('.');
|
|
1777
1510
|
let node = this.data;
|
|
@@ -1783,11 +1516,19 @@ class JsonDB extends Base {
|
|
|
1783
1516
|
}
|
|
1784
1517
|
return this.cloneDeep(node);
|
|
1785
1518
|
}
|
|
1786
|
-
|
|
1519
|
+
/**
|
|
1520
|
+
* Checks if a key exists in the JSON database.
|
|
1521
|
+
* @param prefix - Object path prefix as dot-separated keys.
|
|
1522
|
+
* @returns True if the key exists, false otherwise.
|
|
1523
|
+
*/ has(prefix) {
|
|
1787
1524
|
if (!prefix) return true;
|
|
1788
1525
|
return this.getSafe(prefix) !== undefined;
|
|
1789
1526
|
}
|
|
1790
|
-
|
|
1527
|
+
/**
|
|
1528
|
+
* Deletes a value from the JSON database.
|
|
1529
|
+
* @param prefix - Object path prefix as dot-separated keys.
|
|
1530
|
+
* @param save - Whether to save the data to the JSON file.
|
|
1531
|
+
*/ delete(prefix, save = true) {
|
|
1791
1532
|
if (!prefix) {
|
|
1792
1533
|
this.data = {};
|
|
1793
1534
|
} else {
|
|
@@ -1804,160 +1545,112 @@ class JsonDB extends Base {
|
|
|
1804
1545
|
}
|
|
1805
1546
|
if (save) this.save();
|
|
1806
1547
|
}
|
|
1807
|
-
cloneDeep(obj) {
|
|
1808
|
-
return JSON.parse(JSON.stringify(obj));
|
|
1809
|
-
}
|
|
1810
1548
|
/**
|
|
1811
|
-
*
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
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)
|
|
1549
|
+
* Creates a deep clone of an object.
|
|
1550
|
+
* @param obj - The object to clone.
|
|
1551
|
+
* @returns The cloned object.
|
|
1552
|
+
*/ cloneDeep(obj) {
|
|
1553
|
+
if (typeof obj !== 'object') return obj;
|
|
1554
|
+
return JSON.parse(JSON.stringify(obj));
|
|
1837
1555
|
}
|
|
1838
1556
|
}
|
|
1839
1557
|
|
|
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
1558
|
/**
|
|
1888
1559
|
* A class that represents the user-presets section of the JSON file used as simple database.
|
|
1889
1560
|
*/ class PresetsSection extends AbstractJsonFileSection {
|
|
1890
|
-
|
|
1891
|
-
|
|
1561
|
+
/**
|
|
1562
|
+
* Creates an instance of AppDataSection.
|
|
1563
|
+
* @param file - The parent JsonFile instance.
|
|
1564
|
+
* @param name - The name of the section.
|
|
1565
|
+
*/ constructor(file, name){
|
|
1566
|
+
super(file, name, false);
|
|
1567
|
+
countInstance(PresetsSection);
|
|
1568
|
+
this.defineProperty('defaults', {
|
|
1569
|
+
description: 'All presets inherit from this preset',
|
|
1570
|
+
presets: [],
|
|
1571
|
+
args: this.cmd.arguments.map((arg)=>arg.defaultValue ?? null),
|
|
1572
|
+
options: this.cmd.getOwnAndGlobalOptions().reduce((acc, opt)=>{
|
|
1573
|
+
acc[opt.attributeName()] = opt.defaultValue ?? (opt.isBoolean() ? false : null);
|
|
1574
|
+
return acc;
|
|
1575
|
+
}, {})
|
|
1576
|
+
});
|
|
1577
|
+
}
|
|
1578
|
+
/**
|
|
1579
|
+
* Asserts that a key-value pair is valid.
|
|
1580
|
+
* @param key - The key to assert.
|
|
1581
|
+
* @param value - The preset options to validate.
|
|
1582
|
+
*/ assertValid(key, value) {
|
|
1583
|
+
this.cmd.assertValidPreset(key, value);
|
|
1892
1584
|
}
|
|
1893
|
-
|
|
1894
|
-
|
|
1585
|
+
/**
|
|
1586
|
+
* Defines a property for the section.
|
|
1587
|
+
* @param key - The key of the property.
|
|
1588
|
+
* @param options - The options for the property.
|
|
1589
|
+
*/ defineProperty(key, options) {
|
|
1590
|
+
if (this.defaultValues[key]) throw new Error(`Cannot redefine preset '${key}'.`);
|
|
1591
|
+
this.defaultValues[key] = JSON.parse(JSON.stringify(options));
|
|
1895
1592
|
this.assertValid(key, options);
|
|
1896
1593
|
this.isInitialized = false;
|
|
1897
1594
|
}
|
|
1898
|
-
|
|
1595
|
+
/**
|
|
1596
|
+
* Initializes the section.
|
|
1597
|
+
* @param save - Indicates whether to save the section after initialization.
|
|
1598
|
+
* @returns A string if an error occurred during initialization, otherwise void.
|
|
1599
|
+
*/ initialize(save = false) {
|
|
1899
1600
|
if (this.isInitialized) return;
|
|
1900
|
-
const data = this.
|
|
1901
|
-
const
|
|
1902
|
-
const
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
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;
|
|
1601
|
+
const data = this.db.getSafe(this.prefix());
|
|
1602
|
+
const presets = objAssign({}, this.defaultValues, data || {});
|
|
1603
|
+
const presetNames = Object.keys(presets);
|
|
1604
|
+
for (const preset of Object.values(presets)){
|
|
1605
|
+
for (const pname of presetNames){
|
|
1606
|
+
if (preset.options[pname]) {
|
|
1607
|
+
preset.presets.push(pname);
|
|
1608
|
+
delete preset.options[pname];
|
|
1609
|
+
save = true;
|
|
1610
|
+
}
|
|
1925
1611
|
}
|
|
1926
1612
|
}
|
|
1927
|
-
this.
|
|
1613
|
+
this.db.set(this.prefix(), presets, save);
|
|
1928
1614
|
this.isInitialized = true;
|
|
1929
1615
|
}
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
if (
|
|
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');
|
|
1616
|
+
/**
|
|
1617
|
+
* Sets all the values in the section.
|
|
1618
|
+
* @param values - The values to set.
|
|
1619
|
+
* @param save - Indicates whether to save the section after setting the values.
|
|
1620
|
+
*/ async setAll(presets, save = true) {
|
|
1621
|
+
if (!presets['defaults']) presets['defaults'] = JSON.parse(JSON.stringify(this.defaultValues['defaults']));
|
|
1940
1622
|
super.setAll(presets, save);
|
|
1941
1623
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
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
|
-
});
|
|
1624
|
+
/**
|
|
1625
|
+
* Deletes the value associated with the specified key.
|
|
1626
|
+
* @param key - The key to delete.
|
|
1627
|
+
* @param save - Indicates whether to save the section after deleting the value.
|
|
1628
|
+
*/ delete(name, save = true) {
|
|
1629
|
+
if (Object.hasOwn(this.defaultValues, name)) throw new Error('Cannot delete the builtin presets.');
|
|
1630
|
+
super.delete(name, save);
|
|
1955
1631
|
}
|
|
1956
1632
|
}
|
|
1957
1633
|
|
|
1958
1634
|
/**
|
|
1959
1635
|
* A class that represents the JSON file used as a simple database.
|
|
1960
|
-
*/ class JsonFile
|
|
1636
|
+
*/ class JsonFile {
|
|
1637
|
+
/**
|
|
1638
|
+
* @param cmd The parent CommandBuilder instance.
|
|
1639
|
+
*/ constructor(cmd){
|
|
1640
|
+
this.cmd = cmd;
|
|
1641
|
+
countInstance(JsonFile);
|
|
1642
|
+
}
|
|
1643
|
+
/**
|
|
1644
|
+
* A lazy-loaded instance of the JsonDB instance containing the data.
|
|
1645
|
+
* Upon first property access, it is stored as a property on the instance.
|
|
1646
|
+
* If the command is a subcommand, the root command's JsonDB instance is returned.
|
|
1647
|
+
*/ get db() {
|
|
1648
|
+
if (this.cmd.isRoot) {
|
|
1649
|
+
return realizeLazyProperty(this, 'db', new JsonDB(this.cmd.dataFilepath, 2));
|
|
1650
|
+
} else {
|
|
1651
|
+
return this.cmd.root.db.db;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1961
1654
|
/**
|
|
1962
1655
|
* A lazy-loaded instance of the `config` section of the JSON file.
|
|
1963
1656
|
* Upon first property access, it is stored as a property on the instance.
|
|
@@ -1965,30 +1658,23 @@ const objAssign = createObjectMerger((value)=>value != null);
|
|
|
1965
1658
|
return realizeLazyProperty(this, 'config', new ConfigSection(this, 'config'));
|
|
1966
1659
|
}
|
|
1967
1660
|
/**
|
|
1661
|
+
* A lazy-loaded instance of the `appData` section of the JSON file.
|
|
1662
|
+
* Upon first property access, it is stored as a property on the instance.
|
|
1663
|
+
*/ get appData() {
|
|
1664
|
+
return realizeLazyProperty(this, 'appData', new AppDataSection(this, 'appData'));
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1968
1667
|
* A lazy-loaded instance of the `presets` section of the JSON file.
|
|
1969
1668
|
*/ get presets() {
|
|
1970
1669
|
return realizeLazyProperty(this, 'presets', new PresetsSection(this, 'presets'));
|
|
1971
1670
|
}
|
|
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
1671
|
}
|
|
1980
1672
|
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1673
|
+
class OptionArgumentParserSelector extends ParserSelector {
|
|
1674
|
+
constructor(builder){
|
|
1675
|
+
super(builder);
|
|
1676
|
+
countInstance(OptionArgumentParserSelector);
|
|
1984
1677
|
}
|
|
1985
|
-
}
|
|
1986
|
-
|
|
1987
|
-
function optHasArgument(opt) {
|
|
1988
|
-
return /[<>[\]]/.test(opt.flags);
|
|
1989
|
-
}
|
|
1990
|
-
|
|
1991
|
-
class OptionArgumentParserSelector extends AbstractStringParserSelector {
|
|
1992
1678
|
custom(parser) {
|
|
1993
1679
|
const name = this.builder.$.attributeName();
|
|
1994
1680
|
this.builder.cmd.meta.optParsers[name] = parser;
|
|
@@ -1996,7 +1682,11 @@ class OptionArgumentParserSelector extends AbstractStringParserSelector {
|
|
|
1996
1682
|
}
|
|
1997
1683
|
}
|
|
1998
1684
|
|
|
1999
|
-
class OptionArgumentValidatorSelector extends
|
|
1685
|
+
class OptionArgumentValidatorSelector extends ValidatorSelector {
|
|
1686
|
+
constructor(builder){
|
|
1687
|
+
super(builder);
|
|
1688
|
+
countInstance(OptionArgumentValidatorSelector);
|
|
1689
|
+
}
|
|
2000
1690
|
custom(validator) {
|
|
2001
1691
|
const name = this.builder.$.attributeName();
|
|
2002
1692
|
const obj = this.builder.cmd.meta.optValidators;
|
|
@@ -2006,7 +1696,55 @@ class OptionArgumentValidatorSelector extends AbstractValidatorSelector {
|
|
|
2006
1696
|
}
|
|
2007
1697
|
}
|
|
2008
1698
|
|
|
2009
|
-
|
|
1699
|
+
/**
|
|
1700
|
+
* Extract the argument name from an option's 'flags' string.
|
|
1701
|
+
*/ function getArgumentName(opt) {
|
|
1702
|
+
const result = arrLast(opt.flags.trim().split(' '));
|
|
1703
|
+
if (/-/.test(result)) return undefined;
|
|
1704
|
+
return result;
|
|
1705
|
+
}
|
|
1706
|
+
/**
|
|
1707
|
+
* Check if an option has an argument.
|
|
1708
|
+
*/ function hasArgument(opt) {
|
|
1709
|
+
return /[<>[\]]/.test(opt.flags);
|
|
1710
|
+
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Update an Option's 'flags' property from its 'short' and 'long' properties.
|
|
1713
|
+
* The flags property is not automatically updated when 'short' or 'long' are changed.
|
|
1714
|
+
*/ function renderFlags(opt) {
|
|
1715
|
+
const shortLong = [];
|
|
1716
|
+
if (opt.short) shortLong.push(opt.short);
|
|
1717
|
+
if (opt.long) shortLong.push(opt.long);
|
|
1718
|
+
const argName = getArgumentName(opt);
|
|
1719
|
+
return shortLong.join(', ') + (argName ? ' ' + argName : '');
|
|
1720
|
+
}
|
|
1721
|
+
/**
|
|
1722
|
+
* Set an Option's 'long' name. The 'flags' property is updated accordingly.
|
|
1723
|
+
* The '--' prefix is automatically added if not present.
|
|
1724
|
+
*/ function setLong(opt, long) {
|
|
1725
|
+
opt.long = strEnsureStartsWith(long, '--').replace(/^-+/, '--');
|
|
1726
|
+
opt.flags = renderFlags(opt);
|
|
1727
|
+
}
|
|
1728
|
+
/**
|
|
1729
|
+
* Set an Option's 'short' name. The 'flags' property is updated accordingly.
|
|
1730
|
+
* The '-' prefix is automatically added if not present.
|
|
1731
|
+
*/ function setShort(opt, short) {
|
|
1732
|
+
opt.short = strEnsureStartsWith(short, '-').replace(/^-+/, '-');
|
|
1733
|
+
opt.flags = renderFlags(opt);
|
|
1734
|
+
}
|
|
1735
|
+
const OptionHelpers = {
|
|
1736
|
+
getArgumentName,
|
|
1737
|
+
hasArgument,
|
|
1738
|
+
renderFlags,
|
|
1739
|
+
setLong,
|
|
1740
|
+
setShort
|
|
1741
|
+
};
|
|
1742
|
+
|
|
1743
|
+
class OptionReader {
|
|
1744
|
+
constructor(parent){
|
|
1745
|
+
this.parent = parent;
|
|
1746
|
+
countInstance(OptionReader);
|
|
1747
|
+
}
|
|
2010
1748
|
get $() {
|
|
2011
1749
|
return this.parent.$;
|
|
2012
1750
|
}
|
|
@@ -2019,9 +1757,6 @@ class OptionReader extends Base {
|
|
|
2019
1757
|
get optional() {
|
|
2020
1758
|
return this.$.optional;
|
|
2021
1759
|
}
|
|
2022
|
-
// get negate() {
|
|
2023
|
-
// return this.$.negate
|
|
2024
|
-
// }
|
|
2025
1760
|
get mandatory() {
|
|
2026
1761
|
return this.$.mandatory;
|
|
2027
1762
|
}
|
|
@@ -2037,9 +1772,9 @@ class OptionReader extends Base {
|
|
|
2037
1772
|
get long() {
|
|
2038
1773
|
return this.$.long;
|
|
2039
1774
|
}
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
1775
|
+
get preset() {
|
|
1776
|
+
return this.$.presetArg;
|
|
1777
|
+
}
|
|
2043
1778
|
get default() {
|
|
2044
1779
|
return this.$.defaultValue;
|
|
2045
1780
|
}
|
|
@@ -2058,37 +1793,34 @@ class OptionReader extends Base {
|
|
|
2058
1793
|
get attributeName() {
|
|
2059
1794
|
return this.$.attributeName();
|
|
2060
1795
|
}
|
|
2061
|
-
get fullDescription() {
|
|
2062
|
-
return this.$.fullDescription();
|
|
2063
|
-
}
|
|
2064
1796
|
get defaultValueDescription() {
|
|
2065
1797
|
return this.$.defaultValueDescription;
|
|
2066
1798
|
}
|
|
2067
1799
|
get hasArgument() {
|
|
2068
|
-
return
|
|
2069
|
-
}
|
|
2070
|
-
constructor(parent){
|
|
2071
|
-
super();
|
|
2072
|
-
this.parent = parent;
|
|
1800
|
+
return OptionHelpers.hasArgument(this.parent.$);
|
|
2073
1801
|
}
|
|
2074
1802
|
}
|
|
2075
1803
|
|
|
2076
1804
|
/**
|
|
2077
1805
|
* Wrapper around the @see Option class, for more intuitive construction.
|
|
2078
1806
|
* @remarks Options are one of boolean, negated, required argument, or optional argument.
|
|
2079
|
-
*/ class OptionBuilder
|
|
1807
|
+
*/ class OptionBuilder {
|
|
1808
|
+
constructor(cmd, flags){
|
|
1809
|
+
this.cmd = cmd;
|
|
1810
|
+
countInstance(OptionBuilder);
|
|
1811
|
+
this.$ = new Option(flags);
|
|
1812
|
+
if (!OptionHelpers.hasArgument(this.$) && this.$.long?.startsWith('--no-')) {
|
|
1813
|
+
this.$.default(true);
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
2080
1816
|
description(string) {
|
|
2081
1817
|
this.$.description = string;
|
|
2082
1818
|
return this;
|
|
2083
1819
|
}
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
// mandatory(mandatory = true) {
|
|
2089
|
-
// this.$.makeOptionMandatory(mandatory)
|
|
2090
|
-
// return this
|
|
2091
|
-
// }
|
|
1820
|
+
mandatory(mandatory = true) {
|
|
1821
|
+
this.$.makeOptionMandatory(mandatory);
|
|
1822
|
+
return this;
|
|
1823
|
+
}
|
|
2092
1824
|
hideHelp(hide = true) {
|
|
2093
1825
|
this.$.hideHelp(hide);
|
|
2094
1826
|
return this;
|
|
@@ -2097,21 +1829,18 @@ class OptionReader extends Base {
|
|
|
2097
1829
|
this.$.hidden = hidden;
|
|
2098
1830
|
return this;
|
|
2099
1831
|
}
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
1832
|
+
preset(arg) {
|
|
1833
|
+
this.$.preset(arg);
|
|
1834
|
+
return this;
|
|
1835
|
+
}
|
|
2104
1836
|
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
1837
|
this.$.default(value, description);
|
|
2112
1838
|
return this;
|
|
2113
1839
|
}
|
|
2114
1840
|
choices(values) {
|
|
1841
|
+
if (!OptionHelpers.hasArgument(this.$)) {
|
|
1842
|
+
throw new Error('Cannot set choices on option with no argument: ' + this.$.name());
|
|
1843
|
+
}
|
|
2115
1844
|
this.$.choices(values);
|
|
2116
1845
|
return this;
|
|
2117
1846
|
}
|
|
@@ -2128,116 +1857,335 @@ class OptionReader extends Base {
|
|
|
2128
1857
|
return this;
|
|
2129
1858
|
}
|
|
2130
1859
|
short(short) {
|
|
2131
|
-
|
|
1860
|
+
OptionHelpers.setShort(this.$, short);
|
|
2132
1861
|
return this;
|
|
2133
1862
|
}
|
|
2134
1863
|
get parser() {
|
|
2135
|
-
if (this
|
|
2136
|
-
throw new Error('Cannot set parser on
|
|
1864
|
+
if (!OptionHelpers.hasArgument(this.$)) {
|
|
1865
|
+
throw new Error('Cannot set parser on option with no argument: ' + this.$.attributeName());
|
|
2137
1866
|
}
|
|
2138
1867
|
return new OptionArgumentParserSelector(this);
|
|
2139
1868
|
}
|
|
2140
1869
|
get validator() {
|
|
2141
|
-
if (this
|
|
2142
|
-
throw new Error('Cannot set validator on
|
|
1870
|
+
if (!OptionHelpers.hasArgument(this.$)) {
|
|
1871
|
+
throw new Error('Cannot set validator on option with no argument: ' + this.$.attributeName());
|
|
2143
1872
|
}
|
|
2144
1873
|
return new OptionArgumentValidatorSelector(this);
|
|
2145
1874
|
}
|
|
2146
1875
|
get get() {
|
|
2147
1876
|
return realizeLazyProperty(this, 'get', new OptionReader(this));
|
|
2148
1877
|
}
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
/**
|
|
1881
|
+
* Updates the property descriptors of the specified properties on the given object.
|
|
1882
|
+
* @param object - The object whose property descriptors are to be updated.
|
|
1883
|
+
* @param properties - An array of property names for which the descriptors are to be updated.
|
|
1884
|
+
* @param update - A function that takes a property descriptor and a property name, and returns a new property descriptor.
|
|
1885
|
+
* @throws Will throw an error if any of the specified properties do not exist on the object.
|
|
1886
|
+
* @example ```ts
|
|
1887
|
+
* const obj = { a: 1, b: 2 };
|
|
1888
|
+
* objUpdatePropertyDescriptors(obj, ['a', 'b'], (descriptor, property) => {
|
|
1889
|
+
* descriptor.writable = true;
|
|
1890
|
+
* return obj;
|
|
1891
|
+
* });
|
|
1892
|
+
* ```
|
|
1893
|
+
*/ function objUpdatePropertyDescriptors(object, properties, update) {
|
|
1894
|
+
for (const p of properties){
|
|
1895
|
+
if (!Reflect.has(object, p)) {
|
|
1896
|
+
throw new Error(`Property, '${p}' does not exist on object.`);
|
|
2156
1897
|
}
|
|
1898
|
+
const descriptor = update(Object.getOwnPropertyDescriptor(object, p), p);
|
|
1899
|
+
Object.defineProperty(object, p, descriptor);
|
|
2157
1900
|
}
|
|
2158
1901
|
}
|
|
2159
1902
|
|
|
1903
|
+
/**
|
|
1904
|
+
* Sets the specified properties of an object as non-enumerable.
|
|
1905
|
+
* @remarks This function modifies the original object by setting the specified properties as non-enumerable.
|
|
1906
|
+
* If the object or any of the property names are not valid, it throws an error.
|
|
1907
|
+
* @param object The object whose properties are to be set as non-enumerable.
|
|
1908
|
+
* @param properties The names of the properties to be set as non-enumerable.
|
|
1909
|
+
* @throws Will throw an error if any of the specified properties do not exist on the object.
|
|
1910
|
+
* @example ```ts
|
|
1911
|
+
* setNonEnumerable({ a: 1, b: 2, c: 3 }, 'a', 'b');
|
|
1912
|
+
* Object.keys({ a: 1, b: 2, c: 3 });;
|
|
1913
|
+
* //=> ['c']
|
|
1914
|
+
* ```
|
|
1915
|
+
*/ function setNonEnumerable(object, ...properties) {
|
|
1916
|
+
objUpdatePropertyDescriptors(object, properties, (descriptor)=>{
|
|
1917
|
+
descriptor.enumerable = false;
|
|
1918
|
+
return descriptor;
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1921
|
+
|
|
2160
1922
|
/**
|
|
2161
1923
|
* Wrapper around the @see Command class, for more intuitive construction.
|
|
2162
|
-
*/ class CommandBuilder
|
|
1924
|
+
*/ class CommandBuilder {
|
|
1925
|
+
static{
|
|
1926
|
+
this.dataDirectory = path.join(os.homedir(), 'config', 'cli');
|
|
1927
|
+
}
|
|
2163
1928
|
get db() {
|
|
2164
1929
|
return realizeLazyProperty(this, 'db', new JsonFile(this));
|
|
2165
1930
|
}
|
|
2166
|
-
|
|
2167
|
-
|
|
1931
|
+
constructor(name, callback, parent, isNative = false){
|
|
1932
|
+
this.features = new CommandFeatureSelector(this);
|
|
1933
|
+
this.parent = null;
|
|
1934
|
+
this.meta = new CommandBuilderMetaData();
|
|
1935
|
+
countInstance(CommandBuilder);
|
|
1936
|
+
this.meta.isNative = isNative;
|
|
1937
|
+
this.$ = new Command(name);
|
|
1938
|
+
commanderBackRefs.set(this.$, this);
|
|
1939
|
+
if (parent) {
|
|
1940
|
+
this.parent = parent;
|
|
1941
|
+
this.parent.meta.subcommands.push(this);
|
|
1942
|
+
this.parent.$.addCommand(this.$);
|
|
1943
|
+
}
|
|
1944
|
+
this.initializeHelp();
|
|
1945
|
+
this.initializeActionWrapper();
|
|
1946
|
+
if (callback) callback.call(this, this);
|
|
1947
|
+
if (this.parent) {
|
|
1948
|
+
this.$.copyInheritedSettings(this.parent.$);
|
|
1949
|
+
this.features.inheritFrom(this.parent.features);
|
|
1950
|
+
this.inheritParentHiddenGlobals();
|
|
1951
|
+
}
|
|
1952
|
+
if (!this.meta.isNative) {
|
|
1953
|
+
this.assertCommandNameNotReserved(this.name);
|
|
1954
|
+
this.addUtilCommands();
|
|
1955
|
+
}
|
|
1956
|
+
if (this.features.isAutoAssignSubCommandAliasesEnabled) {
|
|
1957
|
+
this.assignSubCommandAliases();
|
|
1958
|
+
if (!this.meta.isNative) {
|
|
1959
|
+
this.assertNoDuplicateCommandNames();
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
if (this.features.isAutoAssignMissingOptionFlagsEnabled) {
|
|
1963
|
+
this.assignMissingOptionFlags();
|
|
1964
|
+
if (!this.meta.isNative) {
|
|
1965
|
+
this.assertNoDuplicateOptionNames();
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
this.meta.isInitialized = true;
|
|
1969
|
+
}
|
|
1970
|
+
setRecommended() {
|
|
1971
|
+
this.enableBuiltinOptions({
|
|
1972
|
+
debug: true,
|
|
1973
|
+
disableStderr: true,
|
|
1974
|
+
disableStdout: true
|
|
1975
|
+
});
|
|
1976
|
+
this.autoAssignMissingOptionFlags();
|
|
1977
|
+
this.autoAssignSubCommandAliases();
|
|
1978
|
+
this.presetsEnabled();
|
|
1979
|
+
}
|
|
1980
|
+
deleteDataFile() {
|
|
1981
|
+
const filepath = this.dataFilepath;
|
|
1982
|
+
if (fs.existsSync(filepath)) remove(filepath);
|
|
2168
1983
|
}
|
|
2169
1984
|
version(string) {
|
|
1985
|
+
this.assertNotInitialized();
|
|
2170
1986
|
this.$.version(string);
|
|
1987
|
+
const opt = this.options.find((o)=>o.attributeName() === 'version');
|
|
1988
|
+
if (opt) this.meta.globalOptions.push(opt);
|
|
1989
|
+
return this;
|
|
1990
|
+
}
|
|
1991
|
+
description(...lines) {
|
|
1992
|
+
this.assertNotInitialized();
|
|
1993
|
+
const description = lines.join('\n');
|
|
1994
|
+
const summary = description.split(/(\. ?|\n|$)/)[0];
|
|
1995
|
+
this.$.summary(summary + '.');
|
|
1996
|
+
this.$.description(description);
|
|
2171
1997
|
return this;
|
|
2172
1998
|
}
|
|
2173
1999
|
alias(alias) {
|
|
2174
|
-
|
|
2000
|
+
this.assertNotInitialized();
|
|
2001
|
+
this.assertCommandNameNotReserved(alias);
|
|
2175
2002
|
this.$.alias(alias);
|
|
2176
2003
|
return this;
|
|
2177
2004
|
}
|
|
2178
2005
|
aliases(...aliases) {
|
|
2179
|
-
|
|
2006
|
+
this.assertNotInitialized();
|
|
2007
|
+
aliases.forEach((alias)=>this.assertCommandNameNotReserved(alias));
|
|
2008
|
+
this.$.aliases(aliases);
|
|
2180
2009
|
return this;
|
|
2181
2010
|
}
|
|
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
2011
|
enableBuiltinOptions(options) {
|
|
2192
|
-
|
|
2012
|
+
this.assertNotInitialized();
|
|
2193
2013
|
if (!options || options.debug) this.globalOption('-D, --debug', 'Output debugging information.');
|
|
2194
2014
|
if (!options || options.disableColor) this.globalOption('-C, --disable-color', 'Disable color in terminal output.');
|
|
2195
2015
|
if (!options || options.disableStderr) this.globalOption('-E, --disable-stderr', 'Mute all output to stderr.');
|
|
2196
2016
|
if (!options || options.disableStdout) this.globalOption('-O, --disable-stdout', 'Mute all output to stdout.');
|
|
2017
|
+
return this;
|
|
2197
2018
|
}
|
|
2198
|
-
|
|
2199
|
-
this
|
|
2019
|
+
argument(name, cb) {
|
|
2020
|
+
this.assertNotInitialized();
|
|
2021
|
+
const ins = new ArgumentBuilder(this, name);
|
|
2022
|
+
this.$.addArgument(ins.$);
|
|
2023
|
+
if (typeof cb === 'function') {
|
|
2024
|
+
cb(ins, this);
|
|
2025
|
+
} else if (typeof cb === 'string') {
|
|
2026
|
+
ins.description(cb);
|
|
2027
|
+
}
|
|
2028
|
+
return this;
|
|
2200
2029
|
}
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
this
|
|
2030
|
+
option(flags, cb) {
|
|
2031
|
+
this.assertNotInitialized();
|
|
2032
|
+
const ins = new OptionBuilder(this, flags);
|
|
2033
|
+
if (this.hasIdenticalParentOption(ins.$)) return this;
|
|
2034
|
+
this.$.addOption(ins.$);
|
|
2035
|
+
if (typeof cb === 'function') {
|
|
2036
|
+
cb(ins, this);
|
|
2037
|
+
} else if (typeof cb === 'string') {
|
|
2038
|
+
ins.description(cb);
|
|
2039
|
+
}
|
|
2040
|
+
return this;
|
|
2205
2041
|
}
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2042
|
+
globalOption(flags, cb) {
|
|
2043
|
+
return this.option(flags, (ins)=>{
|
|
2044
|
+
const opt = ins.$;
|
|
2045
|
+
this.meta.globalOptions.push(opt);
|
|
2046
|
+
if (typeof cb === 'function') {
|
|
2047
|
+
cb(ins, this);
|
|
2048
|
+
} else if (typeof cb === 'string') {
|
|
2049
|
+
ins.description(cb);
|
|
2050
|
+
}
|
|
2051
|
+
if (opt.hidden) this.meta.hiddenGlobalOptions.add(opt);
|
|
2052
|
+
});
|
|
2053
|
+
}
|
|
2054
|
+
command(name, cb) {
|
|
2055
|
+
this.assertNotInitialized();
|
|
2056
|
+
new CommandBuilder(name, cb, this);
|
|
2211
2057
|
return this;
|
|
2212
2058
|
}
|
|
2213
|
-
|
|
2214
|
-
this
|
|
2059
|
+
nativeCommand(name, cb) {
|
|
2060
|
+
this.assertNotInitialized();
|
|
2061
|
+
new CommandBuilder(name, cb, this, true);
|
|
2215
2062
|
return this;
|
|
2216
2063
|
}
|
|
2217
|
-
|
|
2218
|
-
this
|
|
2064
|
+
action(fn) {
|
|
2065
|
+
this.assertNotInitialized();
|
|
2066
|
+
Object.defineProperty(this.meta, 'actionHandler', {
|
|
2067
|
+
value: fn,
|
|
2068
|
+
configurable: true
|
|
2069
|
+
});
|
|
2219
2070
|
return this;
|
|
2220
2071
|
}
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2072
|
+
errorHandler(fn) {
|
|
2073
|
+
this.assertNotInitialized();
|
|
2074
|
+
Object.defineProperty(this.meta, 'errorHandler', {
|
|
2075
|
+
value: fn,
|
|
2076
|
+
configurable: true
|
|
2077
|
+
});
|
|
2225
2078
|
return this;
|
|
2226
2079
|
}
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2080
|
+
appData(key, value) {
|
|
2081
|
+
this.assertNotInitialized();
|
|
2082
|
+
this.features.appData(true);
|
|
2083
|
+
this.db.appData.defineProperty(key, value);
|
|
2084
|
+
return this;
|
|
2230
2085
|
}
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2086
|
+
config(key, entry) {
|
|
2087
|
+
this.assertNotInitialized();
|
|
2088
|
+
this.features.config(true);
|
|
2089
|
+
this.db.config.defineProperty(key, entry);
|
|
2090
|
+
return this;
|
|
2091
|
+
}
|
|
2092
|
+
preset(name, preset) {
|
|
2093
|
+
this.assertNotInitialized();
|
|
2094
|
+
this.features.presets();
|
|
2095
|
+
this.meta.presetOptionKeys.push(name);
|
|
2096
|
+
this.db.presets.defineProperty(name, {
|
|
2097
|
+
description: preset.description,
|
|
2098
|
+
presets: preset.presets ?? [],
|
|
2099
|
+
args: preset.args ?? [],
|
|
2100
|
+
options: preset.options ?? {}
|
|
2101
|
+
});
|
|
2102
|
+
return this;
|
|
2103
|
+
}
|
|
2104
|
+
presetsEnabled(boolean = true) {
|
|
2105
|
+
this.assertNotInitialized();
|
|
2106
|
+
this.features.presets(boolean);
|
|
2107
|
+
return this;
|
|
2108
|
+
}
|
|
2109
|
+
autoAssignMissingOptionFlags(boolean = true) {
|
|
2110
|
+
this.assertNotInitialized();
|
|
2111
|
+
this.features.autoAssignMissingOptionFlags(boolean);
|
|
2112
|
+
return this;
|
|
2113
|
+
}
|
|
2114
|
+
autoAssignSubCommandAliases(boolean = true) {
|
|
2115
|
+
this.assertNotInitialized();
|
|
2116
|
+
this.features.autoAssignSubCommandAliases(boolean);
|
|
2117
|
+
return this;
|
|
2118
|
+
}
|
|
2119
|
+
allowExcessArguments(bool = true) {
|
|
2120
|
+
this.assertNotInitialized();
|
|
2121
|
+
this.$.allowExcessArguments(bool);
|
|
2122
|
+
return this;
|
|
2123
|
+
}
|
|
2124
|
+
allowUnknownOption(bool = true) {
|
|
2125
|
+
this.assertNotInitialized();
|
|
2126
|
+
this.$.allowUnknownOption(bool);
|
|
2127
|
+
return this;
|
|
2128
|
+
}
|
|
2129
|
+
/**
|
|
2130
|
+
* Register callback to use as replacement for calling process.exit.
|
|
2131
|
+
*/ exitOverride(callback) {
|
|
2132
|
+
this.assertNotInitialized();
|
|
2133
|
+
this.$.exitOverride(callback);
|
|
2134
|
+
return this;
|
|
2135
|
+
}
|
|
2136
|
+
throwInsteadOfProcessExit() {
|
|
2137
|
+
this.assertNotInitialized();
|
|
2138
|
+
const onErr = (err)=>{
|
|
2139
|
+
throw err;
|
|
2140
|
+
};
|
|
2141
|
+
this.exitOverride(onErr);
|
|
2142
|
+
this.errorHandler(onErr);
|
|
2143
|
+
}
|
|
2144
|
+
/**
|
|
2145
|
+
* Add hook for life cycle event.
|
|
2146
|
+
*/ hook(event, listener) {
|
|
2147
|
+
this.assertNotInitialized();
|
|
2148
|
+
this.$.hook(event, listener);
|
|
2149
|
+
return this;
|
|
2150
|
+
}
|
|
2151
|
+
/**
|
|
2152
|
+
* You can customise the help by overriding Help properties using configureHelp(),
|
|
2153
|
+
* or with a subclass of Help by overriding createHelp().
|
|
2154
|
+
*/ configureHelp(configuration) {
|
|
2155
|
+
this.assertNotInitialized();
|
|
2156
|
+
this.$.configureHelp(configuration);
|
|
2157
|
+
return this;
|
|
2158
|
+
}
|
|
2159
|
+
/**
|
|
2160
|
+
* Display the help or a custom message after an error occurs.
|
|
2161
|
+
*/ showHelpAfterError(displayHelp) {
|
|
2162
|
+
this.assertNotInitialized();
|
|
2163
|
+
this.$.showHelpAfterError(displayHelp);
|
|
2164
|
+
return this;
|
|
2165
|
+
}
|
|
2166
|
+
/**
|
|
2167
|
+
* Display suggestion of similar commands for unknown commands, or options for unknown options.
|
|
2168
|
+
*/ showSuggestionAfterError(displaySuggestion) {
|
|
2169
|
+
this.assertNotInitialized();
|
|
2170
|
+
this.$.showSuggestionAfterError(displaySuggestion);
|
|
2171
|
+
return this;
|
|
2172
|
+
}
|
|
2173
|
+
/**
|
|
2174
|
+
* Add additional text to be displayed with the built-in help.
|
|
2175
|
+
*
|
|
2176
|
+
* Position is 'before' or 'after' to affect just this command,
|
|
2177
|
+
* and 'beforeAll' or 'afterAll' to affect this command and all its subcommands.
|
|
2178
|
+
*/ addHelpText(position, text) {
|
|
2179
|
+
this.assertNotInitialized();
|
|
2180
|
+
this.$.addHelpText(position, text);
|
|
2181
|
+
return this;
|
|
2182
|
+
}
|
|
2183
|
+
throwCommanderError(message, exitCode = 1, type = 'error') {
|
|
2184
|
+
throw new CommanderError(exitCode, type, message);
|
|
2238
2185
|
}
|
|
2239
2186
|
hideGlobalOptions(...names) {
|
|
2240
|
-
|
|
2187
|
+
this.assertNotInitialized();
|
|
2188
|
+
const globals = this.getGlobalOptions();
|
|
2241
2189
|
names = names.length ? names : globals.map((opt)=>opt.attributeName());
|
|
2242
2190
|
for (const name of names){
|
|
2243
2191
|
if (!name) continue;
|
|
@@ -2249,12 +2197,13 @@ class OptionReader extends Base {
|
|
|
2249
2197
|
break;
|
|
2250
2198
|
}
|
|
2251
2199
|
}
|
|
2252
|
-
if (!found)
|
|
2200
|
+
if (!found) this.throwCommanderError(`Unknown global option name: ${name} for command, ${this.name}`);
|
|
2253
2201
|
}
|
|
2254
2202
|
return this;
|
|
2255
2203
|
}
|
|
2256
2204
|
unhideGlobalOptions(...names) {
|
|
2257
|
-
|
|
2205
|
+
this.assertNotInitialized();
|
|
2206
|
+
const globals = this.getGlobalOptions();
|
|
2258
2207
|
names = names.length ? names : globals.map((opt)=>opt.attributeName());
|
|
2259
2208
|
for (const name of names){
|
|
2260
2209
|
if (!name) continue;
|
|
@@ -2266,190 +2215,872 @@ class OptionReader extends Base {
|
|
|
2266
2215
|
break;
|
|
2267
2216
|
}
|
|
2268
2217
|
}
|
|
2269
|
-
if (!found)
|
|
2218
|
+
if (!found) this.throwCommanderError(`Unknown global option name: ${name} for command, ${this.name}`);
|
|
2270
2219
|
}
|
|
2271
2220
|
return this;
|
|
2272
2221
|
}
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
} else if (typeof cb === 'string') {
|
|
2279
|
-
ins.description(cb);
|
|
2280
|
-
}
|
|
2281
|
-
objDestroy(ins);
|
|
2222
|
+
/**
|
|
2223
|
+
* Set the directory for searching for executable subcommands of this command.
|
|
2224
|
+
*/ executableDir(path) {
|
|
2225
|
+
this.assertNotInitialized();
|
|
2226
|
+
this.$.executableDir(path);
|
|
2282
2227
|
return this;
|
|
2283
2228
|
}
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
} else if (typeof cb === 'string') {
|
|
2290
|
-
ins.description(cb);
|
|
2291
|
-
}
|
|
2292
|
-
objDestroy(ins);
|
|
2229
|
+
/**
|
|
2230
|
+
* Store option value.
|
|
2231
|
+
*/ setOptionValue(key, value) {
|
|
2232
|
+
this.assertNotInitialized();
|
|
2233
|
+
this.$.setOptionValue(key, value);
|
|
2293
2234
|
return this;
|
|
2294
2235
|
}
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2236
|
+
/**
|
|
2237
|
+
* Store option value and where the value came from.
|
|
2238
|
+
*/ setOptionValueWithSource(key, value, source) {
|
|
2239
|
+
this.assertNotInitialized();
|
|
2240
|
+
this.$.setOptionValueWithSource(key, value, source);
|
|
2241
|
+
return this;
|
|
2242
|
+
}
|
|
2243
|
+
setDataFilepath(filepath) {
|
|
2244
|
+
this.assertNotInitialized();
|
|
2245
|
+
Object.defineProperty(this, 'dataFilepath', {
|
|
2246
|
+
value: filepath
|
|
2305
2247
|
});
|
|
2248
|
+
if (Object.hasOwn(this, 'db') && Object.hasOwn(this.db, 'db')) {
|
|
2249
|
+
this.db.db.setFilepath(filepath);
|
|
2250
|
+
}
|
|
2306
2251
|
}
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2252
|
+
/**
|
|
2253
|
+
* Display error message and exit (or call exitOverride).
|
|
2254
|
+
*/ outputError(message, options) {
|
|
2255
|
+
this.$.error(message, options);
|
|
2310
2256
|
}
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2257
|
+
/**
|
|
2258
|
+
* Output help information for this command.
|
|
2259
|
+
*/ outputHelp() {
|
|
2260
|
+
console.log(this.getRenderedHelp());
|
|
2261
|
+
}
|
|
2262
|
+
/**
|
|
2263
|
+
* Display error message and exit (or call exitOverride).
|
|
2264
|
+
*/ outputDebugMessage(event, getProps = ()=>({})) {
|
|
2265
|
+
OutputManager.getInstance().outputDebug(()=>({
|
|
2266
|
+
event,
|
|
2267
|
+
cmd: this.getPrefixString(),
|
|
2268
|
+
...getProps()
|
|
2269
|
+
}));
|
|
2270
|
+
}
|
|
2271
|
+
parseArguments(args) {
|
|
2272
|
+
const last = this.arguments.length - 1;
|
|
2273
|
+
return args.map((arg, i)=>{
|
|
2274
|
+
if (!arg) return arg;
|
|
2275
|
+
const parse = this.meta.argParsers[i > last ? last : i];
|
|
2276
|
+
return parse ? Array.isArray(arg) ? arg.map(parse) : parse(arg) : arg;
|
|
2315
2277
|
});
|
|
2316
2278
|
}
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2279
|
+
/**
|
|
2280
|
+
* Parses (and validates) options using the parsers defined in the command builder.
|
|
2281
|
+
*/ parseOptions(opts) {
|
|
2282
|
+
for (const [key, value] of Object.entries(opts)){
|
|
2283
|
+
const parse = this.meta.optParsers[key];
|
|
2284
|
+
opts[key] = parse ? Array.isArray(value) ? value.map(parse) : parse(value) : value;
|
|
2285
|
+
}
|
|
2286
|
+
return opts;
|
|
2320
2287
|
}
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2288
|
+
/**
|
|
2289
|
+
* Validate ALREADY PARSED args using the validators defined in the command builder.
|
|
2290
|
+
*/ assertValidArguments(parsedArgs) {
|
|
2291
|
+
const last = this.arguments.length - 1;
|
|
2292
|
+
parsedArgs.forEach((arg, i)=>{
|
|
2293
|
+
if (arg == null) return;
|
|
2294
|
+
const index = i > last ? last : i;
|
|
2295
|
+
const validators = this.meta.argValidators[index];
|
|
2296
|
+
if (!validators) return;
|
|
2297
|
+
for (const isValid of validators){
|
|
2298
|
+
ensureThat(arg, isValid, {
|
|
2299
|
+
Err: InvalidArgumentError
|
|
2300
|
+
});
|
|
2301
|
+
}
|
|
2302
|
+
});
|
|
2303
|
+
return parsedArgs;
|
|
2325
2304
|
}
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2305
|
+
/**
|
|
2306
|
+
* Validate ALREADY PARSED options using the validators defined in the command builder.
|
|
2307
|
+
*/ assertValidOptions(parsedOptions) {
|
|
2308
|
+
for (const [key, value] of Object.entries(parsedOptions)){
|
|
2309
|
+
if (!this.meta.optValidators[key]) continue;
|
|
2310
|
+
if (value == null) continue;
|
|
2311
|
+
for (const isValid of this.meta.optValidators[key]){
|
|
2312
|
+
ensureThat(value, isValid);
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
return parsedOptions;
|
|
2316
|
+
}
|
|
2317
|
+
assertValidPreset(key, preset) {
|
|
2318
|
+
const { description, presets, args, options } = preset;
|
|
2319
|
+
ensureThat(key, isStringWithNoSpacesOrDashes);
|
|
2320
|
+
ensureThat(description, isString);
|
|
2321
|
+
ensureThat(presets, isStringArray);
|
|
2322
|
+
ensureThat(args, isArray);
|
|
2323
|
+
this.assertPresetArgsOptional(args);
|
|
2324
|
+
this.assertValidArguments(args);
|
|
2325
|
+
ensureThat(options, isObject);
|
|
2326
|
+
this.assertValidOptions(options);
|
|
2327
|
+
}
|
|
2328
|
+
get name() {
|
|
2329
|
+
return this.$.name();
|
|
2330
|
+
}
|
|
2331
|
+
/**
|
|
2332
|
+
* Get the command at the root of the command tree.
|
|
2333
|
+
*/ get root() {
|
|
2334
|
+
if (this.isRoot) return this;
|
|
2335
|
+
return this.getAncestors().pop();
|
|
2336
|
+
}
|
|
2337
|
+
get isRoot() {
|
|
2338
|
+
return !this.parent;
|
|
2339
|
+
}
|
|
2340
|
+
get arguments() {
|
|
2341
|
+
return this.$.registeredArguments;
|
|
2342
|
+
}
|
|
2343
|
+
get options() {
|
|
2344
|
+
return this.$.options;
|
|
2345
|
+
}
|
|
2346
|
+
get commander() {
|
|
2347
|
+
return this.$;
|
|
2348
|
+
}
|
|
2349
|
+
get hasGrandChildren() {
|
|
2350
|
+
return this.meta.subcommands.some((sub)=>!!sub.meta.subcommands.length);
|
|
2351
|
+
}
|
|
2352
|
+
/**
|
|
2353
|
+
* Returns whether a command's last argument is variadic.
|
|
2354
|
+
*/ get isLastArgVariadic() {
|
|
2355
|
+
if (!this.arguments.length) return false;
|
|
2356
|
+
return arrLast(this.arguments).variadic;
|
|
2357
|
+
}
|
|
2358
|
+
get dataFilepath() {
|
|
2359
|
+
return path.join(CommandBuilder.dataDirectory, this.root.name + '.json');
|
|
2360
|
+
}
|
|
2361
|
+
/**
|
|
2362
|
+
* Get the executable search directory.
|
|
2363
|
+
*/ getExecutableDir() {
|
|
2364
|
+
return this.$.executableDir();
|
|
2365
|
+
}
|
|
2366
|
+
/**
|
|
2367
|
+
* Retrieve option value.
|
|
2368
|
+
*/ getOptionValue(key) {
|
|
2369
|
+
return this.$.getOptionValue(key);
|
|
2370
|
+
}
|
|
2371
|
+
/**
|
|
2372
|
+
* Get source of option value.
|
|
2373
|
+
*/ getOptionValueSource(key) {
|
|
2374
|
+
return this.$.getOptionValueSource(key);
|
|
2375
|
+
}
|
|
2376
|
+
/**
|
|
2377
|
+
* Get source of option value. See also .optsWithGlobals().
|
|
2378
|
+
*/ getOptionValueSourceWithGlobals(key) {
|
|
2379
|
+
return this.$.getOptionValueSourceWithGlobals(key);
|
|
2380
|
+
}
|
|
2381
|
+
getActionHandler() {
|
|
2382
|
+
return this.meta.actionHandler;
|
|
2383
|
+
}
|
|
2384
|
+
getDescription() {
|
|
2385
|
+
return this.$.description();
|
|
2386
|
+
}
|
|
2387
|
+
getSummary() {
|
|
2388
|
+
return this.$.summary();
|
|
2389
|
+
}
|
|
2390
|
+
getVersion() {
|
|
2391
|
+
return this.$.version();
|
|
2392
|
+
}
|
|
2393
|
+
getAlias() {
|
|
2394
|
+
return this.$.alias();
|
|
2395
|
+
}
|
|
2396
|
+
getAliases() {
|
|
2397
|
+
return this.$.aliases();
|
|
2398
|
+
}
|
|
2399
|
+
/**
|
|
2400
|
+
* Get a commands prefix array based on all its parent/ancestor commands.
|
|
2401
|
+
*/ getPrefixArray() {
|
|
2402
|
+
return this.getAncestors({
|
|
2403
|
+
includeSelf: true
|
|
2404
|
+
}).reverse().map((node)=>node.name);
|
|
2405
|
+
}
|
|
2406
|
+
/**
|
|
2407
|
+
* Get a commands prefix string based on all its parent/ancestor commands.
|
|
2408
|
+
*/ getPrefixString() {
|
|
2409
|
+
return this.getPrefixArray().join(' ');
|
|
2410
|
+
}
|
|
2411
|
+
getGlobalOptions() {
|
|
2412
|
+
const result = [];
|
|
2413
|
+
for (const anc of this.getAncestors({
|
|
2414
|
+
includeSelf: true
|
|
2415
|
+
}).reverse()){
|
|
2416
|
+
for (const gopt of anc.meta.globalOptions){
|
|
2417
|
+
if (!this.meta.hiddenGlobalOptions.has(gopt)) {
|
|
2418
|
+
result.push(gopt);
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
return result;
|
|
2423
|
+
}
|
|
2424
|
+
getOwnAndGlobalOptions() {
|
|
2425
|
+
return this.options.concat(this.getGlobalOptions());
|
|
2426
|
+
}
|
|
2427
|
+
*getChildrenIterator(options) {
|
|
2428
|
+
if (options?.includeSelf) yield this;
|
|
2429
|
+
for (const sub of this.meta.subcommands){
|
|
2430
|
+
yield sub;
|
|
2431
|
+
yield* sub.getChildrenIterator();
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
getChildren(options) {
|
|
2435
|
+
return [
|
|
2436
|
+
...this.getChildrenIterator(options)
|
|
2437
|
+
];
|
|
2438
|
+
}
|
|
2439
|
+
*getAncestorsIterator(options) {
|
|
2440
|
+
if (options?.includeSelf) yield this;
|
|
2441
|
+
let node = this.parent;
|
|
2442
|
+
while(node){
|
|
2443
|
+
yield node;
|
|
2444
|
+
node = node.parent;
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
/**
|
|
2448
|
+
* Get a command's ancestors, optionally starting from the command itself.
|
|
2449
|
+
*/ getAncestors(options) {
|
|
2450
|
+
return [
|
|
2451
|
+
...this.getAncestorsIterator(options)
|
|
2452
|
+
];
|
|
2453
|
+
}
|
|
2454
|
+
*getSiblingsIterator() {
|
|
2455
|
+
if (!this.parent) return;
|
|
2456
|
+
for (const sub of this.parent.meta.subcommands){
|
|
2457
|
+
if (sub === this) continue;
|
|
2458
|
+
yield sub;
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
/**
|
|
2462
|
+
* Returns an array of sibling CommandBuilder objects.
|
|
2463
|
+
*/ getSiblings() {
|
|
2464
|
+
return [
|
|
2465
|
+
...this.getSiblingsIterator()
|
|
2466
|
+
];
|
|
2467
|
+
}
|
|
2468
|
+
getClosestNonNativeParent() {
|
|
2469
|
+
for (const anc of this.getAncestorsIterator({
|
|
2470
|
+
includeSelf: true
|
|
2471
|
+
})){
|
|
2472
|
+
if (!anc.meta.isNative) return anc;
|
|
2473
|
+
}
|
|
2474
|
+
this.throwCommanderError('No non-native parent found');
|
|
2475
|
+
}
|
|
2476
|
+
getRenderedHelp() {
|
|
2477
|
+
return this.$.helpInformation();
|
|
2478
|
+
}
|
|
2479
|
+
getOptsWithGlobalsParsed() {
|
|
2480
|
+
return this.parseOptions(this.$.optsWithGlobals());
|
|
2481
|
+
}
|
|
2482
|
+
getParsedValidArgsOptsWithPresets() {
|
|
2483
|
+
const [presetArgs, presetOpts, presetOrder] = this.getPresetArgsAndOpts();
|
|
2484
|
+
const args = this.getParsedValidArgsWithPresets(presetArgs);
|
|
2485
|
+
const opts = this.getParsedValidOptsWithPresets(presetOpts);
|
|
2486
|
+
this.debugLogArgsOpts(args, opts, presetArgs, presetOpts, presetOrder);
|
|
2487
|
+
return [
|
|
2488
|
+
args,
|
|
2489
|
+
opts
|
|
2490
|
+
];
|
|
2491
|
+
}
|
|
2492
|
+
getParsedValidArgsWithPresets(presetArgs) {
|
|
2493
|
+
const result = arrAssign([], ...presetArgs, this.parseArguments(this.$.args));
|
|
2494
|
+
this.combineVariadicArgs(result);
|
|
2495
|
+
this.assertValidArguments(result);
|
|
2496
|
+
return this.padArgsWithUndefinedUntilExpectedLength(result);
|
|
2497
|
+
}
|
|
2498
|
+
getParsedValidOptsWithPresets(presetOpts) {
|
|
2499
|
+
const parsed = this.getOptsWithGlobalsParsed();
|
|
2500
|
+
const opts = presetOpts.length ? objAssign({}, ...presetOpts, parsed) : parsed;
|
|
2501
|
+
this.deleteOptionsWithDefaultOrNoValue(opts);
|
|
2502
|
+
this.assertValidOptions(opts);
|
|
2503
|
+
return opts;
|
|
2504
|
+
}
|
|
2505
|
+
getPresetArgsAndOpts() {
|
|
2506
|
+
if (!this.features.isPresetsEnabled) return [
|
|
2507
|
+
[],
|
|
2508
|
+
[],
|
|
2509
|
+
[]
|
|
2510
|
+
];
|
|
2511
|
+
const presets = this.db.presets.getAll();
|
|
2512
|
+
const opts = this.$.optsWithGlobals();
|
|
2513
|
+
const selectedPresets = Object.keys(presets).filter((name)=>opts[name] === true);
|
|
2514
|
+
const presetOrder = Object.keys(opts).filter((key)=>selectedPresets.includes(key));
|
|
2515
|
+
const presetArgs = presetOrder.map((name)=>presets[name].args);
|
|
2516
|
+
const presetOpts = presetOrder.map((name)=>presets[name].options);
|
|
2517
|
+
return [
|
|
2518
|
+
presetArgs,
|
|
2519
|
+
presetOpts,
|
|
2520
|
+
presetOrder
|
|
2521
|
+
];
|
|
2522
|
+
}
|
|
2523
|
+
combineVariadicArgs(result) {
|
|
2524
|
+
if (this.isLastArgVariadic && result.length && !Array.isArray(arrLast(result))) {
|
|
2525
|
+
const rest = result.splice(this.arguments.length - 1);
|
|
2526
|
+
result.push(rest.filter((arg)=>arg != null));
|
|
2527
|
+
}
|
|
2528
|
+
return result;
|
|
2529
|
+
}
|
|
2530
|
+
debugLogArgsOpts(args, opts, presetArgs, presetOpts, presetOrder) {
|
|
2531
|
+
if (opts['debug']) {
|
|
2532
|
+
if (this.features.isPresetsEnabled) {
|
|
2533
|
+
this.outputDebugMessage('parsePresets', ()=>({
|
|
2534
|
+
presetOrder,
|
|
2535
|
+
presetArgs,
|
|
2536
|
+
presetOpts
|
|
2537
|
+
}));
|
|
2538
|
+
}
|
|
2539
|
+
this.outputDebugMessage('parseArgsOpts', ()=>{
|
|
2540
|
+
return {
|
|
2541
|
+
args,
|
|
2542
|
+
opts,
|
|
2543
|
+
command: [
|
|
2544
|
+
this.root.name,
|
|
2545
|
+
...this.meta.rawArgs
|
|
2546
|
+
].join(' ')
|
|
2547
|
+
};
|
|
2548
|
+
});
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
deleteOptionsWithDefaultOrNoValue(opts) {
|
|
2552
|
+
const names = new Set(this.getOwnAndGlobalOptions().map((o)=>o.attributeName()));
|
|
2553
|
+
for (const [key, value] of Object.entries(opts)){
|
|
2554
|
+
if (!names.has(key) || value === false || value == null) {
|
|
2555
|
+
setNonEnumerable(opts, key);
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
for (const key of this.meta.presetOptionKeys){
|
|
2559
|
+
if (Object.hasOwn(opts, key)) {
|
|
2560
|
+
setNonEnumerable(opts, key);
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
return opts;
|
|
2564
|
+
}
|
|
2565
|
+
handleOutputOptions() {
|
|
2566
|
+
const opts = this.$.optsWithGlobals();
|
|
2567
|
+
const om = OutputManager.getInstance().reset();
|
|
2568
|
+
if (opts['disableColor']) om.colors.enabled = false;
|
|
2569
|
+
if (opts['disableStderr']) om.stderr.disable();
|
|
2570
|
+
if (opts['disableStdout']) om.stdout.disable();
|
|
2571
|
+
if (opts['debug']) {
|
|
2572
|
+
om.debug.enable();
|
|
2573
|
+
om.drainDebugMessageQueue();
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
padArgsWithUndefinedUntilExpectedLength(args) {
|
|
2577
|
+
while(args.length < this.arguments.length)args.push(undefined);
|
|
2578
|
+
return args;
|
|
2579
|
+
}
|
|
2580
|
+
assertPresetArgsOptional(args) {
|
|
2581
|
+
args.forEach((arg, i)=>{
|
|
2582
|
+
if (arg != null && i < this.arguments.length && this.arguments[i].required) {
|
|
2583
|
+
this.throwCommanderError(`Cannot preset required arguments.`);
|
|
2584
|
+
}
|
|
2334
2585
|
});
|
|
2335
|
-
return this;
|
|
2336
2586
|
}
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2587
|
+
addUtilCommands() {
|
|
2588
|
+
if (!this.hasGrandChildren && !this.features.isConfigEnabled && !this.features.isPresetsEnabled && !this.features.isAppDataEnabled) {
|
|
2589
|
+
return;
|
|
2590
|
+
}
|
|
2591
|
+
this.nativeCommand('util', (u)=>{
|
|
2592
|
+
const cmd = u.getClosestNonNativeParent();
|
|
2593
|
+
u.alias('u');
|
|
2594
|
+
u.description('Utility commands.');
|
|
2595
|
+
if (cmd.features.isConfigEnabled) {
|
|
2596
|
+
u.nativeCommand('config', createConfigCommand);
|
|
2597
|
+
}
|
|
2598
|
+
if (cmd.features.isPresetsEnabled && cmd.meta.hasCustomActionHandler) {
|
|
2599
|
+
u.nativeCommand('presets', createPresetsCommand);
|
|
2600
|
+
}
|
|
2601
|
+
if (cmd.hasGrandChildren) {
|
|
2602
|
+
u.nativeCommand('list', createUtilListCommand);
|
|
2603
|
+
}
|
|
2604
|
+
if (cmd.features.isConfigEnabled || cmd.features.isPresetsEnabled || cmd.features.isAppDataEnabled) {
|
|
2605
|
+
u.nativeCommand('filepath', createUtilFilepathCommand);
|
|
2606
|
+
}
|
|
2607
|
+
function createUtilFilepathCommand(f) {
|
|
2608
|
+
f.alias('f');
|
|
2609
|
+
f.description('Print filepath to JSON file containing user data, eg. config and presets.');
|
|
2610
|
+
f.action(async ()=>console.log(cmd.dataFilepath));
|
|
2611
|
+
}
|
|
2612
|
+
function createUtilListCommand(l) {
|
|
2613
|
+
l.alias('l');
|
|
2614
|
+
l.description('List nested subcommands.');
|
|
2615
|
+
l.option('--all', 'Include utility commands.');
|
|
2616
|
+
l.action(async (opts)=>{
|
|
2617
|
+
const filter = opts.all ? undefined : (prefix)=>{
|
|
2618
|
+
return !/ (config|presets|util)( .+)?$/gi.test(prefix);
|
|
2619
|
+
};
|
|
2620
|
+
const table = [];
|
|
2621
|
+
for (const c of cmd.getChildrenIterator({
|
|
2622
|
+
includeSelf: true
|
|
2623
|
+
})){
|
|
2624
|
+
const prefix = c.getPrefixString();
|
|
2625
|
+
if (filter && !filter(prefix)) continue;
|
|
2626
|
+
table.push([
|
|
2627
|
+
prefix,
|
|
2628
|
+
c.getSummary()
|
|
2629
|
+
]);
|
|
2630
|
+
}
|
|
2631
|
+
const ansi = table.map((row)=>{
|
|
2632
|
+
const arr = row[0].split(' ');
|
|
2633
|
+
const last = arr.pop();
|
|
2634
|
+
let col = colors.magenta;
|
|
2635
|
+
if (row[1].startsWith('[Preset]')) {
|
|
2636
|
+
col = colors.green;
|
|
2637
|
+
} else if (/ (util|config|presets) /.test(row[0])) {
|
|
2638
|
+
col = colors.gray;
|
|
2639
|
+
} else if (/ (util|config|presets)/.test(row[0])) {
|
|
2640
|
+
col = colors.dim;
|
|
2641
|
+
}
|
|
2642
|
+
row[0] = arr.map(colors.dim).concat(col(last)).join(' ');
|
|
2643
|
+
return row;
|
|
2644
|
+
});
|
|
2645
|
+
console.log(formatTableForTerminal(ansi, [
|
|
2646
|
+
'Command',
|
|
2647
|
+
'Summary'
|
|
2648
|
+
]));
|
|
2649
|
+
});
|
|
2650
|
+
}
|
|
2651
|
+
function createPresetsCommand(p) {
|
|
2652
|
+
const db = cmd.db.presets;
|
|
2653
|
+
p.alias('p');
|
|
2654
|
+
p.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.');
|
|
2655
|
+
p.nativeCommand('edit', (e)=>{
|
|
2656
|
+
e.alias('e');
|
|
2657
|
+
e.description('Edit as JSON in a text editor.');
|
|
2658
|
+
e.option('--editor [cmd]', 'The command to launch your preferred text editor.');
|
|
2659
|
+
e.action(async (opts)=>{
|
|
2660
|
+
db.edit(opts.editor);
|
|
2661
|
+
console.info(db.getAll());
|
|
2662
|
+
});
|
|
2663
|
+
});
|
|
2664
|
+
p.nativeCommand('list', (l)=>{
|
|
2665
|
+
l.alias('l');
|
|
2666
|
+
l.description('List all presets.');
|
|
2667
|
+
l.action(async ()=>console.dir(db.getAll(), {
|
|
2668
|
+
depth: null
|
|
2669
|
+
}));
|
|
2670
|
+
});
|
|
2671
|
+
for (const [key, preset] of Object.entries(db.getAll())){
|
|
2672
|
+
if (key === 'defaults') continue;
|
|
2673
|
+
cmd.option(`--${key}`, (o)=>{
|
|
2674
|
+
o.description('[Preset]: ' + preset.description);
|
|
2675
|
+
const implied = {
|
|
2676
|
+
defaults: true
|
|
2677
|
+
};
|
|
2678
|
+
const recurse = (preset)=>{
|
|
2679
|
+
if (implied[preset]) return;
|
|
2680
|
+
implied[preset] = true;
|
|
2681
|
+
db.get(preset).presets.forEach((k)=>recurse(k));
|
|
2682
|
+
};
|
|
2683
|
+
recurse(key);
|
|
2684
|
+
o.implies(implied);
|
|
2685
|
+
});
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
function createConfigCommand(c) {
|
|
2689
|
+
const db = cmd.db.config;
|
|
2690
|
+
c.alias('c');
|
|
2691
|
+
c.description('Manage configuration file.');
|
|
2692
|
+
c.nativeCommand('edit', (e)=>{
|
|
2693
|
+
e.alias('e');
|
|
2694
|
+
e.description('Edit as JSON in a text editor.');
|
|
2695
|
+
e.option('--editor [cmd]', 'The command to launch your preferred text editor.');
|
|
2696
|
+
e.action(async (opts)=>{
|
|
2697
|
+
db.edit(opts.editor);
|
|
2698
|
+
console.info(db.getAll());
|
|
2699
|
+
});
|
|
2700
|
+
});
|
|
2701
|
+
c.nativeCommand('list', (l)=>{
|
|
2702
|
+
l.alias('l');
|
|
2703
|
+
l.description('Print entire config with details.');
|
|
2704
|
+
l.action(async ()=>{
|
|
2705
|
+
const result = db.keys.map((key)=>({
|
|
2706
|
+
key,
|
|
2707
|
+
description: db.descriptions[key],
|
|
2708
|
+
value: db.get(key),
|
|
2709
|
+
defaultValue: db.defaultValues
|
|
2710
|
+
}));
|
|
2711
|
+
console.dir(result, {
|
|
2712
|
+
depth: null
|
|
2713
|
+
});
|
|
2714
|
+
});
|
|
2715
|
+
});
|
|
2716
|
+
c.nativeCommand('get', (g)=>{
|
|
2717
|
+
g.alias('g');
|
|
2718
|
+
g.description('Print value(s) from the config.');
|
|
2719
|
+
g.argument('[key]', 'The key to print the value of. Omit to print all values.');
|
|
2720
|
+
g.action(async (key)=>console.log(key ? db.get(key) : db.getAll()));
|
|
2721
|
+
});
|
|
2722
|
+
c.nativeCommand('set', (s)=>{
|
|
2723
|
+
s.alias('s');
|
|
2724
|
+
s.description('Set a value in the config.');
|
|
2725
|
+
s.argument('<key>', 'The key to set the value of.');
|
|
2726
|
+
s.argument('<value>', 'The new value.');
|
|
2727
|
+
s.action(async (key, val)=>{
|
|
2728
|
+
const parse = db.parsers[key];
|
|
2729
|
+
const value = typeof parse === 'function' ? parse(val) : val;
|
|
2730
|
+
db.set(key, value);
|
|
2731
|
+
console.info({
|
|
2732
|
+
[key]: value
|
|
2733
|
+
});
|
|
2734
|
+
});
|
|
2735
|
+
});
|
|
2736
|
+
c.nativeCommand('reset', (r)=>{
|
|
2737
|
+
r.alias('r');
|
|
2738
|
+
r.description('Reset to defaults.');
|
|
2739
|
+
r.argument('[key]', 'The key for which to reset the value. Omit to reset entire config.');
|
|
2740
|
+
r.action(async (key)=>{
|
|
2741
|
+
if (key) db.reset(key);
|
|
2742
|
+
else db.resetAll();
|
|
2743
|
+
console.info(db.getAll());
|
|
2744
|
+
});
|
|
2745
|
+
});
|
|
2746
|
+
/*
|
|
2747
|
+
config.option('--editor [cmd]', (o) => {
|
|
2748
|
+
o.description('The command to launch your preferred text editor.')
|
|
2749
|
+
})
|
|
2750
|
+
config.argument('[action]', (a) => {
|
|
2751
|
+
a.description('The action to perform.')
|
|
2752
|
+
a.choices(['edit', 'list', 'get', 'set', 'reset'])
|
|
2753
|
+
a.default('edit')
|
|
2754
|
+
})
|
|
2755
|
+
config.argument('[key]', (a) => {
|
|
2756
|
+
a.description('Property key (if applicable)')
|
|
2757
|
+
})
|
|
2758
|
+
config.argument('[value]', (a) => {
|
|
2759
|
+
a.description('Value to set (if applicable)')
|
|
2760
|
+
})
|
|
2761
|
+
config.action(
|
|
2762
|
+
async (action: string, key: string, value: string, opts: { editor: string }, config: CommandBuilder) => {
|
|
2763
|
+
const cmd = config.getClosestNonNativeParent()
|
|
2764
|
+
const cfg = cmd.db.config
|
|
2765
|
+
if (!action || action === 'edit') {
|
|
2766
|
+
cfg.edit(opts.editor)
|
|
2767
|
+
return console.info(cfg.getAll())
|
|
2768
|
+
} else if (action === 'list') {
|
|
2769
|
+
return console.dir(
|
|
2770
|
+
cfg.keys.map((key: string) => {
|
|
2771
|
+
return {
|
|
2772
|
+
key,
|
|
2773
|
+
description: cfg.descriptions[key],
|
|
2774
|
+
value: cfg.get(key),
|
|
2775
|
+
defaultValue: cfg.defaultValues,
|
|
2776
|
+
}
|
|
2777
|
+
})
|
|
2778
|
+
)
|
|
2779
|
+
} else if (action === 'get') {
|
|
2780
|
+
if (key) return console.log(cfg.get(key))
|
|
2781
|
+
else return console.log(cfg.getAll())
|
|
2782
|
+
} else if (action === 'set') {
|
|
2783
|
+
const from = cfg.get(key)
|
|
2784
|
+
const parse = cfg.parsers[key]
|
|
2785
|
+
const to = typeof parse === 'function' ? cfg.parsers[key](value) : value
|
|
2786
|
+
cfg.set(key, to)
|
|
2787
|
+
return console.info({ changed: key, from, to })
|
|
2788
|
+
} else if (action === 'reset') {
|
|
2789
|
+
if (key) cfg.reset(key)
|
|
2790
|
+
else cfg.resetAll()
|
|
2791
|
+
return console.info(cfg.getAll())
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
)*/ }
|
|
2795
|
+
});
|
|
2796
|
+
}
|
|
2797
|
+
/**
|
|
2798
|
+
* Makes aliases for the command.
|
|
2799
|
+
* The idea is to be able to navigate the command tree by only typing the first letter(s) of the command names.
|
|
2800
|
+
*
|
|
2801
|
+
* Example: A command 'cola' would get these aliases: ['c', 'co', 'col'].
|
|
2802
|
+
* However, if there are namespace clashes with sibling subcommands that start with the same letter,
|
|
2803
|
+
* eg. like 'cola' and 'coal' where the first two letters clash, cola's aliases are reduced to only ['col'] and similarly for 'coal'.
|
|
2804
|
+
*
|
|
2805
|
+
* This method creates the aliases, ensuring there are no clashes with sublings, why it is important that the
|
|
2806
|
+
* entire command tree is built before invoking this method.
|
|
2807
|
+
*/ assignSubCommandAliases() {
|
|
2808
|
+
if (this.getAlias() || this.name.length <= 1) return this;
|
|
2809
|
+
const sibAliases = this.getSiblings().map((sib)=>sib.getAliases()).flat();
|
|
2810
|
+
for(let i = 0; i < this.name.length - 1; i++){
|
|
2811
|
+
let cmdAlias = this.name.substring(0, i + 1);
|
|
2812
|
+
let isClash = arrSome(sibAliases, (sibAlias)=>{
|
|
2813
|
+
return cmdAlias === sibAlias;
|
|
2341
2814
|
});
|
|
2342
|
-
|
|
2815
|
+
if (isClash && i === 0) {
|
|
2816
|
+
cmdAlias = cmdAlias.charAt(0).toUpperCase();
|
|
2817
|
+
isClash = arrSome(sibAliases, (sibAlias)=>{
|
|
2818
|
+
return cmdAlias === sibAlias;
|
|
2819
|
+
});
|
|
2820
|
+
}
|
|
2821
|
+
if (isClash) continue;
|
|
2822
|
+
this.alias(cmdAlias);
|
|
2823
|
+
return this;
|
|
2824
|
+
}
|
|
2825
|
+
return this;
|
|
2343
2826
|
}
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2827
|
+
/**
|
|
2828
|
+
* Automatically set 'short' and 'long' names to options that don't have one assigned yet.
|
|
2829
|
+
*
|
|
2830
|
+
* First, it tries to assign a short name based on the first letter of the option's attribute name
|
|
2831
|
+
* Both lower and upper case are tried. If these is not available, the next letter of the option name is tried.
|
|
2832
|
+
*
|
|
2833
|
+
* If none of the letters of the option name are available, the option is skipped until all other
|
|
2834
|
+
* options have had letters from their names attempted assigned.
|
|
2835
|
+
* Those that remain are assigned the first available letter of the alphabet + 0-9.
|
|
2836
|
+
* If there are 64 options for the command and no more alphanumeric characters are available,
|
|
2837
|
+
* the option is not assigned a short name.
|
|
2838
|
+
*/ assignMissingOptionFlags() {
|
|
2839
|
+
const taken = new Set();
|
|
2840
|
+
for (const anc of this.getAncestorsIterator({
|
|
2841
|
+
includeSelf: true
|
|
2842
|
+
})){
|
|
2843
|
+
anc.options.forEach((opt)=>{
|
|
2844
|
+
if (!opt.short) return;
|
|
2845
|
+
taken.add(opt.short.replace(/^-/g, ''));
|
|
2846
|
+
});
|
|
2847
|
+
}
|
|
2848
|
+
const failed = new Set();
|
|
2849
|
+
// assign letter from option name
|
|
2850
|
+
this.options.forEach((opt)=>{
|
|
2851
|
+
if (opt.short) return;
|
|
2852
|
+
const name = opt.attributeName();
|
|
2853
|
+
for(let c = 0; c < name.length; c++){
|
|
2854
|
+
let char = name.charAt(c).toLowerCase();
|
|
2855
|
+
if (taken.has(char)) {
|
|
2856
|
+
char = char.toUpperCase();
|
|
2857
|
+
if (taken.has(char)) continue;
|
|
2858
|
+
}
|
|
2859
|
+
OptionHelpers.setShort(opt, char);
|
|
2860
|
+
taken.add(char);
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
failed.add(opt);
|
|
2864
|
+
});
|
|
2865
|
+
// assign random alphanumeric character.
|
|
2866
|
+
const name = 'abcdefghijklmnopqrstuvwxyz1234567890';
|
|
2867
|
+
failed.forEach((opt)=>{
|
|
2868
|
+
for(let c = 0; c < name.length; c++){
|
|
2869
|
+
let char = name.charAt(c);
|
|
2870
|
+
if (taken.has(char)) {
|
|
2871
|
+
char = char.toUpperCase();
|
|
2872
|
+
if (taken.has(char)) continue;
|
|
2873
|
+
}
|
|
2874
|
+
OptionHelpers.setShort(opt, char);
|
|
2875
|
+
taken.add(char);
|
|
2876
|
+
return;
|
|
2877
|
+
}
|
|
2878
|
+
});
|
|
2353
2879
|
}
|
|
2354
|
-
|
|
2355
|
-
(()=>
|
|
2356
|
-
|
|
2357
|
-
}
|
|
2358
|
-
|
|
2359
|
-
return new CommandBuilder(name, callback);
|
|
2360
|
-
}
|
|
2361
|
-
class CommandReader extends Base {
|
|
2362
|
-
get action() {
|
|
2363
|
-
return this.cmd.$.builder.meta.actionHandler;
|
|
2880
|
+
assertNoDuplicateCommandNames() {
|
|
2881
|
+
const names = this.$.commands.map((sub)=>sub.aliases().concat(sub.name())).flat();
|
|
2882
|
+
if (names.length !== new Set(names).size) {
|
|
2883
|
+
this.throwCommanderError(`Duplicate subcommand names/aliases found for command, ${this.name}: ${names.join(', ')}`);
|
|
2884
|
+
}
|
|
2364
2885
|
}
|
|
2365
|
-
|
|
2366
|
-
|
|
2886
|
+
hasIdenticalParentOption(option) {
|
|
2887
|
+
const flags = option.flags;
|
|
2888
|
+
for (const anc of this.getAncestorsIterator({
|
|
2889
|
+
includeSelf: true
|
|
2890
|
+
})){
|
|
2891
|
+
for (const opt of anc.$.options){
|
|
2892
|
+
if (flags === opt.flags) {
|
|
2893
|
+
return true;
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
return false;
|
|
2367
2898
|
}
|
|
2368
|
-
|
|
2369
|
-
|
|
2899
|
+
assertNoDuplicateOptionNames() {
|
|
2900
|
+
const throwErr = (cmd, opt, anc)=>{
|
|
2901
|
+
this.throwCommanderError(`Duplicate option names > cmd: ${cmd.name}, ${anc ? `anc: ${anc.name}, ` : ''}opt: ${opt}`);
|
|
2902
|
+
};
|
|
2903
|
+
const set = new Set();
|
|
2904
|
+
for (const opt of this.options){
|
|
2905
|
+
if (opt.name() === 'help') continue;
|
|
2906
|
+
if (opt.short) {
|
|
2907
|
+
if (set.has(opt.short)) throwErr(this, opt.short);
|
|
2908
|
+
set.add(opt.short);
|
|
2909
|
+
}
|
|
2910
|
+
if (opt.long) {
|
|
2911
|
+
if (set.has(opt.long)) throwErr(this, opt.long);
|
|
2912
|
+
set.add(opt.long);
|
|
2913
|
+
}
|
|
2914
|
+
if (opt.attributeName()) {
|
|
2915
|
+
if (set.has(opt.attributeName())) throwErr(this, opt.attributeName());
|
|
2916
|
+
set.add(opt.attributeName());
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
for (const anc of this.getAncestorsIterator()){
|
|
2920
|
+
for (const opt of anc.$.options){
|
|
2921
|
+
if (opt.short && set.has(opt.short)) {
|
|
2922
|
+
if (opt.short !== 'V') continue;
|
|
2923
|
+
throwErr(this, opt.short, anc);
|
|
2924
|
+
}
|
|
2925
|
+
if (opt.long && set.has(opt.long)) {
|
|
2926
|
+
throwErr(this, opt.long, anc);
|
|
2927
|
+
}
|
|
2928
|
+
if (opt.attributeName() && set.has(opt.attributeName())) {
|
|
2929
|
+
throwErr(this, opt.attributeName(), anc);
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2370
2933
|
}
|
|
2371
|
-
|
|
2372
|
-
|
|
2934
|
+
initializeActionWrapper() {
|
|
2935
|
+
this.$.action(()=>{
|
|
2936
|
+
const isAsync = isAsyncFunction(this.meta.actionHandler) || /^\(.+\) ?=> ?tslib_1\.__awaiter\(/.test(this.meta.actionHandler.toString().trim());
|
|
2937
|
+
if (isAsync) {
|
|
2938
|
+
try {
|
|
2939
|
+
this.handleOutputOptions();
|
|
2940
|
+
const [args, opts] = this.getParsedValidArgsOptsWithPresets();
|
|
2941
|
+
if (opts['help']) return Promise.resolve(this.outputHelp());
|
|
2942
|
+
return Promise.resolve(this.meta.actionHandler.call(this, ...args, opts, this));
|
|
2943
|
+
} catch (error) {
|
|
2944
|
+
this.meta.errorHandler.call(this, error, this);
|
|
2945
|
+
return Promise.reject(error);
|
|
2946
|
+
}
|
|
2947
|
+
} else {
|
|
2948
|
+
try {
|
|
2949
|
+
this.handleOutputOptions();
|
|
2950
|
+
const [args, opts] = this.getParsedValidArgsOptsWithPresets();
|
|
2951
|
+
if (opts['help']) return this.outputHelp();
|
|
2952
|
+
this.meta.actionHandler.call(this, ...args, opts, this);
|
|
2953
|
+
} catch (error) {
|
|
2954
|
+
this.meta.errorHandler.call(this, error, this);
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
});
|
|
2373
2958
|
}
|
|
2374
|
-
|
|
2375
|
-
|
|
2959
|
+
initializeHelp() {
|
|
2960
|
+
if (this.isRoot) this.globalOption('-h, --help', 'show help');
|
|
2961
|
+
this.$.addHelpCommand('?', 'show help');
|
|
2962
|
+
this.$.configureHelp(DefaultHelpConfig);
|
|
2376
2963
|
}
|
|
2377
|
-
|
|
2378
|
-
|
|
2964
|
+
inheritParentHiddenGlobals() {
|
|
2965
|
+
if (!this.parent) return;
|
|
2966
|
+
for (const opt of this.parent.meta.hiddenGlobalOptions){
|
|
2967
|
+
this.meta.hiddenGlobalOptions.add(opt);
|
|
2968
|
+
}
|
|
2379
2969
|
}
|
|
2380
|
-
|
|
2381
|
-
|
|
2970
|
+
assertCommandNameNotReserved(name) {
|
|
2971
|
+
if (this.meta.isNative) return;
|
|
2972
|
+
if (name === 'u' || name === 'util') {
|
|
2973
|
+
this.throwCommanderError(`Name '${name}' is reserved and is not available as name or alias.`);
|
|
2974
|
+
}
|
|
2382
2975
|
}
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
this.cmd = cmd;
|
|
2976
|
+
assertNotInitialized() {
|
|
2977
|
+
if (this.meta.isInitialized) this.throwCommanderError('Command already initialized: ' + this.name);
|
|
2386
2978
|
}
|
|
2387
2979
|
}
|
|
2980
|
+
process.argv = splitCombinedArgvShorts(process.argv.slice());
|
|
2388
2981
|
|
|
2389
|
-
|
|
2390
|
-
|
|
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;
|
|
2982
|
+
function CLI(name, callback) {
|
|
2983
|
+
return ()=>new CommandBuilder(name, callback).commander;
|
|
2407
2984
|
}
|
|
2408
2985
|
|
|
2409
2986
|
/**
|
|
2410
|
-
*
|
|
2411
|
-
*
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2987
|
+
* Reads a file and returns the file's contents.
|
|
2988
|
+
*
|
|
2989
|
+
* Identical to fs.readFileSync, except that:
|
|
2990
|
+
* - it uses utf8 encoding by default
|
|
2991
|
+
* - if operation fails, returns undefined instead of throwing
|
|
2992
|
+
*
|
|
2993
|
+
* @param filepath - The path to the file.
|
|
2994
|
+
* @param encoding - The encoding to use when reading the file.
|
|
2995
|
+
* @returns The file's contents or undefined if the file does not exist.
|
|
2996
|
+
*/ function readFileSafeSync(filepath, encoding = 'utf8') {
|
|
2997
|
+
try {
|
|
2998
|
+
return fs.readFileSync(filepath, encoding);
|
|
2999
|
+
} catch (error) {
|
|
3000
|
+
return undefined;
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
|
|
3004
|
+
function execInherit(command) {
|
|
3005
|
+
return new Promise((resolve, reject)=>{
|
|
3006
|
+
try {
|
|
3007
|
+
const buffer = execSync(command, {
|
|
3008
|
+
stdio: 'inherit'
|
|
3009
|
+
});
|
|
3010
|
+
const string = buffer && buffer.toString ? buffer.toString().trim() : '';
|
|
3011
|
+
resolve(string);
|
|
3012
|
+
} catch (error) {
|
|
3013
|
+
const oError = error instanceof Error ? error : new Error(String(error));
|
|
3014
|
+
reject(oError);
|
|
2430
3015
|
}
|
|
2431
|
-
}
|
|
3016
|
+
});
|
|
2432
3017
|
}
|
|
2433
3018
|
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
3019
|
+
/**
|
|
3020
|
+
* Checks if the given string is in lower case.
|
|
3021
|
+
* @param input The string to be checked.
|
|
3022
|
+
* @example ```ts
|
|
3023
|
+
* strIsLowerCase('hello');
|
|
3024
|
+
* //=> true
|
|
3025
|
+
* strIsLowerCase('Hello');
|
|
3026
|
+
* //=> false
|
|
3027
|
+
* ```
|
|
3028
|
+
*/ function strIsLowerCase(input) {
|
|
3029
|
+
return input === input.toLowerCase();
|
|
2438
3030
|
}
|
|
2439
3031
|
|
|
2440
3032
|
/**
|
|
2441
|
-
*
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
3033
|
+
* Checks if the given string is in upper case.
|
|
3034
|
+
* @param input The string to be checked.
|
|
3035
|
+
* @example ```ts
|
|
3036
|
+
* strIsUpperCase('HELLO');;
|
|
3037
|
+
* //=> true
|
|
3038
|
+
* strIsUpperCase('HEllo');;
|
|
3039
|
+
* //=> false
|
|
3040
|
+
* ```
|
|
3041
|
+
*/ function strIsUpperCase(input) {
|
|
3042
|
+
return input === input.toUpperCase();
|
|
2445
3043
|
}
|
|
2446
3044
|
|
|
2447
3045
|
/**
|
|
2448
|
-
*
|
|
2449
|
-
*
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
3046
|
+
* Returns an array of words in the string
|
|
3047
|
+
* @param word The camel case word to split.
|
|
3048
|
+
* @throws Throws an error if the input is not a string.
|
|
3049
|
+
* @param input input string
|
|
3050
|
+
* @example ```ts
|
|
3051
|
+
* strSplitCamelCase('someCamel10Case')
|
|
3052
|
+
* //=> ['some', 'Camel10', 'Case']
|
|
3053
|
+
* ```
|
|
3054
|
+
*/ function strSplitCamelCase(word) {
|
|
3055
|
+
if (!word) return [];
|
|
3056
|
+
if (word.length <= 2) return [
|
|
3057
|
+
word
|
|
3058
|
+
];
|
|
3059
|
+
const result = [];
|
|
3060
|
+
const lastCharIndex = word.length - 1;
|
|
3061
|
+
let lastSplitIndex = 0;
|
|
3062
|
+
let foundCamelCase = false;
|
|
3063
|
+
for(let i = 1; i < word.length; i++){
|
|
3064
|
+
if (isWordSplitIndex(word, i)) {
|
|
3065
|
+
const sub = word.substring(lastSplitIndex, i);
|
|
3066
|
+
if (sub) {
|
|
3067
|
+
result.push(sub);
|
|
3068
|
+
lastSplitIndex = i;
|
|
3069
|
+
foundCamelCase = true;
|
|
3070
|
+
}
|
|
3071
|
+
}
|
|
3072
|
+
if (foundCamelCase && i === lastCharIndex) {
|
|
3073
|
+
const sub = word.substring(lastSplitIndex);
|
|
3074
|
+
if (sub) result.push(sub);
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
if (!foundCamelCase) result.push(word);
|
|
3078
|
+
return result;
|
|
3079
|
+
}
|
|
3080
|
+
const regInteger = /\d+/g;
|
|
3081
|
+
const regSpecial = /[^\w]+/g;
|
|
3082
|
+
function isWordSplitIndex(word, index) {
|
|
3083
|
+
return strIsLowerCase(word[index - 1]) && strIsUpperCase(word[index]) && !regInteger.test(word[index - 1]) && !regInteger.test(word[index]) && !regSpecial.test(word[index - 1]) && !regSpecial.test(word[index]);
|
|
2453
3084
|
}
|
|
2454
3085
|
|
|
2455
3086
|
/**
|
|
@@ -2482,33 +3113,6 @@ function getChildren(cmd, options) {
|
|
|
2482
3113
|
};
|
|
2483
3114
|
}
|
|
2484
3115
|
|
|
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
3116
|
/**
|
|
2513
3117
|
* Parses a string into a boolean.
|
|
2514
3118
|
*
|
|
@@ -2535,24 +3139,4 @@ function isBoolean(value) {
|
|
|
2535
3139
|
return typeof value === 'boolean';
|
|
2536
3140
|
}
|
|
2537
3141
|
|
|
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 };
|
|
3142
|
+
export { AbstractJsonFileSection, AppDataSection, ArgumentBuilder, ArgumentParserSelector, ArgumentReader, ArgumentValidatorSelector, CLI, CommandBuilder, CommandBuilderMetaData, CommandFeatureSelector, ConfigSection, DefaultHelpConfig, ErrorParser, JsonDB, JsonFile, MethodDisabler, OptionArgumentParserSelector, OptionArgumentValidatorSelector, OptionBuilder, OptionHelpers, OptionReader, OutputManager, ParserSelector, PresetsSection, ValidatorSelector, arrAssign, arrLast, arrSome, commanderBackRefs, countInstance, createArrayMerger, createBooleanParser, createObjectMerger, createTypedArrayValidator, createTypedListParser, defaultOpenInEditorCommand, ensureThat, errParseStack, errPrettyStack, errToObject, execInherit, formatTableForTerminal, funSetName, getTempDataPath, isArray, isBoolean, isFunction, isInteger, isNamedFunction, isNamedFunctionArray, isOSX, isObject, isPlainObject, isPrimitive, isString, isStringArray, isStringWithNoSpacesOrDashes, isValidNumber, isVsCodeInstalled, isWindows, objAssign, objUpdatePropertyDescriptors, parseBoolean, parseInteger, parseNumber, parseString, printCounts, promptUserEditInTextEditorSync, promptUserEditJsonInTextEditorSync, readFileSafeSync, readFileSync, readJsonFileSafeSync, realizeLazyProperty, regexEscapeString, setNonEnumerable, splitCombinedArgvShorts, strEnsureStartsWith, strFirstCharToUpperCase, strIsLowerCase, strIsUpperCase, strSplitCamelCase, tempFileSync };
|