@silvery/commander 0.1.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 +62 -0
- package/package.json +44 -0
- package/src/index.ts +140 -0
- package/src/typed.ts +288 -0
package/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# @silvery/commander
|
|
2
|
+
|
|
3
|
+
Type-safe, colorized [Commander.js](https://github.com/tj/commander.js) wrapper. Infers option types from `.option()` calls using TypeScript 5.4+ const type parameters and template literal types — no codegen, no separate type package.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { createCLI } from "@silvery/commander"
|
|
9
|
+
|
|
10
|
+
const cli = createCLI("myapp")
|
|
11
|
+
.description("My CLI tool")
|
|
12
|
+
.version("1.0.0")
|
|
13
|
+
.option("-v, --verbose", "Verbose output")
|
|
14
|
+
.option("-p, --port <number>", "Port to listen on")
|
|
15
|
+
.option("-o, --output [path]", "Output path")
|
|
16
|
+
.option("--no-color", "Disable color output")
|
|
17
|
+
|
|
18
|
+
cli.parse()
|
|
19
|
+
const { verbose, port, output, color } = cli.opts()
|
|
20
|
+
// ^boolean ^string ^string|true ^boolean
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Help output is automatically colorized using Commander's built-in `configureHelp()` style hooks (headings bold, flags green, commands cyan, descriptions dim, arguments yellow).
|
|
24
|
+
|
|
25
|
+
You can also use `colorizeHelp()` standalone with a plain Commander `Command`:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { Command } from "commander"
|
|
29
|
+
import { colorizeHelp } from "@silvery/commander"
|
|
30
|
+
|
|
31
|
+
const program = new Command("myapp").description("My CLI tool")
|
|
32
|
+
colorizeHelp(program) // applies recursively to all subcommands
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Improvements over @commander-js/extra-typings
|
|
36
|
+
|
|
37
|
+
| Feature | extra-typings | @silvery/commander |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| Type inference | 1536-line .d.ts with recursive generic accumulation | ~60 lines using TS 5.4+ const type params + template literals |
|
|
40
|
+
| Colorized help | Not included | Built-in via Commander's native style hooks |
|
|
41
|
+
| Package size | Types only (25 lines runtime) | Types + colorizer (~200 lines, zero deps) |
|
|
42
|
+
| Installation | Separate package alongside commander | Single package, re-exports Commander |
|
|
43
|
+
| React dependency | None | None |
|
|
44
|
+
| Negated flags (--no-X) | Partial | Key extraction + boolean type inference |
|
|
45
|
+
| Typed action handlers | Yes (full signature inference) | Not yet (planned) |
|
|
46
|
+
| Custom parser types | Yes (.option with parseFloat -> number) | Not yet (planned) |
|
|
47
|
+
|
|
48
|
+
## What's planned
|
|
49
|
+
|
|
50
|
+
- Custom parser function type inference (`.option("-p, --port <n>", "Port", parseInt)` -> `number`)
|
|
51
|
+
- Typed action handler signatures
|
|
52
|
+
- `.choices()` narrowing to union types
|
|
53
|
+
|
|
54
|
+
## Credits
|
|
55
|
+
|
|
56
|
+
- **Commander.js** by TJ Holowaychuk and contributors -- the underlying CLI framework
|
|
57
|
+
- **@commander-js/extra-typings** -- inspired the type inference approach; our implementation uses modern TypeScript features (const type parameters, template literal types) to achieve similar results in fewer lines
|
|
58
|
+
- Uses Commander's built-in `configureHelp()` style hooks (added in Commander 12) for colorization
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@silvery/commander",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Colorized Commander.js help output using ANSI escape codes",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ansi",
|
|
7
|
+
"cli",
|
|
8
|
+
"color",
|
|
9
|
+
"commander",
|
|
10
|
+
"help"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Bjørn Stabell <bjorn@stabell.org>",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/beorn/silvery.git",
|
|
17
|
+
"directory": "packages/commander"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"src"
|
|
21
|
+
],
|
|
22
|
+
"type": "module",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": "./src/index.ts"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@commander-js/extra-typings": ">=12.0.0",
|
|
31
|
+
"commander": ">=12.0.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependenciesMeta": {
|
|
34
|
+
"commander": {
|
|
35
|
+
"optional": true
|
|
36
|
+
},
|
|
37
|
+
"@commander-js/extra-typings": {
|
|
38
|
+
"optional": true
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=23.6.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Re-export Commander classes (drop-in replacement for @silvery/commander)
|
|
2
|
+
export { Command, Option, Argument, CommanderError, InvalidArgumentError, Help } from "commander"
|
|
3
|
+
export type { OptionValues } from "commander"
|
|
4
|
+
|
|
5
|
+
// Re-export typed CLI
|
|
6
|
+
export { TypedCommand, createCLI } from "./typed.ts"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Commander.js help colorization using ANSI escape codes.
|
|
10
|
+
*
|
|
11
|
+
* Uses Commander's built-in style hooks (styleTitle, styleOptionText, etc.)
|
|
12
|
+
* rather than regex post-processing. Works with @silvery/commander
|
|
13
|
+
* or plain commander — accepts a minimal CommandLike interface so Commander
|
|
14
|
+
* is a peer dependency, not a hard one.
|
|
15
|
+
*
|
|
16
|
+
* Zero dependencies — only raw ANSI escape codes.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { Command } from "@silvery/commander"
|
|
21
|
+
* import { colorizeHelp } from "@silvery/commander"
|
|
22
|
+
*
|
|
23
|
+
* const program = new Command("myapp").description("My CLI tool")
|
|
24
|
+
* colorizeHelp(program)
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// Raw ANSI escape codes — no framework dependencies.
|
|
29
|
+
const RESET = "\x1b[0m"
|
|
30
|
+
const BOLD = "\x1b[1m"
|
|
31
|
+
const DIM = "\x1b[2m"
|
|
32
|
+
const CYAN = "\x1b[36m"
|
|
33
|
+
const GREEN = "\x1b[32m"
|
|
34
|
+
const YELLOW = "\x1b[33m"
|
|
35
|
+
|
|
36
|
+
/** Wrap a string with ANSI codes, handling nested resets. */
|
|
37
|
+
function ansi(text: string, code: string): string {
|
|
38
|
+
return `${code}${text}${RESET}`
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Minimal interface for Commander's Command — avoids requiring Commander
|
|
43
|
+
* as a direct dependency. Works with both `commander` and
|
|
44
|
+
* `@silvery/commander`.
|
|
45
|
+
*
|
|
46
|
+
* Uses permissive types to ensure structural compatibility with all
|
|
47
|
+
* Commander versions, overloads, and generic instantiations.
|
|
48
|
+
*/
|
|
49
|
+
export interface CommandLike {
|
|
50
|
+
// biome-ignore lint: permissive to match Commander's overloaded signatures
|
|
51
|
+
configureHelp(...args: any[]): any
|
|
52
|
+
// biome-ignore lint: permissive to match Commander's overloaded signatures
|
|
53
|
+
configureOutput(...args: any[]): any
|
|
54
|
+
// biome-ignore lint: permissive to match Commander's Command[] structurally
|
|
55
|
+
readonly commands: readonly any[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Color scheme for help output. Values are raw ANSI escape sequences. */
|
|
59
|
+
export interface ColorizeHelpOptions {
|
|
60
|
+
/** ANSI code for command/subcommand names. Default: cyan */
|
|
61
|
+
commands?: string
|
|
62
|
+
/** ANSI code for --flags and -short options. Default: green */
|
|
63
|
+
flags?: string
|
|
64
|
+
/** ANSI code for description text. Default: dim */
|
|
65
|
+
description?: string
|
|
66
|
+
/** ANSI code for section headings (Usage:, Options:, etc.). Default: bold */
|
|
67
|
+
heading?: string
|
|
68
|
+
/** ANSI code for <required> and [optional] argument brackets. Default: yellow */
|
|
69
|
+
brackets?: string
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Apply colorized help output to a Commander.js program and all its subcommands.
|
|
74
|
+
*
|
|
75
|
+
* Uses Commander's built-in `configureHelp()` style hooks rather than
|
|
76
|
+
* post-processing the formatted string. This approach is robust against
|
|
77
|
+
* formatting changes in Commander and handles wrapping correctly.
|
|
78
|
+
*
|
|
79
|
+
* @param program - A Commander Command instance (or compatible object)
|
|
80
|
+
* @param options - Override default ANSI color codes for each element
|
|
81
|
+
*/
|
|
82
|
+
export function colorizeHelp(program: CommandLike, options?: ColorizeHelpOptions): void {
|
|
83
|
+
const cmds = options?.commands ?? CYAN
|
|
84
|
+
const flags = options?.flags ?? GREEN
|
|
85
|
+
const desc = options?.description ?? DIM
|
|
86
|
+
const heading = options?.heading ?? BOLD
|
|
87
|
+
const brackets = options?.brackets ?? YELLOW
|
|
88
|
+
|
|
89
|
+
const helpConfig: Record<string, unknown> = {
|
|
90
|
+
// Section headings: "Usage:", "Options:", "Commands:", "Arguments:"
|
|
91
|
+
styleTitle(str: string): string {
|
|
92
|
+
return ansi(str, heading)
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
// Command name in usage line and subcommand terms
|
|
96
|
+
styleCommandText(str: string): string {
|
|
97
|
+
return ansi(str, cmds)
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// Option terms: "-v, --verbose", "--repo <path>", "[options]"
|
|
101
|
+
styleOptionText(str: string): string {
|
|
102
|
+
return ansi(str, flags)
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// Subcommand names in the commands list
|
|
106
|
+
styleSubcommandText(str: string): string {
|
|
107
|
+
return ansi(str, cmds)
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
// Argument terms: "<file>", "[dir]"
|
|
111
|
+
styleArgumentText(str: string): string {
|
|
112
|
+
return ansi(str, brackets)
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// Description text for options, subcommands, arguments
|
|
116
|
+
styleDescriptionText(str: string): string {
|
|
117
|
+
return ansi(str, desc)
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
// Command description (the main program description line) — keep normal
|
|
121
|
+
styleCommandDescription(str: string): string {
|
|
122
|
+
return str
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
program.configureHelp(helpConfig)
|
|
127
|
+
|
|
128
|
+
// Tell Commander that color output is supported, even when stdout is not
|
|
129
|
+
// a TTY (e.g., piped output, CI, tests). Without this, Commander strips
|
|
130
|
+
// all ANSI codes from helpInformation() output.
|
|
131
|
+
program.configureOutput({
|
|
132
|
+
getOutHasColors: () => true,
|
|
133
|
+
getErrHasColors: () => true,
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
// Apply recursively to all existing subcommands
|
|
137
|
+
for (const sub of program.commands) {
|
|
138
|
+
colorizeHelp(sub, options)
|
|
139
|
+
}
|
|
140
|
+
}
|
package/src/typed.ts
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
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 ~60 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")
|
|
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: string, 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
|
+
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> = IsNegated<S> extends true
|
|
76
|
+
? boolean // negated flags are always boolean
|
|
77
|
+
: S extends `${string}<${string}>`
|
|
78
|
+
? string // required arg → string
|
|
79
|
+
: S extends `${string}[${string}]`
|
|
80
|
+
? string | true // optional arg → string | true
|
|
81
|
+
: boolean // no arg → boolean
|
|
82
|
+
|
|
83
|
+
/** Add a flag to an options record */
|
|
84
|
+
type AddOption<Opts, Flags extends string, Default = undefined> = Opts & {
|
|
85
|
+
[K in ExtractLongName<Flags>]: Default extends undefined ? FlagValueType<Flags> | undefined : FlagValueType<Flags>
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// --- Typed Command ---
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* A Commander Command with inferred option types.
|
|
92
|
+
* Wraps Commander's Command and tracks option types at the type level.
|
|
93
|
+
* Help is automatically colorized.
|
|
94
|
+
*/
|
|
95
|
+
export class TypedCommand<Opts = {}> {
|
|
96
|
+
readonly _cmd: BaseCommand
|
|
97
|
+
|
|
98
|
+
constructor(name?: string) {
|
|
99
|
+
this._cmd = new BaseCommand(name)
|
|
100
|
+
colorizeHelp(this._cmd as any)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Set program description */
|
|
104
|
+
description(str: string, argsDescription?: Record<string, string>): this {
|
|
105
|
+
this._cmd.description(str, argsDescription as any)
|
|
106
|
+
return this
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Set program version */
|
|
110
|
+
version(str: string, flags?: string, description?: string): this {
|
|
111
|
+
this._cmd.version(str, flags, description)
|
|
112
|
+
return this
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Add an option with type inference */
|
|
116
|
+
option<const F extends string, D = undefined>(
|
|
117
|
+
flags: F,
|
|
118
|
+
description?: string,
|
|
119
|
+
defaultValue?: D,
|
|
120
|
+
): TypedCommand<AddOption<Opts, F, D>> {
|
|
121
|
+
;(this._cmd as any).option(flags, description ?? "", defaultValue)
|
|
122
|
+
return this as any
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Add a required option */
|
|
126
|
+
requiredOption<const F extends string>(
|
|
127
|
+
flags: F,
|
|
128
|
+
description?: string,
|
|
129
|
+
defaultValue?: string,
|
|
130
|
+
): TypedCommand<Opts & { [K in ExtractLongName<F>]: FlagValueType<F> }> {
|
|
131
|
+
;(this._cmd as any).requiredOption(flags, description ?? "", defaultValue)
|
|
132
|
+
return this as any
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Add a subcommand */
|
|
136
|
+
command(nameAndArgs: string, description?: string): TypedCommand<{}> {
|
|
137
|
+
const sub = (this._cmd as any).command(nameAndArgs, description)
|
|
138
|
+
colorizeHelp(sub as any)
|
|
139
|
+
const typed = new TypedCommand<{}>()
|
|
140
|
+
// Replace the internal command with the one Commander created
|
|
141
|
+
;(typed as any)._cmd = sub
|
|
142
|
+
return typed
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Add an argument */
|
|
146
|
+
argument(name: string, description?: string, defaultValue?: unknown): this {
|
|
147
|
+
this._cmd.argument(name, description ?? "", defaultValue)
|
|
148
|
+
return this
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Set action handler */
|
|
152
|
+
action(fn: (this: TypedCommand<Opts>, ...args: any[]) => void | Promise<void>): this {
|
|
153
|
+
this._cmd.action(fn as any)
|
|
154
|
+
return this
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Get typed parsed options */
|
|
158
|
+
opts(): Prettify<Opts> {
|
|
159
|
+
return this._cmd.opts() as any
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Parse argv */
|
|
163
|
+
parse(argv?: readonly string[], options?: { from?: "node" | "electron" | "user" }): this {
|
|
164
|
+
this._cmd.parse(argv as any, options as any)
|
|
165
|
+
return this
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Parse argv async */
|
|
169
|
+
async parseAsync(argv?: readonly string[], options?: { from?: "node" | "electron" | "user" }): Promise<this> {
|
|
170
|
+
await this._cmd.parseAsync(argv as any, options as any)
|
|
171
|
+
return this
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Get help text */
|
|
175
|
+
helpInformation(): string {
|
|
176
|
+
return this._cmd.helpInformation()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Allow unknown options */
|
|
180
|
+
allowUnknownOption(allow?: boolean): this {
|
|
181
|
+
this._cmd.allowUnknownOption(allow)
|
|
182
|
+
return this
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Allow excess arguments */
|
|
186
|
+
allowExcessArguments(allow?: boolean): this {
|
|
187
|
+
this._cmd.allowExcessArguments(allow)
|
|
188
|
+
return this
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Pass through options after -- */
|
|
192
|
+
passThroughOptions(passThrough?: boolean): this {
|
|
193
|
+
this._cmd.passThroughOptions(passThrough)
|
|
194
|
+
return this
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Enable positional options */
|
|
198
|
+
enablePositionalOptions(positional?: boolean): this {
|
|
199
|
+
this._cmd.enablePositionalOptions(positional)
|
|
200
|
+
return this
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Hook into lifecycle events */
|
|
204
|
+
hook(event: string, listener: (...args: any[]) => void | Promise<void>): this {
|
|
205
|
+
;(this._cmd as any).hook(event, listener)
|
|
206
|
+
return this
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Set custom name */
|
|
210
|
+
name(str: string): this {
|
|
211
|
+
this._cmd.name(str)
|
|
212
|
+
return this
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Add alias */
|
|
216
|
+
alias(alias: string): this {
|
|
217
|
+
this._cmd.alias(alias)
|
|
218
|
+
return this
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Add multiple aliases */
|
|
222
|
+
aliases(aliases: readonly string[]): this {
|
|
223
|
+
this._cmd.aliases(aliases as string[])
|
|
224
|
+
return this
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Configure help display */
|
|
228
|
+
configureHelp(config: Record<string, unknown>): this {
|
|
229
|
+
;(this._cmd as any).configureHelp(config)
|
|
230
|
+
return this
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Configure output streams */
|
|
234
|
+
configureOutput(config: Record<string, unknown>): this {
|
|
235
|
+
;(this._cmd as any).configureOutput(config)
|
|
236
|
+
return this
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** Access underlying Commander Command for advanced use */
|
|
240
|
+
get commands(): readonly BaseCommand[] {
|
|
241
|
+
return this._cmd.commands
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Show help */
|
|
245
|
+
help(context?: { error?: boolean }): never {
|
|
246
|
+
return (this._cmd as any).help(context) as never
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Add help text */
|
|
250
|
+
addHelpText(position: "before" | "after" | "beforeAll" | "afterAll", text: string): this {
|
|
251
|
+
this._cmd.addHelpText(position, text)
|
|
252
|
+
return this
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** Show help after error */
|
|
256
|
+
showHelpAfterError(displayHelp?: boolean | string): this {
|
|
257
|
+
this._cmd.showHelpAfterError(displayHelp)
|
|
258
|
+
return this
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Show suggestion after error */
|
|
262
|
+
showSuggestionAfterError(displaySuggestion?: boolean): this {
|
|
263
|
+
this._cmd.showSuggestionAfterError(displaySuggestion)
|
|
264
|
+
return this
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Create a typed, colorized CLI program.
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* ```ts
|
|
273
|
+
* import { createCLI } from "@silvery/commander"
|
|
274
|
+
*
|
|
275
|
+
* const program = createCLI("myapp")
|
|
276
|
+
* .description("My tool")
|
|
277
|
+
* .version("1.0.0")
|
|
278
|
+
* .option("-v, --verbose", "Verbose output")
|
|
279
|
+
* .option("-p, --port <number>", "Port")
|
|
280
|
+
*
|
|
281
|
+
* program.parse()
|
|
282
|
+
* const { verbose, port } = program.opts()
|
|
283
|
+
* // ^boolean ^string | undefined
|
|
284
|
+
* ```
|
|
285
|
+
*/
|
|
286
|
+
export function createCLI(name?: string): TypedCommand<{}> {
|
|
287
|
+
return new TypedCommand(name)
|
|
288
|
+
}
|