politty 0.0.1 → 0.1.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/README.md +297 -28
- package/dist/arg-registry-ClI2WGgH.d.cts +89 -0
- package/dist/arg-registry-ClI2WGgH.d.cts.map +1 -0
- package/dist/arg-registry-D4NsqcNZ.d.ts +89 -0
- package/dist/arg-registry-D4NsqcNZ.d.ts.map +1 -0
- package/dist/augment.cjs +0 -0
- package/dist/augment.d.cts +17 -0
- package/dist/augment.d.cts.map +1 -0
- package/dist/augment.d.ts +17 -0
- package/dist/augment.d.ts.map +1 -0
- package/dist/augment.js +1 -0
- package/dist/command-Bgd-yIwv.cjs +25 -0
- package/dist/command-Bgd-yIwv.cjs.map +1 -0
- package/dist/command-CvKyk4ag.js +20 -0
- package/dist/command-CvKyk4ag.js.map +1 -0
- package/dist/completion/index.cjs +595 -0
- package/dist/completion/index.cjs.map +1 -0
- package/dist/completion/index.d.cts +153 -0
- package/dist/completion/index.d.cts.map +1 -0
- package/dist/completion/index.d.ts +153 -0
- package/dist/completion/index.d.ts.map +1 -0
- package/dist/completion/index.js +588 -0
- package/dist/completion/index.js.map +1 -0
- package/dist/docs/index.cjs +1239 -0
- package/dist/docs/index.cjs.map +1 -0
- package/dist/docs/index.d.cts +500 -0
- package/dist/docs/index.d.cts.map +1 -0
- package/dist/docs/index.d.ts +500 -0
- package/dist/docs/index.d.ts.map +1 -0
- package/dist/docs/index.js +1182 -0
- package/dist/docs/index.js.map +1 -0
- package/dist/index.cjs +29 -0
- package/dist/index.d.cts +478 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +478 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/runner-BZuYiRhi.cjs +1492 -0
- package/dist/runner-BZuYiRhi.cjs.map +1 -0
- package/dist/runner-D2BXiWtg.cjs +4 -0
- package/dist/runner-DceWXOwD.js +1372 -0
- package/dist/runner-DceWXOwD.js.map +1 -0
- package/dist/runner-KCql2UKz.js +4 -0
- package/dist/schema-extractor-B9D3Rf22.cjs +354 -0
- package/dist/schema-extractor-B9D3Rf22.cjs.map +1 -0
- package/dist/schema-extractor-D-Eo7I77.d.cts +303 -0
- package/dist/schema-extractor-D-Eo7I77.d.cts.map +1 -0
- package/dist/schema-extractor-Dk5Z0Iei.js +324 -0
- package/dist/schema-extractor-Dk5Z0Iei.js.map +1 -0
- package/dist/schema-extractor-kkajLb9E.d.ts +303 -0
- package/dist/schema-extractor-kkajLb9E.d.ts.map +1 -0
- package/dist/subcommand-router-BiSvDXHg.js +153 -0
- package/dist/subcommand-router-BiSvDXHg.js.map +1 -0
- package/dist/subcommand-router-Vf-0w9P4.cjs +189 -0
- package/dist/subcommand-router-Vf-0w9P4.cjs.map +1 -0
- package/package.json +108 -6
|
@@ -0,0 +1,1492 @@
|
|
|
1
|
+
const require_schema_extractor = require('./schema-extractor-B9D3Rf22.cjs');
|
|
2
|
+
const require_subcommand_router = require('./subcommand-router-Vf-0w9P4.cjs');
|
|
3
|
+
const require_docs_index = require('./docs/index.cjs');
|
|
4
|
+
let zod = require("zod");
|
|
5
|
+
let node_util = require("node:util");
|
|
6
|
+
|
|
7
|
+
//#region src/executor/command-runner.ts
|
|
8
|
+
/**
|
|
9
|
+
* Execute a command lifecycle: setup → run → cleanup
|
|
10
|
+
*
|
|
11
|
+
* This is an internal function that executes the command's lifecycle hooks.
|
|
12
|
+
* For running commands with argument parsing, use `runCommand` instead.
|
|
13
|
+
*
|
|
14
|
+
* @param command - The command to execute
|
|
15
|
+
* @param args - Already validated arguments
|
|
16
|
+
* @param options - Lifecycle options
|
|
17
|
+
* @returns The result of command execution
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
async function executeLifecycle(command, args, _options = {}) {
|
|
21
|
+
let error;
|
|
22
|
+
let result;
|
|
23
|
+
const collector = _options.captureLogs ?? false ? require_subcommand_router.createLogCollector() : null;
|
|
24
|
+
collector?.start();
|
|
25
|
+
const setupContext = { args };
|
|
26
|
+
const cleanupContext = {
|
|
27
|
+
args,
|
|
28
|
+
error
|
|
29
|
+
};
|
|
30
|
+
let signalHandler;
|
|
31
|
+
if (_options.handleSignals) {
|
|
32
|
+
signalHandler = async (_signal) => {
|
|
33
|
+
if (signalHandler) {
|
|
34
|
+
process.off("SIGINT", signalHandler);
|
|
35
|
+
process.off("SIGTERM", signalHandler);
|
|
36
|
+
}
|
|
37
|
+
if (command.cleanup) try {
|
|
38
|
+
await command.cleanup(cleanupContext);
|
|
39
|
+
} catch (e) {
|
|
40
|
+
console.error("Error during signal cleanup:", e);
|
|
41
|
+
}
|
|
42
|
+
collector?.stop();
|
|
43
|
+
process.exit(1);
|
|
44
|
+
};
|
|
45
|
+
process.on("SIGINT", signalHandler);
|
|
46
|
+
process.on("SIGTERM", signalHandler);
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
if (command.setup) await command.setup(setupContext);
|
|
50
|
+
if (command.run) result = await command.run(args);
|
|
51
|
+
} catch (e) {
|
|
52
|
+
error = e instanceof Error ? e : new Error(String(e));
|
|
53
|
+
} finally {
|
|
54
|
+
if (signalHandler) {
|
|
55
|
+
process.off("SIGINT", signalHandler);
|
|
56
|
+
process.off("SIGTERM", signalHandler);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (command.cleanup) {
|
|
60
|
+
cleanupContext.error = error;
|
|
61
|
+
try {
|
|
62
|
+
await command.cleanup(cleanupContext);
|
|
63
|
+
} catch (cleanupError) {
|
|
64
|
+
if (!error) error = cleanupError instanceof Error ? cleanupError : new Error(String(cleanupError));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
collector?.stop();
|
|
68
|
+
const logs = require_subcommand_router.mergeLogs(_options.existingLogs ?? require_subcommand_router.emptyLogs(), collector?.getLogs() ?? require_subcommand_router.emptyLogs());
|
|
69
|
+
if (error) return {
|
|
70
|
+
success: false,
|
|
71
|
+
error,
|
|
72
|
+
exitCode: 1,
|
|
73
|
+
logs
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
success: true,
|
|
77
|
+
result,
|
|
78
|
+
exitCode: 0,
|
|
79
|
+
logs
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/output/logger.ts
|
|
85
|
+
/**
|
|
86
|
+
* Check if color output should be disabled
|
|
87
|
+
*/
|
|
88
|
+
function shouldDisableColor() {
|
|
89
|
+
if (process.env.NO_COLOR !== void 0) return true;
|
|
90
|
+
if (process.env.FORCE_COLOR === "0") return true;
|
|
91
|
+
if (process.env.FORCE_COLOR) return false;
|
|
92
|
+
if (process.env.CI) return true;
|
|
93
|
+
if (!process.stdout.isTTY) return true;
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Global flag to control color output
|
|
98
|
+
*/
|
|
99
|
+
let colorDisabled = shouldDisableColor();
|
|
100
|
+
/**
|
|
101
|
+
* Enable or disable color output programmatically
|
|
102
|
+
*/
|
|
103
|
+
function setColorEnabled(enabled) {
|
|
104
|
+
colorDisabled = !enabled;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Check if color output is currently enabled
|
|
108
|
+
*/
|
|
109
|
+
function isColorEnabled() {
|
|
110
|
+
return !colorDisabled;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Create a style function that applies the given styles
|
|
114
|
+
*/
|
|
115
|
+
function createStyleFn(...styleArgs) {
|
|
116
|
+
return (text) => {
|
|
117
|
+
if (colorDisabled) return text;
|
|
118
|
+
let result = text;
|
|
119
|
+
for (const style of styleArgs) result = (0, node_util.styleText)(style, result);
|
|
120
|
+
return result;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Semantic style functions for inline text styling
|
|
125
|
+
*/
|
|
126
|
+
const styles = {
|
|
127
|
+
success: createStyleFn("green"),
|
|
128
|
+
error: createStyleFn("red"),
|
|
129
|
+
warning: createStyleFn("yellow"),
|
|
130
|
+
info: createStyleFn("cyan"),
|
|
131
|
+
bold: createStyleFn("bold"),
|
|
132
|
+
dim: createStyleFn("dim"),
|
|
133
|
+
italic: createStyleFn("italic"),
|
|
134
|
+
underline: createStyleFn("underline"),
|
|
135
|
+
red: createStyleFn("red"),
|
|
136
|
+
green: createStyleFn("green"),
|
|
137
|
+
yellow: createStyleFn("yellow"),
|
|
138
|
+
blue: createStyleFn("blue"),
|
|
139
|
+
magenta: createStyleFn("magenta"),
|
|
140
|
+
cyan: createStyleFn("cyan"),
|
|
141
|
+
white: createStyleFn("white"),
|
|
142
|
+
gray: createStyleFn("gray"),
|
|
143
|
+
command: createStyleFn("bold"),
|
|
144
|
+
commandName: createStyleFn("bold", "cyan"),
|
|
145
|
+
option: createStyleFn("cyan"),
|
|
146
|
+
optionName: createStyleFn("bold"),
|
|
147
|
+
placeholder: createStyleFn("dim"),
|
|
148
|
+
defaultValue: createStyleFn("dim"),
|
|
149
|
+
required: createStyleFn("yellow"),
|
|
150
|
+
description: (text) => text,
|
|
151
|
+
sectionHeader: createStyleFn("bold", "underline"),
|
|
152
|
+
version: createStyleFn("dim")
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Standardized symbols for CLI output
|
|
156
|
+
*/
|
|
157
|
+
const symbols = {
|
|
158
|
+
success: styles.green("✓"),
|
|
159
|
+
error: styles.red("✖"),
|
|
160
|
+
warning: styles.yellow("⚠"),
|
|
161
|
+
info: styles.cyan("ℹ"),
|
|
162
|
+
bullet: styles.gray("•"),
|
|
163
|
+
arrow: styles.gray("→")
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Logger for CLI output
|
|
167
|
+
*/
|
|
168
|
+
const logger = {
|
|
169
|
+
info(message) {
|
|
170
|
+
console.log(message);
|
|
171
|
+
},
|
|
172
|
+
success(message) {
|
|
173
|
+
console.log(`${symbols.success} ${styles.success(message)}`);
|
|
174
|
+
},
|
|
175
|
+
warn(message) {
|
|
176
|
+
console.warn(`${symbols.warning} ${styles.warning(message)}`);
|
|
177
|
+
},
|
|
178
|
+
error(message) {
|
|
179
|
+
console.error(`${symbols.error} ${styles.error(message)}`);
|
|
180
|
+
},
|
|
181
|
+
log(message) {
|
|
182
|
+
console.log(message);
|
|
183
|
+
},
|
|
184
|
+
newline() {
|
|
185
|
+
console.log("");
|
|
186
|
+
},
|
|
187
|
+
debug(message) {
|
|
188
|
+
console.log(styles.dim(message));
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
//#endregion
|
|
193
|
+
//#region src/output/help-generator.ts
|
|
194
|
+
/**
|
|
195
|
+
* Default descriptions for built-in options
|
|
196
|
+
*/
|
|
197
|
+
const defaultBuiltinDescriptions = {
|
|
198
|
+
help: "Show help",
|
|
199
|
+
helpAll: "Show help with all subcommand options",
|
|
200
|
+
version: "Show version"
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* Build full command name from context
|
|
204
|
+
*/
|
|
205
|
+
function buildFullCommandName(command, context) {
|
|
206
|
+
if (context?.rootName && context.commandPath && context.commandPath.length > 0) return context.commandPath.join(" ");
|
|
207
|
+
return command.name ?? "command";
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Build usage command name (includes root name for subcommands)
|
|
211
|
+
*/
|
|
212
|
+
function buildUsageCommandName(command, context) {
|
|
213
|
+
if (context?.rootName && context.commandPath && context.commandPath.length > 0) return `${context.rootName} ${context.commandPath.join(" ")}`;
|
|
214
|
+
return command.name ?? "command";
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Render the usage line for a command
|
|
218
|
+
*/
|
|
219
|
+
function renderUsageLine(command, context) {
|
|
220
|
+
const parts = [];
|
|
221
|
+
const name = buildUsageCommandName(command, context);
|
|
222
|
+
parts.push(styles.commandName(name));
|
|
223
|
+
const extracted = require_schema_extractor.getExtractedFields(command);
|
|
224
|
+
if (extracted) {
|
|
225
|
+
const positionals = extracted.fields.filter((a) => a.positional);
|
|
226
|
+
if (extracted.fields.filter((a) => !a.positional).length > 0) parts.push(styles.placeholder("[options]"));
|
|
227
|
+
if (command.subCommands && Object.keys(command.subCommands).length > 0) parts.push(styles.placeholder("[command]"));
|
|
228
|
+
for (const arg of positionals) if (arg.required) parts.push(styles.option(`<${arg.name}>`));
|
|
229
|
+
else parts.push(styles.placeholder(`[${arg.name}]`));
|
|
230
|
+
} else if (command.subCommands && Object.keys(command.subCommands).length > 0) parts.push(styles.placeholder("[command]"));
|
|
231
|
+
return parts.join(" ");
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Render the options section
|
|
235
|
+
*/
|
|
236
|
+
function renderOptions(command, descriptions = {}, context) {
|
|
237
|
+
const lines = [];
|
|
238
|
+
const desc = {
|
|
239
|
+
help: descriptions.help ?? defaultBuiltinDescriptions.help,
|
|
240
|
+
helpAll: descriptions.helpAll ?? defaultBuiltinDescriptions.helpAll,
|
|
241
|
+
version: descriptions.version ?? defaultBuiltinDescriptions.version
|
|
242
|
+
};
|
|
243
|
+
const extracted = require_schema_extractor.getExtractedFields(command);
|
|
244
|
+
const hasUserDefinedh = extracted?.fields.some((f) => f.alias === "h" && f.overrideBuiltinAlias === true) ?? false;
|
|
245
|
+
const hasUserDefinedH = extracted?.fields.some((f) => f.alias === "H" && f.overrideBuiltinAlias === true) ?? false;
|
|
246
|
+
if (hasUserDefinedh) lines.push(formatOption(styles.option("--help"), desc.help));
|
|
247
|
+
else lines.push(formatOption(`${styles.option("-h")}, ${styles.option("--help")}`, desc.help));
|
|
248
|
+
if (hasUserDefinedH) lines.push(formatOption(styles.option("--help-all"), desc.helpAll));
|
|
249
|
+
else lines.push(formatOption(`${styles.option("-H")}, ${styles.option("--help-all")}`, desc.helpAll));
|
|
250
|
+
if (context?.rootVersion) lines.push(formatOption(styles.option("--version"), desc.version));
|
|
251
|
+
if (!extracted) return lines.join("\n");
|
|
252
|
+
if (extracted.schemaType === "discriminatedUnion" && extracted.discriminator) return renderDiscriminatedUnionOptions(extracted, command, lines);
|
|
253
|
+
if (extracted.schemaType === "union" && extracted.unionOptions) return renderUnionOptions(extracted, command, lines);
|
|
254
|
+
if (extracted.schemaType === "xor" && extracted.unionOptions) return renderUnionOptions(extracted, command, lines);
|
|
255
|
+
const options = extracted.fields.filter((a) => !a.positional);
|
|
256
|
+
for (const opt of options) {
|
|
257
|
+
const flags = formatFlags(opt);
|
|
258
|
+
let desc$1 = opt.description ?? "";
|
|
259
|
+
if (opt.defaultValue !== void 0) desc$1 += ` ${styles.defaultValue(`(default: ${JSON.stringify(opt.defaultValue)})`)}`;
|
|
260
|
+
if (opt.required) desc$1 += ` ${styles.required("(required)")}`;
|
|
261
|
+
const envInfo = formatEnvInfo(opt.env);
|
|
262
|
+
if (envInfo) desc$1 += ` ${envInfo}`;
|
|
263
|
+
lines.push(formatOption(flags, desc$1));
|
|
264
|
+
}
|
|
265
|
+
return lines.join("\n");
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Render options for discriminated union with variants
|
|
269
|
+
*/
|
|
270
|
+
function renderDiscriminatedUnionOptions(extracted, _command, lines) {
|
|
271
|
+
const discriminator = extracted.discriminator;
|
|
272
|
+
const variants = extracted.variants ?? [];
|
|
273
|
+
const discriminatorField = extracted.fields.find((f) => f.name === discriminator);
|
|
274
|
+
if (discriminatorField) {
|
|
275
|
+
const variantValues = variants.map((v) => v.discriminatorValue).join("|");
|
|
276
|
+
const flags = `${styles.option(`--${discriminator}`)} ${styles.placeholder(`<${variantValues}>`)}`;
|
|
277
|
+
const description = extracted.description ?? discriminatorField.description ?? "Action to perform";
|
|
278
|
+
lines.push(formatOption(flags, description));
|
|
279
|
+
}
|
|
280
|
+
const commonFields = /* @__PURE__ */ new Set();
|
|
281
|
+
const allFieldNames = /* @__PURE__ */ new Set();
|
|
282
|
+
for (const variant of variants) for (const field of variant.fields) allFieldNames.add(field.name);
|
|
283
|
+
for (const fieldName of allFieldNames) {
|
|
284
|
+
if (fieldName === discriminator) continue;
|
|
285
|
+
if (variants.every((v) => v.fields.some((f) => f.name === fieldName))) commonFields.add(fieldName);
|
|
286
|
+
}
|
|
287
|
+
for (const fieldName of commonFields) {
|
|
288
|
+
const field = extracted.fields.find((f) => f.name === fieldName);
|
|
289
|
+
if (field && !field.positional) {
|
|
290
|
+
const flags = formatFlags(field);
|
|
291
|
+
let desc = field.description ?? "";
|
|
292
|
+
if (field.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(field.defaultValue)})`)}`;
|
|
293
|
+
const envInfo = formatEnvInfo(field.env);
|
|
294
|
+
if (envInfo) desc += ` ${envInfo}`;
|
|
295
|
+
lines.push(formatOption(flags, desc));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
for (const variant of variants) {
|
|
299
|
+
const variantFields = variant.fields.filter((f) => f.name !== discriminator && !commonFields.has(f.name) && !f.positional);
|
|
300
|
+
if (variantFields.length > 0) {
|
|
301
|
+
lines.push("");
|
|
302
|
+
const variantLabel = variant.description ? `${styles.dim("When")} ${styles.option(discriminator)}=${styles.bold(variant.discriminatorValue)}: ${variant.description}` : `${styles.dim("When")} ${styles.option(discriminator)}=${styles.bold(variant.discriminatorValue)}:`;
|
|
303
|
+
lines.push(variantLabel);
|
|
304
|
+
for (const field of variantFields) {
|
|
305
|
+
const flags = formatFlags(field);
|
|
306
|
+
let desc = field.description ?? "";
|
|
307
|
+
if (field.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(field.defaultValue)})`)}`;
|
|
308
|
+
if (field.required) desc += ` ${styles.required("(required)")}`;
|
|
309
|
+
const envInfo = formatEnvInfo(field.env);
|
|
310
|
+
if (envInfo) desc += ` ${envInfo}`;
|
|
311
|
+
lines.push(formatOption(` ${flags}`, desc));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return lines.join("\n");
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Render options for union with multiple options
|
|
319
|
+
*/
|
|
320
|
+
function renderUnionOptions(extracted, _command, lines) {
|
|
321
|
+
const unionOptions = extracted.unionOptions ?? [];
|
|
322
|
+
const commonFields = /* @__PURE__ */ new Set();
|
|
323
|
+
const allFieldNames = /* @__PURE__ */ new Set();
|
|
324
|
+
for (const option of unionOptions) for (const field of option.fields) allFieldNames.add(field.name);
|
|
325
|
+
for (const fieldName of allFieldNames) if (unionOptions.every((o) => o.fields.some((f) => f.name === fieldName))) commonFields.add(fieldName);
|
|
326
|
+
for (const fieldName of commonFields) {
|
|
327
|
+
const field = extracted.fields.find((f) => f.name === fieldName);
|
|
328
|
+
if (field && !field.positional) {
|
|
329
|
+
const flags = formatFlags(field);
|
|
330
|
+
let desc = field.description ?? "";
|
|
331
|
+
if (field.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(field.defaultValue)})`)}`;
|
|
332
|
+
const envInfo = formatEnvInfo(field.env);
|
|
333
|
+
if (envInfo) desc += ` ${envInfo}`;
|
|
334
|
+
lines.push(formatOption(flags, desc));
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
for (let i = 0; i < unionOptions.length; i++) {
|
|
338
|
+
const option = unionOptions[i];
|
|
339
|
+
if (!option) continue;
|
|
340
|
+
const uniqueFields = option.fields.filter((f) => !commonFields.has(f.name) && !f.positional);
|
|
341
|
+
if (uniqueFields.length > 0) {
|
|
342
|
+
lines.push("");
|
|
343
|
+
const label = option.description ?? `Variant ${i + 1}`;
|
|
344
|
+
lines.push(` ${styles.bold(`${label}:`)}`);
|
|
345
|
+
for (const field of uniqueFields) {
|
|
346
|
+
const flags = formatFlags(field);
|
|
347
|
+
let desc = field.description ?? "";
|
|
348
|
+
if (field.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(field.defaultValue)})`)}`;
|
|
349
|
+
if (field.required) desc += ` ${styles.required("(required)")}`;
|
|
350
|
+
const envInfo = formatEnvInfo(field.env);
|
|
351
|
+
if (envInfo) desc += ` ${envInfo}`;
|
|
352
|
+
lines.push(formatOption(` ${flags}`, desc));
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return lines.join("\n");
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Format option flags (-v, --verbose <VALUE>)
|
|
360
|
+
* Uses cliName (kebab-case) for display
|
|
361
|
+
*/
|
|
362
|
+
function formatFlags(opt) {
|
|
363
|
+
const parts = [];
|
|
364
|
+
if (opt.alias) parts.push(styles.option(`-${opt.alias}`));
|
|
365
|
+
let longFlag = styles.option(`--${opt.cliName}`);
|
|
366
|
+
if (opt.type !== "boolean") {
|
|
367
|
+
const placeholder = opt.placeholder ?? opt.cliName.toUpperCase();
|
|
368
|
+
longFlag += ` ${styles.placeholder(`<${placeholder}>`)}`;
|
|
369
|
+
}
|
|
370
|
+
parts.push(longFlag);
|
|
371
|
+
return parts.join(", ");
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Format environment variable info for help display
|
|
375
|
+
*/
|
|
376
|
+
function formatEnvInfo(env) {
|
|
377
|
+
if (!env) return "";
|
|
378
|
+
const envNames = Array.isArray(env) ? env : [env];
|
|
379
|
+
return styles.dim(`[env: ${envNames.join(", ")}]`);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Strip ANSI escape codes from a string to get visual length
|
|
383
|
+
*/
|
|
384
|
+
function stripAnsi(str) {
|
|
385
|
+
return str.replace(/\x1B\[[0-9;]*m/g, "");
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Pad a string that may contain ANSI codes to a visual width
|
|
389
|
+
*/
|
|
390
|
+
function padEndVisual(str, width) {
|
|
391
|
+
const visualLength = stripAnsi(str).length;
|
|
392
|
+
const padding = Math.max(0, width - visualLength);
|
|
393
|
+
return str + " ".repeat(padding);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Format a single option line
|
|
397
|
+
* If flags exceed the column width, description is moved to the next line
|
|
398
|
+
*/
|
|
399
|
+
function formatOption(flags, description, indent = 0, extraDescPadding = 0) {
|
|
400
|
+
const flagWidth = 32;
|
|
401
|
+
const indentStr = " ".repeat(indent);
|
|
402
|
+
const visualFlagLength = stripAnsi(flags).length;
|
|
403
|
+
const effectiveFlagWidth = flagWidth - indent * 2 + extraDescPadding;
|
|
404
|
+
if (visualFlagLength >= effectiveFlagWidth) return `${indentStr} ${flags}\n${" ".repeat(effectiveFlagWidth + 2 + indent * 2)}${description}`;
|
|
405
|
+
return `${indentStr} ${padEndVisual(flags, effectiveFlagWidth)}${description}`;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Render options for a subcommand (used by showSubcommandOptions)
|
|
409
|
+
*/
|
|
410
|
+
function renderSubcommandOptionsCompact(command, indent) {
|
|
411
|
+
const lines = [];
|
|
412
|
+
const extracted = require_schema_extractor.getExtractedFields(command);
|
|
413
|
+
if (extracted) {
|
|
414
|
+
const options = extracted.fields.filter((a) => !a.positional);
|
|
415
|
+
for (const opt of options) {
|
|
416
|
+
const flags = formatFlags(opt);
|
|
417
|
+
let desc = opt.description ?? "";
|
|
418
|
+
if (opt.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(opt.defaultValue)})`)}`;
|
|
419
|
+
const envInfo = formatEnvInfo(opt.env);
|
|
420
|
+
if (envInfo) desc += ` ${envInfo}`;
|
|
421
|
+
lines.push(formatOption(flags, desc, indent, 2));
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return lines;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Render subcommands recursively with their options (flat style)
|
|
428
|
+
*/
|
|
429
|
+
function renderSubcommandsWithOptions(subCommands, parentPath, baseIndent) {
|
|
430
|
+
const lines = [];
|
|
431
|
+
for (const [name, subCmd] of Object.entries(subCommands)) {
|
|
432
|
+
const cmd = typeof subCmd === "function" ? null : subCmd;
|
|
433
|
+
const fullPath = parentPath ? `${parentPath} ${name}` : name;
|
|
434
|
+
const desc = cmd?.description ?? "";
|
|
435
|
+
lines.push(formatOption(styles.command(fullPath), desc, baseIndent));
|
|
436
|
+
if (cmd) {
|
|
437
|
+
const optionLines = renderSubcommandOptionsCompact(cmd, baseIndent + 1);
|
|
438
|
+
lines.push(...optionLines);
|
|
439
|
+
if (cmd.subCommands && Object.keys(cmd.subCommands).length > 0) {
|
|
440
|
+
const nestedLines = renderSubcommandsWithOptions(cmd.subCommands, fullPath, baseIndent);
|
|
441
|
+
lines.push(...nestedLines);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return lines;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Generate help text for a command
|
|
449
|
+
*
|
|
450
|
+
* @param command - The command to generate help for
|
|
451
|
+
* @param options - Help generation options
|
|
452
|
+
* @returns Formatted help text
|
|
453
|
+
*/
|
|
454
|
+
function generateHelp(command, options) {
|
|
455
|
+
const sections = [];
|
|
456
|
+
const context = options.context;
|
|
457
|
+
const headerLines = [];
|
|
458
|
+
const displayName = buildFullCommandName(command, context);
|
|
459
|
+
if (displayName) {
|
|
460
|
+
let header = styles.commandName(displayName);
|
|
461
|
+
if (context?.rootName && context.commandPath && context.commandPath.length > 0) if (context.rootVersion) header += ` ${styles.version(`(${context.rootName} v${context.rootVersion})`)}`;
|
|
462
|
+
else header += ` ${styles.version(`(${context.rootName})`)}`;
|
|
463
|
+
else if (context?.rootVersion) header += ` ${styles.version(`v${context.rootVersion}`)}`;
|
|
464
|
+
headerLines.push(header);
|
|
465
|
+
}
|
|
466
|
+
if (command.description) headerLines.push(command.description);
|
|
467
|
+
if (headerLines.length > 0) sections.push(headerLines.join("\n"));
|
|
468
|
+
sections.push(`${styles.sectionHeader("Usage:")} ${renderUsageLine(command, context)}`);
|
|
469
|
+
const optionsText = renderOptions(command, options.descriptions, context);
|
|
470
|
+
if (optionsText) sections.push(`${styles.sectionHeader("Options:")}\n${optionsText}`);
|
|
471
|
+
if (options.showSubcommands !== false && command.subCommands && Object.keys(command.subCommands).length > 0) {
|
|
472
|
+
const currentPath = context?.commandPath?.join(" ") ?? "";
|
|
473
|
+
if (options.showSubcommandOptions) {
|
|
474
|
+
const subLines = renderSubcommandsWithOptions(command.subCommands, currentPath, 0);
|
|
475
|
+
sections.push(`${styles.sectionHeader("Commands:")}\n${subLines.join("\n")}`);
|
|
476
|
+
} else {
|
|
477
|
+
const subLines = [];
|
|
478
|
+
for (const [name, subCmd] of Object.entries(command.subCommands)) {
|
|
479
|
+
const desc = (typeof subCmd === "function" ? { description: void 0 } : subCmd).description ?? "";
|
|
480
|
+
const fullName = currentPath ? `${currentPath} ${name}` : name;
|
|
481
|
+
subLines.push(formatOption(styles.command(fullName), desc));
|
|
482
|
+
}
|
|
483
|
+
sections.push(`${styles.sectionHeader("Commands:")}\n${subLines.join("\n")}`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
if (command.examples && command.examples.length > 0) {
|
|
487
|
+
const exampleLines = renderExamplesForHelp(command.examples, context);
|
|
488
|
+
sections.push(`${styles.sectionHeader("Examples:")}\n${exampleLines}`);
|
|
489
|
+
}
|
|
490
|
+
if (command.notes) sections.push(`${styles.sectionHeader("Notes:")}\n${command.notes}`);
|
|
491
|
+
return sections.join("\n\n");
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Render examples for CLI help output
|
|
495
|
+
*/
|
|
496
|
+
function renderExamplesForHelp(examples, context) {
|
|
497
|
+
const lines = [];
|
|
498
|
+
const cmdPrefix = context?.rootName ? `${context.rootName} ` : "";
|
|
499
|
+
const cmdPath = context?.commandPath?.join(" ") ?? "";
|
|
500
|
+
const fullPrefix = cmdPath ? `${cmdPrefix}${cmdPath} ` : cmdPrefix;
|
|
501
|
+
for (const example of examples) {
|
|
502
|
+
lines.push(` ${styles.dim(example.desc)}`);
|
|
503
|
+
lines.push(` ${styles.dim("$")} ${fullPrefix}${example.cmd}`);
|
|
504
|
+
if (example.output) for (const line of example.output.split("\n")) lines.push(` ${line}`);
|
|
505
|
+
lines.push("");
|
|
506
|
+
}
|
|
507
|
+
if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
508
|
+
return lines.join("\n");
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
//#endregion
|
|
512
|
+
//#region src/validator/validation-errors.ts
|
|
513
|
+
/**
|
|
514
|
+
* Error thrown when positional argument configuration is invalid
|
|
515
|
+
*/
|
|
516
|
+
var PositionalConfigError = class extends Error {
|
|
517
|
+
constructor(message) {
|
|
518
|
+
super(message);
|
|
519
|
+
this.name = "PositionalConfigError";
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
/**
|
|
523
|
+
* Error thrown when a reserved alias is used
|
|
524
|
+
*/
|
|
525
|
+
var ReservedAliasError = class extends Error {
|
|
526
|
+
constructor(message) {
|
|
527
|
+
super(message);
|
|
528
|
+
this.name = "ReservedAliasError";
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
/**
|
|
532
|
+
* Error thrown when duplicate field names are detected
|
|
533
|
+
*/
|
|
534
|
+
var DuplicateFieldError = class extends Error {
|
|
535
|
+
constructor(message) {
|
|
536
|
+
super(message);
|
|
537
|
+
this.name = "DuplicateFieldError";
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
/**
|
|
541
|
+
* Error thrown when duplicate aliases are detected
|
|
542
|
+
*/
|
|
543
|
+
var DuplicateAliasError = class extends Error {
|
|
544
|
+
constructor(message) {
|
|
545
|
+
super(message);
|
|
546
|
+
this.name = "DuplicateAliasError";
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
//#endregion
|
|
551
|
+
//#region src/validator/command-validator.ts
|
|
552
|
+
/**
|
|
553
|
+
* Check for duplicate field names
|
|
554
|
+
*/
|
|
555
|
+
function checkDuplicateFields(extracted, commandPath) {
|
|
556
|
+
const errors = [];
|
|
557
|
+
const seenNames = /* @__PURE__ */ new Map();
|
|
558
|
+
for (const field of extracted.fields) {
|
|
559
|
+
if (seenNames.has(field.name)) errors.push({
|
|
560
|
+
commandPath,
|
|
561
|
+
type: "duplicate_field",
|
|
562
|
+
message: `Duplicate field name "${field.name}" detected.`,
|
|
563
|
+
field: field.name
|
|
564
|
+
});
|
|
565
|
+
seenNames.set(field.name, field.name);
|
|
566
|
+
}
|
|
567
|
+
return errors;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Check for duplicate aliases and alias-field name conflicts
|
|
571
|
+
*/
|
|
572
|
+
function checkDuplicateAliases(extracted, commandPath) {
|
|
573
|
+
const errors = [];
|
|
574
|
+
const seenAliases = /* @__PURE__ */ new Map();
|
|
575
|
+
const fieldNames = new Set(extracted.fields.map((f) => f.name));
|
|
576
|
+
for (const field of extracted.fields) {
|
|
577
|
+
if (!field.alias) continue;
|
|
578
|
+
if (fieldNames.has(field.alias)) errors.push({
|
|
579
|
+
commandPath,
|
|
580
|
+
type: "duplicate_alias",
|
|
581
|
+
message: `Alias "${field.alias}" for field "${field.name}" conflicts with existing field name "${field.alias}".`,
|
|
582
|
+
field: field.name
|
|
583
|
+
});
|
|
584
|
+
const existingField = seenAliases.get(field.alias);
|
|
585
|
+
if (existingField) errors.push({
|
|
586
|
+
commandPath,
|
|
587
|
+
type: "duplicate_alias",
|
|
588
|
+
message: `Duplicate alias "${field.alias}" detected. Both "${existingField}" and "${field.name}" use the same alias.`,
|
|
589
|
+
field: field.name
|
|
590
|
+
});
|
|
591
|
+
seenAliases.set(field.alias, field.name);
|
|
592
|
+
}
|
|
593
|
+
return errors;
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Check positional argument configuration
|
|
597
|
+
*/
|
|
598
|
+
function checkPositionalConfig(extracted, commandPath) {
|
|
599
|
+
const errors = [];
|
|
600
|
+
const positionalFields = extracted.fields.filter((f) => f.positional);
|
|
601
|
+
let foundArrayPositional = null;
|
|
602
|
+
let foundOptionalPositional = null;
|
|
603
|
+
for (const field of positionalFields) {
|
|
604
|
+
if (foundArrayPositional !== null) errors.push({
|
|
605
|
+
commandPath,
|
|
606
|
+
type: "positional_config",
|
|
607
|
+
message: `Positional argument "${field.name}" cannot follow array positional argument "${foundArrayPositional}".`,
|
|
608
|
+
field: field.name
|
|
609
|
+
});
|
|
610
|
+
if (field.type === "array" && foundOptionalPositional !== null) errors.push({
|
|
611
|
+
commandPath,
|
|
612
|
+
type: "positional_config",
|
|
613
|
+
message: `Array positional "${field.name}" cannot be used with optional positional "${foundOptionalPositional}" (ambiguous parsing).`,
|
|
614
|
+
field: field.name
|
|
615
|
+
});
|
|
616
|
+
if (foundOptionalPositional !== null && field.required) errors.push({
|
|
617
|
+
commandPath,
|
|
618
|
+
type: "positional_config",
|
|
619
|
+
message: `Required positional "${field.name}" cannot follow optional positional "${foundOptionalPositional}".`,
|
|
620
|
+
field: field.name
|
|
621
|
+
});
|
|
622
|
+
if (field.type === "array") foundArrayPositional = field.name;
|
|
623
|
+
if (!field.required) foundOptionalPositional = field.name;
|
|
624
|
+
}
|
|
625
|
+
return errors;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Check for reserved aliases used without override flag
|
|
629
|
+
*/
|
|
630
|
+
function checkReservedAliases(extracted, commandPath) {
|
|
631
|
+
const errors = [];
|
|
632
|
+
for (const field of extracted.fields) if ((field.alias === "h" || field.alias === "H") && field.overrideBuiltinAlias !== true) errors.push({
|
|
633
|
+
commandPath,
|
|
634
|
+
type: "reserved_alias",
|
|
635
|
+
message: `Alias "${field.alias}" is reserved for --${field.alias === "h" ? "help" : "help-all"}.`,
|
|
636
|
+
field: field.name
|
|
637
|
+
});
|
|
638
|
+
return errors;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Validate that no duplicate field names exist
|
|
642
|
+
*
|
|
643
|
+
* @param extracted - Extracted fields from schema
|
|
644
|
+
* @throws {DuplicateFieldError} If duplicate field names are found
|
|
645
|
+
*/
|
|
646
|
+
function validateDuplicateFields(extracted) {
|
|
647
|
+
const errors = checkDuplicateFields(extracted, []);
|
|
648
|
+
if (errors.length > 0) throw new DuplicateFieldError(`Duplicate field name "${errors[0]?.field ?? "unknown"}" detected. Each field must have a unique name.`);
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Validate that no duplicate aliases exist
|
|
652
|
+
*
|
|
653
|
+
* Also checks for conflicts between aliases and field names
|
|
654
|
+
*
|
|
655
|
+
* @param extracted - Extracted fields from schema
|
|
656
|
+
* @throws {DuplicateAliasError} If duplicate aliases are found or alias conflicts with field name
|
|
657
|
+
*/
|
|
658
|
+
function validateDuplicateAliases(extracted) {
|
|
659
|
+
const errors = checkDuplicateAliases(extracted, []);
|
|
660
|
+
if (errors.length > 0) {
|
|
661
|
+
const err = errors[0];
|
|
662
|
+
throw new DuplicateAliasError(err.message);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Validate positional argument configuration
|
|
667
|
+
*
|
|
668
|
+
* Rules:
|
|
669
|
+
* - Array positional arguments must be the last positional
|
|
670
|
+
* - No positional arguments can follow an array positional
|
|
671
|
+
* - Required positional arguments cannot follow optional positional arguments
|
|
672
|
+
* - Array positional and optional positional cannot be used together (ambiguous parsing)
|
|
673
|
+
*
|
|
674
|
+
* @param extracted - Extracted fields from schema
|
|
675
|
+
* @throws {PositionalConfigError} If configuration is invalid
|
|
676
|
+
*/
|
|
677
|
+
function validatePositionalConfig(extracted) {
|
|
678
|
+
const errors = checkPositionalConfig(extracted, []);
|
|
679
|
+
if (errors.length > 0) {
|
|
680
|
+
const err = errors[0];
|
|
681
|
+
throw new PositionalConfigError(err.message);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Validate that no reserved aliases are used without explicit override
|
|
686
|
+
*
|
|
687
|
+
* Reserved aliases:
|
|
688
|
+
* - 'h' is reserved for --help
|
|
689
|
+
* - 'H' is reserved for --help-all
|
|
690
|
+
*
|
|
691
|
+
* Users can override these by setting overrideBuiltinAlias: true
|
|
692
|
+
*
|
|
693
|
+
* @param extracted - Extracted fields from schema
|
|
694
|
+
* @param _hasSubCommands - Whether the command has subcommands (reserved for future use)
|
|
695
|
+
* @throws {ReservedAliasError} If a reserved alias is used without override flag
|
|
696
|
+
*/
|
|
697
|
+
function validateReservedAliases(extracted, _hasSubCommands) {
|
|
698
|
+
const errors = checkReservedAliases(extracted, []);
|
|
699
|
+
if (errors.length > 0) {
|
|
700
|
+
const field = errors[0].field ?? "unknown";
|
|
701
|
+
const alias = extracted.fields.find((f) => f.name === field)?.alias ?? "h";
|
|
702
|
+
throw new ReservedAliasError(`Alias "${alias}" is reserved for --${alias === "h" ? "help" : "help-all"}. To override this, set { alias: "${alias}", overrideBuiltinAlias: true } for "${field}".`);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Collect validation errors for a single command's schema (non-throwing)
|
|
707
|
+
*/
|
|
708
|
+
function collectSchemaErrors(extracted, _hasSubCommands, commandPath) {
|
|
709
|
+
return [
|
|
710
|
+
...checkDuplicateFields(extracted, commandPath),
|
|
711
|
+
...checkDuplicateAliases(extracted, commandPath),
|
|
712
|
+
...checkPositionalConfig(extracted, commandPath),
|
|
713
|
+
...checkReservedAliases(extracted, commandPath)
|
|
714
|
+
];
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Validate a command and all its subcommands recursively
|
|
718
|
+
*
|
|
719
|
+
* This function collects all validation errors without throwing,
|
|
720
|
+
* making it suitable for test assertions.
|
|
721
|
+
*
|
|
722
|
+
* @param command - The command to validate
|
|
723
|
+
* @param options - Validation options
|
|
724
|
+
* @returns Validation result with all errors collected
|
|
725
|
+
*
|
|
726
|
+
* @example
|
|
727
|
+
* ```ts
|
|
728
|
+
* const result = await validateCommand(myCommand);
|
|
729
|
+
* if (!result.valid) {
|
|
730
|
+
* console.error(result.errors);
|
|
731
|
+
* }
|
|
732
|
+
* ```
|
|
733
|
+
*/
|
|
734
|
+
async function validateCommand(command, options = {}) {
|
|
735
|
+
const commandPath = options.commandPath ?? [command.name];
|
|
736
|
+
const errors = [];
|
|
737
|
+
const hasSubCommands = command.subCommands ? Object.keys(command.subCommands).length > 0 : false;
|
|
738
|
+
if (command.args) {
|
|
739
|
+
const extracted = require_schema_extractor.extractFields(command.args);
|
|
740
|
+
errors.push(...collectSchemaErrors(extracted, hasSubCommands, commandPath));
|
|
741
|
+
}
|
|
742
|
+
if (command.subCommands) for (const [name, subCmd] of Object.entries(command.subCommands)) {
|
|
743
|
+
const subResult = await validateCommand(await require_subcommand_router.resolveLazyCommand(subCmd), { commandPath: [...commandPath, name] });
|
|
744
|
+
if (!subResult.valid) errors.push(...subResult.errors);
|
|
745
|
+
}
|
|
746
|
+
if (errors.length === 0) return { valid: true };
|
|
747
|
+
return {
|
|
748
|
+
valid: false,
|
|
749
|
+
errors
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Format command validation errors for display
|
|
754
|
+
*
|
|
755
|
+
* @param errors - Array of validation errors
|
|
756
|
+
* @returns Formatted error message
|
|
757
|
+
*/
|
|
758
|
+
function formatCommandValidationErrors(errors) {
|
|
759
|
+
if (errors.length === 0) return "";
|
|
760
|
+
const lines = ["Command definition errors:"];
|
|
761
|
+
for (const error of errors) {
|
|
762
|
+
const path = error.commandPath.join(" > ");
|
|
763
|
+
lines.push(` - [${path}] ${error.message}`);
|
|
764
|
+
}
|
|
765
|
+
return lines.join("\n");
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
//#endregion
|
|
769
|
+
//#region src/parser/argv-parser.ts
|
|
770
|
+
/**
|
|
771
|
+
* Parse argv into a flat record
|
|
772
|
+
*
|
|
773
|
+
* Supports:
|
|
774
|
+
* - Long options: --flag, --flag=value, --flag value
|
|
775
|
+
* - Short options: -f, -f=value, -f value
|
|
776
|
+
* - Combined short options: -abc (treated as -a -b -c if all are boolean)
|
|
777
|
+
* - Positional arguments
|
|
778
|
+
* - -- to stop parsing options
|
|
779
|
+
*
|
|
780
|
+
* @param argv - Command line arguments
|
|
781
|
+
* @param options - Parser options
|
|
782
|
+
* @returns Parsed arguments
|
|
783
|
+
*/
|
|
784
|
+
function parseArgv(argv, options = {}) {
|
|
785
|
+
const { aliasMap = /* @__PURE__ */ new Map(), booleanFlags = /* @__PURE__ */ new Set(), arrayFlags = /* @__PURE__ */ new Set() } = options;
|
|
786
|
+
const result = {
|
|
787
|
+
options: {},
|
|
788
|
+
positionals: [],
|
|
789
|
+
rest: []
|
|
790
|
+
};
|
|
791
|
+
let i = 0;
|
|
792
|
+
let stopParsing = false;
|
|
793
|
+
const setOption = (name, value) => {
|
|
794
|
+
const resolvedName = aliasMap.get(name) ?? name;
|
|
795
|
+
if (arrayFlags.has(resolvedName)) {
|
|
796
|
+
const existing = result.options[resolvedName];
|
|
797
|
+
if (Array.isArray(existing)) existing.push(value);
|
|
798
|
+
else if (existing !== void 0) result.options[resolvedName] = [existing, value];
|
|
799
|
+
else result.options[resolvedName] = [value];
|
|
800
|
+
} else result.options[resolvedName] = value;
|
|
801
|
+
};
|
|
802
|
+
while (i < argv.length) {
|
|
803
|
+
const arg = argv[i];
|
|
804
|
+
if (stopParsing) {
|
|
805
|
+
result.rest.push(arg);
|
|
806
|
+
i++;
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
if (arg === "--") {
|
|
810
|
+
stopParsing = true;
|
|
811
|
+
i++;
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
if (arg.startsWith("--")) {
|
|
815
|
+
const withoutDashes = arg.slice(2);
|
|
816
|
+
if (withoutDashes.startsWith("no-")) {
|
|
817
|
+
const flagName = withoutDashes.slice(3);
|
|
818
|
+
const resolvedName = aliasMap.get(flagName) ?? flagName;
|
|
819
|
+
if (booleanFlags.has(resolvedName)) {
|
|
820
|
+
setOption(flagName, false);
|
|
821
|
+
i++;
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
const eqIndex = withoutDashes.indexOf("=");
|
|
826
|
+
if (eqIndex !== -1) {
|
|
827
|
+
setOption(withoutDashes.slice(0, eqIndex), withoutDashes.slice(eqIndex + 1));
|
|
828
|
+
i++;
|
|
829
|
+
} else {
|
|
830
|
+
const name = withoutDashes;
|
|
831
|
+
const resolvedName = aliasMap.get(name) ?? name;
|
|
832
|
+
if (booleanFlags.has(resolvedName)) {
|
|
833
|
+
setOption(name, true);
|
|
834
|
+
i++;
|
|
835
|
+
} else {
|
|
836
|
+
const nextArg = argv[i + 1];
|
|
837
|
+
if (nextArg !== void 0 && !nextArg.startsWith("-")) {
|
|
838
|
+
setOption(name, nextArg);
|
|
839
|
+
i += 2;
|
|
840
|
+
} else {
|
|
841
|
+
setOption(name, true);
|
|
842
|
+
i++;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
if (arg.startsWith("-") && arg.length > 1 && !arg.startsWith("--")) {
|
|
849
|
+
const withoutDash = arg.slice(1);
|
|
850
|
+
const eqIndex = withoutDash.indexOf("=");
|
|
851
|
+
if (eqIndex !== -1) {
|
|
852
|
+
setOption(withoutDash.slice(0, eqIndex), withoutDash.slice(eqIndex + 1));
|
|
853
|
+
i++;
|
|
854
|
+
} else if (withoutDash.length === 1) {
|
|
855
|
+
const name = withoutDash;
|
|
856
|
+
const resolvedName = aliasMap.get(name) ?? name;
|
|
857
|
+
if (booleanFlags.has(resolvedName)) {
|
|
858
|
+
setOption(name, true);
|
|
859
|
+
i++;
|
|
860
|
+
} else {
|
|
861
|
+
const nextArg = argv[i + 1];
|
|
862
|
+
if (nextArg !== void 0 && !nextArg.startsWith("-")) {
|
|
863
|
+
setOption(name, nextArg);
|
|
864
|
+
i += 2;
|
|
865
|
+
} else {
|
|
866
|
+
setOption(name, true);
|
|
867
|
+
i++;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
} else {
|
|
871
|
+
for (const char of withoutDash) setOption(char, true);
|
|
872
|
+
i++;
|
|
873
|
+
}
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
result.positionals.push(arg);
|
|
877
|
+
i++;
|
|
878
|
+
}
|
|
879
|
+
return result;
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Build parser options from extracted fields
|
|
883
|
+
*/
|
|
884
|
+
function buildParserOptions(extracted) {
|
|
885
|
+
const aliasMap = /* @__PURE__ */ new Map();
|
|
886
|
+
const booleanFlags = /* @__PURE__ */ new Set();
|
|
887
|
+
const arrayFlags = /* @__PURE__ */ new Set();
|
|
888
|
+
for (const field of extracted.fields) {
|
|
889
|
+
if (field.cliName !== field.name) aliasMap.set(field.cliName, field.name);
|
|
890
|
+
if (field.alias) aliasMap.set(field.alias, field.name);
|
|
891
|
+
if (field.type === "boolean") booleanFlags.add(field.name);
|
|
892
|
+
if (field.type === "array") arrayFlags.add(field.name);
|
|
893
|
+
}
|
|
894
|
+
return {
|
|
895
|
+
aliasMap,
|
|
896
|
+
booleanFlags,
|
|
897
|
+
arrayFlags
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Merge parsed argv with positional fields to create a flat record
|
|
902
|
+
*/
|
|
903
|
+
function mergeWithPositionals(parsed, extracted) {
|
|
904
|
+
const result = { ...parsed.options };
|
|
905
|
+
const positionalFields = extracted.fields.filter((f) => f.positional);
|
|
906
|
+
let positionalIndex = 0;
|
|
907
|
+
for (const field of positionalFields) {
|
|
908
|
+
if (positionalIndex >= parsed.positionals.length) break;
|
|
909
|
+
if (field.type === "array") {
|
|
910
|
+
result[field.name] = parsed.positionals.slice(positionalIndex);
|
|
911
|
+
break;
|
|
912
|
+
} else {
|
|
913
|
+
result[field.name] = parsed.positionals[positionalIndex];
|
|
914
|
+
positionalIndex++;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return result;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
//#endregion
|
|
921
|
+
//#region src/parser/arg-parser.ts
|
|
922
|
+
/**
|
|
923
|
+
* Parse CLI arguments for a command
|
|
924
|
+
*
|
|
925
|
+
* @param argv - Command line arguments
|
|
926
|
+
* @param command - The command to parse for
|
|
927
|
+
* @param options - Parse options
|
|
928
|
+
* @returns Parse result
|
|
929
|
+
*/
|
|
930
|
+
function parseArgs(argv, command, options = {}) {
|
|
931
|
+
const subCommandNames = command.subCommands ? Object.keys(command.subCommands) : [];
|
|
932
|
+
const hasSubCommands = subCommandNames.length > 0;
|
|
933
|
+
if (hasSubCommands && argv.length > 0) {
|
|
934
|
+
const firstArg = argv[0];
|
|
935
|
+
if (firstArg && !firstArg.startsWith("-") && subCommandNames.includes(firstArg)) return {
|
|
936
|
+
helpRequested: false,
|
|
937
|
+
helpAllRequested: false,
|
|
938
|
+
versionRequested: false,
|
|
939
|
+
subCommand: firstArg,
|
|
940
|
+
remainingArgs: argv.slice(1),
|
|
941
|
+
rawArgs: {},
|
|
942
|
+
positionals: [],
|
|
943
|
+
unknownFlags: []
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
let extracted;
|
|
947
|
+
if (command.args) {
|
|
948
|
+
extracted = require_schema_extractor.extractFields(command.args);
|
|
949
|
+
if (!options.skipValidation) {
|
|
950
|
+
validateDuplicateFields(extracted);
|
|
951
|
+
validateDuplicateAliases(extracted);
|
|
952
|
+
validatePositionalConfig(extracted);
|
|
953
|
+
validateReservedAliases(extracted, hasSubCommands);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
const hasUserDefinedH = extracted?.fields.some((f) => f.alias === "H" && f.overrideBuiltinAlias === true) ?? false;
|
|
957
|
+
const hasUserDefinedh = extracted?.fields.some((f) => f.alias === "h" && f.overrideBuiltinAlias === true) ?? false;
|
|
958
|
+
const helpAllRequested = argv.includes("--help-all") || !hasUserDefinedH && argv.includes("-H");
|
|
959
|
+
const helpRequested = !helpAllRequested && (argv.includes("--help") || !hasUserDefinedh && argv.includes("-h"));
|
|
960
|
+
const versionRequested = argv.includes("--version");
|
|
961
|
+
if (helpRequested || helpAllRequested || versionRequested) return {
|
|
962
|
+
helpRequested,
|
|
963
|
+
helpAllRequested,
|
|
964
|
+
versionRequested,
|
|
965
|
+
subCommand: void 0,
|
|
966
|
+
remainingArgs: [],
|
|
967
|
+
rawArgs: {},
|
|
968
|
+
positionals: [],
|
|
969
|
+
unknownFlags: []
|
|
970
|
+
};
|
|
971
|
+
if (!extracted) return {
|
|
972
|
+
helpRequested: false,
|
|
973
|
+
helpAllRequested: false,
|
|
974
|
+
versionRequested: false,
|
|
975
|
+
subCommand: void 0,
|
|
976
|
+
remainingArgs: [],
|
|
977
|
+
rawArgs: {},
|
|
978
|
+
positionals: [],
|
|
979
|
+
unknownFlags: []
|
|
980
|
+
};
|
|
981
|
+
const parsed = parseArgv(argv, buildParserOptions(extracted));
|
|
982
|
+
const rawArgs = mergeWithPositionals(parsed, extracted);
|
|
983
|
+
for (const field of extracted.fields) if (field.env && rawArgs[field.name] === void 0) {
|
|
984
|
+
const envNames = Array.isArray(field.env) ? field.env : [field.env];
|
|
985
|
+
for (const envName of envNames) {
|
|
986
|
+
const envValue = process.env[envName];
|
|
987
|
+
if (envValue !== void 0) {
|
|
988
|
+
rawArgs[field.name] = envValue;
|
|
989
|
+
break;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
const knownFlags = new Set(extracted.fields.map((f) => f.name));
|
|
994
|
+
const knownCliNames = new Set(extracted.fields.map((f) => f.cliName));
|
|
995
|
+
const knownAliases = new Set(extracted.fields.filter((f) => f.alias).map((f) => f.alias));
|
|
996
|
+
const unknownFlags = [];
|
|
997
|
+
for (const key of Object.keys(parsed.options)) if (!knownFlags.has(key) && !knownCliNames.has(key) && !knownAliases.has(key)) unknownFlags.push(key);
|
|
998
|
+
return {
|
|
999
|
+
helpRequested: false,
|
|
1000
|
+
helpAllRequested: false,
|
|
1001
|
+
versionRequested: false,
|
|
1002
|
+
subCommand: void 0,
|
|
1003
|
+
remainingArgs: [],
|
|
1004
|
+
rawArgs,
|
|
1005
|
+
positionals: parsed.positionals,
|
|
1006
|
+
unknownFlags,
|
|
1007
|
+
extractedFields: extracted
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
//#endregion
|
|
1012
|
+
//#region src/validator/error-formatter.ts
|
|
1013
|
+
/**
|
|
1014
|
+
* Calculate Levenshtein distance between two strings
|
|
1015
|
+
*/
|
|
1016
|
+
function levenshteinDistance(a, b) {
|
|
1017
|
+
const matrix = [];
|
|
1018
|
+
for (let i = 0; i <= b.length; i++) matrix[i] = [i];
|
|
1019
|
+
for (let j = 0; j <= a.length; j++) matrix[0][j] = j;
|
|
1020
|
+
for (let i = 1; i <= b.length; i++) for (let j = 1; j <= a.length; j++) if (b.charAt(i - 1) === a.charAt(j - 1)) matrix[i][j] = matrix[i - 1][j - 1];
|
|
1021
|
+
else matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1);
|
|
1022
|
+
return matrix[b.length][a.length];
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Find similar strings from a list
|
|
1026
|
+
*/
|
|
1027
|
+
function findSimilar(target, candidates) {
|
|
1028
|
+
const threshold = Math.max(2, Math.floor(target.length / 2));
|
|
1029
|
+
return candidates.map((candidate) => ({
|
|
1030
|
+
candidate,
|
|
1031
|
+
distance: levenshteinDistance(target.toLowerCase(), candidate.toLowerCase())
|
|
1032
|
+
})).filter(({ distance }) => distance <= threshold).sort((a, b) => a.distance - b.distance).map(({ candidate }) => candidate).slice(0, 3);
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Format validation errors into a human-readable message
|
|
1036
|
+
*
|
|
1037
|
+
* @param errors - Array of validation errors
|
|
1038
|
+
* @returns Formatted error message
|
|
1039
|
+
*/
|
|
1040
|
+
function formatValidationErrors$1(errors) {
|
|
1041
|
+
if (errors.length === 0) return "";
|
|
1042
|
+
const lines = [styles.error("Validation errors:")];
|
|
1043
|
+
for (const error of errors) {
|
|
1044
|
+
const path = error.path.join(".");
|
|
1045
|
+
lines.push(` ${symbols.bullet} ${styles.bold(path)}: ${error.message}`);
|
|
1046
|
+
}
|
|
1047
|
+
return lines.join("\n");
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Format unknown flag error with suggestions
|
|
1051
|
+
*
|
|
1052
|
+
* @param flag - The unknown flag (e.g., "--verbos")
|
|
1053
|
+
* @param knownFlags - List of known flag names
|
|
1054
|
+
* @returns Formatted error message with suggestions
|
|
1055
|
+
*/
|
|
1056
|
+
function formatUnknownFlag(flag, knownFlags) {
|
|
1057
|
+
const similar = findSimilar(flag.replace(/^-{1,2}/, ""), knownFlags);
|
|
1058
|
+
let message = `${styles.error("Unknown option:")} ${styles.bold(flag)}`;
|
|
1059
|
+
if (similar.length > 0) {
|
|
1060
|
+
message += `\n\n${styles.info("Did you mean?")}`;
|
|
1061
|
+
for (const suggestion of similar) message += `\n ${symbols.arrow} ${styles.option(`--${suggestion}`)}`;
|
|
1062
|
+
}
|
|
1063
|
+
return message;
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Format unknown flag warning with suggestions (for strip mode)
|
|
1067
|
+
*
|
|
1068
|
+
* @param flag - The unknown flag (e.g., "--verbos")
|
|
1069
|
+
* @param knownFlags - List of known flag names
|
|
1070
|
+
* @returns Formatted warning message with suggestions
|
|
1071
|
+
*/
|
|
1072
|
+
function formatUnknownFlagWarning(flag, knownFlags) {
|
|
1073
|
+
const similar = findSimilar(flag.replace(/^-{1,2}/, ""), knownFlags);
|
|
1074
|
+
let message = `${styles.warning("Warning: Unknown option:")} ${styles.bold(flag)}`;
|
|
1075
|
+
if (similar.length > 0) {
|
|
1076
|
+
message += `\n\n${styles.info("Did you mean?")}`;
|
|
1077
|
+
for (const suggestion of similar) message += `\n ${symbols.arrow} ${styles.option(`--${suggestion}`)}`;
|
|
1078
|
+
}
|
|
1079
|
+
return message;
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Format runtime error
|
|
1083
|
+
*
|
|
1084
|
+
* @param error - The error that occurred
|
|
1085
|
+
* @param debug - Whether to include stack trace
|
|
1086
|
+
* @returns Formatted error message
|
|
1087
|
+
*/
|
|
1088
|
+
function formatRuntimeError(error, debug) {
|
|
1089
|
+
if (debug && error.stack) return `${styles.error("Error:")} ${error.message}\n\n${styles.dim(error.stack)}`;
|
|
1090
|
+
return `${styles.error("Error:")} ${error.message}`;
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Format unknown subcommand error with suggestions
|
|
1094
|
+
*
|
|
1095
|
+
* @param subcommand - The unknown subcommand name
|
|
1096
|
+
* @param knownSubcommands - List of known subcommand names
|
|
1097
|
+
* @returns Formatted error message with suggestions
|
|
1098
|
+
*/
|
|
1099
|
+
function formatUnknownSubcommand(subcommand, knownSubcommands) {
|
|
1100
|
+
const similar = findSimilar(subcommand, knownSubcommands);
|
|
1101
|
+
let message = `${styles.error("Unknown command:")} ${styles.bold(subcommand)}`;
|
|
1102
|
+
if (similar.length > 0) {
|
|
1103
|
+
message += `\n\n${styles.info("Did you mean?")}`;
|
|
1104
|
+
for (const suggestion of similar) message += `\n ${symbols.arrow} ${styles.command(suggestion)}`;
|
|
1105
|
+
}
|
|
1106
|
+
return message;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
//#endregion
|
|
1110
|
+
//#region src/validator/zod-validator.ts
|
|
1111
|
+
/**
|
|
1112
|
+
* Convert ZodError to ValidationError array (zod v4 compatible)
|
|
1113
|
+
*/
|
|
1114
|
+
function formatZodErrors(error) {
|
|
1115
|
+
return error.issues.map((issue) => ({
|
|
1116
|
+
path: issue.path.map(String),
|
|
1117
|
+
message: issue.message,
|
|
1118
|
+
code: issue.code,
|
|
1119
|
+
received: "received" in issue ? issue.received : void 0,
|
|
1120
|
+
expected: "expected" in issue ? String(issue.expected) : void 0
|
|
1121
|
+
}));
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Validate raw arguments against a schema
|
|
1125
|
+
*
|
|
1126
|
+
* @param rawArgs - Parsed but unvalidated arguments
|
|
1127
|
+
* @param schema - Zod schema (ZodObject, ZodDiscriminatedUnion, etc.)
|
|
1128
|
+
* @returns Validation result with typed data or errors
|
|
1129
|
+
*/
|
|
1130
|
+
function validateArgs(rawArgs, schema) {
|
|
1131
|
+
const result = schema.safeParse(rawArgs);
|
|
1132
|
+
if (result.success) return {
|
|
1133
|
+
success: true,
|
|
1134
|
+
data: result.data
|
|
1135
|
+
};
|
|
1136
|
+
return {
|
|
1137
|
+
success: false,
|
|
1138
|
+
errors: formatZodErrors(result.error)
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Format validation errors for display
|
|
1143
|
+
*/
|
|
1144
|
+
function formatValidationErrors(errors) {
|
|
1145
|
+
return errors.map((e) => {
|
|
1146
|
+
return `${e.path.length > 0 ? `${e.path.join(".")}: ` : ""}${e.message}`;
|
|
1147
|
+
}).join("\n");
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
//#endregion
|
|
1151
|
+
//#region src/core/runner.ts
|
|
1152
|
+
/**
|
|
1153
|
+
* Default logger using console
|
|
1154
|
+
*/
|
|
1155
|
+
const defaultLogger = {
|
|
1156
|
+
log: (message) => console.log(message),
|
|
1157
|
+
error: (message) => console.error(message)
|
|
1158
|
+
};
|
|
1159
|
+
/**
|
|
1160
|
+
* Run a command with the given arguments (programmatic/test usage)
|
|
1161
|
+
*
|
|
1162
|
+
* This function parses arguments, validates them, routes to subcommands,
|
|
1163
|
+
* and executes the command. It does NOT call process.exit.
|
|
1164
|
+
*
|
|
1165
|
+
* @param command - The command to run
|
|
1166
|
+
* @param argv - Command line arguments to parse
|
|
1167
|
+
* @param options - Run options
|
|
1168
|
+
* @returns The result of command execution
|
|
1169
|
+
*
|
|
1170
|
+
* @example
|
|
1171
|
+
* ```ts
|
|
1172
|
+
* import { defineCommand, runCommand } from "politty";
|
|
1173
|
+
*
|
|
1174
|
+
* const command = defineCommand({
|
|
1175
|
+
* name: "my-cli",
|
|
1176
|
+
* args: z.object({ name: z.string() }),
|
|
1177
|
+
* run: ({ name }) => console.log(`Hello, ${name}!`),
|
|
1178
|
+
* });
|
|
1179
|
+
*
|
|
1180
|
+
* // In tests
|
|
1181
|
+
* const result = await runCommand(command, ["--name", "World"]);
|
|
1182
|
+
* expect(result.exitCode).toBe(0);
|
|
1183
|
+
* ```
|
|
1184
|
+
*/
|
|
1185
|
+
async function runCommand(command, argv, options = {}) {
|
|
1186
|
+
return runCommandInternal(command, argv, {
|
|
1187
|
+
...options,
|
|
1188
|
+
handleSignals: false,
|
|
1189
|
+
skipValidation: options.skipValidation,
|
|
1190
|
+
logger: options.logger
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Run a CLI command as the main entry point
|
|
1195
|
+
*
|
|
1196
|
+
* This function:
|
|
1197
|
+
* - Uses process.argv for arguments
|
|
1198
|
+
* - Handles SIGINT/SIGTERM signals
|
|
1199
|
+
* - Calls process.exit with the appropriate exit code
|
|
1200
|
+
*
|
|
1201
|
+
* @param command - The command to run
|
|
1202
|
+
* @param options - Main options (version, debug)
|
|
1203
|
+
*
|
|
1204
|
+
* @example
|
|
1205
|
+
* ```ts
|
|
1206
|
+
* import { defineCommand, runMain } from "politty";
|
|
1207
|
+
*
|
|
1208
|
+
* const command = defineCommand({
|
|
1209
|
+
* name: "my-cli",
|
|
1210
|
+
* run: () => console.log("Hello!"),
|
|
1211
|
+
* });
|
|
1212
|
+
*
|
|
1213
|
+
* runMain(command, { version: "1.0.0" });
|
|
1214
|
+
* ```
|
|
1215
|
+
*/
|
|
1216
|
+
async function runMain(command, options = {}) {
|
|
1217
|
+
const result = await runCommandInternal(command, process.argv.slice(2), {
|
|
1218
|
+
debug: options.debug,
|
|
1219
|
+
captureLogs: options.captureLogs,
|
|
1220
|
+
skipValidation: options.skipValidation,
|
|
1221
|
+
handleSignals: true,
|
|
1222
|
+
logger: options.logger,
|
|
1223
|
+
_context: {
|
|
1224
|
+
commandPath: [],
|
|
1225
|
+
rootName: command.name,
|
|
1226
|
+
rootVersion: options.version
|
|
1227
|
+
}
|
|
1228
|
+
});
|
|
1229
|
+
process.exit(result.exitCode);
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Internal implementation of command running
|
|
1233
|
+
*/
|
|
1234
|
+
async function runCommandInternal(command, argv, options = {}) {
|
|
1235
|
+
const logger$1 = options.logger ?? defaultLogger;
|
|
1236
|
+
const context = options._context ?? {
|
|
1237
|
+
commandPath: [],
|
|
1238
|
+
rootName: command.name
|
|
1239
|
+
};
|
|
1240
|
+
const collector = options.captureLogs ?? false ? require_subcommand_router.createLogCollector() : null;
|
|
1241
|
+
collector?.start();
|
|
1242
|
+
const getCurrentLogs = () => {
|
|
1243
|
+
return require_subcommand_router.mergeLogs(options._existingLogs ?? require_subcommand_router.emptyLogs(), collector?.getLogs() ?? require_subcommand_router.emptyLogs());
|
|
1244
|
+
};
|
|
1245
|
+
try {
|
|
1246
|
+
const parseResult = parseArgs(argv, command, { skipValidation: options.skipValidation });
|
|
1247
|
+
if (parseResult.helpRequested || parseResult.helpAllRequested) {
|
|
1248
|
+
let hasUnknownSubcommand = false;
|
|
1249
|
+
const subCmdNames = require_subcommand_router.listSubCommands(command);
|
|
1250
|
+
if (subCmdNames.length > 0) {
|
|
1251
|
+
const potentialSubCmd = argv.find((arg) => !arg.startsWith("-"));
|
|
1252
|
+
if (potentialSubCmd && !subCmdNames.includes(potentialSubCmd)) {
|
|
1253
|
+
logger$1.error(formatUnknownSubcommand(potentialSubCmd, subCmdNames));
|
|
1254
|
+
logger$1.error("");
|
|
1255
|
+
hasUnknownSubcommand = true;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
const help = generateHelp(command, {
|
|
1259
|
+
showSubcommands: options.showSubcommands ?? true,
|
|
1260
|
+
showSubcommandOptions: parseResult.helpAllRequested || options.showSubcommandOptions,
|
|
1261
|
+
context
|
|
1262
|
+
});
|
|
1263
|
+
logger$1.log(help);
|
|
1264
|
+
collector?.stop();
|
|
1265
|
+
if (hasUnknownSubcommand) return {
|
|
1266
|
+
success: false,
|
|
1267
|
+
error: /* @__PURE__ */ new Error(`Unknown subcommand: ${argv.find((arg) => !arg.startsWith("-"))}`),
|
|
1268
|
+
exitCode: 1,
|
|
1269
|
+
logs: getCurrentLogs()
|
|
1270
|
+
};
|
|
1271
|
+
return {
|
|
1272
|
+
success: true,
|
|
1273
|
+
result: void 0,
|
|
1274
|
+
exitCode: 0,
|
|
1275
|
+
logs: getCurrentLogs()
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
if (parseResult.versionRequested) {
|
|
1279
|
+
const version = context.rootVersion;
|
|
1280
|
+
if (version) logger$1.log(version);
|
|
1281
|
+
collector?.stop();
|
|
1282
|
+
return {
|
|
1283
|
+
success: true,
|
|
1284
|
+
result: void 0,
|
|
1285
|
+
exitCode: 0,
|
|
1286
|
+
logs: getCurrentLogs()
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
if (parseResult.subCommand) {
|
|
1290
|
+
const subCmd = await require_subcommand_router.resolveSubcommand(command, parseResult.subCommand);
|
|
1291
|
+
if (subCmd) {
|
|
1292
|
+
const subContext = {
|
|
1293
|
+
commandPath: [...context.commandPath ?? [], parseResult.subCommand],
|
|
1294
|
+
rootName: context.rootName,
|
|
1295
|
+
rootVersion: context.rootVersion
|
|
1296
|
+
};
|
|
1297
|
+
collector?.stop();
|
|
1298
|
+
return runCommandInternal(subCmd, parseResult.remainingArgs, {
|
|
1299
|
+
...options,
|
|
1300
|
+
_context: subContext,
|
|
1301
|
+
_existingLogs: getCurrentLogs()
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
if (require_subcommand_router.listSubCommands(command).length > 0 && !parseResult.subCommand && !command.run) {
|
|
1306
|
+
const help = generateHelp(command, {
|
|
1307
|
+
showSubcommands: options.showSubcommands ?? true,
|
|
1308
|
+
context
|
|
1309
|
+
});
|
|
1310
|
+
logger$1.log(help);
|
|
1311
|
+
collector?.stop();
|
|
1312
|
+
return {
|
|
1313
|
+
success: true,
|
|
1314
|
+
result: void 0,
|
|
1315
|
+
exitCode: 0,
|
|
1316
|
+
logs: getCurrentLogs()
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
if (parseResult.unknownFlags.length > 0) {
|
|
1320
|
+
const unknownKeysMode = parseResult.extractedFields?.unknownKeysMode ?? "strip";
|
|
1321
|
+
const knownFlags = parseResult.extractedFields?.fields.map((f) => f.name) ?? [];
|
|
1322
|
+
if (unknownKeysMode === "strict") {
|
|
1323
|
+
for (const flag of parseResult.unknownFlags) logger$1.error(formatUnknownFlag(flag, knownFlags));
|
|
1324
|
+
collector?.stop();
|
|
1325
|
+
return {
|
|
1326
|
+
success: false,
|
|
1327
|
+
error: /* @__PURE__ */ new Error(`Unknown flags: ${parseResult.unknownFlags.join(", ")}`),
|
|
1328
|
+
exitCode: 1,
|
|
1329
|
+
logs: getCurrentLogs()
|
|
1330
|
+
};
|
|
1331
|
+
} else if (unknownKeysMode === "strip") for (const flag of parseResult.unknownFlags) logger$1.error(formatUnknownFlagWarning(flag, knownFlags));
|
|
1332
|
+
}
|
|
1333
|
+
if (!command.args) {
|
|
1334
|
+
collector?.stop();
|
|
1335
|
+
return await executeLifecycle(command, {}, {
|
|
1336
|
+
handleSignals: options.handleSignals,
|
|
1337
|
+
captureLogs: options.captureLogs,
|
|
1338
|
+
existingLogs: getCurrentLogs()
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
const validationResult = validateArgs(parseResult.rawArgs, command.args);
|
|
1342
|
+
if (!validationResult.success) {
|
|
1343
|
+
logger$1.error(formatValidationErrors$1(validationResult.errors));
|
|
1344
|
+
collector?.stop();
|
|
1345
|
+
return {
|
|
1346
|
+
success: false,
|
|
1347
|
+
error: new Error(formatValidationErrors$1(validationResult.errors)),
|
|
1348
|
+
exitCode: 1,
|
|
1349
|
+
logs: getCurrentLogs()
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
collector?.stop();
|
|
1353
|
+
return await executeLifecycle(command, validationResult.data, {
|
|
1354
|
+
handleSignals: options.handleSignals,
|
|
1355
|
+
captureLogs: options.captureLogs,
|
|
1356
|
+
existingLogs: getCurrentLogs()
|
|
1357
|
+
});
|
|
1358
|
+
} catch (error) {
|
|
1359
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1360
|
+
logger$1.error(formatRuntimeError(err, options.debug ?? false));
|
|
1361
|
+
collector?.stop();
|
|
1362
|
+
return {
|
|
1363
|
+
success: false,
|
|
1364
|
+
error: err,
|
|
1365
|
+
exitCode: 1,
|
|
1366
|
+
logs: getCurrentLogs()
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
//#endregion
|
|
1372
|
+
Object.defineProperty(exports, 'DuplicateAliasError', {
|
|
1373
|
+
enumerable: true,
|
|
1374
|
+
get: function () {
|
|
1375
|
+
return DuplicateAliasError;
|
|
1376
|
+
}
|
|
1377
|
+
});
|
|
1378
|
+
Object.defineProperty(exports, 'DuplicateFieldError', {
|
|
1379
|
+
enumerable: true,
|
|
1380
|
+
get: function () {
|
|
1381
|
+
return DuplicateFieldError;
|
|
1382
|
+
}
|
|
1383
|
+
});
|
|
1384
|
+
Object.defineProperty(exports, 'PositionalConfigError', {
|
|
1385
|
+
enumerable: true,
|
|
1386
|
+
get: function () {
|
|
1387
|
+
return PositionalConfigError;
|
|
1388
|
+
}
|
|
1389
|
+
});
|
|
1390
|
+
Object.defineProperty(exports, 'ReservedAliasError', {
|
|
1391
|
+
enumerable: true,
|
|
1392
|
+
get: function () {
|
|
1393
|
+
return ReservedAliasError;
|
|
1394
|
+
}
|
|
1395
|
+
});
|
|
1396
|
+
Object.defineProperty(exports, 'formatCommandValidationErrors', {
|
|
1397
|
+
enumerable: true,
|
|
1398
|
+
get: function () {
|
|
1399
|
+
return formatCommandValidationErrors;
|
|
1400
|
+
}
|
|
1401
|
+
});
|
|
1402
|
+
Object.defineProperty(exports, 'formatValidationErrors', {
|
|
1403
|
+
enumerable: true,
|
|
1404
|
+
get: function () {
|
|
1405
|
+
return formatValidationErrors;
|
|
1406
|
+
}
|
|
1407
|
+
});
|
|
1408
|
+
Object.defineProperty(exports, 'generateHelp', {
|
|
1409
|
+
enumerable: true,
|
|
1410
|
+
get: function () {
|
|
1411
|
+
return generateHelp;
|
|
1412
|
+
}
|
|
1413
|
+
});
|
|
1414
|
+
Object.defineProperty(exports, 'isColorEnabled', {
|
|
1415
|
+
enumerable: true,
|
|
1416
|
+
get: function () {
|
|
1417
|
+
return isColorEnabled;
|
|
1418
|
+
}
|
|
1419
|
+
});
|
|
1420
|
+
Object.defineProperty(exports, 'logger', {
|
|
1421
|
+
enumerable: true,
|
|
1422
|
+
get: function () {
|
|
1423
|
+
return logger;
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1426
|
+
Object.defineProperty(exports, 'parseArgv', {
|
|
1427
|
+
enumerable: true,
|
|
1428
|
+
get: function () {
|
|
1429
|
+
return parseArgv;
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
Object.defineProperty(exports, 'runCommand', {
|
|
1433
|
+
enumerable: true,
|
|
1434
|
+
get: function () {
|
|
1435
|
+
return runCommand;
|
|
1436
|
+
}
|
|
1437
|
+
});
|
|
1438
|
+
Object.defineProperty(exports, 'runMain', {
|
|
1439
|
+
enumerable: true,
|
|
1440
|
+
get: function () {
|
|
1441
|
+
return runMain;
|
|
1442
|
+
}
|
|
1443
|
+
});
|
|
1444
|
+
Object.defineProperty(exports, 'setColorEnabled', {
|
|
1445
|
+
enumerable: true,
|
|
1446
|
+
get: function () {
|
|
1447
|
+
return setColorEnabled;
|
|
1448
|
+
}
|
|
1449
|
+
});
|
|
1450
|
+
Object.defineProperty(exports, 'styles', {
|
|
1451
|
+
enumerable: true,
|
|
1452
|
+
get: function () {
|
|
1453
|
+
return styles;
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
Object.defineProperty(exports, 'symbols', {
|
|
1457
|
+
enumerable: true,
|
|
1458
|
+
get: function () {
|
|
1459
|
+
return symbols;
|
|
1460
|
+
}
|
|
1461
|
+
});
|
|
1462
|
+
Object.defineProperty(exports, 'validateCommand', {
|
|
1463
|
+
enumerable: true,
|
|
1464
|
+
get: function () {
|
|
1465
|
+
return validateCommand;
|
|
1466
|
+
}
|
|
1467
|
+
});
|
|
1468
|
+
Object.defineProperty(exports, 'validateDuplicateAliases', {
|
|
1469
|
+
enumerable: true,
|
|
1470
|
+
get: function () {
|
|
1471
|
+
return validateDuplicateAliases;
|
|
1472
|
+
}
|
|
1473
|
+
});
|
|
1474
|
+
Object.defineProperty(exports, 'validateDuplicateFields', {
|
|
1475
|
+
enumerable: true,
|
|
1476
|
+
get: function () {
|
|
1477
|
+
return validateDuplicateFields;
|
|
1478
|
+
}
|
|
1479
|
+
});
|
|
1480
|
+
Object.defineProperty(exports, 'validatePositionalConfig', {
|
|
1481
|
+
enumerable: true,
|
|
1482
|
+
get: function () {
|
|
1483
|
+
return validatePositionalConfig;
|
|
1484
|
+
}
|
|
1485
|
+
});
|
|
1486
|
+
Object.defineProperty(exports, 'validateReservedAliases', {
|
|
1487
|
+
enumerable: true,
|
|
1488
|
+
get: function () {
|
|
1489
|
+
return validateReservedAliases;
|
|
1490
|
+
}
|
|
1491
|
+
});
|
|
1492
|
+
//# sourceMappingURL=runner-BZuYiRhi.cjs.map
|