@silvery/commander 0.5.0 → 0.6.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.
Files changed (3) hide show
  1. package/README.md +48 -150
  2. package/package.json +1 -1
  3. package/src/command.ts +5 -5
package/README.md CHANGED
@@ -1,178 +1,76 @@
1
1
  # @silvery/commander
2
2
 
3
- Enhanced [Commander.js](https://github.com/tj/commander.js) with type-safe options, auto-colorized help, [Standard Schema](https://github.com/standard-schema/standard-schema) validation, and built-in CLI types.
3
+ Type-safe [Commander.js](https://github.com/tj/commander.js) with validated options, colorized help, and [Standard Schema](https://github.com/standard-schema/standard-schema) support. Drop-in replacement — `Command` extends Commander's `Command`. Install once, Commander is included.
4
4
 
5
- Drop-in replacement -- `Command` is a subclass of Commander's `Command` with full type inference for options, arguments, and parsed values. Install once, Commander is included.
5
+ ```bash
6
+ npm install @silvery/commander
7
+ ```
6
8
 
7
- ## Usage
9
+ ## Example
8
10
 
9
11
  ```typescript
10
- import { Command, port, csv } from "@silvery/commander"
12
+ import { Command, z } from "@silvery/commander"
11
13
 
12
- new Command("deploy")
13
- .description("Deploy the application")
14
+ const program = new Command("deploy")
15
+ .description("Deploy to an environment")
14
16
  .version("1.0.0")
15
- .option("-p, --port <n>", "Port", port)
16
- .option("--tags <t>", "Tags", csv)
17
- .option("-e, --env <e>", "Env", ["dev", "staging", "prod"])
17
+ .option("-e, --env <env>", "Target environment", z.enum(["dev", "staging", "prod"]))
18
+ .option("-p, --port <n>", "Port number", z.port)
19
+ .option("-r, --retries <n>", "Retry count", z.int)
20
+ .option("--tags <t>", "Labels", z.csv)
21
+ .option("-f, --force", "Skip confirmation")
18
22
 
19
23
  program.parse()
24
+ const { env, port, retries, tags, force } = program.opts()
25
+ // │ │ │ │ └─ boolean | undefined
26
+ // │ │ │ └──────── string[]
27
+ // │ │ └────────────────── number
28
+ // │ └──────────────────────── number (1–65535)
29
+ // └─────────────────────────────── "dev" | "staging" | "prod"
20
30
  ```
21
31
 
22
- Help output is automatically colorized -- bold headings, green flags, cyan commands, dim descriptions, yellow arguments. Uses [Commander's](https://github.com/tj/commander.js) built-in `configureHelp()` style hooks.
23
-
24
- Colorization works out of the box with raw ANSI codes. Install [`@silvery/ansi`](https://github.com/beorn/silvery/tree/main/packages/ansi) for full terminal capability detection (respects `NO_COLOR`, `FORCE_COLOR`, and `isTTY`).
25
-
26
- ## Validated options with built-in types
27
-
28
- Commander's `.option()` accepts a string and gives you a string back. Our built-in types parse and validate in one step:
29
-
30
- ```typescript
31
- import { Command, port, csv, int } from "@silvery/commander"
32
-
33
- new Command("deploy")
34
- .option("-p, --port <n>", "Port", port) // number (1-65535, validated)
35
- .option("--tags <t>", "Tags", csv) // string[]
36
- .option("-r, --retries <n>", "Retries", int) // number (integer)
37
- .option("-e, --env <e>", "Env", ["dev", "staging", "prod"]) // choices
38
- ```
39
-
40
- These types are **not part of Commander** -- they're provided by `@silvery/commander`. Each implements [Standard Schema v1](https://github.com/standard-schema/standard-schema), so they work with any schema-aware tooling. They have zero dependencies.
41
-
42
- ### Available types
43
-
44
- | Type | Output | Validation |
45
- | ------- | ---------- | ---------------------------------------- |
46
- | `int` | `number` | Integer (coerced from string) |
47
- | `uint` | `number` | Unsigned integer (>= 0) |
48
- | `float` | `number` | Any finite number (rejects NaN) |
49
- | `port` | `number` | Integer 1-65535 |
50
- | `url` | `string` | Valid URL (via `URL` constructor) |
51
- | `path` | `string` | Non-empty string |
52
- | `csv` | `string[]` | Comma-separated, trimmed, empty filtered |
53
- | `json` | `unknown` | Parsed JSON |
54
- | `bool` | `boolean` | true/false/yes/no/1/0 (case-insensitive) |
55
- | `date` | `Date` | Valid date string |
56
- | `email` | `string` | Basic email validation |
57
- | `regex` | `RegExp` | Valid regex pattern |
58
-
59
- ### Factory type
60
-
61
- ```typescript
62
- import { intRange } from "@silvery/commander"
63
-
64
- intRange(1, 100) // CLIType<number> -- integer within bounds
65
- ```
66
-
67
- ### Array choices
32
+ With plain Commander, `opts()` returns `Record<string, any>` every value is untyped. With `@silvery/commander`, each option's type is inferred from its schema: `z.port` produces `number`, `z.enum(...)` produces a union, `z.csv` produces `string[]`. Invalid values are rejected at parse time with clear error messages — not silently passed through as strings.
68
33
 
69
- Pass an array as the third argument to restrict an option to a fixed set of values:
34
+ [Zod](https://github.com/colinhacks/zod) is entirely optional `z` is tree-shaken from your bundle if you don't import it. Without Zod, use the built-in types (`port`, `int`, `csv`) or plain Commander.
70
35
 
71
- ```typescript
72
- .option("-e, --env <e>", "Env", ["dev", "staging", "prod"])
73
- ```
36
+ <pre><code>$ deploy --help
74
37
 
75
- Commander validates the choice at parse time and rejects invalid values.
76
-
77
- ### Standalone usage
78
-
79
- Types work outside Commander too -- for validating env vars, config files, etc.:
80
-
81
- ```typescript
82
- import { port, csv } from "@silvery/commander/parse"
83
-
84
- port.parse("3000") // 3000
85
- port.parse("abc") // throws: 'Expected port (1-65535), got "abc"'
86
- port.safeParse("3000") // { success: true, value: 3000 }
87
- port.safeParse("abc") // { success: false, issues: [{ message: "..." }] }
88
- ```
38
+ <b>Usage:</b> <span style="color:#56b6c2">deploy</span> <span style="color:#98c379">[options]</span>
89
39
 
90
- The `/parse` subpath has zero dependencies -- no Commander, no [Zod](https://github.com/colinhacks/zod).
40
+ Deploy to an environment
91
41
 
92
- ## Standard Schema validation
93
-
94
- Pass any [Standard Schema v1](https://github.com/standard-schema/standard-schema) schema as the third argument to `.option()`. This works with [Zod](https://github.com/colinhacks/zod) (>=3.24), [Valibot](https://github.com/fabian-hiller/valibot) (>=1.0), [ArkType](https://github.com/arktypeio/arktype) (>=2.0), and any library implementing the protocol:
95
-
96
- ```typescript
97
- import { Command } from "@silvery/commander"
98
- import { z } from "zod"
99
-
100
- new Command("deploy")
101
- .option("-p, --port <n>", "Port", z.coerce.number().min(1).max(65535))
102
- .option("-e, --env <env>", "Env", z.enum(["dev", "staging", "prod"]))
103
- .option(
104
- "--tags <t>",
105
- "Tags",
106
- z.string().transform((v) => v.split(",")),
107
- )
108
- ```
109
-
110
- Schema libraries are optional peer dependencies -- detected at runtime, never imported at the top level.
111
-
112
- ## Zod CLI types
113
-
114
- Import `z` from `@silvery/commander` for [Zod](https://github.com/colinhacks/zod) extended with CLI-specific schemas:
115
-
116
- ```typescript
117
- import { Command, z } from "@silvery/commander"
118
-
119
- new Command("deploy")
120
- .option("-p, --port <n>", "Port", z.port)
121
- .option("--tags <t>", "Tags", z.csv)
122
- .option("-r, --retries <n>", "Retries", z.int)
123
- .option("-e, --env <e>", "Env", ["dev", "staging", "prod"])
124
- ```
125
-
126
- The `z` export is tree-shakeable -- if you don't import it, [Zod](https://github.com/colinhacks/zod) won't be in your bundle. Requires `zod` as a peer dependency.
127
-
128
- Available: `z.port`, `z.int`, `z.uint`, `z.float`, `z.csv`, `z.url`, `z.path`, `z.email`, `z.date`, `z.json`, `z.bool`, `z.intRange(min, max)`.
129
-
130
- ## Function parsers
131
-
132
- [Commander's](https://github.com/tj/commander.js) standard parser function pattern also works:
133
-
134
- ```typescript
135
- new Command("app")
136
- .option("-p, --port <n>", "Port", parseInt) // number
137
- .option("--tags <items>", "Tags", (v) => v.split(",")) // string[]
138
- .option("-p, --port <n>", "Port", parseInt, 8080) // number (with default)
139
- ```
140
-
141
- ## colorizeHelp()
142
-
143
- Use standalone with a plain [Commander](https://github.com/tj/commander.js) `Command` (without subclassing):
144
-
145
- ```typescript
146
- import { Command } from "commander"
147
- import { colorizeHelp } from "@silvery/commander"
148
-
149
- const program = new Command("myapp")
150
- colorizeHelp(program) // applies recursively to all subcommands
151
- ```
42
+ <b>Options:</b>
43
+ <span style="color:#98c379">-V, --version</span> <span style="color:#888">output the version number</span>
44
+ <span style="color:#98c379">-e, --env &lt;env&gt;</span> <span style="color:#888">Target environment</span>
45
+ <span style="color:#98c379">-p, --port &lt;n&gt;</span> <span style="color:#888">Port number</span>
46
+ <span style="color:#98c379">-r, --retries &lt;n&gt;</span> <span style="color:#888">Retry count</span>
47
+ <span style="color:#98c379">--tags &lt;t&gt;</span> <span style="color:#888">Labels</span>
48
+ <span style="color:#98c379">-f, --force</span> <span style="color:#888">Skip confirmation</span>
49
+ <span style="color:#98c379">-h, --help</span> <span style="color:#888">display help for command</span>
50
+ </code></pre>
152
51
 
153
- ## Import paths
52
+ Help is auto-colorized — bold headings, green flags, cyan commands, dim descriptions. Options with [Zod](https://github.com/colinhacks/zod) schemas or built-in types are validated at parse time with clear error messages.
154
53
 
155
- | Path | What | Dependencies |
156
- | -------------------------- | ------------------------------- | ----------------------------------------------- |
157
- | `@silvery/commander` | Command, colorizeHelp, types, z | [commander](https://github.com/tj/commander.js) |
158
- | `@silvery/commander/parse` | Types only (.parse/.safeParse) | none |
54
+ ## What's included
159
55
 
160
- ## Beyond extra-typings
56
+ - **Colorized help** — automatic, with color level detection and [`NO_COLOR`](https://no-color.org)/`FORCE_COLOR` support via [`@silvery/ansi`](https://github.com/beorn/silvery/tree/main/packages/ansi) (optional)
57
+ - **Typed `.option()` parsing** — pass a type as the third argument:
58
+ - 14 built-in types — `port`, `int`, `csv`, `url`, `email`, `date`, [more](https://silvery.dev/reference/commander)
59
+ - Array choices — `["dev", "staging", "prod"]`
60
+ - [Zod](https://github.com/colinhacks/zod) schemas — `z.port`, `z.int`, `z.csv`, or any custom `z.string()`, `z.number()`, etc.
61
+ - Any [Standard Schema](https://github.com/standard-schema/standard-schema) library — [Valibot](https://github.com/fabian-hiller/valibot), [ArkType](https://github.com/arktypeio/arktype)
62
+ - All types usable standalone via `.parse()`/`.safeParse()`
161
63
 
162
- Built on the shoulders of [@commander-js/extra-typings](https://github.com/commander-js/extra-typings). We add:
64
+ ## Docs
163
65
 
164
- - **Auto-colorized help** -- bold headings, green flags, cyan commands
165
- - **Built-in validation** via [Standard Schema](https://github.com/standard-schema/standard-schema) -- works with [Zod](https://github.com/colinhacks/zod), [Valibot](https://github.com/fabian-hiller/valibot), [ArkType](https://github.com/arktypeio/arktype)
166
- - **14 CLI types** -- `port`, `csv`, `int`, `url`, `email` and more, usable standalone via `.parse()`/`.safeParse()`
167
- - **NO_COLOR support** via [`@silvery/ansi`](https://github.com/beorn/silvery/tree/main/packages/ansi) (optional)
168
- - **Commander included** -- one install, no peer dep setup
66
+ Full reference, type table, and API details at **[silvery.dev/reference/commander](https://silvery.dev/reference/commander)**.
169
67
 
170
68
  ## Credits
171
69
 
172
- - **[Commander.js](https://github.com/tj/commander.js)** by TJ Holowaychuk and contributors -- the underlying CLI framework
173
- - **[@commander-js/extra-typings](https://github.com/commander-js/extra-typings)** -- inspired the type inference approach
174
- - **[Standard Schema](https://github.com/standard-schema/standard-schema)** -- universal schema interop protocol
175
- - **[@silvery/ansi](https://github.com/beorn/silvery/tree/main/packages/ansi)** -- optional terminal capability detection
70
+ - **[Commander.js](https://github.com/tj/commander.js)** by TJ Holowaychuk and contributors
71
+ - **[@commander-js/extra-typings](https://github.com/commander-js/extra-typings)** inspired the type inference approach
72
+ - **[Standard Schema](https://github.com/standard-schema/standard-schema)** universal schema interop protocol
73
+ - **[@silvery/ansi](https://github.com/beorn/silvery/tree/main/packages/ansi)** terminal capability detection
176
74
 
177
75
  ## License
178
76
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silvery/commander",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Colorized Commander.js help output using ANSI escape codes",
5
5
  "keywords": [
6
6
  "ansi",
package/src/command.ts CHANGED
@@ -96,17 +96,17 @@ export class Command extends BaseCommand {
96
96
  * 4. **Function** -- passed through as Commander's parser function
97
97
  * 5. **Anything else** -- passed through as a default value
98
98
  */
99
- option(flags: string, description?: string, parseArgOrDefault?: any, defaultValue?: any): this {
99
+ override option(flags: string, description?: string, parseArgOrDefault?: any, defaultValue?: any): this {
100
100
  if (Array.isArray(parseArgOrDefault)) {
101
- const opt = new Option(flags, description ?? "").choices(parseArgOrDefault)
101
+ const opt = new Option(flags, description ?? "").choices(parseArgOrDefault as string[])
102
102
  this.addOption(opt)
103
103
  return this
104
104
  }
105
105
  if (isStandardSchema(parseArgOrDefault)) {
106
- return super.option(flags, description ?? "", standardSchemaParser(parseArgOrDefault))
106
+ return super.option(flags, description ?? "", standardSchemaParser(parseArgOrDefault), defaultValue)
107
107
  }
108
108
  if (isLegacyZodSchema(parseArgOrDefault)) {
109
- return super.option(flags, description ?? "", legacyZodParser(parseArgOrDefault))
109
+ return super.option(flags, description ?? "", legacyZodParser(parseArgOrDefault), defaultValue)
110
110
  }
111
111
  if (typeof parseArgOrDefault === "function") {
112
112
  return super.option(flags, description ?? "", parseArgOrDefault, defaultValue)
@@ -115,7 +115,7 @@ export class Command extends BaseCommand {
115
115
  }
116
116
 
117
117
  // Subcommands also get colorized help, Standard Schema, and array choices
118
- createCommand(name?: string): Command {
118
+ override createCommand(name?: string): Command {
119
119
  return new Command(name)
120
120
  }
121
121
  }