@guanghechen/commander 1.0.12 → 2.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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2020-2023 guanghechen (https://github.com/guanghechen)
3
+ Copyright (c) 2023-present guanghechen (https://github.com/guanghechen)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <header>
2
2
  <h1 align="center">
3
- <a href="https://github.com/guanghechen/node-scaffolds/tree/@guanghechen/commander@1.0.12/packages/commander#readme">@guanghechen/commander</a>
3
+ <a href="https://github.com/guanghechen/sora/tree/@guanghechen/commander@1.0.0/packages/commander#readme">@guanghechen/commander</a>
4
4
  </h1>
5
5
  <div align="center">
6
6
  <a href="https://www.npmjs.com/package/@guanghechen/commander">
@@ -33,12 +33,6 @@
33
33
  src="https://img.shields.io/node/v/@guanghechen/commander"
34
34
  />
35
35
  </a>
36
- <a href="https://github.com/facebook/jest">
37
- <img
38
- alt="ESLint Version"
39
- src="https://img.shields.io/npm/dependency-version/@guanghechen/commander/peer/jest"
40
- />
41
- </a>
42
36
  <a href="https://github.com/facebook/jest">
43
37
  <img
44
38
  alt="Tested with Jest"
@@ -55,247 +49,165 @@
55
49
  </header>
56
50
  <br/>
57
51
 
58
- Utility functions for creating command program tools based on [commander.js][].
52
+ A minimal, type-safe command-line interface builder with fluent API. Supports subcommands, option
53
+ parsing, shell completion generation (bash, fish, pwsh), and built-in help/version handling.
59
54
 
60
55
  ## Install
61
56
 
62
- * npm
57
+ - npm
63
58
 
64
59
  ```bash
65
- npm install --save-dev @guanghechen/commander
60
+ npm install --save @guanghechen/commander
66
61
  ```
67
62
 
68
- * yarn
63
+ - yarn
69
64
 
70
65
  ```bash
71
- yarn add --dev @guanghechen/commander
66
+ yarn add @guanghechen/commander
72
67
  ```
73
68
 
74
69
  ## Usage
75
70
 
76
- This package extends [commander.js][] with enhanced functionality for building CLI applications with configuration management and sub-command support.
77
-
78
- ### Enhanced Command Class
79
-
80
- ```typescript
81
- import { Command, type ICommandActionCallback } from '@guanghechen/commander'
82
-
83
- interface IMyOptions extends ICommandConfigurationOptions {
84
- readonly input: string
85
- readonly output: string
86
- }
87
-
88
- const command = new Command()
89
- .name('my-tool')
90
- .description('My awesome CLI tool')
91
- .action<IMyOptions>((args, options, extra, self) => {
92
- console.log('Arguments:', args)
93
- console.log('Options:', options)
94
- console.log('Extra args:', extra)
95
- })
96
- ```
97
-
98
- ### Creating Top-Level Commands
71
+ ### Basic Command
99
72
 
100
73
  ```typescript
101
- import { createTopCommand } from '@guanghechen/commander'
74
+ import { Command } from '@guanghechen/commander'
102
75
 
103
- const program = createTopCommand('my-cli', '1.0.0')
104
- // The command comes pre-configured with:
105
- // - Configuration file options (--config-path, --parastic-config-path, etc.)
106
- // - Logging options (--log-level, --log-encoding, etc.)
107
- // - Workspace option (--workspace)
108
- ```
109
-
110
- ### Main Command Utilities
111
-
112
- ```typescript
113
- import {
114
- createMainCommandMounter,
115
- createMainCommandExecutor,
116
- type IMainCommandProcessor,
117
- type IMainCommandCreator
118
- } from '@guanghechen/commander'
119
-
120
- // Define your main command processor
121
- const processor: IMainCommandProcessor<IMyOptions> = async (options) => {
122
- console.log('Processing with options:', options)
123
- }
124
-
125
- // Create a command creator
126
- const creator: IMainCommandCreator<IMyOptions> = (handle) => {
127
- return new Command()
128
- .name('build')
129
- .description('Build the project')
130
- .action(handle ? (opts) => handle(opts) : () => {})
131
- }
132
-
133
- // Create and use a mounter for adding to parent command
134
- const mounter = createMainCommandMounter(creator, processor)
135
- mounter(program)
136
-
137
- // Or create an executor for direct execution
138
- const executor = createMainCommandExecutor(creator, processor)
139
- await executor(process.argv)
140
- ```
141
-
142
- ### Sub-Command Architecture
76
+ const cli = new Command({
77
+ name: 'mycli',
78
+ version: '1.0.0',
79
+ description: 'My awesome CLI tool',
80
+ })
143
81
 
144
- ```typescript
145
- import { SubCommand, type ISubCommandOptions, type ISubCommandProcessor } from '@guanghechen/commander'
146
-
147
- interface IBuildOptions extends ISubCommandOptions {
148
- readonly target: string
149
- readonly production: boolean
150
- }
151
-
152
- class BuildCommand extends SubCommand<IBuildOptions> {
153
- public readonly subCommandName = 'build'
154
- public readonly aliases = ['b']
155
-
156
- public command(processor: ISubCommandProcessor<IBuildOptions>): Command {
157
- return new Command()
158
- .name(this.subCommandName)
159
- .aliases(this.aliases)
160
- .description('Build the project')
161
- .option('--target <target>', 'Build target', 'development')
162
- .option('--production', 'Production build', false)
163
- .action(async (args, options, extra) => {
164
- await processor.process(args, options)
165
- })
166
- }
167
-
168
- public async resolveProcessor(args: string[], options: IBuildOptions): Promise<ISubCommandProcessor<IBuildOptions>> {
169
- return {
170
- process: async (args, options) => {
171
- console.log(`Building for ${options.target}`)
172
- if (options.production) {
173
- console.log('Production mode enabled')
174
- }
175
- }
82
+ cli
83
+ .option({
84
+ long: 'verbose',
85
+ short: 'v',
86
+ type: 'boolean',
87
+ description: 'Enable verbose output',
88
+ })
89
+ .option({
90
+ long: 'output',
91
+ short: 'o',
92
+ type: 'string',
93
+ description: 'Output file path',
94
+ default: './output.txt',
95
+ })
96
+ .argument({
97
+ name: 'file',
98
+ kind: 'required',
99
+ description: 'Input file to process',
100
+ })
101
+ .action(({ opts, args, ctx }) => {
102
+ const [file] = args
103
+ ctx.reporter.info(`Processing ${file}...`)
104
+ if (opts['verbose']) {
105
+ ctx.reporter.debug(`Output: ${opts['output']}`)
176
106
  }
177
- }
178
- }
107
+ })
179
108
 
180
- // Usage
181
- const buildCmd = new BuildCommand()
182
- buildCmd.mount(program, { isDefault: false })
109
+ cli.run({
110
+ argv: process.argv.slice(2),
111
+ envs: process.env,
112
+ })
183
113
  ```
184
114
 
185
- ### Utility Functions
115
+ ### Subcommands
186
116
 
187
117
  ```typescript
188
- import { hasGitInstalled, installDependencies } from '@guanghechen/commander'
189
-
190
- // Check if Git is available
191
- if (hasGitInstalled()) {
192
- console.log('Git is available')
193
- }
194
-
195
- // Install dependencies with user selection
196
- await installDependencies({
197
- cwd: process.cwd(),
198
- plopBypass: [], // Or ['yarn'] to skip user prompt
199
- reporter: myReporter
200
- })
201
- ```
118
+ import { Command } from '@guanghechen/commander'
202
119
 
203
- ## API Reference
120
+ const root = new Command({
121
+ name: 'git',
122
+ version: '1.0.0',
123
+ description: 'A simple git-like CLI',
124
+ })
204
125
 
205
- | Name | Signature | Description |
206
- |------|-----------|-------------|
207
- | `Command` | Class | Enhanced version with improved TypeScript support |
208
- | `SubCommand` | `abstract class SubCommand<O>` | Abstract base class for implementing sub-commands with configuration support |
209
- | `createTopCommand` | `(commandName: string, version: string) => Command` | Creates a top-level command with pre-configured common options |
210
- | `createMainCommandMounter` | `<O>(creator, handle) => IMainCommandMounter` | Creates a function for mounting main commands to parent commands |
211
- | `createMainCommandExecutor` | `<O>(creator, handle) => IMainCommandExecutor` | Creates a function for executing main commands directly |
212
- | `hasGitInstalled` | `() => boolean` | Checks if Git is installed and available in PATH |
213
- | `installDependencies` | `(params) => Promise<void>` | Prompts user to install dependencies using npm or yarn |
126
+ const clone = new Command({
127
+ name: 'clone',
128
+ description: 'Clone a repository',
129
+ })
130
+ .argument({ name: 'url', kind: 'required', description: 'Repository URL' })
131
+ .option({ long: 'depth', type: 'number', description: 'Shallow clone depth' })
132
+ .action(({ args, opts }) => {
133
+ console.log(`Cloning ${args[0]} with depth ${opts['depth'] ?? 'full'}`)
134
+ })
214
135
 
215
- ### Detailed Interfaces
136
+ const commit = new Command({
137
+ name: 'commit',
138
+ aliases: ['ci'],
139
+ description: 'Record changes to the repository',
140
+ })
141
+ .option({ long: 'message', short: 'm', type: 'string', required: true, description: 'Commit message' })
142
+ .option({ long: 'amend', type: 'boolean', description: 'Amend previous commit' })
143
+ .action(({ opts }) => {
144
+ console.log(`Committing: ${opts['message']}`)
145
+ })
216
146
 
217
- #### `Command` Class Methods
147
+ root.subcommand(clone).subcommand(commit)
218
148
 
219
- ```typescript
220
- class Command {
221
- action<T>(fn: ICommandActionCallback<T>): this // Register action callback with enhanced type safety
222
- opts<T>(): T // Get options with proper type inference
223
- }
149
+ root.run({ argv: process.argv.slice(2), envs: process.env })
224
150
  ```
225
151
 
226
- #### `SubCommand<O>` Abstract Class
152
+ ### Shell Completion
227
153
 
228
154
  ```typescript
229
- abstract class SubCommand<O extends ISubCommandOptions> {
230
- // Abstract properties
231
- abstract readonly subCommandName: string // The name of the sub-command
232
- abstract readonly aliases: string[] // Command aliases
233
-
234
- // Abstract methods
235
- abstract command(processor: ISubCommandProcessor<O>): Command
236
- abstract resolveProcessor(args: string[], options: O): Promise<ISubCommandProcessor<O>>
237
-
238
- // Instance methods
239
- mount(parentCommand: Command, opts?: { isDefault?: boolean }): void
240
- execute(parentCommand: Command, rawArgs: string[]): Promise<void>
241
- process(args: string[], options: O): Promise<void>
242
- }
243
- ```
155
+ import { Command, CompletionCommand } from '@guanghechen/commander'
244
156
 
245
- #### `createTopCommand` Pre-configured Options
157
+ const root = new Command({
158
+ name: 'mycli',
159
+ version: '1.0.0',
160
+ description: 'My CLI with completion support',
161
+ })
246
162
 
247
- The command comes with these built-in options:
163
+ // Add completion subcommand
164
+ root.subcommand(new CompletionCommand(root))
248
165
 
249
- ```typescript
250
- --config-path, -c // Configuration file paths
251
- --parastic-config-path // Parasitic configuration file path
252
- --parastic-config-entry // Entry key in parasitic config
253
- --workspace // Working directory
254
- --log-level // Logging level
255
- --log-encoding // Log file encoding
256
- --log-filepath // Log file path
257
- --log-name // Logger name
258
- --log-mode // Log format mode
259
- --log-flight // Logger flight options
166
+ // Generate completion scripts:
167
+ // mycli completion --bash > ~/.local/share/bash-completion/completions/mycli
168
+ // mycli completion --fish > ~/.config/fish/completions/mycli.fish
169
+ // mycli completion --pwsh >> $PROFILE
260
170
  ```
261
171
 
262
- #### `installDependencies` Parameters
172
+ ### Option Types
263
173
 
264
174
  ```typescript
265
- interface IInstallDependenciesParams {
266
- cwd: string // Working directory
267
- plopBypass: string[] // Pre-selected choices to bypass prompts
268
- reporter?: IReporter // Optional logger
269
- }
270
- ```
175
+ import { Command } from '@guanghechen/commander'
176
+
177
+ new Command({ name: 'example', description: 'Option types demo' })
178
+ // Boolean (flags)
179
+ .option({ long: 'debug', type: 'boolean', description: 'Enable debug mode' })
180
+
181
+ // String with choices
182
+ .option({
183
+ long: 'format',
184
+ type: 'string',
185
+ choices: ['json', 'yaml', 'toml'],
186
+ default: 'json',
187
+ description: 'Output format'
188
+ })
271
189
 
272
- #### Type Definitions
190
+ // Number
191
+ .option({ long: 'port', type: 'number', default: 3000, description: 'Server port' })
273
192
 
274
- ```typescript
275
- // Action callback function signature
276
- type ICommandActionCallback<T> = (
277
- args: string[],
278
- options: T,
279
- extra: string[],
280
- self: Command,
281
- ) => void | Promise<void> | never
282
-
283
- // Sub-command processor interface
284
- interface ISubCommandProcessor<O> {
285
- process(args: string[], options: O): Promise<void>
286
- }
287
-
288
- // Base interface for sub-command options
289
- interface ISubCommandOptions extends ICommandConfigurationOptions {
290
- // Inherits all configuration options
291
- }
292
- ```
193
+ // Array (can be specified multiple times)
194
+ .option({ long: 'include', type: 'string[]', description: 'Files to include' })
293
195
 
294
- ## Related
196
+ // Required option
197
+ .option({ long: 'config', type: 'string', required: true, description: 'Config file' })
295
198
 
296
- * [commander.js][]
199
+ // Custom coercion
200
+ .option({
201
+ long: 'date',
202
+ type: 'string',
203
+ coerce: (value) => new Date(value),
204
+ description: 'Date value',
205
+ })
206
+ ```
297
207
 
208
+ ## Reference
298
209
 
299
- [homepage]: https://github.com/guanghechen/node-scaffolds/tree/@guanghechen/commander@1.0.12/packages/commander#readme
300
- [commander.js]: https://github.com/tj/commander.js/
210
+ - [homepage][homepage]
301
211
 
212
+ [homepage]:
213
+ https://github.com/guanghechen/sora/tree/@guanghechen/commander@1.0.0/packages/commander#readme