@bemoje/cli 1.0.6 → 1.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 CHANGED
@@ -1,46 +1,33 @@
1
1
  # @bemoje/cli
2
2
 
3
- A type-safe CLI builder focused on command composition and help generation without execution coupling. Parse arguments, generate help, and integrate with existing CLI frameworks through a clean, fluent API.
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 - parse arguments and generate help only
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
- .argument('<input>', 'Input file path')
34
- .argument('[output]', 'Output file path', { defaultValue: 'out.txt' })
35
- .option('-v, --verbose', 'Enable verbose output')
36
- .option('-f, --format <type>', 'Output format', { choices: ['json', 'xml', 'yaml'] })
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('Arguments:', result.arguments) // ['input.txt', 'out.txt']
42
- console.log('Options:', result.options) // { verbose: true, format: 'json' }
43
- console.log('Command:', result.command.name) // 'myapp'
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.argument('<input>', 'Input file path')
51
+ cmd.addArgument('<input>', 'Input file path')
65
52
 
66
53
  // Optional argument with default
67
- cmd.argument('[output]', 'Output file path', { defaultValue: 'dist/output.txt' })
54
+ cmd.addArgument('[output]', 'Output file path', { defaultValue: 'dist/output.txt' })
68
55
 
69
56
  // Required variadic (multiple values)
70
- cmd.argument('<files...>', 'Multiple input files')
57
+ cmd.addArgument('<files...>', 'Multiple input files')
71
58
 
72
59
  // Optional variadic with defaults
73
- cmd.argument('[patterns...]', 'Glob patterns', { defaultValue: ['**/*.js'] })
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.option('-v, --verbose', 'Enable verbose output')
67
+ cmd.addOption('-v, --verbose', 'Enable verbose output')
81
68
 
82
69
  // Required string option
83
- cmd.option('-f, --format <type>', 'Output format')
70
+ cmd.addOption('-f, --format <type>', 'Output format')
84
71
 
85
72
  // Optional string option with default
86
- cmd.option('-o, --output [path]', 'Output directory', { defaultValue: 'dist' })
73
+ cmd.addOption('-o, --output [path]', 'Output directory', { defaultValue: 'dist' })
87
74
 
88
75
  // Required variadic option
89
- cmd.option('-i, --include <patterns...>', 'Include patterns')
76
+ cmd.addOption('-i, --include <patterns...>', 'Include patterns')
90
77
 
91
78
  // Optional variadic option with defaults
92
- cmd.option('-e, --exclude [patterns...]', 'Exclude patterns', {
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.option('-l, --log-level [level]', 'Log level', {
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').option('-c, --config <file>', 'Config file')
96
+ const app = new Command('myapp').addOption('-c, --config <file>', 'Config file')
110
97
 
111
- app.subcommand('build').option('-w, --watch', 'Watch mode')
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.parse(['build', '--config', 'myconfig.json', '--watch'])
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
- .subcommand('add')
111
+ .addSubcommand('add')
125
112
  .setDescription('Add files to staging area')
126
- .argument('<files...>', 'Files to add')
127
- .option('-A, --all', 'Add all files')
113
+ .addArgument('<files...>', 'Files to add')
114
+ .addOption('-A, --all', 'Add all files')
128
115
 
129
116
  const commit = cmd
130
- .subcommand('commit')
117
+ .addSubcommand('commit')
131
118
  .setDescription('Create a commit')
132
- .argument('[message]', 'Commit message')
133
- .option('-m, --message <msg>', 'Commit message')
134
- .option('-a, --all', 'Commit all changes')
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.parse(['add', 'file1.js', 'file2.js', '-A'])
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 default help formatting
173
- console.log(cmd.renderHelp())
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.argument('[optional]', 'Optional arg').argument('<required>', 'Required arg')
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.option('-v, --verbose', 'Verbose output').option('-v, --video', 'Video mode')
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.argument('<files...>', 'First variadic').argument('<more...>', 'Second variadic')
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 Command(name: string, parent?: Command)
214
+ - new (name: string, parent?: Command): Command
327
215
  ```
328
216
 
329
217
  **Structure Methods**:
330
218
 
331
219
  ```ts
332
- - argument(usage, description, options?): this
333
- - option(usage, description, options?): this
334
- - subcommand(name: string): Command
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
- - parse(argv?: string[], globalOptions?: OptionDescriptor[]): ParseResult
357
- - renderHelp(help?: IHelp): string
244
+ - parseArgv(argv?: string[], globalOptions?: OptionDescriptor[]): ParseResult
245
+ - renderHelp(help: IHelp): string
358
246
  ```
package/index.d.ts CHANGED
@@ -1,6 +1,3 @@
1
1
  export * from './lib/Command';
2
- export * from './lib/CommandHelpAdapter';
3
- export * from './lib/CommanderHelpAdapter';
4
2
  export * from './lib/Help';
5
- export * from './lib/renderHelp';
6
3
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,6 +1,3 @@
1
1
  export * from "./lib/Command.js";
2
- export * from "./lib/CommandHelpAdapter.js";
3
- export * from "./lib/CommanderHelpAdapter.js";
4
2
  export * from "./lib/Help.js";
5
- export * from "./lib/renderHelp.js";
6
3
  //# sourceMappingURL=index.js.map