@guanghechen/commander 4.5.1 → 4.7.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/CHANGELOG.md +14 -0
- package/README.md +86 -32
- package/lib/cjs/browser.cjs +1779 -0
- package/lib/cjs/{index.cjs → node.cjs} +620 -166
- package/lib/esm/browser.mjs +1764 -0
- package/lib/esm/{index.mjs → node.mjs} +618 -148
- package/lib/types/browser.d.ts +551 -0
- package/lib/types/{index.d.ts → node.d.ts} +159 -69
- package/package.json +15 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 4.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add explicit runtime entry points for browser and node, and align command runtime abstractions and
|
|
8
|
+
tests.
|
|
9
|
+
|
|
10
|
+
## 4.6.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- Add preset-root and command-level preset resolution for commander, align control/preset pipeline
|
|
15
|
+
behavior with spec, and include updated preset parsing and validation semantics.
|
|
16
|
+
|
|
3
17
|
## 4.5.1
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -52,6 +52,8 @@
|
|
|
52
52
|
A minimal, type-safe command-line interface builder with fluent API. Supports subcommands, option
|
|
53
53
|
parsing, shell completion generation (bash, fish, pwsh), and built-in help/version handling.
|
|
54
54
|
|
|
55
|
+
`opts` / `args` are designed for strong type inference from the current command's own declarations.
|
|
56
|
+
|
|
55
57
|
## Install
|
|
56
58
|
|
|
57
59
|
- npm
|
|
@@ -71,12 +73,12 @@ parsing, shell completion generation (bash, fish, pwsh), and built-in help/versi
|
|
|
71
73
|
### Basic Command
|
|
72
74
|
|
|
73
75
|
```typescript
|
|
74
|
-
import { Command } from '@guanghechen/commander'
|
|
76
|
+
import { Command } from '@guanghechen/commander/browser'
|
|
75
77
|
|
|
76
78
|
const cli = new Command({
|
|
77
79
|
name: 'mycli',
|
|
78
80
|
version: '1.0.0',
|
|
79
|
-
|
|
81
|
+
desc: 'My awesome CLI tool',
|
|
80
82
|
})
|
|
81
83
|
|
|
82
84
|
cli
|
|
@@ -84,25 +86,27 @@ cli
|
|
|
84
86
|
long: 'verbose',
|
|
85
87
|
short: 'v',
|
|
86
88
|
type: 'boolean',
|
|
87
|
-
|
|
89
|
+
args: 'none',
|
|
90
|
+
desc: 'Enable verbose output',
|
|
88
91
|
})
|
|
89
92
|
.option({
|
|
90
93
|
long: 'output',
|
|
91
94
|
short: 'o',
|
|
92
95
|
type: 'string',
|
|
93
|
-
|
|
96
|
+
args: 'required',
|
|
97
|
+
desc: 'Output file path',
|
|
94
98
|
default: './output.txt',
|
|
95
99
|
})
|
|
96
100
|
.argument({
|
|
97
101
|
name: 'file',
|
|
98
102
|
kind: 'required',
|
|
99
|
-
|
|
103
|
+
desc: 'Input file to process',
|
|
100
104
|
})
|
|
101
105
|
.action(({ opts, args, ctx }) => {
|
|
102
|
-
const
|
|
106
|
+
const file = String(args.file)
|
|
103
107
|
ctx.reporter.info(`Processing ${file}...`)
|
|
104
|
-
if (opts
|
|
105
|
-
ctx.reporter.debug(`Output: ${opts
|
|
108
|
+
if (opts.verbose) {
|
|
109
|
+
ctx.reporter.debug(`Output: ${opts.output}`)
|
|
106
110
|
}
|
|
107
111
|
})
|
|
108
112
|
|
|
@@ -115,30 +119,30 @@ cli.run({
|
|
|
115
119
|
### Subcommands
|
|
116
120
|
|
|
117
121
|
```typescript
|
|
118
|
-
import { Command } from '@guanghechen/commander'
|
|
122
|
+
import { Command } from '@guanghechen/commander/browser'
|
|
119
123
|
|
|
120
124
|
const root = new Command({
|
|
121
125
|
name: 'git',
|
|
122
126
|
version: '1.0.0',
|
|
123
|
-
|
|
127
|
+
desc: 'A simple git-like CLI',
|
|
124
128
|
})
|
|
125
129
|
|
|
126
130
|
const clone = new Command({
|
|
127
|
-
|
|
131
|
+
desc: 'Clone a repository',
|
|
128
132
|
})
|
|
129
|
-
.argument({ name: 'url', kind: 'required',
|
|
130
|
-
.option({ long: 'depth', type: 'number',
|
|
133
|
+
.argument({ name: 'url', kind: 'required', desc: 'Repository URL' })
|
|
134
|
+
.option({ long: 'depth', type: 'number', args: 'required', desc: 'Shallow clone depth' })
|
|
131
135
|
.action(({ args, opts }) => {
|
|
132
|
-
console.log(`Cloning ${args
|
|
136
|
+
console.log(`Cloning ${args.url} with depth ${opts.depth ?? 'full'}`)
|
|
133
137
|
})
|
|
134
138
|
|
|
135
139
|
const commit = new Command({
|
|
136
|
-
|
|
140
|
+
desc: 'Record changes to the repository',
|
|
137
141
|
})
|
|
138
|
-
.option({ long: 'message', short: 'm', type: 'string', required: true,
|
|
139
|
-
.option({ long: 'amend', type: 'boolean',
|
|
142
|
+
.option({ long: 'message', short: 'm', type: 'string', args: 'required', required: true, desc: 'Commit message' })
|
|
143
|
+
.option({ long: 'amend', type: 'boolean', args: 'none', desc: 'Amend previous commit' })
|
|
140
144
|
.action(({ opts }) => {
|
|
141
|
-
console.log(`Committing: ${opts
|
|
145
|
+
console.log(`Committing: ${opts.message}`)
|
|
142
146
|
})
|
|
143
147
|
|
|
144
148
|
root.subcommand('clone', clone).subcommand('commit', commit).subcommand('ci', commit)
|
|
@@ -149,12 +153,13 @@ root.run({ argv: process.argv.slice(2), envs: process.env })
|
|
|
149
153
|
### Shell Completion
|
|
150
154
|
|
|
151
155
|
```typescript
|
|
152
|
-
import { Command
|
|
156
|
+
import { Command } from '@guanghechen/commander/browser'
|
|
157
|
+
import { CompletionCommand } from '@guanghechen/commander/node'
|
|
153
158
|
|
|
154
159
|
const root = new Command({
|
|
155
160
|
name: 'mycli',
|
|
156
161
|
version: '1.0.0',
|
|
157
|
-
|
|
162
|
+
desc: 'My CLI with completion support',
|
|
158
163
|
})
|
|
159
164
|
|
|
160
165
|
// Add completion subcommand
|
|
@@ -169,43 +174,92 @@ root.subcommand('completion', new CompletionCommand(root))
|
|
|
169
174
|
### Option Types
|
|
170
175
|
|
|
171
176
|
```typescript
|
|
172
|
-
import { Command } from '@guanghechen/commander'
|
|
177
|
+
import { Command } from '@guanghechen/commander/browser'
|
|
173
178
|
|
|
174
|
-
new Command({ name: 'example',
|
|
179
|
+
new Command({ name: 'example', desc: 'Option types demo' })
|
|
175
180
|
// Boolean (flags)
|
|
176
|
-
.option({ long: 'debug', type: 'boolean',
|
|
181
|
+
.option({ long: 'debug', type: 'boolean', args: 'none', desc: 'Enable debug mode' })
|
|
177
182
|
|
|
178
183
|
// String with choices
|
|
179
184
|
.option({
|
|
180
185
|
long: 'format',
|
|
181
186
|
type: 'string',
|
|
187
|
+
args: 'required',
|
|
182
188
|
choices: ['json', 'yaml', 'toml'],
|
|
183
189
|
default: 'json',
|
|
184
|
-
|
|
190
|
+
desc: 'Output format'
|
|
185
191
|
})
|
|
186
192
|
|
|
187
193
|
// Number
|
|
188
|
-
.option({ long: 'port', type: 'number', default: 3000,
|
|
194
|
+
.option({ long: 'port', type: 'number', args: 'required', default: 3000, desc: 'Server port' })
|
|
189
195
|
|
|
190
|
-
// Array (
|
|
191
|
-
.option({ long: 'include', type: 'string
|
|
196
|
+
// Array (generated by variadic args, not a standalone type)
|
|
197
|
+
.option({ long: 'include', type: 'string', args: 'variadic', desc: 'Files to include' })
|
|
192
198
|
|
|
193
199
|
// Required option
|
|
194
|
-
.option({ long: 'config', type: 'string', required: true,
|
|
200
|
+
.option({ long: 'config', type: 'string', args: 'required', required: true, desc: 'Config file' })
|
|
195
201
|
|
|
196
202
|
// Custom coercion
|
|
197
203
|
.option({
|
|
198
204
|
long: 'date',
|
|
199
205
|
type: 'string',
|
|
206
|
+
args: 'required',
|
|
200
207
|
coerce: (value) => new Date(value),
|
|
201
|
-
|
|
208
|
+
desc: 'Date value',
|
|
202
209
|
})
|
|
203
210
|
```
|
|
204
211
|
|
|
212
|
+
### Preset Input Files
|
|
213
|
+
|
|
214
|
+
`--preset-opts=<file>` and `--preset-envs=<file>` allow injecting preset argv and
|
|
215
|
+
env inputs before normal CLI parsing.
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
mycli --preset-opts=./options.argv --preset-envs=./preset.env --log-level debug --color
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Behavior:
|
|
222
|
+
|
|
223
|
+
1. Route command chain from user argv (name/alias only, no argv rewrite), then store route tokens in `sources.user.cmds`.
|
|
224
|
+
2. Run `control-scan` on user tail argv before preset merge: detect `--help` / `--version` by token scan (`--version` only when `supportsBuiltinVersion(leaf)`), detect `help` only when it is the first tail token, write `ctx.controls`, and strip control tokens from parse input.
|
|
225
|
+
3. In `run()`, execute `run-control` before preset merge: short-circuit by `help > version`. If short-circuit hits, preset files are not loaded.
|
|
226
|
+
4. Scan preset directives before `--`, remove them from control-tail argv, and store cleaned tokens in `sources.user.argv`.
|
|
227
|
+
5. Read options preset file(s) and tokenize by whitespace to `sources.preset.argv`.
|
|
228
|
+
6. Read env preset file(s) and parse via `@guanghechen/env.parse` to `sources.preset.envs`.
|
|
229
|
+
7. Build `effectiveTailArgv = [...sources.preset.argv, ...sources.user.argv]`.
|
|
230
|
+
8. Build `ctx.envs = { ...sources.user.envs, ...sources.preset.envs }`.
|
|
231
|
+
9. Expose source snapshots through `ctx.sources` and reuse existing tokenize/resolve/parse pipeline.
|
|
232
|
+
|
|
233
|
+
Precedence for same option key:
|
|
234
|
+
|
|
235
|
+
1. User CLI tokens (highest)
|
|
236
|
+
2. Tokens loaded from `--preset-opts`
|
|
237
|
+
3. Option `default` / implicit defaults
|
|
238
|
+
4. `NO_COLOR` fallback for color rendering only (applies only when no explicit `--color/--no-color` token appears)
|
|
239
|
+
|
|
240
|
+
Precedence for same env key:
|
|
241
|
+
|
|
242
|
+
1. Key-values loaded from `--preset-envs` (highest)
|
|
243
|
+
2. User envs (e.g. `process.env`)
|
|
244
|
+
|
|
245
|
+
Additional notes:
|
|
246
|
+
|
|
247
|
+
1. `variadic` options append in appearance order.
|
|
248
|
+
2. `NO_COLOR` is evaluated from `ctx.envs` and remains a fallback only when no color token is explicitly provided.
|
|
249
|
+
3. The `--preset-opts` file is expected to contain option fragments (`-x`/`--xxx` and their values), not command-route tokens.
|
|
250
|
+
4. The `--preset-envs` file must be parseable by `@guanghechen/env`.
|
|
251
|
+
5. Only preset flags before `--` are processed; after `--` they are treated as normal args.
|
|
252
|
+
6. Repeated preset flags are processed in appearance order.
|
|
253
|
+
7. Built-in control semantics recognize `--help` / `help` / `--version` only (no short aliases).
|
|
254
|
+
8. `long: 'help'` and `long: 'version'` are reserved and must not be user-defined in `.option()`.
|
|
255
|
+
9. `--help` / `help` / `--version` are forbidden in `--preset-opts` files; loading should fail fast.
|
|
256
|
+
10. `--` is forbidden inside `--preset-opts` files; loading should fail fast.
|
|
257
|
+
11. `parse()` never executes control handlers; it only records control hits in `ctx.controls`.
|
|
258
|
+
|
|
205
259
|
### Built-in Coerce Factories
|
|
206
260
|
|
|
207
261
|
```typescript
|
|
208
|
-
import { Coerce, Command } from '@guanghechen/commander'
|
|
262
|
+
import { Coerce, Command } from '@guanghechen/commander/browser'
|
|
209
263
|
|
|
210
264
|
new Command({ name: 'example', desc: 'Coerce demo' })
|
|
211
265
|
.option({
|
|
@@ -284,7 +338,7 @@ You can still override the message via `Coerce.xxx(name, 'custom error message')
|
|
|
284
338
|
### Built-in Is Helpers
|
|
285
339
|
|
|
286
340
|
```typescript
|
|
287
|
-
import { isDomain, isIp, isIpv4, isIpv6 } from '@guanghechen/commander'
|
|
341
|
+
import { isDomain, isIp, isIpv4, isIpv6 } from '@guanghechen/commander/browser'
|
|
288
342
|
|
|
289
343
|
isIpv4('127.0.0.1') // true
|
|
290
344
|
isIpv6('::1') // true
|
|
@@ -295,7 +349,7 @@ isDomain('example.com') // true
|
|
|
295
349
|
### Help Examples
|
|
296
350
|
|
|
297
351
|
```typescript
|
|
298
|
-
import { Command } from '@guanghechen/commander'
|
|
352
|
+
import { Command } from '@guanghechen/commander/browser'
|
|
299
353
|
|
|
300
354
|
const cli = new Command({ name: 'mycli', desc: 'My CLI tool' })
|
|
301
355
|
|