breadc 0.9.7 → 1.0.0-beta.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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Yet another Command Line Application Framework with fully strong **[TypeScript](https://www.typescriptlang.org/) support**.
6
6
 
7
- ![vscode](https://cdn.jsdelivr.net/gh/yjl9903/Breadc/images/vscode.png)
7
+ ![vscode](https://cdn.jsdelivr.net/gh/yjl9903/Breadc/assets/typescript.png)
8
8
 
9
9
  ## Features
10
10
 
@@ -24,47 +24,25 @@ npm i breadc
24
24
  Try [./examples/echo.ts](./examples/echo.ts).
25
25
 
26
26
  ```ts
27
- import { breadc } from 'breadc'
27
+ import { breadc } from 'breadc';
28
28
 
29
29
  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
30
+ .option('--host <host>', 'specify hostname', { initial: 'localhost' })
31
+ .option('--port <port>', 'specify port', { initial: '3000', cast: (t) => +t });
50
32
 
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.
33
+ cli.command('[message]', 'Say something!').action((message, option) => {
34
+ console.log(message ?? 'You can say anything!');
35
+ const { host, port } = option; // { host: string, port: number, '--': string[] }
36
+ console.log(`Host: ${host}`);
37
+ console.log(`Port: ${port}`);
38
+ });
52
39
 
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')
40
+ cli.run(process.argv.slice(2)).catch((err) => console.error(err));
41
+ ```
57
42
 
58
- cli
59
- .option('--host')
43
+ 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
44
 
61
- cli
62
- .option('--port')
63
- .command('')
64
- .action((option) => {
65
- // The type of option is only { port: boolean }
66
- })
67
- ```
45
+ ![vscode](https://cdn.jsdelivr.net/gh/yjl9903/Breadc/assets/typescript.png)
68
46
 
69
47
  ## Inspiration
70
48
 
package/dist/index.d.mts CHANGED
@@ -1,106 +1,109 @@
1
+ //#region src/parser/lexer.d.ts
1
2
  type TokenType = '--' | '-' | 'number' | 'string' | 'long' | 'short';
2
3
  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;
4
+ private readonly text;
5
+ private _type;
6
+ constructor(text: string);
7
+ /**
8
+ * @returns Raw argument text
9
+ */
10
+ raw(): string;
11
+ /**
12
+ * @returns Number representation
13
+ */
14
+ number(): number;
15
+ /**
16
+ * @returns Remove start - for long or short option
17
+ */
18
+ option(): string;
19
+ isOption(): boolean;
20
+ isText(): boolean;
21
+ type(): TokenType;
21
22
  }
22
23
  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>;
24
+ private readonly rawArgs;
25
+ private cursor;
26
+ constructor(rawArgs: string[]);
27
+ next(): Token | undefined;
28
+ hasNext(): boolean;
29
+ peek(): Token | undefined;
30
+ [Symbol.iterator](): Iterator<Token, undefined>;
30
31
  }
31
-
32
- type Prettify<T> = {
33
- [K in keyof T]: T[K];
34
- } & {};
32
+ //#endregion
33
+ //#region src/types/utils.d.ts
34
+ type Prettify<T> = { [K in keyof T]: T[K] } & {};
35
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
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
37
  type Letter = Lowercase | Uppercase;
38
-
38
+ //#endregion
39
+ //#region src/parser/types.d.ts
39
40
  type CallbackFn = (result: BreadcParseResult) => any;
40
41
  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[];
42
+ /**
43
+ * Arguments that will be passed to action callback
44
+ * When parsing, this array is empty
45
+ */
46
+ arguments: Array<string | string[] | undefined>;
47
+ /**
48
+ * Options map
49
+ */
50
+ options: Record<string, any>;
51
+ /**
52
+ * Rest arguments.
53
+ * When parsing, this contains all the non-option arguments
54
+ */
55
+ '--': string[];
55
56
  }
56
57
  type BreadcParseResult = Prettify<ParseResult & {
57
- callback?: CallbackFn;
58
- meta: Record<string, any>;
59
- matched: {
60
- node: TreeNode;
61
- command?: Command;
62
- option?: Option;
63
- };
58
+ callback?: CallbackFn;
59
+ meta: Record<string, any>;
60
+ matched: {
61
+ node: TreeNode;
62
+ command?: Command;
63
+ option?: Option;
64
+ };
64
65
  }>;
65
66
  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;
67
+ /**
68
+ * Lexer instance
69
+ */
70
+ lexer: Lexer;
71
+ /**
72
+ * Options supported in the current context
73
+ */
74
+ options: Map<string, Option>;
75
+ /**
76
+ * Parse result
77
+ */
78
+ result: ParseResult;
79
+ /**
80
+ * Some other meta information passed
81
+ */
82
+ meta: Record<string, any>;
83
+ /**
84
+ * Configuration
85
+ */
86
+ config: {
87
+ allowUnknownOption: 'error' | 'skip' | 'rest';
88
+ };
89
+ parseOption(cursor: TreeNode, token: Token, context: Context): TreeNode | false;
89
90
  }
90
91
  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;
92
+ command?: Command;
93
+ option?: Option;
94
+ /**
95
+ * Out-going edges from this node
96
+ */
97
+ children: Map<string, TreeNode>;
98
+ init(context: Context): void;
99
+ next(arg: Token, context: Context): TreeNode | false;
100
+ finish(context: Context): F | undefined;
100
101
  }
101
-
102
+ //#endregion
103
+ //#region src/parser/parser.d.ts
102
104
  declare function makeTreeNode(pnode: Partial<TreeNode>): TreeNode;
103
-
105
+ //#endregion
106
+ //#region src/types/extract.d.ts
104
107
  /**
105
108
  * Extract option type, boolean or string
106
109
  */
@@ -121,12 +124,10 @@ type ExtractOptionName<T extends string, R extends string = ExtractOptionRawName
121
124
  /**
122
125
  * Extract option information
123
126
  */
124
- type ExtractOption<T extends string, D = never> = {
125
- [k in ExtractOptionName<T>]: D extends never ? ExtractOptionType<T> : D;
126
- };
127
+ type ExtractOption<T extends string, D = never> = { [k in ExtractOptionName<T>]: D extends never ? ExtractOptionType<T> : D };
127
128
  type Push<T extends any[], U, R> = [...T, U, R];
128
129
  type ActionFn<T extends any[], Option extends object = {}, R = any> = (...arg: Push<T, Prettify<Option & {
129
- '--': string[];
130
+ '--': string[];
130
131
  }>, {}>) => R | Promise<R>;
131
132
  /**
132
133
  * Max Dep: 5
@@ -134,88 +135,90 @@ type ActionFn<T extends any[], Option extends object = {}, R = any> = (...arg: P
134
135
  * Generated by: npx tsx examples/genType.ts 5
135
136
  */
136
137
  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
+ //#endregion
139
+ //#region src/types/breadc.d.ts
138
140
  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
- };
141
+ version?: string;
142
+ description?: string;
143
+ plugins?: Partial<Plugin>[];
144
+ builtin?: {
145
+ version?: false | Partial<{
146
+ description: string;
147
+ content: string;
148
+ }>;
149
+ help?: false | Partial<{
150
+ description: string;
151
+ }>;
152
+ };
151
153
  }
152
154
  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>;
155
+ name: string;
156
+ description: string;
157
+ option<OF extends string = string, OT extends string | boolean = ExtractOptionType<OF>, OO extends OptionOption<OT, any> = OptionOption<OT, any>, OR extends any = (OO extends {
158
+ cast(...args: any[]): infer CR;
159
+ } ? 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>>;
160
+ command<F extends string = string>(format: F, description?: string, option?: CommandOption): Command<F, ExtractCommand<F>, {}, GlobalOption>;
161
+ command<F extends string = string>(format: F, option?: CommandOption): Command<F, ExtractCommand<F>, {}, GlobalOption>;
162
+ parse(args: string[]): BreadcParseResult;
163
+ run<T = any>(args: string[]): Promise<T>;
162
164
  }
163
165
  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;
166
+ callback?: (result: ParseResult) => Promise<any>;
167
+ format: F;
168
+ description: string;
169
+ _default: boolean;
170
+ _arguments: Argument[];
171
+ _options: Option[];
172
+ option<OF extends string = string, OT extends string | boolean = ExtractOptionType<OF>, OO extends OptionOption<OT, any> = OptionOption<OT, any>, OR extends any = (OO extends {
173
+ cast(...args: any[]): infer CR;
174
+ } ? 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>;
175
+ alias(format: string): Command<F, AT, CommandOption, GlobalOption>;
176
+ action(fn: ActionFn<AT, CommandOption & GlobalOption>): void;
175
177
  }
176
178
  interface CommandOption {
177
- description?: string;
178
- /**
179
- * Config how to handle unknown options
180
- */
181
- allowUnknownOption?: 'error' | 'skip' | 'rest';
179
+ description?: string;
180
+ /**
181
+ * Config how to handle unknown options
182
+ */
183
+ allowUnknownOption?: 'error' | 'skip' | 'rest';
182
184
  }
183
185
  interface Argument {
184
- type: 'const' | 'require' | 'optional' | 'rest';
185
- name: string;
186
+ type: 'const' | 'require' | 'optional' | 'rest';
187
+ name: string;
186
188
  }
187
189
  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;
190
+ format: F;
191
+ type: T extends string ? 'string' : T extends boolean ? 'boolean' : never;
192
+ name: string;
193
+ short?: string;
194
+ description: string;
195
+ order: number;
196
+ initial?: R;
197
+ cast?: (value: T extends string ? string : T extends boolean ? boolean : never) => R;
198
+ parse?: (cursor: TreeNode, token: Token, context: Context) => TreeNode | false;
197
199
  }
198
200
  interface OptionOption<T extends string | boolean, R extends any = any> {
199
- description?: string;
200
- default?: T;
201
- cast?: (value: T) => R;
201
+ description?: string;
202
+ default?: T;
203
+ cast?: (value: T) => R;
202
204
  }
203
205
  type CommandHookFn = (result: ParseResult) => void | Promise<void>;
204
206
  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>;
207
+ onInit?(breadc: Breadc, allCommands: Command[], globalOptions: Option[]): void;
208
+ onPreRun?(breadc: Breadc): void | Promise<void>;
209
+ onPreCommand?: Record<string, CommandHookFn> | CommandHookFn;
210
+ onPostCommand?: Record<string, CommandHookFn> | CommandHookFn;
211
+ onPostRun?(breadc: Breadc): void | Promise<void>;
210
212
  }
211
-
213
+ //#endregion
214
+ //#region src/breadc.d.ts
212
215
  declare function breadc(name: string, config?: AppOption): Breadc<{}>;
213
-
216
+ //#endregion
217
+ //#region src/plugin.d.ts
214
218
  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 };
219
+ //#endregion
220
+ //#region src/error.d.ts
221
+ declare class BreadcError extends Error {}
222
+ declare class ParseError extends Error {}
223
+ //#endregion
224
+ export { type AppOption, type Argument, type Breadc, BreadcError, type Command, type Option, ParseError, type Plugin, breadc, definePlugin, makeTreeNode };