@guanghechen/commander 3.3.0 → 4.0.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.
@@ -5,7 +5,7 @@
5
5
  */
6
6
  /**
7
7
  * Reporter interface for logging.
8
- * Users should provide their own implementation.
8
+ * Provided by @guanghechen/reporter or user implementation.
9
9
  */
10
10
  interface IReporter {
11
11
  debug(message: string, ...args: unknown[]): void;
@@ -13,132 +13,193 @@ interface IReporter {
13
13
  warn(message: string, ...args: unknown[]): void;
14
14
  error(message: string, ...args: unknown[]): void;
15
15
  }
16
- /** Supported option value types */
17
- type IOptionType = 'boolean' | 'string' | 'number' | 'string[]' | 'number[]';
16
+ /** Token type: long option, short option, or positional */
17
+ type ICommandTokenType = 'long' | 'short' | 'none';
18
18
  /**
19
- * Option definition.
19
+ * Command token after preprocessing.
20
+ *
21
+ * - original: raw input for error messages (e.g., --LOG-LEVEL=info, -v)
22
+ * - resolved: normalized form (e.g., --logLevel=info, -v)
23
+ * - name: option name for matching (e.g., logLevel, v, '')
24
+ * - type: token type (long/short/none)
25
+ */
26
+ interface ICommandToken {
27
+ /** Raw input, used for error display */
28
+ original: string;
29
+ /** Normalized form, used for parsing */
30
+ resolved: string;
31
+ /** Option name for matching: camelCase for long, single char for short, '' for positional */
32
+ name: string;
33
+ /** Token type */
34
+ type: ICommandTokenType;
35
+ }
36
+ /** Option value type */
37
+ type ICommandOptionType = 'boolean' | 'number' | 'string';
38
+ /** Option argument mode */
39
+ type ICommandOptionArgs = 'none' | 'required' | 'variadic';
40
+ /**
41
+ * Option configuration.
42
+ *
43
+ * `type` and `args` must be specified together. Valid combinations:
44
+ * - boolean + none → boolean
45
+ * - string + required → string
46
+ * - number + required → number
47
+ * - string + variadic → string[]
48
+ * - number + variadic → number[]
49
+ *
20
50
  * @template T - The type of the option value
21
51
  */
22
- interface IOption<T = unknown> {
23
- /** Long option (e.g., 'verbose' for --verbose), also used as merge key */
52
+ interface ICommandOptionConfig<T = unknown> {
53
+ /** Long option name (camelCase, required) */
24
54
  long: string;
25
- /** Short option (single character, e.g., 'v' for -v) */
55
+ /** Short option (single character) */
26
56
  short?: string;
27
- /** Value type, defaults to 'string' */
28
- type?: IOptionType;
57
+ /** Value type (required) */
58
+ type: ICommandOptionType;
59
+ /** Argument mode (required) */
60
+ args: ICommandOptionArgs;
29
61
  /** Description for help text */
30
- description: string;
31
- /** Whether this option is required (cannot be used with default or boolean type) */
62
+ desc: string;
63
+ /** Whether this option is required (mutually exclusive with default) */
32
64
  required?: boolean;
33
65
  /** Default value when not provided */
34
66
  default?: T;
35
67
  /** Allowed values for validation and completion */
36
68
  choices?: T extends Array<infer U> ? U[] : T[];
37
- /** Single value transformation (ignored when resolver is present) */
69
+ /** Single value transformation (called for each value, before choices validation) */
38
70
  coerce?: (rawValue: string) => T extends Array<infer U> ? U : T;
39
- /** Custom resolver that fully replaces builtin parsing (ignores type/coerce) */
40
- resolver?: (argv: string[]) => {
41
- value: T;
42
- remaining: string[];
43
- };
44
71
  /** Callback after parsing, applies value to context */
45
72
  apply?: (value: T, ctx: ICommandContext) => void;
46
73
  }
47
74
  /** Argument kind */
48
- type IArgumentKind = 'required' | 'optional' | 'variadic';
75
+ type ICommandArgumentKind = 'required' | 'optional' | 'variadic';
49
76
  /** Argument value type */
50
- type IArgumentType = 'string' | 'number';
77
+ type ICommandArgumentType = 'string' | 'number';
51
78
  /**
52
- * Positional argument definition.
79
+ * Positional argument configuration.
80
+ *
81
+ * Constraints:
82
+ * - required arguments must come before optional
83
+ * - variadic can only appear once, and must be last
84
+ * - required cannot have default
85
+ *
53
86
  * @template T - The type of the argument value
54
87
  */
55
- interface IArgument<T = unknown> {
88
+ interface ICommandArgumentConfig<T = unknown> {
56
89
  /** Argument name */
57
90
  name: string;
58
91
  /** Argument description */
59
- description: string;
92
+ desc: string;
60
93
  /** Argument kind: required / optional / variadic */
61
- kind: IArgumentKind;
94
+ kind: ICommandArgumentKind;
62
95
  /** Value type, defaults to 'string' */
63
- type?: IArgumentType;
64
- /** Default value when not provided (only effective for optional arguments) */
96
+ type?: ICommandArgumentType;
97
+ /** Default value when not provided (only for optional arguments) */
65
98
  default?: T;
66
99
  /** Custom value transformation (takes precedence over type conversion) */
67
100
  coerce?: (rawValue: string) => T;
68
101
  }
69
102
  /** Command configuration */
70
103
  interface ICommandConfig {
71
- /** Command name (only effective for root command) */
104
+ /** Command name (only for root command) */
72
105
  name?: string;
73
106
  /** Command description */
74
- description: string;
75
- /** Version (only effective for built-in root --version) */
107
+ desc: string;
108
+ /** Version (for root --version) */
76
109
  version?: string;
77
- /** Enable built-in "help" subcommand (only effective when command has subcommands) */
110
+ /** Enable built-in "help" subcommand */
78
111
  help?: boolean;
79
- /** Default reporter for this command (can be overridden by run params) */
112
+ /** Default reporter for this command */
80
113
  reporter?: IReporter;
81
114
  }
82
- /** Forward declaration for Command class */
115
+ /** Command interface */
83
116
  interface ICommand {
84
- readonly name: string;
117
+ readonly name: string | undefined;
85
118
  readonly description: string;
86
119
  readonly version: string | undefined;
87
- readonly parent?: ICommand;
88
- readonly options: IOption[];
89
- readonly arguments: IArgument[];
120
+ readonly parent: ICommand | undefined;
121
+ readonly options: ICommandOptionConfig[];
122
+ readonly arguments: ICommandArgumentConfig[];
123
+ readonly subcommands: Map<string, ICommand>;
90
124
  }
91
125
  /** Execution context */
92
126
  interface ICommandContext {
93
127
  /** Current command node */
94
128
  cmd: ICommand;
95
- /** Environment variables passed in */
129
+ /** Environment variables */
96
130
  envs: Record<string, string | undefined>;
97
131
  /** Reporter instance */
98
132
  reporter: IReporter;
99
133
  /** Original argv */
100
134
  argv: string[];
101
135
  }
102
- /** Action parameters */
103
- interface IActionParams {
136
+ /** Action callback parameters */
137
+ interface ICommandActionParams {
104
138
  /** Execution context */
105
139
  ctx: ICommandContext;
106
- /** Parsed options */
107
- opts: Record<string, unknown>;
140
+ /** Parsed options (keyed by long name) */
141
+ opts: ICommandParsedOpts;
108
142
  /** Parsed positional arguments (keyed by argument name) */
109
- args: Record<string, unknown>;
143
+ args: ICommandParsedArgs;
110
144
  /** Raw positional argument strings (before type conversion) */
111
145
  rawArgs: string[];
112
146
  }
113
147
  /** Action handler function */
114
- type IAction = (params: IActionParams) => void | Promise<void>;
115
- /** run() method parameters */
116
- interface IRunParams {
148
+ type ICommandAction = (params: ICommandActionParams) => void | Promise<void>;
149
+ /** run() / parse() method parameters */
150
+ interface ICommandRunParams {
117
151
  /** Command line arguments (usually process.argv.slice(2)) */
118
152
  argv: string[];
119
153
  /** Environment variables (usually process.env) */
120
154
  envs: Record<string, string | undefined>;
121
- /** Optional reporter override (defaults to command's reporter or console reporter) */
155
+ /** Optional reporter override */
122
156
  reporter?: IReporter;
123
157
  }
124
- /** parse() method result */
125
- interface IParseResult {
158
+ /** Parsed options record */
159
+ type ICommandParsedOpts = Record<string, unknown>;
160
+ /** Parsed arguments record */
161
+ type ICommandParsedArgs = Record<string, unknown>;
162
+ /** Route stage result */
163
+ interface ICommandRouteResult {
164
+ /** Command chain from root to leaf */
165
+ chain: ICommand[];
166
+ /** Remaining argv after routing */
167
+ remaining: string[];
168
+ }
169
+ /** Tokenize stage result */
170
+ interface ICommandTokenizeResult {
171
+ /** Option tokens (before --) */
172
+ optionTokens: ICommandToken[];
173
+ /** Arguments after -- */
174
+ restArgs: string[];
175
+ }
176
+ /** Resolve stage result */
177
+ interface ICommandResolveResult {
178
+ /** Tokens consumed by each command */
179
+ consumedTokens: Map<ICommand, ICommandToken[]>;
180
+ /** Argument tokens (non-option tokens) */
181
+ argTokens: ICommandToken[];
182
+ }
183
+ /** shift() method result (internal) */
184
+ interface ICommandShiftResult {
185
+ /** Tokens consumed by this command */
186
+ consumed: ICommandToken[];
187
+ /** Remaining tokens to pass to parent */
188
+ remaining: ICommandToken[];
189
+ }
190
+ /** Parse stage result */
191
+ interface ICommandParseResult {
192
+ /** Execution context */
193
+ ctx: ICommandContext;
126
194
  /** Parsed options */
127
- opts: Record<string, unknown>;
128
- /** Parsed positional arguments (keyed by argument name) */
129
- args: Record<string, unknown>;
130
- /** Raw positional argument strings (before type conversion) */
195
+ opts: ICommandParsedOpts;
196
+ /** Parsed arguments */
197
+ args: ICommandParsedArgs;
198
+ /** Raw argument strings */
131
199
  rawArgs: string[];
132
200
  }
133
- /** shift() method result */
134
- interface IShiftResult {
135
- /** Options consumed by this command */
136
- opts: Record<string, unknown>;
137
- /** Tokens not consumed, to be passed to parent */
138
- remaining: string[];
139
- }
140
201
  /** Error kinds for command parsing */
141
- type ICommanderErrorKind = 'UnknownOption' | 'UnexpectedArgument' | 'MissingValue' | 'InvalidType' | 'UnsupportedShortSyntax' | 'OptionConflict' | 'MissingRequired' | 'InvalidChoice' | 'InvalidBooleanValue' | 'MissingRequiredArgument' | 'TooManyArguments' | 'ConfigurationError';
202
+ type ICommanderErrorKind = 'InvalidOptionFormat' | 'InvalidNegativeOption' | 'NegativeOptionWithValue' | 'NegativeOptionType' | 'UnknownOption' | 'UnexpectedArgument' | 'MissingValue' | 'InvalidType' | 'UnsupportedShortSyntax' | 'OptionConflict' | 'MissingRequired' | 'InvalidChoice' | 'InvalidBooleanValue' | 'MissingRequiredArgument' | 'TooManyArguments' | 'ConfigurationError';
142
203
  /** Commander error with structured information */
143
204
  declare class CommanderError extends Error {
144
205
  readonly kind: ICommanderErrorKind;
@@ -148,66 +209,74 @@ declare class CommanderError extends Error {
148
209
  format(): string;
149
210
  }
150
211
  /** Shell type for completion */
151
- type IShellType = 'bash' | 'fish' | 'pwsh';
212
+ type ICompletionShellType = 'bash' | 'fish' | 'pwsh';
152
213
  /** Option metadata for completion */
153
214
  interface ICompletionOptionMeta {
215
+ /** Long option name (camelCase) */
154
216
  long: string;
217
+ /** Short option */
155
218
  short?: string;
156
- description: string;
219
+ /** Description */
220
+ desc: string;
221
+ /** Whether option takes value (args !== 'none') */
157
222
  takesValue: boolean;
223
+ /** Allowed values */
158
224
  choices?: string[];
159
225
  }
160
226
  /** Command metadata for completion */
161
227
  interface ICompletionMeta {
228
+ /** Command name */
162
229
  name: string;
163
- description: string;
230
+ /** Description */
231
+ desc: string;
232
+ /** Command aliases */
164
233
  aliases: string[];
234
+ /** Options */
165
235
  options: ICompletionOptionMeta[];
236
+ /** Subcommands */
166
237
  subcommands: ICompletionMeta[];
167
238
  }
168
239
  /** Shell completion paths configuration */
169
240
  interface ICompletionPaths {
170
- /** Bash completion file path (e.g., ~/.local/share/bash-completion/completions/{name}) */
241
+ /** Bash completion file path */
171
242
  bash: string;
172
- /** Fish completion file path (e.g., ~/.config/fish/completions/{name}.fish) */
243
+ /** Fish completion file path */
173
244
  fish: string;
174
- /** PowerShell completion file path (only ~ expansion supported, not $PROFILE) */
245
+ /** PowerShell completion file path */
175
246
  pwsh: string;
176
247
  }
177
248
  /** CompletionCommand configuration */
178
249
  interface ICompletionCommandConfig {
179
250
  /** Program name for completion scripts (defaults to root.name) */
180
251
  programName?: string;
181
- /** Default completion file paths for each shell (required for --write support) */
252
+ /** Default completion file paths for each shell */
182
253
  paths: ICompletionPaths;
183
254
  }
184
255
 
185
256
  /**
186
257
  * Command class - CLI command builder with fluent API
187
258
  *
259
+ * Execution flow: route → tokenize → resolve → parse → run
260
+ *
188
261
  * @module @guanghechen/commander
189
262
  */
190
263
 
191
264
  declare class Command implements ICommand {
192
265
  #private;
193
266
  constructor(config: ICommandConfig);
194
- get name(): string;
267
+ get name(): string | undefined;
195
268
  get description(): string;
196
269
  get version(): string | undefined;
197
270
  get parent(): Command | undefined;
198
- get options(): IOption[];
199
- get arguments(): IArgument[];
200
- option(opt: IOption): this;
201
- argument(arg: IArgument): this;
202
- action(fn: IAction): this;
271
+ get options(): ICommandOptionConfig[];
272
+ get arguments(): ICommandArgumentConfig[];
273
+ get subcommands(): Map<string, ICommand>;
274
+ option<T>(opt: ICommandOptionConfig<T>): this;
275
+ argument<T>(arg: ICommandArgumentConfig<T>): this;
276
+ action(fn: ICommandAction): this;
203
277
  subcommand(name: string, cmd: Command): this;
204
- run(params: IRunParams): Promise<void>;
205
- parse(argv: string[]): IParseResult;
206
- /**
207
- * Shift options from tokens that this command recognizes.
208
- * Unrecognized tokens are returned in `remaining` for parent commands.
209
- */
210
- shift(tokens: string[]): IShiftResult;
278
+ run(params: ICommandRunParams): Promise<void>;
279
+ parse(params: ICommandRunParams): ICommandParseResult;
211
280
  formatHelp(): string;
212
281
  getCompletionMeta(): ICompletionMeta;
213
282
  }
@@ -223,7 +292,7 @@ declare class Command implements ICommand {
223
292
  *
224
293
  * @example
225
294
  * ```typescript
226
- * const root = new Command({ name: 'mycli', description: 'My CLI' })
295
+ * const root = new Command({ name: 'mycli', desc: 'My CLI' })
227
296
  * root.subcommand('completion', new CompletionCommand(root, {
228
297
  * paths: {
229
298
  * bash: `~/.local/share/bash-completion/completions/mycli`,
@@ -258,4 +327,4 @@ declare class PwshCompletion {
258
327
  }
259
328
 
260
329
  export { BashCompletion, Command, CommanderError, CompletionCommand, FishCompletion, PwshCompletion };
261
- export type { IAction, IActionParams, IArgument, IArgumentKind, ICommand, ICommandConfig, ICommandContext, ICommanderErrorKind, ICompletionCommandConfig, ICompletionMeta, ICompletionOptionMeta, ICompletionPaths, IOption, IOptionType, IParseResult, IReporter, IRunParams, IShellType, IShiftResult };
330
+ export type { ICommand, ICommandAction, ICommandActionParams, ICommandArgumentConfig, ICommandArgumentKind, ICommandArgumentType, ICommandConfig, ICommandContext, ICommandOptionArgs, ICommandOptionConfig, ICommandOptionType, ICommandParseResult, ICommandParsedArgs, ICommandParsedOpts, ICommandResolveResult, ICommandRouteResult, ICommandRunParams, ICommandShiftResult, ICommandToken, ICommandTokenType, ICommandTokenizeResult, ICommanderErrorKind, ICompletionCommandConfig, ICompletionMeta, ICompletionOptionMeta, ICompletionPaths, ICompletionShellType, IReporter };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guanghechen/commander",
3
- "version": "3.3.0",
3
+ "version": "4.0.0",
4
4
  "description": "A minimal, type-safe command-line interface builder with fluent API",
5
5
  "author": {
6
6
  "name": "guanghechen",