@bemoje/cli 1.1.1 → 2.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.
@@ -0,0 +1,3 @@
1
+ import type { Argument } from '../types';
2
+ /** Map positional strings to argument definitions, trimming trailing undefineds */
3
+ export declare function resolveArguments(positionals: string[], args: Argument[]): unknown[];
@@ -0,0 +1,3 @@
1
+ import type { Argument, Option } from '../types';
2
+ /** Validate parsed arguments and option values, returning errors or undefined */
3
+ export declare function validateParsed(args: unknown[], optionValues: Record<string, unknown>, argDefs: Argument[], optionDefs: Option[]): string[] | undefined;
package/lib/types.d.ts ADDED
@@ -0,0 +1,383 @@
1
+ import type { Command } from './Command';
2
+ import type { Simplify, AllUnionFields, SetFieldType, SetRequired } from 'type-fest';
3
+ /** Logger interface defining methods for different log levels. */
4
+ export interface Logger {
5
+ start: (...args: unknown[]) => void;
6
+ done: (...args: unknown[]) => void;
7
+ info: (...args: unknown[]) => void;
8
+ log: (...args: unknown[]) => void;
9
+ warn: (...args: unknown[]) => void;
10
+ error: (...args: unknown[]) => void;
11
+ debug: (...args: unknown[]) => void;
12
+ }
13
+ /** Parsed command-line arguments */
14
+ export type Arguments = (undefined | string | string[])[];
15
+ /** Parsed command-line options */
16
+ export type Options = Record<string, undefined | boolean | string | string[]>;
17
+ /** Result of parsing command-line input, including arguments, options, triggered actions, and execution method */
18
+ export type SubCommands = {
19
+ [name: string]: Command<Arguments, Options, any>;
20
+ };
21
+ /** Base descriptor for command-line arguments with shared properties */
22
+ export interface Argument {
23
+ usage: string;
24
+ name: string;
25
+ description?: string;
26
+ required?: boolean;
27
+ variadic?: boolean;
28
+ choices?: string[];
29
+ defaultValue?: string | string[];
30
+ defaultValueDescription?: string;
31
+ }
32
+ /** Base descriptor for command-line options with shared properties */
33
+ export interface Option {
34
+ type: 'boolean' | 'string';
35
+ flags: string;
36
+ short: string;
37
+ long: string;
38
+ name: string;
39
+ argName?: string;
40
+ description?: string;
41
+ required?: boolean;
42
+ variadic?: boolean;
43
+ negate?: boolean;
44
+ defaultValue?: boolean | string | string[];
45
+ defaultValueDescription?: string;
46
+ env?: string;
47
+ hidden?: boolean;
48
+ choices?: string[];
49
+ group?: string;
50
+ }
51
+ /** Complete command configuration including all properties and substructures */
52
+ export interface ICommand {
53
+ /** Parent command if this is a subcommand */
54
+ readonly parent?: ICommand;
55
+ /** Help configuration and rendering */
56
+ /** Command name used for invocation */
57
+ name: string;
58
+ /** Alternative names for this command */
59
+ aliases: string[];
60
+ /** Optional version string */
61
+ version?: string;
62
+ /** Full command description */
63
+ description: string;
64
+ /** Brief single-line description */
65
+ summary?: string;
66
+ /** Whether command should be hidden from help */
67
+ hidden?: boolean;
68
+ /** Group name for organizing commands in help */
69
+ group?: string;
70
+ /** Positional arguments */
71
+ arguments: Argument[];
72
+ /** Named options/flags */
73
+ options: Option[];
74
+ /** Child subcommands */
75
+ commands: {
76
+ [name: string]: ICommand;
77
+ };
78
+ }
79
+ export interface IHelp {
80
+ /** output helpWidth, long lines are wrapped to fit */
81
+ helpWidth: number;
82
+ minWidthToWrap: number;
83
+ sortSubcommands: boolean;
84
+ sortOptions: boolean;
85
+ usageDisplayOptionsAs: string;
86
+ usageDisplaySubcommandAs: string;
87
+ /**
88
+ * Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one.
89
+ */
90
+ visibleCommands(): ICommand[];
91
+ /**
92
+ * Compare options for sort.
93
+ */
94
+ compareOptions(a: Option, b: Option): number;
95
+ /**
96
+ * Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one.
97
+ */
98
+ visibleOptions(): Option[];
99
+ /**
100
+ * Get an array of the arguments if any have a description.
101
+ */
102
+ visibleArguments(): Argument[];
103
+ /**
104
+ * Get the command term to show in the list of subcommands.
105
+ */
106
+ subcommandTerm(sub: ICommand): string;
107
+ /**
108
+ * Get the option term to show in the list of options.
109
+ */
110
+ optionTerm(option: Option): string;
111
+ /**
112
+ * Get the argument term to show in the list of arguments.
113
+ */
114
+ argumentTerm(argument: Argument): string;
115
+ /**
116
+ * Get the longest subcommand primary alias length.
117
+ */
118
+ longestSubcommandAliasLength(): number;
119
+ /**
120
+ * Get the longest subcommand term length.
121
+ */
122
+ longestSubcommandTermLength(): number;
123
+ /**
124
+ * Get the longest option term length.
125
+ */
126
+ longestOptionTermLength(): number;
127
+ /**
128
+ * Get the longest argument term length.
129
+ */
130
+ longestArgumentTermLength(): number;
131
+ /**
132
+ * Get the command usage to be displayed at the top of the built-in help.
133
+ */
134
+ commandUsage(): string;
135
+ /**
136
+ * Get the description for the command.
137
+ */
138
+ commandDescription(): string;
139
+ /**
140
+ * Get the subcommand summary to show in the list of subcommands.
141
+ * (Fallback to description for backwards compatibility.)
142
+ */
143
+ subcommandDescription(sub: ICommand): string;
144
+ /**
145
+ * Get the option description to show in the list of options.
146
+ */
147
+ optionDescription(option: Option): string;
148
+ /**
149
+ * Get the argument description to show in the list of arguments.
150
+ */
151
+ argumentDescription(argument: Argument): string;
152
+ /**
153
+ * Format a list of items, given a heading and an array of formatted items.
154
+ */
155
+ formatItemList(heading: string, items: string[]): string[];
156
+ /**
157
+ * Group items by their help group heading.
158
+ */
159
+ groupItems<T extends ICommand | Option>(unsortedItems: T[], visibleItems: T[], getGroup: (item: T) => string): Map<string, T[]>;
160
+ /**
161
+ * Return display width of string, ignoring ANSI escape sequences. Used in padding and wrapping calculations.
162
+ */
163
+ displayWidth(str: string): number;
164
+ /**
165
+ * Style the title for displaying in the help. Called with 'Usage:', 'Options:', etc.
166
+ */
167
+ styleTitle(str: string): string;
168
+ /**
169
+ * Style the usage line for displaying in the help. Applies specific styling to different parts like options, commands, and arguments.
170
+ */
171
+ styleUsage(str: string): string;
172
+ /**
173
+ * Style command descriptions for display in help output.
174
+ */
175
+ styleCommandDescription(str: string): string;
176
+ /**
177
+ * Style option descriptions for display in help output.
178
+ */
179
+ styleOptionDescription(str: string): string;
180
+ /**
181
+ * Style subcommand descriptions for display in help output.
182
+ */
183
+ styleSubcommandDescription(str: string): string;
184
+ /**
185
+ * Style argument descriptions for display in help output.
186
+ */
187
+ styleArgumentDescription(str: string): string;
188
+ /**
189
+ * Base style used by descriptions. Override in subclass to apply custom formatting.
190
+ */
191
+ styleDescriptionText(str: string): string;
192
+ /**
193
+ * Style option terms (flags) for display in help output.
194
+ */
195
+ styleOptionTerm(str: string): string;
196
+ /**
197
+ * Style subcommand terms for display in help output. Applies specific styling to different parts like options and arguments.
198
+ */
199
+ styleSubcommandTerm(str: string): string;
200
+ /**
201
+ * Style argument terms for display in help output.
202
+ */
203
+ styleArgumentTerm(str: string): string;
204
+ /**
205
+ * Base style used in terms and usage for options. Override in subclass to apply custom formatting.
206
+ */
207
+ styleOptionText(str: string): string;
208
+ /**
209
+ * Base style used in terms and usage for arguments. Override in subclass to apply custom formatting.
210
+ */
211
+ styleArgumentText(str: string): string;
212
+ /**
213
+ * Base style used in terms and usage for subcommands. Override in subclass to apply custom formatting.
214
+ */
215
+ styleSubcommandText(str: string): string;
216
+ /**
217
+ * Base style used in terms and usage for commands. Override in subclass to apply custom formatting.
218
+ */
219
+ styleCommandText(str: string): string;
220
+ /**
221
+ * Calculate the pad width from the maximum term length.
222
+ */
223
+ padWidth(): number;
224
+ /**
225
+ * Detect manually wrapped and indented strings by checking for line break followed by whitespace.
226
+ */
227
+ preformatted(str: string): boolean;
228
+ /**
229
+ * Format the "item", which consists of a term and description. Pad the term and wrap the description, indenting the following lines.
230
+ *
231
+ * So "TTT", 5, "DDD DDDD DD DDD" might be formatted for this.helpWidth=17 like so:
232
+ * TTT DDD DDDD
233
+ * DD DDD
234
+ */
235
+ formatItem(term: string, termWidth: number, description: string): string;
236
+ /**
237
+ * Wrap a string at whitespace, preserving existing line breaks.
238
+ * Wrapping is skipped if the width is less than `minWidthToWrap`.
239
+ */
240
+ boxWrap(str: string, width: number): string;
241
+ /**
242
+ * Generate the built-in help text.
243
+ */
244
+ render(): string;
245
+ }
246
+ /** Action handler function type, which receives parsed arguments and options as well as metadata about the command execution context. */
247
+ export type ActionHandler<A extends Arguments, O extends Options, Subs extends SubCommands> = (...args: [
248
+ ...A,
249
+ O,
250
+ {
251
+ path: string[];
252
+ name: string;
253
+ argv: string[];
254
+ args: A;
255
+ opts: O;
256
+ errors?: string[];
257
+ cmd: Command<A, O, Subs>;
258
+ logger: Logger;
259
+ }
260
+ ]) => Promise<void> | void;
261
+ /** Predicate function type for hooks, which receives the same metadata as action handlers and returns a boolean indicating whether the hook's action should be executed. */
262
+ export type HookPredicate<A extends Arguments = Arguments, O extends Options = Options, Subs extends SubCommands = SubCommands> = (data: {
263
+ path: string[];
264
+ name: string;
265
+ argv: string[];
266
+ args: A;
267
+ opts: O;
268
+ errors?: string[];
269
+ cmd: Command<A, O, Subs>;
270
+ }) => boolean;
271
+ /** Action handler function type for hooks, which receives the same metadata as action handlers and is executed when its predicate returns true. Can also throw to indicate an error. */
272
+ export type HookActionHandler<A extends Arguments = Arguments, O extends Options = Options, Subs extends SubCommands = SubCommands> = (data: {
273
+ path: string[];
274
+ name: string;
275
+ argv: string[];
276
+ args: A;
277
+ opts: O;
278
+ errors?: string[];
279
+ cmd: Command<A, O, Subs>;
280
+ }) => Promise<void> | void | never;
281
+ /** Hook definition type, which includes the option name that triggers the hook, a predicate function to determine when the hook should run, and an action handler to execute when triggered. */
282
+ export type HookDefinition<A extends Arguments = Arguments, O extends Options = Options, Subs extends SubCommands = SubCommands> = {
283
+ name: keyof O;
284
+ predicate: HookPredicate<A, O, Subs>;
285
+ action: HookActionHandler<A, O, Subs>;
286
+ };
287
+ /**
288
+ * @see Command.prototype.parseArgv
289
+ */
290
+ export type ParseArgvResult<A extends Arguments = Arguments, O extends Options = Options, Subs extends SubCommands = SubCommands> = {
291
+ /** The command or subcommand instance */
292
+ get cmd(): Command<A, O, Subs>;
293
+ /** The part of argv that makes out a subcommand path, or empty array when root command */
294
+ path: string[];
295
+ /** Command namer */
296
+ name: string;
297
+ /** Original argv array passed in or from process.argv, excluding subcommand path */
298
+ argv: string[];
299
+ /** Parsed arguments */
300
+ args: A;
301
+ /** Parsed options */
302
+ opts: O;
303
+ /** Error messages if parsing failed, otherwise undefined */
304
+ errors?: string[];
305
+ /** Names of all triggered hooks whose predicate returned true */
306
+ hooks: HookDefinition<A, O, Subs>[];
307
+ /** Calls the action handler with its expected args */
308
+ execute: () => Promise<void>;
309
+ };
310
+ /** required variadic argument */
311
+ export type RequiredVariadicArgumentUsage = `<${string}...>`;
312
+ /** optional variadic argument */
313
+ export type OptionalVariadicArgumentUsage = `[${string}...]`;
314
+ /** required argument */
315
+ export type RequiredArgumentUsage = `<${string}>`;
316
+ /** optional argument */
317
+ export type OptionalArgumentUsage = `[${string}]`;
318
+ /** Union of all argument usage pattern types */
319
+ export type ArgumentUsage = RequiredVariadicArgumentUsage | OptionalVariadicArgumentUsage | RequiredArgumentUsage | OptionalArgumentUsage;
320
+ /** Helper type to infer allowed argument usage patterns based on the command's existing argument types */
321
+ type InferArgumentUsage<T extends Command<any, any, any>> = T extends Command<infer A, any> ? A extends string[] ? ArgumentUsage : A extends (string | (string | undefined))[] ? OptionalArgumentUsage | OptionalVariadicArgumentUsage : never : never;
322
+ /** Helper type to infer allowed argument usage patterns based on the command's existing argument types */
323
+ export type AllowedArgumentUsage<T extends Command<any, any, any>, Usage extends ArgumentUsage> = Usage extends InferArgumentUsage<T> ? Usage : never;
324
+ /** Base type for addArgument options, extended by specific required/optional and variadic/non-variadic argument option types */
325
+ type ArgumentOptionsBase = Omit<Argument, 'name' | 'required' | 'variadic' | 'usage' | 'defaultValue' | 'defaultValueDescription'>;
326
+ /** Base type for addArgument options, extended by specific required/optional and variadic/non-variadic argument option types */
327
+ type ExtendArgumentOptionsBase<T extends object = object> = Simplify<ArgumentOptionsBase & T>;
328
+ /** Required positional argument descriptor. Usage: `<name>` */
329
+ export type RequiredArgumentOptions = ExtendArgumentOptionsBase;
330
+ /** Optional positional argument with string default. Usage: `[name]` */
331
+ export type OptionalArgumentOptions = ExtendArgumentOptionsBase<{
332
+ defaultValue?: string;
333
+ defaultValueDescription?: string;
334
+ }>;
335
+ /** Optional positional argument with required string default. Usage: `[name]` */
336
+ export type OptionalArgumentOptionsWithDefaultValue = SetFieldType<SetRequired<OptionalArgumentOptions, 'defaultValue'>, 'defaultValue', string>;
337
+ /** Required variadic argument accepting variadic values. Usage: `<name...>` */
338
+ export type RequiredVariadicArgumentOptions = ExtendArgumentOptionsBase;
339
+ /** Optional variadic argument with array default. Usage: `[name...]` */
340
+ export type OptionalVariadicArgumentOptions = ExtendArgumentOptionsBase<{
341
+ defaultValue?: string[];
342
+ defaultValueDescription?: string;
343
+ }>;
344
+ /** Union of all addArgument options types */
345
+ export type ArgumentOptions = AllUnionFields<RequiredArgumentOptions | OptionalArgumentOptions | OptionalArgumentOptionsWithDefaultValue | RequiredVariadicArgumentOptions | OptionalVariadicArgumentOptions>;
346
+ /** Helper type for extracting argument name from usage pattern */
347
+ export type InferAddedArgumentType<Opts> = Opts extends {
348
+ choices: infer C extends string[];
349
+ } ? C[number] : string;
350
+ /** Union of all option usage pattern types */
351
+ export type OptionUsage<Long extends string> = `-${string}, --${Long}` | `-${string}, --${Long} <${string}>` | `-${string}, --${Long} [${string}]` | `-${string}, --${Long} <${string}...>` | `-${string}, --${Long} [${string}...]`;
352
+ /** Base type for addOption options, extended by specific boolean/string and required/optional and variadic/non-variadic option types */
353
+ type OptionOptionsBase = Omit<Option, 'name' | 'required' | 'variadic' | 'type' | 'argName' | 'short' | 'long' | 'flags'>;
354
+ /** Helper type to extend base addOption options with specific fields for different option types */
355
+ type ExtendAddOptionOptionsBase<T extends object = object> = Simplify<OptionOptionsBase & T>;
356
+ /** Boolean flag option. Usage: `-v, --verbose` */
357
+ export type BooleanOptionOptions = ExtendAddOptionOptionsBase<{
358
+ defaultValue?: boolean;
359
+ defaultValueDescription?: string;
360
+ }>;
361
+ /** Required string option. Usage: `-f, --file <path>` */
362
+ export type RequiredOptionOptions = ExtendAddOptionOptionsBase<{
363
+ env?: undefined;
364
+ }>;
365
+ /** Optional string option with default. Usage: `-o, --output [path]` */
366
+ export type OptionalOptionOptions = ExtendAddOptionOptionsBase<{
367
+ defaultValue?: string;
368
+ defaultValueDescription?: string;
369
+ }>;
370
+ /** Required option accepting variadic values. Usage: `-i, --include <patterns...>` */
371
+ export type RequiredVariadicOptionOptions = ExtendAddOptionOptionsBase<{
372
+ env?: undefined;
373
+ }>;
374
+ /** Optional option accepting variadic values with defaults. Usage: `-e, --exclude [patterns...]` */
375
+ export type OptionalVariadicOptionOptions = ExtendAddOptionOptionsBase<{
376
+ defaultValue?: string[];
377
+ defaultValueDescription?: string;
378
+ }>;
379
+ /** Union of all addOption options types */
380
+ export type OptionOptions = AllUnionFields<BooleanOptionOptions | RequiredOptionOptions | OptionalOptionOptions | RequiredVariadicOptionOptions | OptionalVariadicOptionOptions>;
381
+ /** Helper type to infer the resulting Command type after adding an option with specific options */
382
+ export type InferAddOptionResult<A extends Arguments, O extends Options, NewOptions extends Options, Subs extends SubCommands> = Command<A, Simplify<O & NewOptions>, Subs>;
383
+ export {};
package/package.json CHANGED
@@ -1,52 +1,58 @@
1
1
  {
2
2
  "name": "@bemoje/cli",
3
+ "version": "2.0.0",
3
4
  "description": "A type-safe CLI composer that can parse argv and generate help without execution coupling.",
4
- "version": "1.1.1",
5
- "private": false,
6
- "sideEffects": false,
7
5
  "type": "module",
8
- "module": "./index.js",
9
- "main": "./index.js",
10
- "exports": {
11
- ".": "./index.js",
12
- "./*": "./lib/*.js",
13
- "./package.json": "./package.json",
14
- "./index.d.ts": "./index.d.ts"
15
- },
16
- "typings": "./index.d.ts",
17
- "dependencies": {},
18
- "devDependencies": {
19
- "commander": "^14.0.0",
20
- "type-fest": "4.41.0"
21
- },
22
- "license": "MIT",
23
- "repository": {
24
- "type": "git",
25
- "url": "git+https://github.com/bemoje/mono.git"
26
- },
6
+ "sideEffects": false,
27
7
  "keywords": [
28
8
  "cli",
29
9
  "command-line",
10
+ "minimist",
30
11
  "typescript",
31
12
  "type-safe",
32
13
  "help-generation",
33
14
  "commander",
34
- "commander.js",
35
15
  "subcommands",
36
- "validation",
37
16
  "parsing",
38
17
  "composition",
39
18
  "terminal",
40
19
  "console",
41
20
  "args",
42
- "argv",
43
- "bemoje",
44
- "cli"
21
+ "argv"
45
22
  ],
23
+ "exports": {
24
+ ".": {
25
+ "types": "./index.d.ts",
26
+ "import": "./index.mjs",
27
+ "default": "./index.mjs"
28
+ }
29
+ },
30
+ "dependencies": {
31
+ "@sinclair/typebox": "^0.34.37",
32
+ "ansi-colors": "^4.1.3",
33
+ "cli-table": "^0.3.11",
34
+ "enhanced-ms": "^4.1.0",
35
+ "es-toolkit": "^1.44.0",
36
+ "humanize-duration": "^3.33.2",
37
+ "iter-tools": "^7.5.4",
38
+ "memoizee": "^0.4.17",
39
+ "mnemonist": "^0.40.3",
40
+ "onetime": "^7.0.0",
41
+ "p-queue": "^8.1.0",
42
+ "upath": "^2.0.1"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "license": "MIT",
46
48
  "author": {
47
- "name": "Benjamin Møller Jensen",
49
+ "name": "Benjamin Moller Jensen",
48
50
  "email": "bemoje@bemoje.net",
49
51
  "url": "https://github.com/bemoje/"
50
52
  },
51
- "homepage": "https://github.com/bemoje/mono/tree/main/libs/cli"
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "https://github.com/bemoje/mono.git",
56
+ "directory": "libs/cli"
57
+ }
52
58
  }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
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.