@cloud-copilot/cli 0.1.39 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -207
- package/dist/cjs/arguments/argument.d.ts +60 -0
- package/dist/cjs/arguments/argument.d.ts.map +1 -0
- package/dist/cjs/arguments/argument.js +3 -0
- package/dist/cjs/arguments/argument.js.map +1 -0
- package/dist/cjs/arguments/arrayValueArgument.d.ts +15 -0
- package/dist/cjs/arguments/arrayValueArgument.d.ts.map +1 -0
- package/dist/cjs/arguments/arrayValueArgument.js +38 -0
- package/dist/cjs/arguments/arrayValueArgument.js.map +1 -0
- package/dist/cjs/arguments/booleanArgument.d.ts +11 -0
- package/dist/cjs/arguments/booleanArgument.d.ts.map +1 -0
- package/dist/cjs/arguments/booleanArgument.js +25 -0
- package/dist/cjs/arguments/booleanArgument.js.map +1 -0
- package/dist/cjs/arguments/enumArgument.d.ts +15 -0
- package/dist/cjs/arguments/enumArgument.d.ts.map +1 -0
- package/dist/cjs/arguments/enumArgument.js +35 -0
- package/dist/cjs/arguments/enumArgument.js.map +1 -0
- package/dist/cjs/arguments/enumArrayArgument.d.ts +15 -0
- package/dist/cjs/arguments/enumArrayArgument.d.ts.map +1 -0
- package/dist/cjs/arguments/enumArrayArgument.js +42 -0
- package/dist/cjs/arguments/enumArrayArgument.js.map +1 -0
- package/dist/cjs/arguments/mapArgument.d.ts +18 -0
- package/dist/cjs/arguments/mapArgument.d.ts.map +1 -0
- package/dist/cjs/arguments/mapArgument.js +35 -0
- package/dist/cjs/arguments/mapArgument.js.map +1 -0
- package/dist/cjs/arguments/numberArguments.d.ts +17 -0
- package/dist/cjs/arguments/numberArguments.d.ts.map +1 -0
- package/dist/cjs/arguments/numberArguments.js +15 -0
- package/dist/cjs/arguments/numberArguments.js.map +1 -0
- package/dist/cjs/arguments/singleValueArgument.d.ts +15 -0
- package/dist/cjs/arguments/singleValueArgument.d.ts.map +1 -0
- package/dist/cjs/arguments/singleValueArgument.js +35 -0
- package/dist/cjs/arguments/singleValueArgument.js.map +1 -0
- package/dist/cjs/arguments/stringArguments.d.ts +17 -0
- package/dist/cjs/arguments/stringArguments.d.ts.map +1 -0
- package/dist/cjs/arguments/stringArguments.js +14 -0
- package/dist/cjs/arguments/stringArguments.js.map +1 -0
- package/dist/cjs/cli.d.ts +37 -89
- package/dist/cjs/cli.d.ts.map +1 -1
- package/dist/cjs/cli.js +171 -293
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/index.d.ts +11 -2
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +20 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/readRelative.d.ts +25 -7
- package/dist/cjs/readRelative.d.ts.map +1 -1
- package/dist/cjs/readRelative.js +63 -20
- package/dist/cjs/readRelative.js.map +1 -1
- package/dist/esm/arguments/argument.d.ts +60 -0
- package/dist/esm/arguments/argument.d.ts.map +1 -0
- package/dist/esm/arguments/argument.js +2 -0
- package/dist/esm/arguments/argument.js.map +1 -0
- package/dist/esm/arguments/arrayValueArgument.d.ts +15 -0
- package/dist/esm/arguments/arrayValueArgument.d.ts.map +1 -0
- package/dist/esm/arguments/arrayValueArgument.js +35 -0
- package/dist/esm/arguments/arrayValueArgument.js.map +1 -0
- package/dist/esm/arguments/booleanArgument.d.ts +11 -0
- package/dist/esm/arguments/booleanArgument.d.ts.map +1 -0
- package/dist/esm/arguments/booleanArgument.js +22 -0
- package/dist/esm/arguments/booleanArgument.js.map +1 -0
- package/dist/esm/arguments/enumArgument.d.ts +15 -0
- package/dist/esm/arguments/enumArgument.d.ts.map +1 -0
- package/dist/esm/arguments/enumArgument.js +32 -0
- package/dist/esm/arguments/enumArgument.js.map +1 -0
- package/dist/esm/arguments/enumArrayArgument.d.ts +15 -0
- package/dist/esm/arguments/enumArrayArgument.d.ts.map +1 -0
- package/dist/esm/arguments/enumArrayArgument.js +39 -0
- package/dist/esm/arguments/enumArrayArgument.js.map +1 -0
- package/dist/esm/arguments/mapArgument.d.ts +18 -0
- package/dist/esm/arguments/mapArgument.d.ts.map +1 -0
- package/dist/esm/arguments/mapArgument.js +32 -0
- package/dist/esm/arguments/mapArgument.js.map +1 -0
- package/dist/esm/arguments/numberArguments.d.ts +17 -0
- package/dist/esm/arguments/numberArguments.d.ts.map +1 -0
- package/dist/esm/arguments/numberArguments.js +12 -0
- package/dist/esm/arguments/numberArguments.js.map +1 -0
- package/dist/esm/arguments/singleValueArgument.d.ts +15 -0
- package/dist/esm/arguments/singleValueArgument.d.ts.map +1 -0
- package/dist/esm/arguments/singleValueArgument.js +32 -0
- package/dist/esm/arguments/singleValueArgument.js.map +1 -0
- package/dist/esm/arguments/stringArguments.d.ts +17 -0
- package/dist/esm/arguments/stringArguments.d.ts.map +1 -0
- package/dist/esm/arguments/stringArguments.js +11 -0
- package/dist/esm/arguments/stringArguments.js.map +1 -0
- package/dist/esm/cli.d.ts +37 -89
- package/dist/esm/cli.d.ts.map +1 -1
- package/dist/esm/cli.js +171 -292
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/index.d.ts +11 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +10 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/readRelative.d.ts +25 -7
- package/dist/esm/readRelative.d.ts.map +1 -1
- package/dist/esm/readRelative.js +58 -19
- package/dist/esm/readRelative.js.map +1 -1
- package/package.json +2 -2
package/dist/cjs/cli.js
CHANGED
|
@@ -1,125 +1,53 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createConfig = createConfig;
|
|
4
3
|
exports.parseCliArguments = parseCliArguments;
|
|
5
4
|
exports.printHelpContents = printHelpContents;
|
|
6
5
|
const utils_js_1 = require("./utils.js");
|
|
7
|
-
function isBooleanOption(option) {
|
|
8
|
-
return option.type === 'boolean';
|
|
9
|
-
}
|
|
10
|
-
function isEnumOption(option) {
|
|
11
|
-
return option.type === 'enum';
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Create a type safe configuration for CLI arguments.
|
|
15
|
-
*
|
|
16
|
-
* @param config the configuration for the CLI arguments.
|
|
17
|
-
* @returns the configuration object.
|
|
18
|
-
*/
|
|
19
|
-
function createConfig(config) {
|
|
20
|
-
return config;
|
|
21
|
-
}
|
|
22
6
|
/**
|
|
23
7
|
* Parse CLI Arguments and return the parsed typesafe results.
|
|
24
8
|
*
|
|
25
9
|
* @param command the name of the command arguments are being parsed for.
|
|
26
10
|
* @param subcommands the list of subcommands that can be used, if any.
|
|
27
|
-
* @param
|
|
28
|
-
* @param
|
|
11
|
+
* @param cliArgs the configuration options for the CLI command.
|
|
12
|
+
* @param additionalOptions additional arguments to be used for parsing and displaying help.
|
|
29
13
|
* @returns the parsed arguments, operands, and subcommand if applicable.
|
|
30
14
|
*/
|
|
31
|
-
function parseCliArguments(command, subcommands,
|
|
32
|
-
const args =
|
|
33
|
-
const env =
|
|
15
|
+
async function parseCliArguments(command, subcommands, cliArgs, additionalOptions) {
|
|
16
|
+
const args = additionalOptions?.args ?? process.argv.slice(2);
|
|
17
|
+
const env = additionalOptions?.env ?? process.env;
|
|
34
18
|
const parsedArgs = {};
|
|
35
19
|
const operands = [];
|
|
36
20
|
const booleanOptions = {};
|
|
37
21
|
const subcommandKeys = Object.keys(subcommands);
|
|
38
22
|
const numberOfSubcommands = subcommandKeys.length;
|
|
39
|
-
const combinedOptions = { ...
|
|
23
|
+
const combinedOptions = { ...cliArgs };
|
|
24
|
+
const logger = additionalOptions?.consoleLogger ?? console;
|
|
40
25
|
let subcommand;
|
|
41
|
-
if (args.length === 0 &&
|
|
42
|
-
printHelpContents(command, subcommands,
|
|
26
|
+
if (args.length === 0 && additionalOptions?.showHelpIfNoArgs) {
|
|
27
|
+
printHelpContents(command, subcommands, cliArgs, additionalOptions);
|
|
43
28
|
(0, utils_js_1.exit)(0, undefined);
|
|
44
29
|
return {};
|
|
45
30
|
}
|
|
46
31
|
// Step 1: Initialize defaults
|
|
47
|
-
|
|
32
|
+
const parsedEnvironmentArgs = {};
|
|
33
|
+
initializeOptionDefaults(parsedEnvironmentArgs, booleanOptions, cliArgs);
|
|
48
34
|
// Step 2: Handle environment variables
|
|
49
|
-
parseEnvironmentVariables(
|
|
50
|
-
if (additionalArgs?.envPrefix) {
|
|
51
|
-
const prefix = additionalArgs.envPrefix + '_';
|
|
52
|
-
const envToKeys = Object.keys(cliOptions).reduce((acc, key) => {
|
|
53
|
-
acc[camelToCapitalSnakeCase(key)] = key;
|
|
54
|
-
return acc;
|
|
55
|
-
}, {});
|
|
56
|
-
for (const [key, value] of Object.entries(env)) {
|
|
57
|
-
if (key.startsWith(prefix)) {
|
|
58
|
-
const optionKey = key.slice(prefix.length);
|
|
59
|
-
const option = envToKeys[optionKey];
|
|
60
|
-
if (option) {
|
|
61
|
-
const config = combinedOptions[option];
|
|
62
|
-
if (isEnumOption(config)) {
|
|
63
|
-
if (config.values === 'single') {
|
|
64
|
-
const matchingValue = config.validValues.find((v) => v.toLowerCase() === value.toLowerCase());
|
|
65
|
-
if (!matchingValue) {
|
|
66
|
-
(0, utils_js_1.exit)(2, `Environment ${key} allows only the following values: ${config.validValues.join(', ')}`);
|
|
67
|
-
}
|
|
68
|
-
parsedArgs[option] = matchingValue;
|
|
69
|
-
}
|
|
70
|
-
else if (config.values === 'multiple') {
|
|
71
|
-
const invalidValues = [];
|
|
72
|
-
const validValues = [];
|
|
73
|
-
const values = value.split(' ');
|
|
74
|
-
for (const v of values) {
|
|
75
|
-
const matchingValue = config.validValues.find((valid) => valid.toLowerCase() === v.toLowerCase());
|
|
76
|
-
if (matchingValue) {
|
|
77
|
-
validValues.push(matchingValue);
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
invalidValues.push(value);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (invalidValues.length > 0) {
|
|
84
|
-
(0, utils_js_1.exit)(2, `Environment ${key} allows only the following values: ${config.validValues.join(', ')}`);
|
|
85
|
-
}
|
|
86
|
-
parsedArgs[option] = validValues;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
else if (config.type === 'boolean') {
|
|
90
|
-
parsedArgs[option] = true;
|
|
91
|
-
}
|
|
92
|
-
else if (config.values === 'single') {
|
|
93
|
-
const { parsed, invalid } = validateTypes(config.type, value);
|
|
94
|
-
if (invalid.length > 0) {
|
|
95
|
-
(0, utils_js_1.exit)(2, `Environment ${key} expects a valid ${config.type}, but received: ${invalid.join(', ')}`);
|
|
96
|
-
}
|
|
97
|
-
parsedArgs[option] = parsed;
|
|
98
|
-
}
|
|
99
|
-
else if (config.values === 'multiple') {
|
|
100
|
-
const values = value.split(' ');
|
|
101
|
-
const { parsed, invalid } = validateTypes(config.type, values);
|
|
102
|
-
if (invalid.length > 0) {
|
|
103
|
-
(0, utils_js_1.exit)(2, `Environment ${key} expects a valid ${config.type}, but received: ${invalid.join(', ')}`);
|
|
104
|
-
}
|
|
105
|
-
parsedArgs[option] = parsed;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
35
|
+
await parseEnvironmentVariables(cliArgs, parsedEnvironmentArgs, env, additionalOptions?.envPrefix);
|
|
111
36
|
// Step 3: Group arguments into objects
|
|
112
37
|
const commandChunks = groupArguments(args);
|
|
113
38
|
// Step 4: Validation and parsing arguments
|
|
114
39
|
for (const { first, rest, isLast, isFirst } of commandChunks) {
|
|
115
40
|
// Handle --help and --version
|
|
116
41
|
if (first === '--help') {
|
|
117
|
-
printHelpContents(command, subcommands,
|
|
42
|
+
printHelpContents(command, subcommands, cliArgs, additionalOptions, subcommand);
|
|
118
43
|
(0, utils_js_1.exit)(0, undefined);
|
|
44
|
+
return {};
|
|
119
45
|
}
|
|
120
46
|
if (first === '--version') {
|
|
121
|
-
if (
|
|
122
|
-
(
|
|
47
|
+
if (additionalOptions?.version) {
|
|
48
|
+
await printVersion(additionalOptions.version, logger);
|
|
49
|
+
(0, utils_js_1.exit)(0, undefined);
|
|
50
|
+
return {};
|
|
123
51
|
}
|
|
124
52
|
}
|
|
125
53
|
// Handle commands if applicable
|
|
@@ -146,9 +74,9 @@ function parseCliArguments(command, subcommands, cliOptions, additionalArgs) {
|
|
|
146
74
|
return {};
|
|
147
75
|
}
|
|
148
76
|
subcommand = matchingCommands.at(0);
|
|
149
|
-
const subcommandOptions = subcommands[subcommand].
|
|
150
|
-
initializeOptionDefaults(
|
|
151
|
-
parseEnvironmentVariables(subcommandOptions,
|
|
77
|
+
const subcommandOptions = subcommands[subcommand].arguments;
|
|
78
|
+
initializeOptionDefaults(parsedEnvironmentArgs, booleanOptions, subcommandOptions);
|
|
79
|
+
await parseEnvironmentVariables(subcommandOptions, parsedEnvironmentArgs, env, additionalOptions?.envPrefix);
|
|
152
80
|
for (const [key, option] of Object.entries(subcommandOptions)) {
|
|
153
81
|
combinedOptions[key] = option;
|
|
154
82
|
}
|
|
@@ -175,6 +103,16 @@ function parseCliArguments(command, subcommands, cliOptions, additionalArgs) {
|
|
|
175
103
|
matchingOption = exactMatch;
|
|
176
104
|
}
|
|
177
105
|
else {
|
|
106
|
+
if ('--help'.startsWith(first)) {
|
|
107
|
+
printHelpContents(command, subcommands, cliArgs, additionalOptions, subcommand);
|
|
108
|
+
(0, utils_js_1.exit)(0, undefined);
|
|
109
|
+
return {};
|
|
110
|
+
}
|
|
111
|
+
else if ('--version'.startsWith(first) && additionalOptions?.version) {
|
|
112
|
+
await printVersion(additionalOptions.version, logger);
|
|
113
|
+
(0, utils_js_1.exit)(0, undefined);
|
|
114
|
+
return {};
|
|
115
|
+
}
|
|
178
116
|
(0, utils_js_1.exit)(2, `Unknown argument: ${first}`);
|
|
179
117
|
}
|
|
180
118
|
if (!matchingOption) {
|
|
@@ -182,93 +120,41 @@ function parseCliArguments(command, subcommands, cliOptions, additionalArgs) {
|
|
|
182
120
|
return {};
|
|
183
121
|
}
|
|
184
122
|
const optionConfig = combinedOptions[matchingOption];
|
|
185
|
-
if (
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
(0, utils_js_1.exit)(2, `Option ${first} expects at least one value, but received none`);
|
|
192
|
-
}
|
|
123
|
+
if (optionConfig.present) {
|
|
124
|
+
parsedArgs[matchingOption] = await optionConfig.present(parsedArgs[matchingOption]);
|
|
125
|
+
}
|
|
126
|
+
if (rest.length > 0 && optionConfig.character) {
|
|
127
|
+
if (!isLast) {
|
|
128
|
+
(0, utils_js_1.exit)(2, `Validation error for ${first}: does not accept values but received ${rest.join(', ')}`);
|
|
193
129
|
return {};
|
|
194
130
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
(0, utils_js_1.exit)(2, `Option ${first} expects a single value, but received multiple: ${rest.join(', ')}`);
|
|
198
|
-
}
|
|
199
|
-
const value = rest[0];
|
|
200
|
-
const matchingValue = optionConfig.validValues.find((v) => v.toLowerCase() === value.toLowerCase());
|
|
201
|
-
if (!matchingValue) {
|
|
202
|
-
(0, utils_js_1.exit)(2, `Option ${first} allows only the following values: ${optionConfig.validValues.join(', ')}`);
|
|
203
|
-
}
|
|
204
|
-
parsedArgs[matchingOption] = matchingValue;
|
|
205
|
-
operands.push(...rest.slice(1));
|
|
206
|
-
}
|
|
207
|
-
else if (optionConfig.values === 'multiple') {
|
|
208
|
-
const invalidValues = [];
|
|
209
|
-
const validValues = [];
|
|
210
|
-
for (const value of rest) {
|
|
211
|
-
const matchingValue = optionConfig.validValues.find((v) => v.toLowerCase() === value.toLowerCase());
|
|
212
|
-
if (matchingValue) {
|
|
213
|
-
validValues.push(matchingValue);
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
invalidValues.push(value);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
if (invalidValues.length > 0) {
|
|
220
|
-
(0, utils_js_1.exit)(2, `Option ${first} allows only the following values: ${optionConfig.validValues.join(', ')}`);
|
|
221
|
-
}
|
|
222
|
-
parsedArgs[matchingOption] = validValues;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
else if (optionConfig.type === 'boolean') {
|
|
226
|
-
//set boolean value
|
|
227
|
-
parsedArgs[matchingOption] = true;
|
|
228
|
-
//Handle extra values
|
|
229
|
-
if (rest.length > 0) {
|
|
230
|
-
if (!isLast) {
|
|
231
|
-
(0, utils_js_1.exit)(2, `Boolean option ${first} does not accept values`);
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
operands.push(...rest);
|
|
235
|
-
}
|
|
131
|
+
else {
|
|
132
|
+
operands.push(...rest);
|
|
236
133
|
}
|
|
237
134
|
}
|
|
238
|
-
else
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
//Set the value
|
|
248
|
-
parsedArgs[matchingOption] = parsed;
|
|
249
|
-
if (rest.length > 1) {
|
|
250
|
-
if (!isLast) {
|
|
251
|
-
(0, utils_js_1.exit)(2, `Option ${first} expects a single value, but received multiple: ${rest.join(', ')}`);
|
|
135
|
+
else {
|
|
136
|
+
const acceptsMultiple = optionConfig.acceptMultipleValues
|
|
137
|
+
? optionConfig.acceptMultipleValues()
|
|
138
|
+
: false;
|
|
139
|
+
let theRest = rest;
|
|
140
|
+
if (!acceptsMultiple && rest.length > 1) {
|
|
141
|
+
if (isLast) {
|
|
142
|
+
theRest = [rest[0]];
|
|
143
|
+
operands.push(...rest.slice(1));
|
|
252
144
|
}
|
|
253
145
|
else {
|
|
254
|
-
|
|
146
|
+
(0, utils_js_1.exit)(2, `Validation error for ${first}: expects a single value but received ${rest.join(', ')}`);
|
|
255
147
|
}
|
|
256
148
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (
|
|
260
|
-
(0, utils_js_1.exit)(2, `
|
|
149
|
+
const currentValue = parsedArgs[matchingOption];
|
|
150
|
+
const validation = await optionConfig.validateValues(currentValue, theRest);
|
|
151
|
+
if (!validation.valid) {
|
|
152
|
+
(0, utils_js_1.exit)(2, `Validation error for ${first}: ${validation.message}`);
|
|
153
|
+
return {};
|
|
261
154
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
const { parsed, invalid } = validateTypes(optionConfig.type, rest);
|
|
265
|
-
if (invalid.length > 0) {
|
|
266
|
-
(0, utils_js_1.exit)(2, `Option ${first} expects a valid ${optionConfig.type}, but received: ${invalid.join(', ')}`);
|
|
155
|
+
else {
|
|
156
|
+
parsedArgs[matchingOption] = await optionConfig.reduceValues(currentValue, validation.value);
|
|
267
157
|
}
|
|
268
|
-
parsedArgs[matchingOption] = parsed;
|
|
269
|
-
}
|
|
270
|
-
else {
|
|
271
|
-
throw new Error(`Unrecognized option values ${optionConfig.values}`);
|
|
272
158
|
}
|
|
273
159
|
}
|
|
274
160
|
else if (first.startsWith('-')) {
|
|
@@ -289,56 +175,52 @@ function parseCliArguments(command, subcommands, cliOptions, additionalArgs) {
|
|
|
289
175
|
}
|
|
290
176
|
}
|
|
291
177
|
}
|
|
292
|
-
if (numberOfSubcommands > 0 &&
|
|
178
|
+
if (numberOfSubcommands > 0 && additionalOptions?.requireSubcommand && !subcommand) {
|
|
293
179
|
(0, utils_js_1.exit)(2, `A subcommand is required`);
|
|
294
180
|
}
|
|
295
181
|
// Step 4: Return results
|
|
296
182
|
return {
|
|
297
|
-
args: parsedArgs,
|
|
183
|
+
args: { ...parsedEnvironmentArgs, ...parsedArgs },
|
|
298
184
|
operands,
|
|
299
185
|
subcommand: subcommand,
|
|
300
186
|
anyValues: args.length > 0,
|
|
301
187
|
printHelp: () => {
|
|
302
|
-
printHelpContents(command, subcommands,
|
|
188
|
+
printHelpContents(command, subcommands, cliArgs, additionalOptions, subcommand);
|
|
303
189
|
}
|
|
304
190
|
};
|
|
305
191
|
}
|
|
306
192
|
/**
|
|
307
|
-
* Initialize the default values for arguments
|
|
193
|
+
* Initialize the default values for arguments.
|
|
194
|
+
*
|
|
195
|
+
* Will populate the parsedArgs object with default values from the cliArguments.
|
|
196
|
+
* Will also populate the booleanOptions map with single character boolean options.
|
|
308
197
|
*
|
|
309
198
|
* @param parsedArgs the parsed arguments to default the values in
|
|
310
|
-
* @param
|
|
199
|
+
* @param booleanOptions a map of single character boolean options to their full names
|
|
200
|
+
* @param cliArguments the configuration options for the CLI commands
|
|
311
201
|
*/
|
|
312
|
-
function initializeOptionDefaults(parsedArgs, booleanOptions,
|
|
313
|
-
for (const [key, option] of Object.entries(
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
booleanOptions[option.character.toLowerCase()] = key;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
else if (option.values === 'single') {
|
|
321
|
-
parsedArgs[key] = undefined;
|
|
322
|
-
}
|
|
323
|
-
else if (option.values === 'multiple') {
|
|
324
|
-
parsedArgs[key] = [];
|
|
202
|
+
function initializeOptionDefaults(parsedArgs, booleanOptions, cliArguments) {
|
|
203
|
+
for (const [key, option] of Object.entries(cliArguments)) {
|
|
204
|
+
parsedArgs[key] = option.defaultValue;
|
|
205
|
+
if (option.character) {
|
|
206
|
+
booleanOptions[option.character.toLowerCase()] = key;
|
|
325
207
|
}
|
|
326
208
|
}
|
|
327
209
|
}
|
|
328
210
|
/**
|
|
329
211
|
* Parse environment variables and set the values in the parsed arguments.
|
|
330
212
|
*
|
|
331
|
-
* @param
|
|
213
|
+
* @param cliArguments the configuration options for the CLI commands
|
|
332
214
|
* @param parsedArgs the parsed arguments to set the values in
|
|
333
215
|
* @param env the environment variables to get values from
|
|
334
216
|
* @param envPrefix the prefix to use for environment variables, if any
|
|
335
217
|
*/
|
|
336
|
-
function parseEnvironmentVariables(
|
|
218
|
+
async function parseEnvironmentVariables(cliArguments, parsedArgs, env, envPrefix) {
|
|
337
219
|
if (!envPrefix) {
|
|
338
220
|
return;
|
|
339
221
|
}
|
|
340
222
|
const prefix = envPrefix + '_';
|
|
341
|
-
const envToKeys = Object.keys(
|
|
223
|
+
const envToKeys = Object.keys(cliArguments).reduce((acc, key) => {
|
|
342
224
|
acc[camelToCapitalSnakeCase(key)] = key;
|
|
343
225
|
return acc;
|
|
344
226
|
}, {});
|
|
@@ -347,51 +229,19 @@ function parseEnvironmentVariables(options, parsedArgs, env, envPrefix) {
|
|
|
347
229
|
const optionKey = key.slice(prefix.length);
|
|
348
230
|
const option = envToKeys[optionKey];
|
|
349
231
|
if (option) {
|
|
350
|
-
const config =
|
|
351
|
-
if (
|
|
352
|
-
|
|
353
|
-
const matchingValue = config.validValues.find((v) => v.toLowerCase() === value.toLowerCase());
|
|
354
|
-
if (!matchingValue) {
|
|
355
|
-
(0, utils_js_1.exit)(2, `Environment ${key} allows only the following values: ${config.validValues.join(', ')}`);
|
|
356
|
-
}
|
|
357
|
-
parsedArgs[option] = matchingValue;
|
|
358
|
-
}
|
|
359
|
-
else if (config.values === 'multiple') {
|
|
360
|
-
const invalidValues = [];
|
|
361
|
-
const validValues = [];
|
|
362
|
-
const values = value.split(' ');
|
|
363
|
-
for (const v of values) {
|
|
364
|
-
const matchingValue = config.validValues.find((valid) => valid.toLowerCase() === v.toLowerCase());
|
|
365
|
-
if (matchingValue) {
|
|
366
|
-
validValues.push(matchingValue);
|
|
367
|
-
}
|
|
368
|
-
else {
|
|
369
|
-
invalidValues.push(value);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
if (invalidValues.length > 0) {
|
|
373
|
-
(0, utils_js_1.exit)(2, `Environment ${key} allows only the following values: ${config.validValues.join(', ')}`);
|
|
374
|
-
}
|
|
375
|
-
parsedArgs[option] = validValues;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
else if (config.type === 'boolean') {
|
|
379
|
-
parsedArgs[option] = true;
|
|
380
|
-
}
|
|
381
|
-
else if (config.values === 'single') {
|
|
382
|
-
const { parsed, invalid } = validateTypes(config.type, value);
|
|
383
|
-
if (invalid.length > 0) {
|
|
384
|
-
(0, utils_js_1.exit)(2, `Environment ${key} expects a valid ${config.type}, but received: ${invalid.join(', ')}`);
|
|
385
|
-
}
|
|
386
|
-
parsedArgs[option] = parsed;
|
|
232
|
+
const config = cliArguments[option];
|
|
233
|
+
if (config.present) {
|
|
234
|
+
parsedArgs[option] = await config.present(parsedArgs[option]);
|
|
387
235
|
}
|
|
388
|
-
|
|
236
|
+
if (!config.character) {
|
|
389
237
|
const values = value.split(' ');
|
|
390
|
-
const
|
|
391
|
-
if (
|
|
392
|
-
|
|
238
|
+
const validation = await config.validateValues(parsedArgs[option], values);
|
|
239
|
+
if (!validation.valid) {
|
|
240
|
+
const s = values.length > 1 ? 's' : '';
|
|
241
|
+
(0, utils_js_1.exit)(2, `Invalid value${s} for environment ${key}: ${validation.message}`);
|
|
242
|
+
return;
|
|
393
243
|
}
|
|
394
|
-
parsedArgs[option] =
|
|
244
|
+
parsedArgs[option] = await config.reduceValues(parsedArgs[option], validation.value);
|
|
395
245
|
}
|
|
396
246
|
}
|
|
397
247
|
}
|
|
@@ -438,30 +288,6 @@ function groupArguments(args) {
|
|
|
438
288
|
}
|
|
439
289
|
return grouped;
|
|
440
290
|
}
|
|
441
|
-
/**
|
|
442
|
-
* Validate the types of a standard argument
|
|
443
|
-
*
|
|
444
|
-
* @param type the type the argument accepts
|
|
445
|
-
* @param values the values to validate
|
|
446
|
-
* @returns an object with the invalid values and the parsed values
|
|
447
|
-
*/
|
|
448
|
-
function validateTypes(type, values) {
|
|
449
|
-
if (type === 'string') {
|
|
450
|
-
return { invalid: [], parsed: values };
|
|
451
|
-
}
|
|
452
|
-
if (!Array.isArray(values)) {
|
|
453
|
-
const isValid = !isNaN(Number(values));
|
|
454
|
-
if (isValid) {
|
|
455
|
-
return { invalid: [], parsed: Number(values) };
|
|
456
|
-
}
|
|
457
|
-
return { invalid: [values], parsed: 0 };
|
|
458
|
-
}
|
|
459
|
-
const invalid = values.filter((v) => isNaN(Number(v)));
|
|
460
|
-
if (invalid.length > 0) {
|
|
461
|
-
return { invalid, parsed: [] };
|
|
462
|
-
}
|
|
463
|
-
return { invalid: [], parsed: values.map(Number) };
|
|
464
|
-
}
|
|
465
291
|
function camelToCapitalSnakeCase(input) {
|
|
466
292
|
return input
|
|
467
293
|
.replace(/([a-z])([A-Z])/g, '$1_$2') // Insert underscore before capital letters
|
|
@@ -473,25 +299,27 @@ function camelToKebabCase(input) {
|
|
|
473
299
|
.toLowerCase(); // Convert to uppercase
|
|
474
300
|
}
|
|
475
301
|
function printHelpContents(command, subcommands, cliOptions, additionalArgs, selectedSubcommand) {
|
|
302
|
+
const logger = additionalArgs?.consoleLogger ?? console;
|
|
476
303
|
const operandsExpected = additionalArgs?.expectOperands != undefined ? additionalArgs?.expectOperands : true;
|
|
477
304
|
const operandsName = additionalArgs?.operandsName ?? 'operand';
|
|
478
305
|
const operandsString = operandsExpected ? ` [--] [${operandsName}1] [${operandsName}2]` : '';
|
|
479
|
-
const anyGlobalFlags = Object.values(cliOptions).some((option) =>
|
|
306
|
+
const anyGlobalFlags = Object.values(cliOptions).some((option) => option.character);
|
|
480
307
|
if (selectedSubcommand) {
|
|
481
|
-
const anyCommandFlags = Object.values(subcommands[selectedSubcommand].
|
|
308
|
+
const anyCommandFlags = Object.values(subcommands[selectedSubcommand].arguments).some((option) => option.character);
|
|
482
309
|
const flags = anyGlobalFlags || anyCommandFlags ? ' [flags]' : '';
|
|
483
310
|
let usageString = `Usage: ${command} ${selectedSubcommand} [options]${flags}${operandsString}`;
|
|
484
311
|
if (additionalArgs?.allowOperandsFromStdin) {
|
|
485
312
|
usageString += `\n <${operandsName}s to stdout> | ${command} ${selectedSubcommand} [options]${flags}`;
|
|
486
313
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
314
|
+
logger.log(usageString);
|
|
315
|
+
logger.log('');
|
|
316
|
+
logger.log(`${subcommands[selectedSubcommand].description}`);
|
|
317
|
+
logger.log(`${selectedSubcommand} Options:`);
|
|
318
|
+
printOptions(subcommands[selectedSubcommand].arguments, logger);
|
|
319
|
+
logger.log('');
|
|
492
320
|
}
|
|
493
321
|
else {
|
|
494
|
-
const anyCommandFlags = Object.values(subcommands).some((subcommand) => Object.values(subcommand.
|
|
322
|
+
const anyCommandFlags = Object.values(subcommands).some((subcommand) => Object.values(subcommand.arguments).some((option) => option.character));
|
|
495
323
|
const flags = anyGlobalFlags || anyCommandFlags ? ' [flags]' : '';
|
|
496
324
|
let singleUseString = `${command}`;
|
|
497
325
|
const subcommandKeys = Object.keys(subcommands);
|
|
@@ -506,18 +334,19 @@ function printHelpContents(command, subcommands, cliOptions, additionalArgs, sel
|
|
|
506
334
|
if (additionalArgs?.allowOperandsFromStdin) {
|
|
507
335
|
usageString += `\n <${operandsName}s to stdout> | ${singleUseString}`;
|
|
508
336
|
}
|
|
509
|
-
|
|
337
|
+
logger.log(usageString);
|
|
510
338
|
const longestCommand = subcommandKeys.reduce((acc, cmd) => Math.max(acc, cmd.length), 0);
|
|
511
339
|
if (subcommandKeys.length > 0) {
|
|
512
|
-
|
|
340
|
+
logger.log('Subcommands:');
|
|
513
341
|
for (const cmd of subcommandKeys) {
|
|
514
342
|
const description = subcommands[cmd].description;
|
|
515
|
-
|
|
343
|
+
logger.log(` ${(cmd + ':').padEnd(longestCommand + 1)} ${description}`);
|
|
516
344
|
}
|
|
517
|
-
|
|
345
|
+
logger.log('');
|
|
346
|
+
logger.log(` Use ${command} <subcommand> --help for more information about a subcommand`);
|
|
518
347
|
}
|
|
519
348
|
}
|
|
520
|
-
|
|
349
|
+
logger.log('Global Options:');
|
|
521
350
|
const globalOptions = {
|
|
522
351
|
...cliOptions,
|
|
523
352
|
...{
|
|
@@ -531,46 +360,95 @@ function printHelpContents(command, subcommands, cliOptions, additionalArgs, sel
|
|
|
531
360
|
description: 'Print the version and exit'
|
|
532
361
|
};
|
|
533
362
|
}
|
|
534
|
-
printOptions(globalOptions);
|
|
363
|
+
printOptions(globalOptions, logger);
|
|
535
364
|
}
|
|
536
|
-
function printOptions(cliOptions) {
|
|
365
|
+
function printOptions(cliOptions, logger) {
|
|
537
366
|
const longestOption = Object.keys(cliOptions).reduce((acc, key) => Math.max(acc, camelToKebabCase(key).length + 2), 0) + 1;
|
|
538
367
|
const terminalWidth = process.stdout.columns ?? 80;
|
|
539
368
|
const nonBooleanBuffer = ' ';
|
|
540
369
|
for (const [key, option] of Object.entries(cliOptions)) {
|
|
541
370
|
let optionString = ` --${camelToKebabCase(key)}:`.padEnd(longestOption + 3);
|
|
542
|
-
if (
|
|
371
|
+
if (option.character) {
|
|
543
372
|
optionString += `(-${option.character}) `;
|
|
544
373
|
}
|
|
545
374
|
else {
|
|
546
375
|
optionString += nonBooleanBuffer;
|
|
547
376
|
}
|
|
548
377
|
const leftBar = optionString.length;
|
|
549
|
-
optionString += option.description
|
|
550
|
-
|
|
551
|
-
if (option.values === 'single') {
|
|
552
|
-
optionString += `Must be one of: ${option.validValues.join(', ')}.`;
|
|
553
|
-
}
|
|
554
|
-
else {
|
|
555
|
-
optionString += `Valid values: ${option.validValues.join(', ')}.`;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
else if (option.type === 'boolean') {
|
|
559
|
-
// Do nothing
|
|
560
|
-
}
|
|
561
|
-
else if (option.values === 'single') {
|
|
562
|
-
optionString += `One ${option.type} required`;
|
|
563
|
-
}
|
|
564
|
-
else if (option.values === 'multiple') {
|
|
565
|
-
optionString += `Multiple ${option.type}s allowed`;
|
|
566
|
-
}
|
|
567
|
-
console.log(optionString.slice(0, terminalWidth));
|
|
378
|
+
optionString += option.description;
|
|
379
|
+
logger.log(optionString.slice(0, terminalWidth));
|
|
568
380
|
let stringToPrint = optionString.slice(terminalWidth);
|
|
569
381
|
const secondLineLength = terminalWidth - leftBar;
|
|
570
382
|
while (stringToPrint.length > 0) {
|
|
571
|
-
|
|
383
|
+
logger.log(' '.repeat(leftBar) + stringToPrint.slice(0, secondLineLength).trimStart());
|
|
572
384
|
stringToPrint = stringToPrint.slice(secondLineLength);
|
|
573
385
|
}
|
|
574
386
|
}
|
|
575
387
|
}
|
|
388
|
+
async function printVersion(versionInfo, logger) {
|
|
389
|
+
if (!versionInfo) {
|
|
390
|
+
logger.log('Version information not available. This is a bug.');
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (typeof versionInfo === 'string') {
|
|
394
|
+
logger.log(versionInfo);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
let currentVersion = null;
|
|
398
|
+
if (typeof versionInfo.currentVersion === 'string') {
|
|
399
|
+
currentVersion = versionInfo.currentVersion;
|
|
400
|
+
}
|
|
401
|
+
else if (typeof versionInfo.currentVersion === 'function') {
|
|
402
|
+
currentVersion = await versionInfo.currentVersion();
|
|
403
|
+
}
|
|
404
|
+
if (!currentVersion) {
|
|
405
|
+
logger.log('Current version not available');
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
let latestVersion = null;
|
|
409
|
+
if (typeof versionInfo.checkForUpdates == 'string') {
|
|
410
|
+
latestVersion = await getLatestVersionFromNpm(versionInfo.checkForUpdates);
|
|
411
|
+
}
|
|
412
|
+
else if (typeof versionInfo.checkForUpdates === 'function') {
|
|
413
|
+
latestVersion = await versionInfo.checkForUpdates();
|
|
414
|
+
}
|
|
415
|
+
let updateMessage = undefined;
|
|
416
|
+
if (currentVersion && latestVersion && currentVersion !== latestVersion) {
|
|
417
|
+
if (versionInfo.updateMessage) {
|
|
418
|
+
updateMessage = versionInfo.updateMessage(currentVersion, latestVersion);
|
|
419
|
+
}
|
|
420
|
+
else if (typeof versionInfo.checkForUpdates === 'string') {
|
|
421
|
+
updateMessage = `Latest: ${latestVersion}. To update run: npm update -g ${versionInfo.checkForUpdates}`;
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
updateMessage = `Latest: ${latestVersion}.`;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
else if (currentVersion && latestVersion && currentVersion === latestVersion) {
|
|
428
|
+
// updateMessage = 'You are using the latest version.'
|
|
429
|
+
}
|
|
430
|
+
logger.log(currentVersion);
|
|
431
|
+
if (updateMessage) {
|
|
432
|
+
logger.log(updateMessage);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Fetch the latest version of a package from the npm registry.
|
|
437
|
+
*
|
|
438
|
+
* @param packageName the name of the npm package to check, e.g. "my-cli-tool" or "@my-org/my-cli-tool"
|
|
439
|
+
* @returns the latest version of the package published on npm or null if any error occurs
|
|
440
|
+
*/
|
|
441
|
+
async function getLatestVersionFromNpm(packageName) {
|
|
442
|
+
try {
|
|
443
|
+
const res = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
|
|
444
|
+
if (res.ok) {
|
|
445
|
+
const data = await res.json();
|
|
446
|
+
return data.version || null;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
catch (e) {
|
|
450
|
+
// Ignore errors fetching latest version
|
|
451
|
+
}
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
576
454
|
//# sourceMappingURL=cli.js.map
|