breadc 0.9.7 → 1.0.0-beta.10

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,17 +1,21 @@
1
1
  # 🥪 Breadc
2
2
 
3
- [![version](https://img.shields.io/npm/v/breadc?label=Breadc)](https://www.npmjs.com/package/breadc) [![CI](https://github.com/yjl9903/Breadc/actions/workflows/ci.yml/badge.svg)](https://github.com/yjl9903/Breadc/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/yjl9903/Breadc/branch/main/graph/badge.svg?token=F7PGOG62EF)](https://codecov.io/gh/yjl9903/Breadc)
3
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/yjl9903/Breadc)
4
+ [![version](https://img.shields.io/npm/v/breadc?label=Breadc)](https://www.npmjs.com/package/breadc)
5
+ [![CI](https://github.com/yjl9903/Breadc/actions/workflows/ci.yml/badge.svg)](https://github.com/yjl9903/Breadc/actions/workflows/ci.yml)
6
+ [![codecov](https://codecov.io/gh/yjl9903/Breadc/branch/main/graph/badge.svg?token=F7PGOG62EF)](https://codecov.io/gh/yjl9903/Breadc)
4
7
 
5
- Yet another Command Line Application Framework with fully strong **[TypeScript](https://www.typescriptlang.org/) support**.
8
+ Yet another **Command Line Application Framework** desgined for **[TypeScript](https://www.typescriptlang.org/)**.
6
9
 
7
- ![vscode](https://cdn.jsdelivr.net/gh/yjl9903/Breadc/images/vscode.png)
10
+ - **TypeScript Infer**: infer command arguments, option values, and action signatures in IDE automatically
11
+ - **Command**: support default command, command alias, and nested sub-commands like `git remote add <name> <url>`
12
+ - **Group**: organize commands by modules and build large multi-command CLI applications with clear structure
13
+ - **Option**: support boolean, required, optional, spread options, `--no-*` negation, and `--` passthrough arguments
14
+ - **Middleware**: support middleware pipeline and unknown option handling
15
+ - **Builtin CLI Features**: provide common help / version options and i18n support out of the box
16
+ - **Toolkits**: contains many useful tools to build your next CLI application, such as [ansi color](https://github.com/yjl9903/Breadc/tree/main/packages/color), [process death handler](https://github.com/yjl9903/Breadc/tree/main/packages/death), [shell compelete script generation](https://github.com/yjl9903/Breadc/tree/main/packages/complete) and so on.
8
17
 
9
- ## Features
10
-
11
- + 🔍 **TypeScript Infer**: IDE will automatically infer the type of your command action function;
12
- + 💻 **Commands**: support default command, command alias and sub-commands like `git remote add <name> <url>`;
13
- + 📖 **East to Learn**: very similar with [commander.js](https://github.com/tj/commander.js/), [cac](https://github.com/cacjs/cac) and there are only 5 APIs for building a CLI application: `breadc`, `command`, `option`, `action`, `run`.
14
- + 🧰 **Toolkits**: contains many useful tools to build your next CLI application, such as [ansi color](https://github.com/yjl9903/Breadc/tree/main/packages/color), [process death handler](https://github.com/yjl9903/Breadc/tree/main/packages/death), [shell compelete script generation](https://github.com/yjl9903/Breadc/tree/main/packages/complete) and so on.
18
+ ![vscode](https://raw.githubusercontent.com/yjl9903/Breadc/v1.0.0-beta.1/assets/typescript.png)
15
19
 
16
20
  ## Installation
17
21
 
@@ -24,52 +28,31 @@ npm i breadc
24
28
  Try [./examples/echo.ts](./examples/echo.ts).
25
29
 
26
30
  ```ts
27
- import { breadc } from 'breadc'
31
+ import { breadc } from 'breadc';
28
32
 
29
33
  const cli = breadc('echo', { version: '1.0.0' })
30
- .option('--host <host>', { default: 'localhost' })
31
- .option('--port <port>', { default: '3000', cast: p => +p })
32
-
33
- cli
34
- .command('[message]', 'Say something!')
35
- .action((message, option) => {
36
- const host = option.host
37
- const port = option.port
38
- console.log(`Host: ${host}`)
39
- console.log(`Port: ${port}`)
40
- })
41
-
42
- cli.run(process.argv.slice(2)).catch(err => console.error(err))
43
- ```
44
-
45
- If you are using IDEs that support TypeScript (like [Visual Studio Code](https://code.visualstudio.com/)), input something using `option`, and then you will find the `option` is automatically typed with `{ host: string, port: number }`. In the figure below, [Visual Studio Code](https://code.visualstudio.com/) will automatically infer that the type of `option.host` is `string` and the type of `option.port` is `number`.
46
-
47
- ![vscode](https://cdn.jsdelivr.net/gh/yjl9903/Breadc/images/vscode.png)
48
-
49
- ### Limitation
34
+ .option('--host <host>', 'specify hostname', { initial: 'localhost' })
35
+ .option('--port <port>', 'specify port', { initial: '3000', cast: (t) => +t });
50
36
 
51
- For the limitation of TypeScript, in the command format string, you can only write up to **5** pieces. That is to say, you can only write format string like `<p1> <p2> <p3> <p4> [p5]`, but `<p1> <p2> <p3> <p4> <p5> [p6]` does not work.
37
+ cli.command('[message]', 'Say something!').action((message, option) => {
38
+ console.log(message ?? 'You can say anything!');
39
+ const { host, port } = option; // { host: string, port: number, '--': string[] }
40
+ console.log(`Host: ${host}`);
41
+ console.log(`Port: ${port}`);
42
+ });
52
43
 
53
- You should always use method chaining when registering options and commands. The example below will fail to infer the option `--host`.
54
-
55
- ```ts
56
- const cli = Breadc('cli')
44
+ cli.run(process.argv.slice(2)).catch((err) => console.error(err));
45
+ ```
57
46
 
58
- cli
59
- .option('--host')
47
+ If you are using IDEs that support TypeScript (like [Visual Studio Code](https://code.visualstudio.com/)), input something using `option`, and then you will find the `option` is automatically typed with `{ host: string, port: number }`. In the figure below, [Visual Studio Code](https://code.visualstudio.com/) will automatically infer that the type of `option.host` is `string` and the type of `option.port` is `number`.
60
48
 
61
- cli
62
- .option('--port')
63
- .command('')
64
- .action((option) => {
65
- // The type of option is only { port: boolean }
66
- })
67
- ```
49
+ ![vscode](https://raw.githubusercontent.com/yjl9903/Breadc/v1.0.0-beta.1/assets/typescript.png)
68
50
 
69
51
  ## Inspiration
70
52
 
71
- + [cac](https://github.com/cacjs/cac): Simple yet powerful framework for building command-line apps.
72
- + [TypeScript: Documentation - Template Literal Types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html)
53
+ - [cac](https://github.com/cacjs/cac): Simple yet powerful framework for building command-line apps.
54
+ - [Commander.js](https://github.com/tj/commander.js): Node.js command-line interfaces made easy.
55
+ - [TypeScript: Documentation - Template Literal Types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html)
73
56
 
74
57
  ## License
75
58
 
package/dist/index.d.mts CHANGED
@@ -1,221 +1,5 @@
1
- type TokenType = '--' | '-' | 'number' | 'string' | 'long' | 'short';
2
- declare class Token {
3
- private readonly text;
4
- private _type;
5
- constructor(text: string);
6
- /**
7
- * @returns Raw argument text
8
- */
9
- raw(): string;
10
- /**
11
- * @returns Number representation
12
- */
13
- number(): number;
14
- /**
15
- * @returns Remove start - for long or short option
16
- */
17
- option(): string;
18
- isOption(): boolean;
19
- isText(): boolean;
20
- type(): TokenType;
21
- }
22
- declare class Lexer {
23
- private readonly rawArgs;
24
- private cursor;
25
- constructor(rawArgs: string[]);
26
- next(): Token | undefined;
27
- hasNext(): boolean;
28
- peek(): Token | undefined;
29
- [Symbol.iterator](): Iterator<Token, undefined>;
30
- }
31
-
32
- type Prettify<T> = {
33
- [K in keyof T]: T[K];
34
- } & {};
35
- type Lowercase = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';
36
- type Uppercase = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z';
37
- type Letter = Lowercase | Uppercase;
38
-
39
- type CallbackFn = (result: BreadcParseResult) => any;
40
- interface ParseResult {
41
- /**
42
- * Arguments that will be passed to action callback
43
- * When parsing, this array is empty
44
- */
45
- arguments: Array<string | string[] | undefined>;
46
- /**
47
- * Options map
48
- */
49
- options: Record<string, any>;
50
- /**
51
- * Rest arguments.
52
- * When parsing, this contains all the non-option arguments
53
- */
54
- '--': string[];
55
- }
56
- type BreadcParseResult = Prettify<ParseResult & {
57
- callback?: CallbackFn;
58
- meta: Record<string, any>;
59
- matched: {
60
- node: TreeNode;
61
- command?: Command;
62
- option?: Option;
63
- };
64
- }>;
65
- interface Context {
66
- /**
67
- * Lexer instance
68
- */
69
- lexer: Lexer;
70
- /**
71
- * Options supported in the current context
72
- */
73
- options: Map<string, Option>;
74
- /**
75
- * Parse result
76
- */
77
- result: ParseResult;
78
- /**
79
- * Some other meta information passed
80
- */
81
- meta: Record<string, any>;
82
- /**
83
- * Configuration
84
- */
85
- config: {
86
- allowUnknownOption: 'error' | 'skip' | 'rest';
87
- };
88
- parseOption(cursor: TreeNode, token: Token, context: Context): TreeNode | false;
89
- }
90
- interface TreeNode<F extends CallbackFn = CallbackFn> {
91
- command?: Command;
92
- option?: Option;
93
- /**
94
- * Out-going edges from this node
95
- */
96
- children: Map<string, TreeNode>;
97
- init(context: Context): void;
98
- next(arg: Token, context: Context): TreeNode | false;
99
- finish(context: Context): F | undefined;
100
- }
101
-
102
- declare function makeTreeNode(pnode: Partial<TreeNode>): TreeNode;
103
-
104
- /**
105
- * Extract option type, boolean or string
106
- */
107
- type ExtractOptionType<T extends string> = T extends `-${Letter}, --${infer R} <${infer U}>` ? string : T extends `-${Letter}, --${infer R}` ? boolean : T extends `--${infer R} <${infer U}>` ? string : T extends `--${infer R}` ? boolean : string | boolean;
108
- /**
109
- * Extract option raw name
110
- *
111
- * Examples:
112
- * + const t1: ExtractOption<'--option' | '--hello'> = 'hello'
113
- * + const t2: ExtractOption<'-r, --root'> = 'root'
114
- * + const t3: ExtractOption<'--page-index'> = 'pageIndex'
115
- */
116
- type ExtractOptionRawName<T extends string> = T extends `-${Letter}, --${infer R} <${infer U}>` ? R : T extends `-${Letter}, --no-${infer R}` ? R : T extends `-${Letter}, --${infer R}` ? R : T extends `--${infer R} <${infer U}>` ? R : T extends `--no-${infer R}` ? R : T extends `--${infer R}` ? R : never;
117
- /**
118
- * Extrat camel case option name
119
- */
120
- type ExtractOptionName<T extends string, R extends string = ExtractOptionRawName<T>> = R extends `${infer P1}-${infer P2}-${infer P3}` ? `${P1}${Capitalize<P2>}${Capitalize<P3>}` : R extends `${infer P1}-${infer P2}` ? `${P1}${Capitalize<P2>}` : R;
121
- /**
122
- * Extract option information
123
- */
124
- type ExtractOption<T extends string, D = never> = {
125
- [k in ExtractOptionName<T>]: D extends never ? ExtractOptionType<T> : D;
126
- };
127
- type Push<T extends any[], U, R> = [...T, U, R];
128
- type ActionFn<T extends any[], Option extends object = {}, R = any> = (...arg: Push<T, Prettify<Option & {
129
- '--': string[];
130
- }>, {}>) => R | Promise<R>;
131
- /**
132
- * Max Dep: 5
133
- *
134
- * Generated by: npx tsx examples/genType.ts 5
135
- */
136
- type ExtractCommand<T extends string> = T extends `<${infer P1}> <${infer P2}> <${infer P3}> <${infer P4}> [...${infer P5}]` ? [string, string, string, string, string[]] : T extends `<${infer P1}> <${infer P2}> <${infer P3}> <${infer P4}> [${infer P5}]` ? [string, string, string, string, string | undefined] : T extends `<${infer P1}> <${infer P2}> <${infer P3}> <${infer P4}> <${infer P5}>` ? [string, string, string, string, string] : T extends `${infer P1} <${infer P2}> <${infer P3}> <${infer P4}> [...${infer P5}]` ? [string, string, string, string[]] : T extends `${infer P1} <${infer P2}> <${infer P3}> <${infer P4}> [${infer P5}]` ? [string, string, string, string | undefined] : T extends `${infer P1} <${infer P2}> <${infer P3}> <${infer P4}> <${infer P5}>` ? [string, string, string, string] : T extends `${infer P1} ${infer P2} <${infer P3}> <${infer P4}> [...${infer P5}]` ? [string, string, string[]] : T extends `${infer P1} ${infer P2} <${infer P3}> <${infer P4}> [${infer P5}]` ? [string, string, string | undefined] : T extends `${infer P1} ${infer P2} <${infer P3}> <${infer P4}> <${infer P5}>` ? [string, string, string] : T extends `${infer P1} ${infer P2} ${infer P3} <${infer P4}> [...${infer P5}]` ? [string, string[]] : T extends `${infer P1} ${infer P2} ${infer P3} <${infer P4}> [${infer P5}]` ? [string, string | undefined] : T extends `${infer P1} ${infer P2} ${infer P3} <${infer P4}> <${infer P5}>` ? [string, string] : T extends `<${infer P1}> <${infer P2}> <${infer P3}> [...${infer P4}]` ? [string, string, string, string[]] : T extends `<${infer P1}> <${infer P2}> <${infer P3}> [${infer P4}]` ? [string, string, string, string | undefined] : T extends `<${infer P1}> <${infer P2}> <${infer P3}> <${infer P4}>` ? [string, string, string, string] : T extends `${infer P1} <${infer P2}> <${infer P3}> [...${infer P4}]` ? [string, string, string[]] : T extends `${infer P1} <${infer P2}> <${infer P3}> [${infer P4}]` ? [string, string, string | undefined] : T extends `${infer P1} <${infer P2}> <${infer P3}> <${infer P4}>` ? [string, string, string] : T extends `${infer P1} ${infer P2} <${infer P3}> [...${infer P4}]` ? [string, string[]] : T extends `${infer P1} ${infer P2} <${infer P3}> [${infer P4}]` ? [string, string | undefined] : T extends `${infer P1} ${infer P2} <${infer P3}> <${infer P4}>` ? [string, string] : T extends `${infer P1} ${infer P2} ${infer P3} [...${infer P4}]` ? [string[]] : T extends `${infer P1} ${infer P2} ${infer P3} [${infer P4}]` ? [string | undefined] : T extends `${infer P1} ${infer P2} ${infer P3} <${infer P4}>` ? [string] : T extends `<${infer P1}> <${infer P2}> [...${infer P3}]` ? [string, string, string[]] : T extends `<${infer P1}> <${infer P2}> [${infer P3}]` ? [string, string, string | undefined] : T extends `<${infer P1}> <${infer P2}> <${infer P3}>` ? [string, string, string] : T extends `${infer P1} <${infer P2}> [...${infer P3}]` ? [string, string[]] : T extends `${infer P1} <${infer P2}> [${infer P3}]` ? [string, string | undefined] : T extends `${infer P1} <${infer P2}> <${infer P3}>` ? [string, string] : T extends `${infer P1} ${infer P2} [...${infer P3}]` ? [string[]] : T extends `${infer P1} ${infer P2} [${infer P3}]` ? [string | undefined] : T extends `${infer P1} ${infer P2} <${infer P3}>` ? [string] : T extends `${infer P1} ${infer P2} ${infer P3}` ? [] : T extends `<${infer P1}> [...${infer P2}]` ? [string, string[]] : T extends `<${infer P1}> [${infer P2}]` ? [string, string | undefined] : T extends `<${infer P1}> <${infer P2}>` ? [string, string] : T extends `${infer P1} [...${infer P2}]` ? [string[]] : T extends `${infer P1} [${infer P2}]` ? [string | undefined] : T extends `${infer P1} <${infer P2}>` ? [string] : T extends `${infer P1} ${infer P2}` ? [] : T extends `[...${infer P1}]` ? [string[]] : T extends `[${infer P1}]` ? [string | undefined] : T extends `<${infer P1}>` ? [string] : T extends `${infer P1}` ? [] : T extends `` ? [] : never;
137
-
138
- interface AppOption {
139
- version?: string;
140
- description?: string;
141
- plugins?: Partial<Plugin>[];
142
- builtin?: {
143
- version?: false | Partial<{
144
- description: string;
145
- content: string;
146
- }>;
147
- help?: false | Partial<{
148
- description: string;
149
- }>;
150
- };
151
- }
152
- interface Breadc<GlobalOption extends object = {}> {
153
- name: string;
154
- description: string;
155
- option<OF extends string = string, OT extends string | boolean = ExtractOptionType<OF>, OO extends OptionOption<OT, any> = OptionOption<OT, any>, OR extends any = OO extends {
156
- cast(...args: any[]): infer CR;
157
- } ? CR : OO['default'] extends OT ? OT : OT extends string ? undefined | string : OT extends boolean ? boolean : undefined | string | boolean>(format: OF, description?: string | OO, option?: OO): Breadc<GlobalOption & ExtractOption<OF, OR>>;
158
- command<F extends string = string>(format: F, description?: string, option?: CommandOption): Command<F, ExtractCommand<F>, {}, GlobalOption>;
159
- command<F extends string = string>(format: F, option?: CommandOption): Command<F, ExtractCommand<F>, {}, GlobalOption>;
160
- parse(args: string[]): BreadcParseResult;
161
- run<T = any>(args: string[]): Promise<T>;
162
- }
163
- interface Command<F extends string = string, AT extends any[] = ExtractCommand<F>, CommandOption extends object = {}, GlobalOption extends object = {}> {
164
- callback?: (result: ParseResult) => Promise<any>;
165
- format: F;
166
- description: string;
167
- _default: boolean;
168
- _arguments: Argument[];
169
- _options: Option[];
170
- option<OF extends string = string, OT extends string | boolean = ExtractOptionType<OF>, OO extends OptionOption<OT, any> = OptionOption<OT, any>, OR extends any = OO extends {
171
- cast(...args: any[]): infer CR;
172
- } ? CR : OO['default'] extends OT ? OT : OT extends string ? undefined | string : OT extends boolean ? boolean : undefined | string | boolean>(format: OF, description?: string | OO, option?: OO): Command<F, AT, CommandOption & ExtractOption<OF, OR>, GlobalOption>;
173
- alias(format: string): Command<F, AT, CommandOption, GlobalOption>;
174
- action(fn: ActionFn<AT, CommandOption & GlobalOption>): void;
175
- }
176
- interface CommandOption {
177
- description?: string;
178
- /**
179
- * Config how to handle unknown options
180
- */
181
- allowUnknownOption?: 'error' | 'skip' | 'rest';
182
- }
183
- interface Argument {
184
- type: 'const' | 'require' | 'optional' | 'rest';
185
- name: string;
186
- }
187
- interface Option<F extends string = string, T extends string | boolean = ExtractOptionType<F>, R extends unknown = any> {
188
- format: F;
189
- type: T extends string ? 'string' : T extends boolean ? 'boolean' : never;
190
- name: string;
191
- short?: string;
192
- description: string;
193
- order: number;
194
- initial?: R;
195
- cast?: (value: T extends string ? string : T extends boolean ? boolean : never) => R;
196
- parse?: (cursor: TreeNode, token: Token, context: Context) => TreeNode | false;
197
- }
198
- interface OptionOption<T extends string | boolean, R extends any = any> {
199
- description?: string;
200
- default?: T;
201
- cast?: (value: T) => R;
202
- }
203
- type CommandHookFn = (result: ParseResult) => void | Promise<void>;
204
- interface Plugin {
205
- onInit?(breadc: Breadc, allCommands: Command[], globalOptions: Option[]): void;
206
- onPreRun?(breadc: Breadc): void | Promise<void>;
207
- onPreCommand?: Record<string, CommandHookFn> | CommandHookFn;
208
- onPostCommand?: Record<string, CommandHookFn> | CommandHookFn;
209
- onPostRun?(breadc: Breadc): void | Promise<void>;
210
- }
211
-
212
- declare function breadc(name: string, config?: AppOption): Breadc<{}>;
213
-
214
- declare function definePlugin(plugin: Partial<Plugin>): Partial<Plugin>;
215
-
216
- declare class BreadcError extends Error {
217
- }
218
- declare class ParseError extends Error {
219
- }
220
-
221
- export { type AppOption, type Argument, type Breadc, BreadcError, type Command, type Option, ParseError, type Plugin, breadc, definePlugin, makeTreeNode };
1
+ import { ActionMiddleware, Argument, ArgumentInit, Breadc, BreadcAppError, BreadcError, BreadcInit, Command, CommandInit, Group, GroupInit, Option, OptionInit, ResolveCommandError, ResolveGroupError, ResolveOptionError, UnknownCommandMiddleware, UnknownOptionMiddleware, argument, breadc, command, group, option } from "@breadc/core";
2
+ import { ansi256, ansi256Bg, bgBlack, bgBlue, bgCyan, bgGray, bgGreen, bgLightBlue, bgLightCyan, bgLightGray, bgLightGreen, bgLightMagenta, bgLightRed, bgLightYellow, bgMagenta, bgRed, bgWhite, bgYellow, black, blue, bold, cyan, dim, gray, green, hidden, inverse, italic, lightBlue, lightCyan, lightGray, lightGreen, lightMagenta, lightRed, lightYellow, link, magenta, red, reset, strikethrough, underline, white, yellow } from "@breadc/color";
3
+ export * from "@breadc/death";
4
+ export * from "@breadc/tui";
5
+ export { type ActionMiddleware, type Argument, type ArgumentInit, type Breadc, BreadcAppError, BreadcError, type BreadcInit, type Command, type CommandInit, type Group, type GroupInit, type Option, type OptionInit, ResolveCommandError, ResolveGroupError, ResolveOptionError, type UnknownCommandMiddleware, type UnknownOptionMiddleware, ansi256, ansi256Bg, argument, bgBlack, bgBlue, bgCyan, bgGray, bgGreen, bgLightBlue, bgLightCyan, bgLightGray, bgLightGreen, bgLightMagenta, bgLightRed, bgLightYellow, bgMagenta, bgRed, bgWhite, bgYellow, black, blue, bold, breadc, command, cyan, dim, gray, green, group, hidden, inverse, italic, lightBlue, lightCyan, lightGray, lightGreen, lightMagenta, lightRed, lightYellow, link, magenta, option, red, reset, strikethrough, underline, white, yellow };