@bemoje/cli 1.0.6 → 1.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 +45 -157
- package/index.d.ts +0 -3
- package/index.js +0 -3
- package/lib/Command.d.ts +109 -112
- package/lib/Command.js +93 -106
- package/lib/Help.d.ts +41 -83
- package/lib/Help.js +14 -14
- package/package.json +4 -7
- package/lib/CommandHelpAdapter.d.ts +0 -157
- package/lib/CommandHelpAdapter.js +0 -102
- package/lib/CommanderHelpAdapter.d.ts +0 -56
- package/lib/CommanderHelpAdapter.js +0 -92
- package/lib/internal/lazyProp.d.ts +0 -5
- package/lib/internal/lazyProp.js +0 -24
- package/lib/renderHelp.d.ts +0 -6
- package/lib/renderHelp.js +0 -12
package/README.md
CHANGED
|
@@ -1,46 +1,33 @@
|
|
|
1
1
|
# @bemoje/cli
|
|
2
2
|
|
|
3
|
-
A type-safe CLI
|
|
3
|
+
A type-safe CLI composer that can parse argv and generate help without execution coupling.
|
|
4
4
|
|
|
5
5
|
## Key Features
|
|
6
6
|
|
|
7
|
-
- **🎯 Composition-Focused**: Build command structures without execution logic
|
|
7
|
+
- **🎯 Composition-Focused**: Build command structures without execution logic.
|
|
8
8
|
- **🔒 Type-Safe**: Full TypeScript support with type inference for arguments and options
|
|
9
|
-
- **🚀 Focused API**: Streamlined interface designed specifically for parsing and help generation
|
|
10
9
|
- **🎨 Flexible Help**: Fork of commander.js Help class with enhanced API and adapter support
|
|
11
10
|
- **✅ Validation**: Built-in CLI argument ordering validation and name conflict detection
|
|
12
|
-
- **🔗 Commander.js Compatible**: Adapter allows using this Help system with existing commander.js commands
|
|
13
|
-
|
|
14
|
-
## Core Philosophy
|
|
15
|
-
|
|
16
|
-
While commander.js provides a comprehensive solution that combines command definition with action execution, `@bemoje/cli` takes a different approach by focusing purely on:
|
|
17
|
-
|
|
18
|
-
1. **Command Structure Definition** - Define arguments, options, and subcommands
|
|
19
|
-
2. **Argument Parsing** - Parse `process.argv` into structured data
|
|
20
|
-
3. **Help Generation** - Render formatted help text with customizable styling
|
|
21
|
-
|
|
22
|
-
This separation of concerns allows for architectures where CLI parsing logic is decoupled from business logic, making it ideal for scenarios where you want to handle argument parsing and action execution in separate layers.
|
|
23
11
|
|
|
24
12
|
## Quick Start
|
|
25
13
|
|
|
26
14
|
```ts
|
|
27
15
|
import { Command } from '@bemoje/cli'
|
|
28
16
|
|
|
29
|
-
// Define command structure
|
|
30
17
|
const cmd = new Command('myapp')
|
|
31
18
|
.setVersion('1.0.0')
|
|
32
19
|
.setDescription('My awesome CLI application')
|
|
33
|
-
.
|
|
34
|
-
.
|
|
35
|
-
.
|
|
36
|
-
.
|
|
37
|
-
|
|
38
|
-
// Parse command line arguments
|
|
39
|
-
const result = cmd.parse(process.argv.slice(2))
|
|
20
|
+
.addArgument('<input>', 'Input file path')
|
|
21
|
+
.addArgument('[output]', 'Output file path', { defaultValue: 'out.txt' })
|
|
22
|
+
.addOption('-v, --verbose', 'Enable verbose output')
|
|
23
|
+
.addOption('-f, --format <type>', 'Output format', { choices: ['json', 'xml', 'yaml'] })
|
|
40
24
|
|
|
41
|
-
console.log(
|
|
42
|
-
|
|
43
|
-
|
|
25
|
+
console.log(cmd.parseArgv(['input.txt', '-v', '-f', 'json']))
|
|
26
|
+
// {
|
|
27
|
+
// command: [Getter],
|
|
28
|
+
// arguments: [ 'input.txt', 'out.txt' ],
|
|
29
|
+
// options: { verbose: true, format: 'json' }
|
|
30
|
+
// }
|
|
44
31
|
```
|
|
45
32
|
|
|
46
33
|
## Command Definition
|
|
@@ -61,40 +48,40 @@ Arguments follow strict ordering rules: required → optional → variadic
|
|
|
61
48
|
|
|
62
49
|
```ts
|
|
63
50
|
// Required argument
|
|
64
|
-
cmd.
|
|
51
|
+
cmd.addArgument('<input>', 'Input file path')
|
|
65
52
|
|
|
66
53
|
// Optional argument with default
|
|
67
|
-
cmd.
|
|
54
|
+
cmd.addArgument('[output]', 'Output file path', { defaultValue: 'dist/output.txt' })
|
|
68
55
|
|
|
69
56
|
// Required variadic (multiple values)
|
|
70
|
-
cmd.
|
|
57
|
+
cmd.addArgument('<files...>', 'Multiple input files')
|
|
71
58
|
|
|
72
59
|
// Optional variadic with defaults
|
|
73
|
-
cmd.
|
|
60
|
+
cmd.addArgument('[patterns...]', 'Glob patterns', { defaultValue: ['**/*.js'] })
|
|
74
61
|
```
|
|
75
62
|
|
|
76
63
|
### Options (Named Parameters)
|
|
77
64
|
|
|
78
65
|
```ts
|
|
79
66
|
// Boolean flag
|
|
80
|
-
cmd.
|
|
67
|
+
cmd.addOption('-v, --verbose', 'Enable verbose output')
|
|
81
68
|
|
|
82
69
|
// Required string option
|
|
83
|
-
cmd.
|
|
70
|
+
cmd.addOption('-f, --format <type>', 'Output format')
|
|
84
71
|
|
|
85
72
|
// Optional string option with default
|
|
86
|
-
cmd.
|
|
73
|
+
cmd.addOption('-o, --output [path]', 'Output directory', { defaultValue: 'dist' })
|
|
87
74
|
|
|
88
75
|
// Required variadic option
|
|
89
|
-
cmd.
|
|
76
|
+
cmd.addOption('-i, --include <patterns...>', 'Include patterns')
|
|
90
77
|
|
|
91
78
|
// Optional variadic option with defaults
|
|
92
|
-
cmd.
|
|
79
|
+
cmd.addOption('-e, --exclude [patterns...]', 'Exclude patterns', {
|
|
93
80
|
defaultValue: ['node_modules', '.git'],
|
|
94
81
|
})
|
|
95
82
|
|
|
96
83
|
// Option with choices and environment variable
|
|
97
|
-
cmd.
|
|
84
|
+
cmd.addOption('-l, --log-level [level]', 'Log level', {
|
|
98
85
|
choices: ['error', 'warn', 'info', 'debug'],
|
|
99
86
|
defaultValue: 'info',
|
|
100
87
|
env: 'LOG_LEVEL',
|
|
@@ -106,12 +93,12 @@ cmd.option('-l, --log-level [level]', 'Log level', {
|
|
|
106
93
|
Options defined on parent commands are available to subcommands:
|
|
107
94
|
|
|
108
95
|
```ts
|
|
109
|
-
const app = new Command('myapp').
|
|
96
|
+
const app = new Command('myapp').addOption('-c, --config <file>', 'Config file')
|
|
110
97
|
|
|
111
|
-
app.
|
|
98
|
+
app.addSubcommand('build').addOption('-w, --watch', 'Watch mode')
|
|
112
99
|
|
|
113
100
|
// Both --config and --watch are available to 'build' subcommand
|
|
114
|
-
const result = app.
|
|
101
|
+
const result = app.parseArgv(['build', '--config', 'myconfig.json', '--watch'])
|
|
115
102
|
```
|
|
116
103
|
|
|
117
104
|
### Subcommands
|
|
@@ -121,47 +108,23 @@ const cmd = new Command('git')
|
|
|
121
108
|
|
|
122
109
|
// Create subcommand
|
|
123
110
|
const add = cmd
|
|
124
|
-
.
|
|
111
|
+
.addSubcommand('add')
|
|
125
112
|
.setDescription('Add files to staging area')
|
|
126
|
-
.
|
|
127
|
-
.
|
|
113
|
+
.addArgument('<files...>', 'Files to add')
|
|
114
|
+
.addOption('-A, --all', 'Add all files')
|
|
128
115
|
|
|
129
116
|
const commit = cmd
|
|
130
|
-
.
|
|
117
|
+
.addSubcommand('commit')
|
|
131
118
|
.setDescription('Create a commit')
|
|
132
|
-
.
|
|
133
|
-
.
|
|
134
|
-
.
|
|
119
|
+
.addArgument('[message]', 'Commit message')
|
|
120
|
+
.addOption('-m, --message <msg>', 'Commit message')
|
|
121
|
+
.addOption('-a, --all', 'Commit all changes')
|
|
135
122
|
|
|
136
123
|
// Parsing automatically routes to subcommands
|
|
137
|
-
const result = cmd.
|
|
124
|
+
const result = cmd.parseArgv(['add', 'file1.js', 'file2.js', '-A'])
|
|
138
125
|
// result.command === add subcommand instance
|
|
139
126
|
```
|
|
140
127
|
|
|
141
|
-
## Parsing Results
|
|
142
|
-
|
|
143
|
-
The `parse()` method returns a structured result:
|
|
144
|
-
|
|
145
|
-
```ts
|
|
146
|
-
interface ParseResult {
|
|
147
|
-
command: Command // The command that was executed (handles subcommands)
|
|
148
|
-
arguments: unknown[] // Parsed positional arguments
|
|
149
|
-
options: Record<string, unknown> // Parsed options/flags
|
|
150
|
-
}
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### Argument Types
|
|
154
|
-
|
|
155
|
-
- **Single values**: `string`
|
|
156
|
-
- **Optional with defaults**: `string` (default applied if not provided)
|
|
157
|
-
- **Variadic**: `string[]` (array of values)
|
|
158
|
-
|
|
159
|
-
### Option Types
|
|
160
|
-
|
|
161
|
-
- **Boolean flags**: `boolean`
|
|
162
|
-
- **String options**: `string`
|
|
163
|
-
- **Variadic options**: `string[]`
|
|
164
|
-
|
|
165
128
|
## Help System
|
|
166
129
|
|
|
167
130
|
### Rendering Help
|
|
@@ -169,15 +132,15 @@ interface ParseResult {
|
|
|
169
132
|
```ts
|
|
170
133
|
import { Help } from '@bemoje/cli'
|
|
171
134
|
|
|
172
|
-
// Use
|
|
173
|
-
|
|
135
|
+
// Use help formatting (requires Help instance)
|
|
136
|
+
const help = new Help()
|
|
137
|
+
console.log(cmd.renderHelp(help))
|
|
174
138
|
|
|
175
139
|
// Customize help configuration
|
|
176
140
|
const customHelp = new Help()
|
|
177
141
|
customHelp.helpWidth = 100
|
|
178
142
|
customHelp.sortOptions = true
|
|
179
143
|
customHelp.showGlobalOptions = true
|
|
180
|
-
|
|
181
144
|
console.log(cmd.renderHelp(customHelp))
|
|
182
145
|
```
|
|
183
146
|
|
|
@@ -218,68 +181,6 @@ class ColoredHelp extends Help {
|
|
|
218
181
|
console.log(cmd.renderHelp(new ColoredHelp()))
|
|
219
182
|
```
|
|
220
183
|
|
|
221
|
-
## Commander.js Integration
|
|
222
|
-
|
|
223
|
-
Use the enhanced Help system with existing commander.js commands:
|
|
224
|
-
|
|
225
|
-
```ts
|
|
226
|
-
import { Command } from 'commander'
|
|
227
|
-
import { CommanderHelpAdapter } from '@bemoje/cli'
|
|
228
|
-
|
|
229
|
-
// Existing commander.js command
|
|
230
|
-
const commanderCmd = new Command('example')
|
|
231
|
-
.description('Example commander.js command')
|
|
232
|
-
.option('-v, --verbose', 'verbose output')
|
|
233
|
-
|
|
234
|
-
// Use enhanced help system
|
|
235
|
-
const adapter = new CommanderHelpAdapter(commanderCmd)
|
|
236
|
-
console.log(adapter.renderHelp())
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
## Custom Adapters
|
|
240
|
-
|
|
241
|
-
You can create your own adapters for any command system by implementing the `ICommandHelp` interface. This allows you to use the enhanced Help system with any CLI library or custom command structure.
|
|
242
|
-
|
|
243
|
-
### Creating Custom Adapters
|
|
244
|
-
|
|
245
|
-
**Example**: Adapter for a hypothetical CLI library:
|
|
246
|
-
|
|
247
|
-
```ts
|
|
248
|
-
import { ICommandHelp, IHelp, Help, renderHelp } from '@bemoje/cli'
|
|
249
|
-
|
|
250
|
-
class MyAdapter implements ICommandHelp {
|
|
251
|
-
constructor(private cmd: SomeOtherCliCommand) {}
|
|
252
|
-
|
|
253
|
-
// implement ICommandHelp interface methods
|
|
254
|
-
get name() {
|
|
255
|
-
return this.cmd.getName()
|
|
256
|
-
}
|
|
257
|
-
get commands() {
|
|
258
|
-
return this.cmd.getSubcommands().map((sub) => {
|
|
259
|
-
return new MyAdapter(sub)
|
|
260
|
-
})
|
|
261
|
-
}
|
|
262
|
-
get options() {
|
|
263
|
-
return this.cmd.getOptions().map((opt) => ({
|
|
264
|
-
flags: opt.flags,
|
|
265
|
-
//... map props
|
|
266
|
-
}))
|
|
267
|
-
}
|
|
268
|
-
// etc...
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const myCommand = new SomeOtherCliCommand('myapp')
|
|
272
|
-
const adapter = new MyAdapter(myCommand)
|
|
273
|
-
console.log(renderHelp(adapter, new Help()))
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
This pattern allows you to:
|
|
277
|
-
|
|
278
|
-
- **Bridge Different CLI Libraries**: Use the enhanced Help system with any command framework
|
|
279
|
-
- **Migrate Gradually**: Introduce better help formatting without rewriting existing commands
|
|
280
|
-
- **Standardize Help Output**: Consistent help formatting across different CLI tools in your project
|
|
281
|
-
- **Extend Legacy Systems**: Add modern help features to older CLI codebases
|
|
282
|
-
|
|
283
184
|
## Validation
|
|
284
185
|
|
|
285
186
|
Commands automatically validate:
|
|
@@ -287,51 +188,38 @@ Commands automatically validate:
|
|
|
287
188
|
- Argument ordering (required before optional before variadic)
|
|
288
189
|
|
|
289
190
|
```ts
|
|
290
|
-
cmd.
|
|
191
|
+
cmd.addArgument('[optional]', 'Optional arg').addArgument('<required>', 'Required arg')
|
|
291
192
|
//=> ❌ Error!
|
|
292
193
|
```
|
|
293
194
|
|
|
294
195
|
- Unique option names and short flags, including globals across parent/child commands
|
|
295
196
|
|
|
296
197
|
```ts
|
|
297
|
-
cmd.
|
|
198
|
+
cmd.addOption('-v, --verbose', 'Verbose output').addOption('-v, --video', 'Video mode')
|
|
298
199
|
//=> ❌ Error!
|
|
299
200
|
```
|
|
300
201
|
|
|
301
202
|
- Single variadic argument per command
|
|
302
203
|
|
|
303
204
|
```ts
|
|
304
|
-
cmd.
|
|
205
|
+
cmd.addArgument('<files...>', 'First variadic').addArgument('<more...>', 'Second variadic')
|
|
305
206
|
//=> ❌ Error!
|
|
306
207
|
```
|
|
307
208
|
|
|
308
|
-
## State Management
|
|
309
|
-
|
|
310
|
-
Commands can be serialized and restored:
|
|
311
|
-
|
|
312
|
-
```ts
|
|
313
|
-
// Serialize command configuration
|
|
314
|
-
const state = cmd.toJSON()
|
|
315
|
-
|
|
316
|
-
// Restore from state
|
|
317
|
-
const restoredCmd = new Command('temp')
|
|
318
|
-
restoredCmd.setState(state)
|
|
319
|
-
```
|
|
320
|
-
|
|
321
209
|
## Command Class
|
|
322
210
|
|
|
323
211
|
**Constructor**:
|
|
324
212
|
|
|
325
213
|
```ts
|
|
326
|
-
- new
|
|
214
|
+
- new (name: string, parent?: Command): Command
|
|
327
215
|
```
|
|
328
216
|
|
|
329
217
|
**Structure Methods**:
|
|
330
218
|
|
|
331
219
|
```ts
|
|
332
|
-
-
|
|
333
|
-
-
|
|
334
|
-
-
|
|
220
|
+
- addArgument(usage, description, options?): this
|
|
221
|
+
- addOption(usage, description, options?): this
|
|
222
|
+
- addSubcommand(name: string): Command
|
|
335
223
|
```
|
|
336
224
|
|
|
337
225
|
**Configuration Methods**:
|
|
@@ -353,6 +241,6 @@ restoredCmd.setState(state)
|
|
|
353
241
|
**Parsing & Help**:
|
|
354
242
|
|
|
355
243
|
```ts
|
|
356
|
-
-
|
|
357
|
-
- renderHelp(help
|
|
244
|
+
- parseArgv(argv?: string[], globalOptions?: OptionDescriptor[]): ParseResult
|
|
245
|
+
- renderHelp(help: IHelp): string
|
|
358
246
|
```
|
package/index.d.ts
CHANGED