@guanghechen/commander 1.0.11 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +147 -17
- package/lib/cjs/index.cjs +842 -145
- package/lib/esm/index.mjs +838 -131
- package/lib/types/index.d.ts +199 -119
- package/package.json +18 -19
- package/CHANGELOG.md +0 -106
package/lib/cjs/index.cjs
CHANGED
|
@@ -1,172 +1,869 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
class CommanderError extends Error {
|
|
4
|
+
kind;
|
|
5
|
+
commandPath;
|
|
6
|
+
constructor(kind, message, commandPath) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'CommanderError';
|
|
9
|
+
this.kind = kind;
|
|
10
|
+
this.commandPath = commandPath;
|
|
11
|
+
}
|
|
12
|
+
format() {
|
|
13
|
+
return `Error: ${this.message}\nRun "${this.commandPath} --help" for usage.`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
11
16
|
|
|
12
|
-
class
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
class DefaultReporter {
|
|
18
|
+
debug(message, ...args) {
|
|
19
|
+
console.debug(message, ...args);
|
|
20
|
+
}
|
|
21
|
+
info(message, ...args) {
|
|
22
|
+
console.info(message, ...args);
|
|
23
|
+
}
|
|
24
|
+
warn(message, ...args) {
|
|
25
|
+
console.warn(message, ...args);
|
|
26
|
+
}
|
|
27
|
+
error(message, ...args) {
|
|
28
|
+
console.error(message, ...args);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const BUILTIN_HELP_OPTION = {
|
|
32
|
+
long: 'help',
|
|
33
|
+
short: 'h',
|
|
34
|
+
type: 'boolean',
|
|
35
|
+
description: 'Show help information',
|
|
36
|
+
};
|
|
37
|
+
const BUILTIN_VERSION_OPTION = {
|
|
38
|
+
long: 'version',
|
|
39
|
+
short: 'V',
|
|
40
|
+
type: 'boolean',
|
|
41
|
+
description: 'Show version number',
|
|
42
|
+
};
|
|
43
|
+
class Command {
|
|
44
|
+
#name;
|
|
45
|
+
#description;
|
|
46
|
+
#version;
|
|
47
|
+
#aliases;
|
|
48
|
+
#options = [];
|
|
49
|
+
#arguments = [];
|
|
50
|
+
#subcommands = [];
|
|
51
|
+
#action;
|
|
52
|
+
#parent;
|
|
53
|
+
constructor(config) {
|
|
54
|
+
this.#name = config.name;
|
|
55
|
+
this.#description = config.description;
|
|
56
|
+
this.#version = config.version;
|
|
57
|
+
this.#aliases = config.aliases ?? [];
|
|
58
|
+
}
|
|
59
|
+
get name() {
|
|
60
|
+
return this.#name;
|
|
61
|
+
}
|
|
62
|
+
get aliases() {
|
|
63
|
+
return this.#aliases;
|
|
64
|
+
}
|
|
65
|
+
get description() {
|
|
66
|
+
return this.#description;
|
|
67
|
+
}
|
|
68
|
+
get version() {
|
|
69
|
+
return this.#version ?? this.#parent?.version;
|
|
70
|
+
}
|
|
71
|
+
get parent() {
|
|
72
|
+
return this.#parent;
|
|
73
|
+
}
|
|
74
|
+
get options() {
|
|
75
|
+
return [...this.#options];
|
|
76
|
+
}
|
|
77
|
+
get arguments() {
|
|
78
|
+
return [...this.#arguments];
|
|
79
|
+
}
|
|
80
|
+
option(opt) {
|
|
81
|
+
this.#validateOptionConfig(opt);
|
|
82
|
+
this.#checkOptionUniqueness(opt);
|
|
83
|
+
this.#options.push(opt);
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
argument(arg) {
|
|
87
|
+
this.#validateArgumentConfig(arg);
|
|
88
|
+
this.#arguments.push(arg);
|
|
89
|
+
return this;
|
|
22
90
|
}
|
|
23
91
|
action(fn) {
|
|
24
|
-
|
|
25
|
-
const expectedArgsCount = this.registeredArguments.length;
|
|
26
|
-
const actionArgs = [
|
|
27
|
-
args.slice(0, expectedArgsCount),
|
|
28
|
-
this.opts(),
|
|
29
|
-
args.slice(expectedArgsCount),
|
|
30
|
-
this,
|
|
31
|
-
];
|
|
32
|
-
const actionResult = fn.apply(this, actionArgs);
|
|
33
|
-
return actionResult;
|
|
34
|
-
};
|
|
35
|
-
this._actionHandler = listener;
|
|
92
|
+
this.#action = fn;
|
|
36
93
|
return this;
|
|
37
94
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
95
|
+
subcommand(cmd) {
|
|
96
|
+
cmd.#parent = this;
|
|
97
|
+
this.#subcommands.push(cmd);
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
async run(params) {
|
|
101
|
+
const { argv, envs, reporter } = params;
|
|
102
|
+
try {
|
|
103
|
+
const { command, remaining } = this.#route(argv);
|
|
104
|
+
const { opts, args } = command.parse(remaining);
|
|
105
|
+
const ctx = {
|
|
106
|
+
cmd: command,
|
|
107
|
+
envs,
|
|
108
|
+
reporter: reporter ?? new DefaultReporter(),
|
|
109
|
+
argv,
|
|
110
|
+
};
|
|
111
|
+
const allOptions = command.#getMergedOptions();
|
|
112
|
+
const hasUserHelp = allOptions.some(o => o.long === 'help' && !command.#isBuiltinOption(o));
|
|
113
|
+
const hasUserVersion = allOptions.some(o => o.long === 'version' && !command.#isBuiltinOption(o));
|
|
114
|
+
if (!hasUserHelp && opts['help'] === true) {
|
|
115
|
+
console.log(command.formatHelp());
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (!hasUserVersion && opts['version'] === true) {
|
|
119
|
+
console.log(command.version ?? 'unknown');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
for (const opt of allOptions) {
|
|
123
|
+
if (opt.apply && opts[opt.long] !== undefined) {
|
|
124
|
+
opt.apply(opts[opt.long], ctx);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const actionParams = { ctx, opts, args };
|
|
128
|
+
if (command.#action) {
|
|
129
|
+
try {
|
|
130
|
+
await command.#action(actionParams);
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
if (err instanceof Error) {
|
|
134
|
+
console.error(`Error: ${err.message}`);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
console.error('Error: action failed');
|
|
138
|
+
}
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else if (command.#subcommands.length > 0) {
|
|
143
|
+
console.log(command.formatHelp());
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
throw new CommanderError('ConfigurationError', `no action defined for command "${command.#getCommandPath()}"`, command.#getCommandPath());
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
if (err instanceof CommanderError) {
|
|
151
|
+
console.error(err.format());
|
|
152
|
+
process.exit(2);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
throw err;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
parse(argv) {
|
|
159
|
+
const allOptions = this.#getMergedOptions();
|
|
160
|
+
const opts = {};
|
|
161
|
+
const args = [];
|
|
162
|
+
for (const opt of allOptions) {
|
|
163
|
+
if (opt.default !== undefined) {
|
|
164
|
+
opts[opt.long] = opt.default;
|
|
165
|
+
}
|
|
166
|
+
else if (opt.type === 'boolean') {
|
|
167
|
+
opts[opt.long] = false;
|
|
168
|
+
}
|
|
169
|
+
else if (opt.type === 'string[]' || opt.type === 'number[]') {
|
|
170
|
+
opts[opt.long] = [];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
let remaining = [...argv];
|
|
174
|
+
const resolverOptions = allOptions.filter(o => o.resolver);
|
|
175
|
+
for (const opt of resolverOptions) {
|
|
176
|
+
const result = opt.resolver(remaining);
|
|
177
|
+
opts[opt.long] = result.value;
|
|
178
|
+
remaining = result.remaining;
|
|
42
179
|
}
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
180
|
+
const optionByLong = new Map();
|
|
181
|
+
const optionByShort = new Map();
|
|
182
|
+
for (const opt of allOptions) {
|
|
183
|
+
if (!opt.resolver) {
|
|
184
|
+
optionByLong.set(opt.long, opt);
|
|
185
|
+
if (opt.short) {
|
|
186
|
+
optionByShort.set(opt.short, opt);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
let i = 0;
|
|
191
|
+
while (i < remaining.length) {
|
|
192
|
+
const token = remaining[i];
|
|
193
|
+
if (token === '--') {
|
|
194
|
+
args.push(...remaining.slice(i + 1));
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
if (token.startsWith('--')) {
|
|
198
|
+
i = this.#parseLongOption(remaining, i, optionByLong, opts);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (token.startsWith('-') && token.length > 1) {
|
|
202
|
+
i = this.#parseShortOption(remaining, i, optionByShort, opts);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
args.push(token);
|
|
206
|
+
i += 1;
|
|
207
|
+
}
|
|
208
|
+
for (const opt of allOptions) {
|
|
209
|
+
if (opt.required && opts[opt.long] === undefined) {
|
|
210
|
+
throw new CommanderError('MissingRequired', `missing required option "--${opt.long}" for command "${this.#getCommandPath()}"`, this.#getCommandPath());
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
for (const opt of allOptions) {
|
|
214
|
+
if (opt.choices && opts[opt.long] !== undefined) {
|
|
215
|
+
const value = opts[opt.long];
|
|
216
|
+
const values = Array.isArray(value) ? value : [value];
|
|
217
|
+
const choices = opt.choices;
|
|
218
|
+
for (const v of values) {
|
|
219
|
+
if (!choices.includes(v)) {
|
|
220
|
+
throw new CommanderError('InvalidChoice', `invalid value "${v}" for option "--${opt.long}". Allowed: ${opt.choices.join(', ')}`, this.#getCommandPath());
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const requiredArgs = this.#arguments.filter(a => a.kind === 'required');
|
|
226
|
+
if (args.length < requiredArgs.length) {
|
|
227
|
+
const missing = requiredArgs.slice(args.length).map(a => a.name);
|
|
228
|
+
throw new CommanderError('MissingRequiredArgument', `missing required argument(s): ${missing.join(', ')}`, this.#getCommandPath());
|
|
229
|
+
}
|
|
230
|
+
return { opts, args };
|
|
231
|
+
}
|
|
232
|
+
formatHelp() {
|
|
233
|
+
const lines = [];
|
|
234
|
+
const allOptions = this.#getMergedOptions();
|
|
235
|
+
lines.push(this.#description);
|
|
236
|
+
lines.push('');
|
|
237
|
+
const commandPath = this.#getCommandPath();
|
|
238
|
+
let usage = `Usage: ${commandPath}`;
|
|
239
|
+
if (allOptions.length > 0)
|
|
240
|
+
usage += ' [options]';
|
|
241
|
+
if (this.#subcommands.length > 0)
|
|
242
|
+
usage += ' [command]';
|
|
243
|
+
for (const arg of this.#arguments) {
|
|
244
|
+
if (arg.kind === 'required') {
|
|
245
|
+
usage += ` <${arg.name}>`;
|
|
246
|
+
}
|
|
247
|
+
else if (arg.kind === 'optional') {
|
|
248
|
+
usage += ` [${arg.name}]`;
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
usage += ` [${arg.name}...]`;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
lines.push(usage);
|
|
255
|
+
lines.push('');
|
|
256
|
+
if (allOptions.length > 0) {
|
|
257
|
+
lines.push('Options:');
|
|
258
|
+
const optLines = [];
|
|
259
|
+
for (const opt of allOptions) {
|
|
260
|
+
let sig = opt.short ? `-${opt.short}, ` : ' ';
|
|
261
|
+
sig += `--${opt.long}`;
|
|
262
|
+
const effectiveType = opt.type ?? 'string';
|
|
263
|
+
if (effectiveType !== 'boolean') {
|
|
264
|
+
sig += ' <value>';
|
|
265
|
+
}
|
|
266
|
+
let desc = opt.description;
|
|
267
|
+
if (opt.default !== undefined && effectiveType !== 'boolean') {
|
|
268
|
+
desc += ` (default: ${JSON.stringify(opt.default)})`;
|
|
269
|
+
}
|
|
270
|
+
if (opt.choices) {
|
|
271
|
+
desc += ` [choices: ${opt.choices.join(', ')}]`;
|
|
272
|
+
}
|
|
273
|
+
optLines.push({ sig, desc });
|
|
274
|
+
if (effectiveType === 'boolean') {
|
|
275
|
+
optLines.push({
|
|
276
|
+
sig: ` --no-${opt.long}`,
|
|
277
|
+
desc: opt.description,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const maxSigLen = Math.max(...optLines.map(l => l.sig.length));
|
|
282
|
+
for (const { sig, desc } of optLines) {
|
|
283
|
+
const padding = ' '.repeat(maxSigLen - sig.length + 2);
|
|
284
|
+
lines.push(` ${sig}${padding}${desc}`);
|
|
285
|
+
}
|
|
286
|
+
lines.push('');
|
|
287
|
+
}
|
|
288
|
+
if (this.#subcommands.length > 0) {
|
|
289
|
+
lines.push('Commands:');
|
|
290
|
+
const cmdLines = [];
|
|
291
|
+
for (const sub of this.#subcommands) {
|
|
292
|
+
let name = sub.#name;
|
|
293
|
+
if (sub.#aliases.length > 0) {
|
|
294
|
+
name += `, ${sub.#aliases.join(', ')}`;
|
|
295
|
+
}
|
|
296
|
+
cmdLines.push({ name, desc: sub.#description });
|
|
297
|
+
}
|
|
298
|
+
const maxNameLen = Math.max(...cmdLines.map(l => l.name.length));
|
|
299
|
+
for (const { name, desc } of cmdLines) {
|
|
300
|
+
const padding = ' '.repeat(maxNameLen - name.length + 2);
|
|
301
|
+
lines.push(` ${name}${padding}${desc}`);
|
|
302
|
+
}
|
|
303
|
+
lines.push('');
|
|
304
|
+
}
|
|
305
|
+
return lines.join('\n');
|
|
306
|
+
}
|
|
307
|
+
getCompletionMeta() {
|
|
308
|
+
const allOptions = this.#getMergedOptions();
|
|
309
|
+
const options = [];
|
|
310
|
+
for (const opt of allOptions) {
|
|
311
|
+
const effectiveType = opt.type ?? 'string';
|
|
312
|
+
options.push({
|
|
313
|
+
long: opt.long,
|
|
314
|
+
short: opt.short,
|
|
315
|
+
description: opt.description,
|
|
316
|
+
takesValue: effectiveType !== 'boolean',
|
|
317
|
+
choices: opt.choices,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
name: this.#name,
|
|
322
|
+
description: this.#description,
|
|
323
|
+
aliases: this.#aliases,
|
|
324
|
+
options,
|
|
325
|
+
subcommands: this.#subcommands.map(sub => sub.getCompletionMeta()),
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
#route(argv) {
|
|
329
|
+
let current = this;
|
|
330
|
+
let idx = 0;
|
|
331
|
+
while (idx < argv.length) {
|
|
332
|
+
const token = argv[idx];
|
|
333
|
+
if (token.startsWith('-'))
|
|
334
|
+
break;
|
|
335
|
+
const sub = current.#subcommands.find(c => c.#name === token || c.#aliases.includes(token));
|
|
336
|
+
if (!sub)
|
|
337
|
+
break;
|
|
338
|
+
current = sub;
|
|
339
|
+
idx += 1;
|
|
340
|
+
}
|
|
341
|
+
return { command: current, remaining: argv.slice(idx) };
|
|
342
|
+
}
|
|
343
|
+
#parseLongOption(argv, idx, optionByLong, opts) {
|
|
344
|
+
const token = argv[idx];
|
|
345
|
+
const eqIdx = token.indexOf('=');
|
|
346
|
+
let optName;
|
|
347
|
+
let inlineValue;
|
|
348
|
+
if (eqIdx !== -1) {
|
|
349
|
+
optName = token.slice(2, eqIdx);
|
|
350
|
+
inlineValue = token.slice(eqIdx + 1);
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
optName = token.slice(2);
|
|
354
|
+
}
|
|
355
|
+
if (optName.startsWith('no-')) {
|
|
356
|
+
const actualName = optName.slice(3);
|
|
357
|
+
const opt = optionByLong.get(actualName);
|
|
358
|
+
if (opt && opt.type === 'boolean') {
|
|
359
|
+
if (inlineValue !== undefined) {
|
|
360
|
+
throw new CommanderError('InvalidBooleanValue', `"--no-${actualName}" does not accept a value`, this.#getCommandPath());
|
|
361
|
+
}
|
|
362
|
+
opts[actualName] = false;
|
|
363
|
+
return idx + 1;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const opt = optionByLong.get(optName);
|
|
367
|
+
if (!opt) {
|
|
368
|
+
throw new CommanderError('UnknownOption', `unknown option "--${optName}" for command "${this.#getCommandPath()}"`, this.#getCommandPath());
|
|
369
|
+
}
|
|
370
|
+
if (opt.type === 'boolean') {
|
|
371
|
+
if (inlineValue !== undefined) {
|
|
372
|
+
if (inlineValue === 'true') {
|
|
373
|
+
opts[optName] = true;
|
|
374
|
+
}
|
|
375
|
+
else if (inlineValue === 'false') {
|
|
376
|
+
opts[optName] = false;
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
throw new CommanderError('InvalidBooleanValue', `invalid value "${inlineValue}" for boolean option "--${optName}". Use "true" or "false"`, this.#getCommandPath());
|
|
50
380
|
}
|
|
51
381
|
}
|
|
52
382
|
else {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
383
|
+
opts[optName] = true;
|
|
384
|
+
}
|
|
385
|
+
return idx + 1;
|
|
386
|
+
}
|
|
387
|
+
let value;
|
|
388
|
+
let nextIdx = idx;
|
|
389
|
+
if (inlineValue !== undefined) {
|
|
390
|
+
value = inlineValue;
|
|
391
|
+
}
|
|
392
|
+
else if (idx + 1 < argv.length) {
|
|
393
|
+
value = argv[idx + 1];
|
|
394
|
+
nextIdx += 1;
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
throw new CommanderError('MissingValue', `option "--${optName}" requires a value`, this.#getCommandPath());
|
|
398
|
+
}
|
|
399
|
+
this.#applyValue(opt, value, opts);
|
|
400
|
+
return nextIdx + 1;
|
|
401
|
+
}
|
|
402
|
+
#parseShortOption(argv, idx, optionByShort, opts) {
|
|
403
|
+
const token = argv[idx];
|
|
404
|
+
if (token.includes('=')) {
|
|
405
|
+
throw new CommanderError('UnsupportedShortSyntax', `"-${token.slice(1)}" is not supported. Use "-${token[1]} ${token.slice(3)}" instead`, this.#getCommandPath());
|
|
406
|
+
}
|
|
407
|
+
const flags = token.slice(1);
|
|
408
|
+
for (let j = 0; j < flags.length; j++) {
|
|
409
|
+
const flag = flags[j];
|
|
410
|
+
const opt = optionByShort.get(flag);
|
|
411
|
+
if (!opt) {
|
|
412
|
+
throw new CommanderError('UnknownOption', `unknown option "-${flag}" for command "${this.#getCommandPath()}"`, this.#getCommandPath());
|
|
413
|
+
}
|
|
414
|
+
if (opt.type === 'boolean') {
|
|
415
|
+
opts[opt.long] = true;
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (j < flags.length - 1) {
|
|
419
|
+
throw new CommanderError('UnsupportedShortSyntax', `"-${flags}" is not supported. Use "-${flags.slice(0, j + 1)} ${flags.slice(j + 1)}" or separate options`, this.#getCommandPath());
|
|
420
|
+
}
|
|
421
|
+
if (idx + 1 < argv.length && !argv[idx + 1].startsWith('-')) {
|
|
422
|
+
const value = argv[idx + 1];
|
|
423
|
+
this.#applyValue(opt, value, opts);
|
|
424
|
+
return idx + 2;
|
|
425
|
+
}
|
|
426
|
+
throw new CommanderError('MissingValue', `option "-${flag}" requires a value`, this.#getCommandPath());
|
|
427
|
+
}
|
|
428
|
+
return idx + 1;
|
|
429
|
+
}
|
|
430
|
+
#applyValue(opt, rawValue, opts) {
|
|
431
|
+
const type = opt.type ?? 'string';
|
|
432
|
+
let parsedValue;
|
|
433
|
+
if (opt.coerce) {
|
|
434
|
+
parsedValue = opt.coerce(rawValue);
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
switch (type) {
|
|
438
|
+
case 'string':
|
|
439
|
+
case 'string[]':
|
|
440
|
+
parsedValue = rawValue;
|
|
441
|
+
break;
|
|
442
|
+
case 'number':
|
|
443
|
+
case 'number[]': {
|
|
444
|
+
const num = Number(rawValue);
|
|
445
|
+
if (Number.isNaN(num)) {
|
|
446
|
+
throw new CommanderError('InvalidType', `invalid number "${rawValue}" for option "--${opt.long}"`, this.#getCommandPath());
|
|
447
|
+
}
|
|
448
|
+
parsedValue = num;
|
|
449
|
+
break;
|
|
56
450
|
}
|
|
451
|
+
default:
|
|
452
|
+
parsedValue = rawValue;
|
|
57
453
|
}
|
|
58
454
|
}
|
|
59
|
-
|
|
455
|
+
if (type === 'string[]' || type === 'number[]') {
|
|
456
|
+
const currentValue = opts[opt.long];
|
|
457
|
+
const current = Array.isArray(currentValue) ? currentValue : [];
|
|
458
|
+
opts[opt.long] = [...current, parsedValue];
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
opts[opt.long] = parsedValue;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
#getMergedOptions() {
|
|
465
|
+
const ancestors = [];
|
|
466
|
+
for (let node = this; node; node = node.#parent) {
|
|
467
|
+
ancestors.unshift(node);
|
|
468
|
+
}
|
|
469
|
+
const optionMap = new Map();
|
|
470
|
+
const hasUserHelp = ancestors.some(c => c.#options.some(o => o.long === 'help'));
|
|
471
|
+
const hasUserVersion = ancestors.some(c => c.#options.some(o => o.long === 'version'));
|
|
472
|
+
if (!hasUserHelp) {
|
|
473
|
+
optionMap.set('help', BUILTIN_HELP_OPTION);
|
|
474
|
+
}
|
|
475
|
+
if (!hasUserVersion) {
|
|
476
|
+
optionMap.set('version', BUILTIN_VERSION_OPTION);
|
|
477
|
+
}
|
|
478
|
+
for (const ancestor of ancestors) {
|
|
479
|
+
for (const opt of ancestor.#options) {
|
|
480
|
+
optionMap.set(opt.long, opt);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const shortToLong = new Map();
|
|
484
|
+
for (const [long, opt] of optionMap) {
|
|
485
|
+
if (opt.short) {
|
|
486
|
+
const existing = shortToLong.get(opt.short);
|
|
487
|
+
if (existing && existing !== long) {
|
|
488
|
+
throw new CommanderError('OptionConflict', `short option "-${opt.short}" is used by both "--${existing}" and "--${long}"`, this.#getCommandPath());
|
|
489
|
+
}
|
|
490
|
+
shortToLong.set(opt.short, long);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return Array.from(optionMap.values());
|
|
494
|
+
}
|
|
495
|
+
#validateOptionConfig(opt) {
|
|
496
|
+
if (opt.long.startsWith('no-')) {
|
|
497
|
+
throw new CommanderError('ConfigurationError', `option long name cannot start with "no-": "${opt.long}"`, this.#getCommandPath());
|
|
498
|
+
}
|
|
499
|
+
if (opt.required && opt.default !== undefined) {
|
|
500
|
+
throw new CommanderError('ConfigurationError', `option "--${opt.long}" cannot be both required and have a default value`, this.#getCommandPath());
|
|
501
|
+
}
|
|
502
|
+
if (opt.type === 'boolean' && opt.required) {
|
|
503
|
+
throw new CommanderError('ConfigurationError', `boolean option "--${opt.long}" cannot be required`, this.#getCommandPath());
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
#checkOptionUniqueness(opt) {
|
|
507
|
+
if (this.#options.some(o => o.long === opt.long)) {
|
|
508
|
+
throw new CommanderError('OptionConflict', `option "--${opt.long}" is already defined`, this.#getCommandPath());
|
|
509
|
+
}
|
|
510
|
+
if (opt.short && this.#options.some(o => o.short === opt.short)) {
|
|
511
|
+
throw new CommanderError('OptionConflict', `short option "-${opt.short}" is already defined`, this.#getCommandPath());
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
#validateArgumentConfig(arg) {
|
|
515
|
+
if (arg.kind === 'variadic') {
|
|
516
|
+
if (this.#arguments.some(a => a.kind === 'variadic')) {
|
|
517
|
+
throw new CommanderError('ConfigurationError', 'only one variadic argument is allowed', this.#getCommandPath());
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (this.#arguments.length > 0) {
|
|
521
|
+
const last = this.#arguments[this.#arguments.length - 1];
|
|
522
|
+
if (last.kind === 'variadic') {
|
|
523
|
+
throw new CommanderError('ConfigurationError', 'variadic argument must be the last argument', this.#getCommandPath());
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
if (arg.kind === 'required') {
|
|
527
|
+
const hasOptional = this.#arguments.some(a => a.kind === 'optional' || a.kind === 'variadic');
|
|
528
|
+
if (hasOptional) {
|
|
529
|
+
throw new CommanderError('ConfigurationError', `required argument "${arg.name}" cannot come after optional/variadic arguments`, this.#getCommandPath());
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
#isBuiltinOption(opt) {
|
|
534
|
+
return opt === BUILTIN_HELP_OPTION || opt === BUILTIN_VERSION_OPTION;
|
|
535
|
+
}
|
|
536
|
+
#getCommandPath() {
|
|
537
|
+
const parts = [];
|
|
538
|
+
for (let node = this; node; node = node.#parent) {
|
|
539
|
+
parts.unshift(node.#name);
|
|
540
|
+
}
|
|
541
|
+
return parts.join(' ');
|
|
60
542
|
}
|
|
61
543
|
}
|
|
62
544
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
program.addCommand(command, { ...opts, isDefault: true });
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
function createMainCommandExecutor(create, handle) {
|
|
73
|
-
return (args) => {
|
|
74
|
-
return new Promise(resolve => {
|
|
75
|
-
const wrappedHandler = async (options) => {
|
|
76
|
-
await handle(options);
|
|
77
|
-
resolve();
|
|
78
|
-
};
|
|
79
|
-
const command = create(wrappedHandler);
|
|
80
|
-
command.parse(args);
|
|
545
|
+
class CompletionCommand extends Command {
|
|
546
|
+
constructor(root, config) {
|
|
547
|
+
const name = config?.name ?? 'completion';
|
|
548
|
+
super({
|
|
549
|
+
name,
|
|
550
|
+
description: 'Generate shell completion script',
|
|
81
551
|
});
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
552
|
+
this.option({
|
|
553
|
+
long: 'bash',
|
|
554
|
+
type: 'boolean',
|
|
555
|
+
description: 'Generate Bash completion script',
|
|
556
|
+
})
|
|
557
|
+
.option({
|
|
558
|
+
long: 'fish',
|
|
559
|
+
type: 'boolean',
|
|
560
|
+
description: 'Generate Fish completion script',
|
|
561
|
+
})
|
|
562
|
+
.option({
|
|
563
|
+
long: 'pwsh',
|
|
564
|
+
type: 'boolean',
|
|
565
|
+
description: 'Generate PowerShell completion script',
|
|
566
|
+
})
|
|
567
|
+
.action(({ opts }) => {
|
|
568
|
+
const meta = root.getCompletionMeta();
|
|
569
|
+
const programName = root.name;
|
|
570
|
+
const selectedShells = [
|
|
571
|
+
opts['bash'] && 'bash',
|
|
572
|
+
opts['fish'] && 'fish',
|
|
573
|
+
opts['pwsh'] && 'pwsh',
|
|
574
|
+
].filter(Boolean);
|
|
575
|
+
if (selectedShells.length === 0) {
|
|
576
|
+
console.error('Please specify a shell: --bash, --fish, or --pwsh');
|
|
577
|
+
process.exit(1);
|
|
578
|
+
}
|
|
579
|
+
if (selectedShells.length > 1) {
|
|
580
|
+
console.error('Please specify only one shell option');
|
|
581
|
+
process.exit(1);
|
|
582
|
+
}
|
|
583
|
+
switch (selectedShells[0]) {
|
|
584
|
+
case 'bash':
|
|
585
|
+
console.log(new BashCompletion(meta, programName).generate());
|
|
586
|
+
break;
|
|
587
|
+
case 'fish':
|
|
588
|
+
console.log(new FishCompletion(meta, programName).generate());
|
|
589
|
+
break;
|
|
590
|
+
case 'pwsh':
|
|
591
|
+
console.log(new PwshCompletion(meta, programName).generate());
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
107
594
|
});
|
|
108
595
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
596
|
+
}
|
|
597
|
+
class BashCompletion {
|
|
598
|
+
#meta;
|
|
599
|
+
#programName;
|
|
600
|
+
constructor(meta, programName) {
|
|
601
|
+
this.#meta = meta;
|
|
602
|
+
this.#programName = programName;
|
|
603
|
+
}
|
|
604
|
+
generate() {
|
|
605
|
+
const funcName = `_${this.#sanitizeName(this.#programName)}_completions`;
|
|
606
|
+
const lines = [
|
|
607
|
+
`# Bash completion for ${this.#programName}`,
|
|
608
|
+
'# Generated by @guanghechen/commander',
|
|
609
|
+
'',
|
|
610
|
+
`${funcName}() {`,
|
|
611
|
+
' local cur prev words cword',
|
|
612
|
+
' _init_completion || return',
|
|
613
|
+
'',
|
|
614
|
+
...this.#generateCommandCase(this.#meta, 1),
|
|
615
|
+
'',
|
|
616
|
+
' COMPREPLY=($(compgen -W "$opts" -- "$cur"))',
|
|
617
|
+
'}',
|
|
618
|
+
'',
|
|
619
|
+
`complete -F ${funcName} ${this.#programName}`,
|
|
620
|
+
'',
|
|
621
|
+
];
|
|
622
|
+
return lines.join('\n');
|
|
623
|
+
}
|
|
624
|
+
#generateCommandCase(cmd, depth) {
|
|
625
|
+
const indent = ' '.repeat(depth);
|
|
626
|
+
const lines = [];
|
|
627
|
+
const optParts = [];
|
|
628
|
+
for (const opt of cmd.options) {
|
|
629
|
+
if (opt.short)
|
|
630
|
+
optParts.push(`-${opt.short}`);
|
|
631
|
+
optParts.push(`--${opt.long}`);
|
|
632
|
+
if (!opt.takesValue) {
|
|
633
|
+
optParts.push(`--no-${opt.long}`);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
const subParts = cmd.subcommands.flatMap(sub => [sub.name, ...sub.aliases]);
|
|
637
|
+
const allOpts = [...optParts, ...subParts].join(' ');
|
|
638
|
+
if (cmd.subcommands.length > 0) {
|
|
639
|
+
lines.push(`${indent}case "\${words[${depth}]}" in`);
|
|
640
|
+
for (const sub of cmd.subcommands) {
|
|
641
|
+
const pattern = [sub.name, ...sub.aliases].join('|');
|
|
642
|
+
lines.push(`${indent} ${pattern})`);
|
|
643
|
+
lines.push(...this.#generateCommandCase(sub, depth + 1));
|
|
644
|
+
lines.push(`${indent} ;;`);
|
|
645
|
+
}
|
|
646
|
+
lines.push(`${indent} *)`);
|
|
647
|
+
lines.push(`${indent} opts="${allOpts}"`);
|
|
648
|
+
lines.push(`${indent} ;;`);
|
|
649
|
+
lines.push(`${indent}esac`);
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
lines.push(`${indent}opts="${allOpts}"`);
|
|
653
|
+
}
|
|
654
|
+
return lines;
|
|
655
|
+
}
|
|
656
|
+
#sanitizeName(name) {
|
|
657
|
+
return name.replace(/[^a-zA-Z0-9]/g, '_');
|
|
112
658
|
}
|
|
113
659
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
.
|
|
130
|
-
|
|
131
|
-
|
|
660
|
+
class FishCompletion {
|
|
661
|
+
#meta;
|
|
662
|
+
#programName;
|
|
663
|
+
constructor(meta, programName) {
|
|
664
|
+
this.#meta = meta;
|
|
665
|
+
this.#programName = programName;
|
|
666
|
+
}
|
|
667
|
+
generate() {
|
|
668
|
+
const lines = [
|
|
669
|
+
`# Fish completion for ${this.#programName}`,
|
|
670
|
+
'# Generated by @guanghechen/commander',
|
|
671
|
+
'',
|
|
672
|
+
...this.#generateCommandCompletions(this.#meta, []),
|
|
673
|
+
'',
|
|
674
|
+
];
|
|
675
|
+
return lines.join('\n');
|
|
676
|
+
}
|
|
677
|
+
#generateCommandCompletions(cmd, parentPath) {
|
|
678
|
+
const lines = [];
|
|
679
|
+
const isRoot = parentPath.length === 0;
|
|
680
|
+
const condition = this.#buildCondition(parentPath);
|
|
681
|
+
for (const opt of cmd.options) {
|
|
682
|
+
let line = `complete -c ${this.#programName}`;
|
|
683
|
+
if (condition)
|
|
684
|
+
line += ` -n '${condition}'`;
|
|
685
|
+
if (opt.short)
|
|
686
|
+
line += ` -s ${opt.short}`;
|
|
687
|
+
line += ` -l ${opt.long}`;
|
|
688
|
+
line += ` -d '${this.#escape(opt.description)}'`;
|
|
689
|
+
if (opt.choices && opt.choices.length > 0) {
|
|
690
|
+
line += ` -xa '${opt.choices.join(' ')}'`;
|
|
691
|
+
}
|
|
692
|
+
lines.push(line);
|
|
693
|
+
if (!opt.takesValue) {
|
|
694
|
+
let noLine = `complete -c ${this.#programName}`;
|
|
695
|
+
if (condition)
|
|
696
|
+
noLine += ` -n '${condition}'`;
|
|
697
|
+
noLine += ` -l no-${opt.long}`;
|
|
698
|
+
noLine += ` -d '${this.#escape(opt.description)}'`;
|
|
699
|
+
lines.push(noLine);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
for (const sub of cmd.subcommands) {
|
|
703
|
+
let line = `complete -c ${this.#programName}`;
|
|
704
|
+
if (isRoot) {
|
|
705
|
+
line += ' -n __fish_use_subcommand';
|
|
706
|
+
}
|
|
707
|
+
else if (condition) {
|
|
708
|
+
line += ` -n '${condition}; and not __fish_seen_subcommand_from ${this.#getSubcommandNames(cmd).join(' ')}'`;
|
|
709
|
+
}
|
|
710
|
+
line += ` -a ${sub.name}`;
|
|
711
|
+
line += ` -d '${this.#escape(sub.description)}'`;
|
|
712
|
+
lines.push(line);
|
|
713
|
+
for (const alias of sub.aliases) {
|
|
714
|
+
let aliasLine = `complete -c ${this.#programName}`;
|
|
715
|
+
if (isRoot) {
|
|
716
|
+
aliasLine += ' -n __fish_use_subcommand';
|
|
717
|
+
}
|
|
718
|
+
else if (condition) {
|
|
719
|
+
aliasLine += ` -n '${condition}; and not __fish_seen_subcommand_from ${this.#getSubcommandNames(cmd).join(' ')}'`;
|
|
720
|
+
}
|
|
721
|
+
aliasLine += ` -a ${alias}`;
|
|
722
|
+
aliasLine += ` -d 'Alias for ${sub.name}'`;
|
|
723
|
+
lines.push(aliasLine);
|
|
724
|
+
}
|
|
725
|
+
const newPath = [...parentPath, sub.name];
|
|
726
|
+
lines.push(...this.#generateCommandCompletions(sub, newPath));
|
|
727
|
+
}
|
|
728
|
+
return lines;
|
|
729
|
+
}
|
|
730
|
+
#buildCondition(path) {
|
|
731
|
+
if (path.length === 0)
|
|
732
|
+
return '';
|
|
733
|
+
return `__fish_seen_subcommand_from ${path[path.length - 1]}`;
|
|
734
|
+
}
|
|
735
|
+
#getSubcommandNames(cmd) {
|
|
736
|
+
return cmd.subcommands.flatMap(sub => [sub.name, ...sub.aliases]);
|
|
737
|
+
}
|
|
738
|
+
#escape(s) {
|
|
739
|
+
return s.replace(/'/g, "\\'");
|
|
740
|
+
}
|
|
132
741
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
742
|
+
class PwshCompletion {
|
|
743
|
+
#meta;
|
|
744
|
+
#programName;
|
|
745
|
+
constructor(meta, programName) {
|
|
746
|
+
this.#meta = meta;
|
|
747
|
+
this.#programName = programName;
|
|
748
|
+
}
|
|
749
|
+
generate() {
|
|
750
|
+
const lines = [
|
|
751
|
+
`# PowerShell completion for ${this.#programName}`,
|
|
752
|
+
'# Generated by @guanghechen/commander',
|
|
753
|
+
'',
|
|
754
|
+
`Register-ArgumentCompleter -Native -CommandName ${this.#programName} -ScriptBlock {`,
|
|
755
|
+
' param($wordToComplete, $commandAst, $cursorPosition)',
|
|
756
|
+
'',
|
|
757
|
+
' $commands = @{',
|
|
758
|
+
this.#generateCommandHash(this.#meta, ' '),
|
|
759
|
+
' }',
|
|
760
|
+
'',
|
|
761
|
+
' $words = $commandAst.CommandElements | ForEach-Object { $_.ToString() }',
|
|
762
|
+
' $current = $wordToComplete',
|
|
763
|
+
'',
|
|
764
|
+
' # Find current command context',
|
|
765
|
+
' $cmd = $commands',
|
|
766
|
+
' foreach ($word in $words[1..($words.Count - 1)]) {',
|
|
767
|
+
' if ($word.StartsWith("-")) { continue }',
|
|
768
|
+
' if ($cmd.subcommands -and $cmd.subcommands.ContainsKey($word)) {',
|
|
769
|
+
' $cmd = $cmd.subcommands[$word]',
|
|
770
|
+
' }',
|
|
771
|
+
' }',
|
|
772
|
+
'',
|
|
773
|
+
' # Generate completions',
|
|
774
|
+
' $completions = @()',
|
|
775
|
+
'',
|
|
776
|
+
' # Options',
|
|
777
|
+
' if ($current.StartsWith("-")) {',
|
|
778
|
+
' foreach ($opt in $cmd.options) {',
|
|
779
|
+
' if ("--$($opt.long)" -like "$current*") {',
|
|
780
|
+
' $completions += [System.Management.Automation.CompletionResult]::new(',
|
|
781
|
+
' "--$($opt.long)",',
|
|
782
|
+
' $opt.long,',
|
|
783
|
+
' "ParameterName",',
|
|
784
|
+
' $opt.description',
|
|
785
|
+
' )',
|
|
786
|
+
' }',
|
|
787
|
+
' if ($opt.isBoolean -and "--no-$($opt.long)" -like "$current*") {',
|
|
788
|
+
' $completions += [System.Management.Automation.CompletionResult]::new(',
|
|
789
|
+
' "--no-$($opt.long)",',
|
|
790
|
+
' "no-$($opt.long)",',
|
|
791
|
+
' "ParameterName",',
|
|
792
|
+
' $opt.description',
|
|
793
|
+
' )',
|
|
794
|
+
' }',
|
|
795
|
+
' if ($opt.short -and "-$($opt.short)" -like "$current*") {',
|
|
796
|
+
' $completions += [System.Management.Automation.CompletionResult]::new(',
|
|
797
|
+
' "-$($opt.short)",',
|
|
798
|
+
' $opt.short,',
|
|
799
|
+
' "ParameterName",',
|
|
800
|
+
' $opt.description',
|
|
801
|
+
' )',
|
|
802
|
+
' }',
|
|
803
|
+
' }',
|
|
804
|
+
' }',
|
|
805
|
+
'',
|
|
806
|
+
' # Subcommands',
|
|
807
|
+
' if ($cmd.subcommands) {',
|
|
808
|
+
' foreach ($sub in $cmd.subcommands.Keys) {',
|
|
809
|
+
' if ($sub -like "$current*") {',
|
|
810
|
+
' $completions += [System.Management.Automation.CompletionResult]::new(',
|
|
811
|
+
' $sub,',
|
|
812
|
+
' $sub,',
|
|
813
|
+
' "Command",',
|
|
814
|
+
' $cmd.subcommands[$sub].description',
|
|
815
|
+
' )',
|
|
816
|
+
' }',
|
|
817
|
+
' }',
|
|
818
|
+
' }',
|
|
819
|
+
'',
|
|
820
|
+
' return $completions',
|
|
821
|
+
'}',
|
|
822
|
+
'',
|
|
823
|
+
];
|
|
824
|
+
return lines.join('\n');
|
|
825
|
+
}
|
|
826
|
+
#generateCommandHash(cmd, indent) {
|
|
827
|
+
const lines = [];
|
|
828
|
+
lines.push(`${indent}description = '${this.#escape(cmd.description)}'`);
|
|
829
|
+
lines.push(`${indent}options = @(`);
|
|
830
|
+
for (const opt of cmd.options) {
|
|
831
|
+
lines.push(`${indent} @{`);
|
|
832
|
+
if (opt.short)
|
|
833
|
+
lines.push(`${indent} short = '${opt.short}'`);
|
|
834
|
+
lines.push(`${indent} long = '${opt.long}'`);
|
|
835
|
+
lines.push(`${indent} description = '${this.#escape(opt.description)}'`);
|
|
836
|
+
lines.push(`${indent} isBoolean = $${!opt.takesValue}`);
|
|
837
|
+
if (opt.choices) {
|
|
838
|
+
lines.push(`${indent} choices = @('${opt.choices.join("', '")}')`);
|
|
839
|
+
}
|
|
840
|
+
lines.push(`${indent} }`);
|
|
841
|
+
}
|
|
842
|
+
lines.push(`${indent})`);
|
|
843
|
+
if (cmd.subcommands.length > 0) {
|
|
844
|
+
lines.push(`${indent}subcommands = @{`);
|
|
845
|
+
for (const sub of cmd.subcommands) {
|
|
846
|
+
lines.push(`${indent} '${sub.name}' = @{`);
|
|
847
|
+
lines.push(this.#generateCommandHash(sub, `${indent} `));
|
|
848
|
+
lines.push(`${indent} }`);
|
|
849
|
+
for (const alias of sub.aliases) {
|
|
850
|
+
lines.push(`${indent} '${alias}' = @{`);
|
|
851
|
+
lines.push(this.#generateCommandHash(sub, `${indent} `));
|
|
852
|
+
lines.push(`${indent} }`);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
lines.push(`${indent}}`);
|
|
856
|
+
}
|
|
857
|
+
return lines.join('\n');
|
|
858
|
+
}
|
|
859
|
+
#escape(s) {
|
|
860
|
+
return s.replace(/'/g, "''");
|
|
153
861
|
}
|
|
154
|
-
reporter?.debug?.('npmScript:', npmScript);
|
|
155
|
-
if (npmScript === 'skip')
|
|
156
|
-
return;
|
|
157
|
-
await exec.safeExec({ from: 'installDependencies', cmd: npmScript, args: ['install'], cwd, reporter });
|
|
158
862
|
}
|
|
159
863
|
|
|
864
|
+
exports.BashCompletion = BashCompletion;
|
|
160
865
|
exports.Command = Command;
|
|
161
|
-
exports.
|
|
162
|
-
exports.
|
|
163
|
-
exports.
|
|
164
|
-
exports.
|
|
165
|
-
exports.hasGitInstalled = hasGitInstalled;
|
|
166
|
-
exports.installDependencies = installDependencies;
|
|
167
|
-
Object.keys(cli).forEach(function (k) {
|
|
168
|
-
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
169
|
-
enumerable: true,
|
|
170
|
-
get: function () { return cli[k]; }
|
|
171
|
-
});
|
|
172
|
-
});
|
|
866
|
+
exports.CommanderError = CommanderError;
|
|
867
|
+
exports.CompletionCommand = CompletionCommand;
|
|
868
|
+
exports.FishCompletion = FishCompletion;
|
|
869
|
+
exports.PwshCompletion = PwshCompletion;
|