@bemoje/cli 0.3.0 β†’ 1.0.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.
Files changed (3) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +358 -0
  3. package/package.json +26 -5
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Benjamin MΓΈller Jensen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,358 @@
1
+ # @bemoje/cli
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.
4
+
5
+ ## Key Features
6
+
7
+ - **🎯 Composition-Focused**: Build command structures without execution logic - parse arguments and generate help only
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
+ - **🎨 Flexible Help**: Fork of commander.js Help class with enhanced API and adapter support
11
+ - **βœ… 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
+
24
+ ## Quick Start
25
+
26
+ ```ts
27
+ import { Command } from '@bemoje/cli'
28
+
29
+ // Define command structure
30
+ const cmd = new Command('myapp')
31
+ .setVersion('1.0.0')
32
+ .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))
40
+
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'
44
+ ```
45
+
46
+ ## Command Definition
47
+
48
+ ### Basic Setup
49
+
50
+ ```ts
51
+ const cmd = new Command('myapp')
52
+ .setVersion('1.0.0')
53
+ .setDescription('Application description')
54
+ .setSummary('Short summary for help')
55
+ .setAliases(['app', 'my-app'])
56
+ ```
57
+
58
+ ### Arguments (Positional)
59
+
60
+ Arguments follow strict ordering rules: required β†’ optional β†’ variadic
61
+
62
+ ```ts
63
+ // Required argument
64
+ cmd.argument('<input>', 'Input file path')
65
+
66
+ // Optional argument with default
67
+ cmd.argument('[output]', 'Output file path', { defaultValue: 'dist/output.txt' })
68
+
69
+ // Required variadic (multiple values)
70
+ cmd.argument('<files...>', 'Multiple input files')
71
+
72
+ // Optional variadic with defaults
73
+ cmd.argument('[patterns...]', 'Glob patterns', { defaultValue: ['**/*.js'] })
74
+ ```
75
+
76
+ ### Options (Named Parameters)
77
+
78
+ ```ts
79
+ // Boolean flag
80
+ cmd.option('-v, --verbose', 'Enable verbose output')
81
+
82
+ // Required string option
83
+ cmd.option('-f, --format <type>', 'Output format')
84
+
85
+ // Optional string option with default
86
+ cmd.option('-o, --output [path]', 'Output directory', { defaultValue: 'dist' })
87
+
88
+ // Required variadic option
89
+ cmd.option('-i, --include <patterns...>', 'Include patterns')
90
+
91
+ // Optional variadic option with defaults
92
+ cmd.option('-e, --exclude [patterns...]', 'Exclude patterns', {
93
+ defaultValue: ['node_modules', '.git'],
94
+ })
95
+
96
+ // Option with choices and environment variable
97
+ cmd.option('-l, --log-level [level]', 'Log level', {
98
+ choices: ['error', 'warn', 'info', 'debug'],
99
+ defaultValue: 'info',
100
+ env: 'LOG_LEVEL',
101
+ })
102
+ ```
103
+
104
+ ### Global Options
105
+
106
+ Options defined on parent commands are available to subcommands:
107
+
108
+ ```ts
109
+ const app = new Command('myapp').option('-c, --config <file>', 'Config file')
110
+
111
+ app.subcommand('build').option('-w, --watch', 'Watch mode')
112
+
113
+ // Both --config and --watch are available to 'build' subcommand
114
+ const result = app.parse(['build', '--config', 'myconfig.json', '--watch'])
115
+ ```
116
+
117
+ ### Subcommands
118
+
119
+ ```ts
120
+ const cmd = new Command('git')
121
+
122
+ // Create subcommand
123
+ const add = cmd
124
+ .subcommand('add')
125
+ .setDescription('Add files to staging area')
126
+ .argument('<files...>', 'Files to add')
127
+ .option('-A, --all', 'Add all files')
128
+
129
+ const commit = cmd
130
+ .subcommand('commit')
131
+ .setDescription('Create a commit')
132
+ .argument('[message]', 'Commit message')
133
+ .option('-m, --message <msg>', 'Commit message')
134
+ .option('-a, --all', 'Commit all changes')
135
+
136
+ // Parsing automatically routes to subcommands
137
+ const result = cmd.parse(['add', 'file1.js', 'file2.js', '-A'])
138
+ // result.command === add subcommand instance
139
+ ```
140
+
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
+ ## Help System
166
+
167
+ ### Rendering Help
168
+
169
+ ```ts
170
+ import { Help } from '@bemoje/cli'
171
+
172
+ // Use default help formatting
173
+ console.log(cmd.renderHelp())
174
+
175
+ // Customize help configuration
176
+ const customHelp = new Help()
177
+ customHelp.helpWidth = 100
178
+ customHelp.sortOptions = true
179
+ customHelp.showGlobalOptions = true
180
+
181
+ console.log(cmd.renderHelp(customHelp))
182
+ ```
183
+
184
+ ### Help Configuration
185
+
186
+ Configure help behavior per command:
187
+
188
+ ```ts
189
+ cmd.setHelpConfiguration({
190
+ sortOptions: true,
191
+ sortSubcommands: true,
192
+ showGlobalOptions: false,
193
+ helpWidth: 80,
194
+ })
195
+ ```
196
+
197
+ ### Custom Help Styling
198
+
199
+ It may be more convenient to extend the Help class for more extensive customization.
200
+
201
+ ```ts
202
+ import { Help } from '@bemoje/cli'
203
+
204
+ class ColoredHelp extends Help {
205
+ styleTitle(str: string): string {
206
+ return `\x1b[1m${str}\x1b[0m` // Bold
207
+ }
208
+
209
+ styleOptionText(str: string): string {
210
+ return `\x1b[36m${str}\x1b[0m` // Cyan
211
+ }
212
+
213
+ styleArgumentText(str: string): string {
214
+ return `\x1b[33m${str}\x1b[0m` // Yellow
215
+ }
216
+ }
217
+
218
+ console.log(cmd.renderHelp(new ColoredHelp()))
219
+ ```
220
+
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
+ ## Validation
284
+
285
+ Commands automatically validate:
286
+
287
+ - Argument ordering (required before optional before variadic)
288
+
289
+ ```ts
290
+ cmd.argument('[optional]', 'Optional arg').argument('<required>', 'Required arg')
291
+ //=> ❌ Error!
292
+ ```
293
+
294
+ - Unique option names and short flags, including globals across parent/child commands
295
+
296
+ ```ts
297
+ cmd.option('-v, --verbose', 'Verbose output').option('-v, --video', 'Video mode')
298
+ //=> ❌ Error!
299
+ ```
300
+
301
+ - Single variadic argument per command
302
+
303
+ ```ts
304
+ cmd.argument('<files...>', 'First variadic').argument('<more...>', 'Second variadic')
305
+ //=> ❌ Error!
306
+ ```
307
+
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
+ ## Command Class
322
+
323
+ **Constructor**:
324
+
325
+ ```ts
326
+ - new Command(name: string, parent?: Command)
327
+ ```
328
+
329
+ **Structure Methods**:
330
+
331
+ ```ts
332
+ - argument(usage, description, options?): this
333
+ - option(usage, description, options?): this
334
+ - subcommand(name: string): Command
335
+ ```
336
+
337
+ **Configuration Methods**:
338
+
339
+ ```ts
340
+ - setVersion(version?: string): this
341
+ - setName(name: string): this
342
+ - setDescription(...lines: string[]): this
343
+ - setSummary(summary?: string): this
344
+ - setHidden(hidden?: boolean): this
345
+ - setGroup(group?: string): this
346
+ - setHelpConfiguration(config?: Partial<IHelp>): this
347
+ - extendHelpConfiguration(config: Partial<IHelp>): this
348
+ - setAliases(...aliases: (string | string[])[]): this
349
+ - addAliases(...aliases: (string | string[])[]): this
350
+ - setParent(parent: Command | null): this
351
+ ```
352
+
353
+ **Parsing & Help**:
354
+
355
+ ```ts
356
+ - parse(argv?: string[], globalOptions?: OptionDescriptor[]): ParseResult
357
+ - renderHelp(help?: IHelp): string
358
+ ```
package/package.json CHANGED
@@ -1,18 +1,19 @@
1
1
  {
2
2
  "name": "@bemoje/cli",
3
3
  "description": "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.",
4
- "version": "0.3.0",
4
+ "version": "1.0.1",
5
5
  "private": false,
6
6
  "sideEffects": false,
7
7
  "type": "module",
8
- "module": "index.js",
8
+ "module": "./index.js",
9
+ "main": "./index.js",
9
10
  "exports": {
10
11
  ".": "./lib/index.js",
11
12
  "./*": "./lib/*.js",
12
13
  "./package.json": "./package.json",
13
14
  "./index.d.ts": "./index.d.ts"
14
15
  },
15
- "typings": "index.d.ts",
16
+ "typings": "./index.d.ts",
16
17
  "dependencies": {},
17
18
  "devDependencies": {
18
19
  "commander": "^14.0.0"
@@ -23,6 +24,26 @@
23
24
  "url": "git+https://github.com/bemoje/mono.git"
24
25
  },
25
26
  "keywords": [
27
+ "cli",
28
+ "command-line",
29
+ "argument-parser",
30
+ "options-parser",
31
+ "typescript",
32
+ "type-safe",
33
+ "fluent-api",
34
+ "help-generation",
35
+ "commander",
36
+ "commander.js",
37
+ "subcommands",
38
+ "validation",
39
+ "parsing",
40
+ "composition",
41
+ "adapter",
42
+ "terminal",
43
+ "console",
44
+ "args",
45
+ "argv",
46
+ "bemoje",
26
47
  "cli"
27
48
  ],
28
49
  "author": {
@@ -30,5 +51,5 @@
30
51
  "email": "bemoje@bemoje.net",
31
52
  "url": "https://github.com/bemoje/"
32
53
  },
33
- "homepage": "https://github.com/bemoje/mono"
34
- }
54
+ "homepage": "https://github.com/bemoje/mono/tree/main/libs/cli"
55
+ }