@silvery/commander 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -127
- package/package.json +6 -18
- package/src/colorize.ts +164 -0
- package/src/command.ts +121 -0
- package/src/index.ts +11 -173
- package/src/presets.ts +41 -36
- package/src/z.ts +44 -0
- package/src/typed.ts +0 -465
package/src/typed.ts
DELETED
|
@@ -1,465 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type-safe Commander.js wrapper — replaces @commander-js/extra-typings.
|
|
3
|
-
*
|
|
4
|
-
* Uses TypeScript 5.4+ const type parameters and template literal types
|
|
5
|
-
* to infer option types from .option() calls. Inspired by
|
|
6
|
-
* @commander-js/extra-typings, which achieves similar results with a
|
|
7
|
-
* 1536-line .d.ts using recursive generic accumulation. This
|
|
8
|
-
* implementation achieves the same inference in ~100 lines of type-level
|
|
9
|
-
* code by leveraging modern TS features (const type params, template
|
|
10
|
-
* literal types, conditional mapped types).
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* import { createCLI } from "@silvery/commander"
|
|
15
|
-
*
|
|
16
|
-
* const cli = createCLI("myapp")
|
|
17
|
-
* .description("My app")
|
|
18
|
-
* .option("-v, --verbose", "Increase verbosity")
|
|
19
|
-
* .option("-p, --port <number>", "Port to listen on", parseInt)
|
|
20
|
-
* .option("-o, --output [path]", "Output path")
|
|
21
|
-
* .option("--no-color", "Disable color output")
|
|
22
|
-
*
|
|
23
|
-
* cli.parse()
|
|
24
|
-
* const opts = cli.opts()
|
|
25
|
-
* // ^? { verbose: boolean, port: number, output: string | true, color: boolean }
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
import { Command as BaseCommand, Option, Argument } from "commander"
|
|
30
|
-
import { colorizeHelp } from "./index.ts"
|
|
31
|
-
|
|
32
|
-
// --- Type-level option parsing ---
|
|
33
|
-
//
|
|
34
|
-
// Approach: Each .option() call captures the flags string as a const
|
|
35
|
-
// type parameter. Template literal types extract the flag name and
|
|
36
|
-
// determine the value type (boolean for bare flags, string for <value>,
|
|
37
|
-
// string | true for [value]). The result accumulates via intersection
|
|
38
|
-
// types across chained calls. Prettify<T> flattens the intersections
|
|
39
|
-
// for clean hover output.
|
|
40
|
-
//
|
|
41
|
-
// Negated flags (--no-X) are detected and produce a `X: boolean` key.
|
|
42
|
-
|
|
43
|
-
/** Flatten intersection types for clean hover output */
|
|
44
|
-
export type Prettify<T> = { [K in keyof T]: T[K] } & {}
|
|
45
|
-
|
|
46
|
-
/** Check if a flags string is a negated flag like "--no-color" */
|
|
47
|
-
type IsNegated<S extends string> = S extends `${string}--no-${string}` ? true : false
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Extract the option key name from a flags string like "-p, --port <value>".
|
|
51
|
-
*
|
|
52
|
-
* Priority: long flag > short flag. Handles negated flags (--no-X → X),
|
|
53
|
-
* kebab-case conversion (--dry-run → dryRun), and short-only flags (-v → v).
|
|
54
|
-
*/
|
|
55
|
-
type ExtractLongName<S extends string> = S extends `${string}--no-${infer Rest}`
|
|
56
|
-
? Rest extends `${infer Name} ${string}`
|
|
57
|
-
? CamelCase<Name>
|
|
58
|
-
: CamelCase<Rest>
|
|
59
|
-
: S extends `${string}--${infer Rest}`
|
|
60
|
-
? Rest extends `${infer Name} ${string}`
|
|
61
|
-
? CamelCase<Name>
|
|
62
|
-
: CamelCase<Rest>
|
|
63
|
-
: S extends `-${infer Short}`
|
|
64
|
-
? Short extends `${infer C} ${string}`
|
|
65
|
-
? C
|
|
66
|
-
: Short
|
|
67
|
-
: never
|
|
68
|
-
|
|
69
|
-
/** Convert kebab-case to camelCase: "dry-run" → "dryRun" */
|
|
70
|
-
type CamelCase<S extends string> = S extends `${infer A}-${infer B}${infer Rest}`
|
|
71
|
-
? `${A}${Uppercase<B>}${CamelCase<Rest>}`
|
|
72
|
-
: S
|
|
73
|
-
|
|
74
|
-
/** Determine the value type from a flags string */
|
|
75
|
-
type FlagValueType<S extends string> =
|
|
76
|
-
IsNegated<S> extends true
|
|
77
|
-
? boolean // negated flags are always boolean
|
|
78
|
-
: S extends `${string}<${string}>`
|
|
79
|
-
? string // required arg → string
|
|
80
|
-
: S extends `${string}[${string}]`
|
|
81
|
-
? string | true // optional arg → string | true
|
|
82
|
-
: boolean // no arg → boolean
|
|
83
|
-
|
|
84
|
-
/** Add a flag to an options record */
|
|
85
|
-
type AddOption<Opts, Flags extends string, Default = undefined> = Opts & {
|
|
86
|
-
[K in ExtractLongName<Flags>]: Default extends undefined ? FlagValueType<Flags> | undefined : FlagValueType<Flags>
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// --- Type-level argument parsing ---
|
|
90
|
-
|
|
91
|
-
/** Extract whether an argument is required (<name>) or optional ([name]) */
|
|
92
|
-
type ArgType<S extends string> = S extends `<${string}>`
|
|
93
|
-
? string
|
|
94
|
-
: S extends `[${string}]`
|
|
95
|
-
? string | undefined
|
|
96
|
-
: string
|
|
97
|
-
|
|
98
|
-
// --- Typed opts helper (resolves accumulated Opts for action handlers) ---
|
|
99
|
-
/** Resolve accumulated option types for use in action handler signatures */
|
|
100
|
-
export type TypedOpts<Opts> = Prettify<Opts>
|
|
101
|
-
|
|
102
|
-
// --- Standard Schema support (v1) ---
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Standard Schema v1 interface — the universal schema interop protocol.
|
|
106
|
-
* Supports any schema library that implements Standard Schema (Zod >=3.24,
|
|
107
|
-
* Valibot >=1.0, ArkType >=2.0, etc.).
|
|
108
|
-
*
|
|
109
|
-
* Inlined to avoid any dependency on @standard-schema/spec.
|
|
110
|
-
* See: https://github.com/standard-schema/standard-schema
|
|
111
|
-
*/
|
|
112
|
-
export interface StandardSchemaV1<T = unknown> {
|
|
113
|
-
readonly "~standard": {
|
|
114
|
-
readonly version: 1
|
|
115
|
-
readonly vendor: string
|
|
116
|
-
readonly validate: (
|
|
117
|
-
value: unknown,
|
|
118
|
-
) => { value: T } | { issues: ReadonlyArray<{ message: string; path?: ReadonlyArray<unknown> }> }
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/** Type-level extraction: infer the output type from a Standard Schema */
|
|
123
|
-
type InferStandardSchema<S> = S extends StandardSchemaV1<infer T> ? T : never
|
|
124
|
-
|
|
125
|
-
/** Runtime check: is this value a Standard Schema v1 object? */
|
|
126
|
-
function isStandardSchema(value: unknown): value is StandardSchemaV1 {
|
|
127
|
-
return typeof value === "object" && value !== null && "~standard" in (value as any)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** Wrap a Standard Schema as a Commander parser function */
|
|
131
|
-
function standardSchemaParser<T>(schema: StandardSchemaV1<T>): (value: string) => T {
|
|
132
|
-
return (value: string) => {
|
|
133
|
-
const result = schema["~standard"].validate(value)
|
|
134
|
-
if ("issues" in result) {
|
|
135
|
-
const msg = result.issues.map((i) => i.message).join(", ")
|
|
136
|
-
throw new Error(msg)
|
|
137
|
-
}
|
|
138
|
-
return result.value
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// --- Legacy Zod support (pre-3.24, no ~standard) ---
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Duck-type interface for older Zod schemas that don't implement Standard Schema.
|
|
146
|
-
* Any object with `parse(value: string) => T` and `_def` qualifies.
|
|
147
|
-
*/
|
|
148
|
-
interface ZodLike<T = any> {
|
|
149
|
-
parse(value: unknown): T
|
|
150
|
-
_def: unknown
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/** Type-level extraction: if Z is a Zod schema, infer its output type */
|
|
154
|
-
type InferZodOutput<Z> = Z extends { parse(value: unknown): infer T; _def: unknown } ? T : never
|
|
155
|
-
|
|
156
|
-
/** Runtime check: is this value a legacy Zod-like schema (without Standard Schema)? */
|
|
157
|
-
function isLegacyZodSchema(value: unknown): value is ZodLike {
|
|
158
|
-
return (
|
|
159
|
-
typeof value === "object" &&
|
|
160
|
-
value !== null &&
|
|
161
|
-
typeof (value as any).parse === "function" &&
|
|
162
|
-
"_def" in (value as any) &&
|
|
163
|
-
!("~standard" in (value as any))
|
|
164
|
-
)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/** Wrap a legacy Zod schema as a Commander parser function */
|
|
168
|
-
function legacyZodParser<T>(schema: ZodLike<T>): (value: string) => T {
|
|
169
|
-
return (value: string) => {
|
|
170
|
-
try {
|
|
171
|
-
return schema.parse(value)
|
|
172
|
-
} catch (err: any) {
|
|
173
|
-
// Format Zod errors as Commander-style messages
|
|
174
|
-
if (err?.issues) {
|
|
175
|
-
const messages = err.issues.map((i: any) => i.message).join(", ")
|
|
176
|
-
throw new Error(messages)
|
|
177
|
-
}
|
|
178
|
-
throw err
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// --- Typed Command ---
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* A Commander Command with inferred option and argument types.
|
|
187
|
-
* Wraps Commander's Command and tracks option types at the type level.
|
|
188
|
-
* Help is automatically colorized.
|
|
189
|
-
*
|
|
190
|
-
* @typeParam Opts - Accumulated option types from .option() calls
|
|
191
|
-
* @typeParam Args - Accumulated argument types from .argument() calls (tuple)
|
|
192
|
-
*/
|
|
193
|
-
export class TypedCommand<Opts = {}, Args extends any[] = []> {
|
|
194
|
-
readonly _cmd: BaseCommand
|
|
195
|
-
|
|
196
|
-
constructor(name?: string) {
|
|
197
|
-
this._cmd = new BaseCommand(name)
|
|
198
|
-
colorizeHelp(this._cmd as any)
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/** Set program description */
|
|
202
|
-
description(str: string, argsDescription?: Record<string, string>): this {
|
|
203
|
-
this._cmd.description(str, argsDescription as any)
|
|
204
|
-
return this
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/** Set program version */
|
|
208
|
-
version(str: string, flags?: string, description?: string): this {
|
|
209
|
-
this._cmd.version(str, flags, description)
|
|
210
|
-
return this
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Add an option with type inference.
|
|
215
|
-
*
|
|
216
|
-
* Supports five overload patterns:
|
|
217
|
-
* 1. `.option(flags, description?)` — type inferred from flags syntax
|
|
218
|
-
* 2. `.option(flags, description, defaultValue)` — removes `undefined` from type
|
|
219
|
-
* 3. `.option(flags, description, parser, defaultValue?)` — type inferred from parser return type
|
|
220
|
-
* 4. `.option(flags, description, standardSchema)` — type inferred from Standard Schema output
|
|
221
|
-
* 5. `.option(flags, description, zodSchema)` — type inferred from Zod schema output (legacy, pre-3.24)
|
|
222
|
-
*/
|
|
223
|
-
option<const F extends string, S extends StandardSchemaV1>(
|
|
224
|
-
flags: F,
|
|
225
|
-
description: string,
|
|
226
|
-
schema: S,
|
|
227
|
-
): TypedCommand<Opts & { [K in ExtractLongName<F>]: InferStandardSchema<S> }, Args>
|
|
228
|
-
|
|
229
|
-
option<const F extends string, Z extends ZodLike>(
|
|
230
|
-
flags: F,
|
|
231
|
-
description: string,
|
|
232
|
-
schema: Z,
|
|
233
|
-
): TypedCommand<Opts & { [K in ExtractLongName<F>]: InferZodOutput<Z> }, Args>
|
|
234
|
-
|
|
235
|
-
option<const F extends string, P extends (value: string, previous: any) => any>(
|
|
236
|
-
flags: F,
|
|
237
|
-
description: string,
|
|
238
|
-
parseArg: P,
|
|
239
|
-
defaultValue?: ReturnType<P>,
|
|
240
|
-
): TypedCommand<Opts & { [K in ExtractLongName<F>]: ReturnType<P> }, Args>
|
|
241
|
-
|
|
242
|
-
option<const F extends string, D = undefined>(
|
|
243
|
-
flags: F,
|
|
244
|
-
description?: string,
|
|
245
|
-
defaultValue?: D,
|
|
246
|
-
): TypedCommand<AddOption<Opts, F, D>, Args>
|
|
247
|
-
|
|
248
|
-
option(flags: string, description?: string, parseArgOrDefault?: any, defaultValue?: any): any {
|
|
249
|
-
if (isStandardSchema(parseArgOrDefault)) {
|
|
250
|
-
;(this._cmd as any).option(flags, description ?? "", standardSchemaParser(parseArgOrDefault))
|
|
251
|
-
} else if (isLegacyZodSchema(parseArgOrDefault)) {
|
|
252
|
-
;(this._cmd as any).option(flags, description ?? "", legacyZodParser(parseArgOrDefault))
|
|
253
|
-
} else if (typeof parseArgOrDefault === "function") {
|
|
254
|
-
;(this._cmd as any).option(flags, description ?? "", parseArgOrDefault, defaultValue)
|
|
255
|
-
} else {
|
|
256
|
-
;(this._cmd as any).option(flags, description ?? "", parseArgOrDefault)
|
|
257
|
-
}
|
|
258
|
-
return this
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/** Add a required option */
|
|
262
|
-
requiredOption<const F extends string>(
|
|
263
|
-
flags: F,
|
|
264
|
-
description?: string,
|
|
265
|
-
defaultValue?: string,
|
|
266
|
-
): TypedCommand<Opts & { [K in ExtractLongName<F>]: FlagValueType<F> }, Args> {
|
|
267
|
-
;(this._cmd as any).requiredOption(flags, description ?? "", defaultValue)
|
|
268
|
-
return this as any
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Add an option with a fixed set of allowed values (choices).
|
|
273
|
-
* The option type is narrowed to a union of the provided values.
|
|
274
|
-
*
|
|
275
|
-
* @example
|
|
276
|
-
* ```ts
|
|
277
|
-
* .optionWithChoices("-e, --env <env>", "Environment", ["dev", "staging", "prod"] as const)
|
|
278
|
-
* // → env: "dev" | "staging" | "prod" | undefined
|
|
279
|
-
* ```
|
|
280
|
-
*/
|
|
281
|
-
optionWithChoices<const F extends string, const C extends readonly string[]>(
|
|
282
|
-
flags: F,
|
|
283
|
-
description: string,
|
|
284
|
-
choices: C,
|
|
285
|
-
): TypedCommand<Opts & { [K in ExtractLongName<F>]: C[number] | undefined }, Args> {
|
|
286
|
-
const option = new Option(flags, description).choices(choices as unknown as string[])
|
|
287
|
-
;(this._cmd as any).addOption(option)
|
|
288
|
-
return this as any
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/** Add a subcommand */
|
|
292
|
-
command(nameAndArgs: string, description?: string): TypedCommand<{}> {
|
|
293
|
-
const sub = (this._cmd as any).command(nameAndArgs, description)
|
|
294
|
-
colorizeHelp(sub as any)
|
|
295
|
-
const typed = new TypedCommand<{}>()
|
|
296
|
-
// Replace the internal command with the one Commander created
|
|
297
|
-
;(typed as any)._cmd = sub
|
|
298
|
-
return typed
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Add an argument with type tracking.
|
|
303
|
-
* `<name>` = required (string), `[name]` = optional (string | undefined).
|
|
304
|
-
*/
|
|
305
|
-
argument<const N extends string>(
|
|
306
|
-
name: N,
|
|
307
|
-
description?: string,
|
|
308
|
-
defaultValue?: unknown,
|
|
309
|
-
): TypedCommand<Opts, [...Args, ArgType<N>]> {
|
|
310
|
-
this._cmd.argument(name, description ?? "", defaultValue)
|
|
311
|
-
return this as any
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Set action handler with typed parameters.
|
|
316
|
-
* Callback receives: ...arguments, opts, command.
|
|
317
|
-
*/
|
|
318
|
-
action(fn: (...args: [...Args, Prettify<Opts>, TypedCommand<Opts, Args>]) => void | Promise<void>): this {
|
|
319
|
-
this._cmd.action(fn as any)
|
|
320
|
-
return this
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/** Get typed parsed options */
|
|
324
|
-
opts(): Prettify<Opts> {
|
|
325
|
-
return this._cmd.opts() as any
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/** Parse argv */
|
|
329
|
-
parse(argv?: readonly string[], options?: { from?: "node" | "electron" | "user" }): this {
|
|
330
|
-
this._cmd.parse(argv as any, options as any)
|
|
331
|
-
return this
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/** Parse argv async */
|
|
335
|
-
async parseAsync(argv?: readonly string[], options?: { from?: "node" | "electron" | "user" }): Promise<this> {
|
|
336
|
-
await this._cmd.parseAsync(argv as any, options as any)
|
|
337
|
-
return this
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/** Get help text */
|
|
341
|
-
helpInformation(): string {
|
|
342
|
-
return this._cmd.helpInformation()
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/** Allow unknown options */
|
|
346
|
-
allowUnknownOption(allow?: boolean): this {
|
|
347
|
-
this._cmd.allowUnknownOption(allow)
|
|
348
|
-
return this
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/** Allow excess arguments */
|
|
352
|
-
allowExcessArguments(allow?: boolean): this {
|
|
353
|
-
this._cmd.allowExcessArguments(allow)
|
|
354
|
-
return this
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/** Pass through options after -- */
|
|
358
|
-
passThroughOptions(passThrough?: boolean): this {
|
|
359
|
-
this._cmd.passThroughOptions(passThrough)
|
|
360
|
-
return this
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/** Enable positional options */
|
|
364
|
-
enablePositionalOptions(positional?: boolean): this {
|
|
365
|
-
this._cmd.enablePositionalOptions(positional)
|
|
366
|
-
return this
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/** Hook into lifecycle events */
|
|
370
|
-
hook(event: string, listener: (...args: any[]) => void | Promise<void>): this {
|
|
371
|
-
;(this._cmd as any).hook(event, listener)
|
|
372
|
-
return this
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/** Set custom name */
|
|
376
|
-
name(str: string): this {
|
|
377
|
-
this._cmd.name(str)
|
|
378
|
-
return this
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/** Add alias */
|
|
382
|
-
alias(alias: string): this {
|
|
383
|
-
this._cmd.alias(alias)
|
|
384
|
-
return this
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/** Add multiple aliases */
|
|
388
|
-
aliases(aliases: readonly string[]): this {
|
|
389
|
-
this._cmd.aliases(aliases as string[])
|
|
390
|
-
return this
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/** Configure help display */
|
|
394
|
-
configureHelp(config: Record<string, unknown>): this {
|
|
395
|
-
;(this._cmd as any).configureHelp(config)
|
|
396
|
-
return this
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/** Configure output streams */
|
|
400
|
-
configureOutput(config: Record<string, unknown>): this {
|
|
401
|
-
;(this._cmd as any).configureOutput(config)
|
|
402
|
-
return this
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/** Access underlying Commander Command for advanced use */
|
|
406
|
-
get commands(): readonly BaseCommand[] {
|
|
407
|
-
return this._cmd.commands
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/** Show help */
|
|
411
|
-
help(context?: { error?: boolean }): never {
|
|
412
|
-
return (this._cmd as any).help(context) as never
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/** Add help text */
|
|
416
|
-
addHelpText(position: "before" | "after" | "beforeAll" | "afterAll", text: string): this {
|
|
417
|
-
this._cmd.addHelpText(position, text)
|
|
418
|
-
return this
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/** Show help after error */
|
|
422
|
-
showHelpAfterError(displayHelp?: boolean | string): this {
|
|
423
|
-
this._cmd.showHelpAfterError(displayHelp)
|
|
424
|
-
return this
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
/** Show suggestion after error */
|
|
428
|
-
showSuggestionAfterError(displaySuggestion?: boolean): this {
|
|
429
|
-
this._cmd.showSuggestionAfterError(displaySuggestion)
|
|
430
|
-
return this
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
/** Set environment variable for the last added option (passthrough) */
|
|
434
|
-
env(name: string): this {
|
|
435
|
-
// Commander's .env() is on Option, not Command. We apply it to the last option.
|
|
436
|
-
const opts = (this._cmd as any).options as any[]
|
|
437
|
-
if (opts.length > 0) {
|
|
438
|
-
opts[opts.length - 1].envVar = name
|
|
439
|
-
opts[opts.length - 1].envVarRequired = false
|
|
440
|
-
}
|
|
441
|
-
return this
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* Create a typed, colorized CLI program.
|
|
447
|
-
*
|
|
448
|
-
* @example
|
|
449
|
-
* ```ts
|
|
450
|
-
* import { createCLI } from "@silvery/commander"
|
|
451
|
-
*
|
|
452
|
-
* const program = createCLI("myapp")
|
|
453
|
-
* .description("My tool")
|
|
454
|
-
* .version("1.0.0")
|
|
455
|
-
* .option("-v, --verbose", "Verbose output")
|
|
456
|
-
* .option("-p, --port <number>", "Port", parseInt)
|
|
457
|
-
*
|
|
458
|
-
* program.parse()
|
|
459
|
-
* const { verbose, port } = program.opts()
|
|
460
|
-
* // ^boolean ^number | undefined
|
|
461
|
-
* ```
|
|
462
|
-
*/
|
|
463
|
-
export function createCLI(name?: string): TypedCommand<{}> {
|
|
464
|
-
return new TypedCommand(name)
|
|
465
|
-
}
|