breadc 0.9.6 → 0.9.7
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 +1 -1
- package/dist/index.cjs +18 -2
- package/dist/index.d.cts +221 -0
- package/dist/index.d.mts +221 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +18 -2
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Yet another Command Line Application Framework with fully strong **[TypeScript](
|
|
|
11
11
|
+ 🔍 **TypeScript Infer**: IDE will automatically infer the type of your command action function;
|
|
12
12
|
+ 💻 **Commands**: support default command, command alias and sub-commands like `git remote add <name> <url>`;
|
|
13
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
|
-
+ 🧰 **
|
|
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.
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
package/dist/index.cjs
CHANGED
|
@@ -82,8 +82,16 @@ const initContextOptions = (options, context) => {
|
|
|
82
82
|
}
|
|
83
83
|
};
|
|
84
84
|
|
|
85
|
+
var __defProp = Object.defineProperty;
|
|
86
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
87
|
+
var __publicField = (obj, key, value) => {
|
|
88
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
89
|
+
return value;
|
|
90
|
+
};
|
|
85
91
|
class Token {
|
|
86
92
|
constructor(text) {
|
|
93
|
+
__publicField(this, "text");
|
|
94
|
+
__publicField(this, "_type");
|
|
87
95
|
this.text = text;
|
|
88
96
|
}
|
|
89
97
|
/**
|
|
@@ -131,7 +139,8 @@ class Token {
|
|
|
131
139
|
}
|
|
132
140
|
class Lexer {
|
|
133
141
|
constructor(rawArgs) {
|
|
134
|
-
this
|
|
142
|
+
__publicField(this, "rawArgs");
|
|
143
|
+
__publicField(this, "cursor", 0);
|
|
135
144
|
this.rawArgs = rawArgs;
|
|
136
145
|
}
|
|
137
146
|
next() {
|
|
@@ -193,7 +202,14 @@ function parseOption(cursor, token, context) {
|
|
|
193
202
|
if (option.parse) {
|
|
194
203
|
return option.parse(cursor, token, context);
|
|
195
204
|
} else if (option.type === "boolean") {
|
|
196
|
-
|
|
205
|
+
const negative = key.startsWith("no-");
|
|
206
|
+
if (rawV === void 0 || ["true", "yes", "t", "y"].includes(rawV.toLowerCase())) {
|
|
207
|
+
context.result.options[name] = !negative ? true : false;
|
|
208
|
+
} else if (["false", "no", "f", "n"].includes(rawV.toLowerCase())) {
|
|
209
|
+
context.result.options[name] = !negative ? false : true;
|
|
210
|
+
} else {
|
|
211
|
+
throw new ParseError(`Unexpected value ${rawV} for ${option.format}`);
|
|
212
|
+
}
|
|
197
213
|
} else if (option.type === "string") {
|
|
198
214
|
if (rawV !== void 0) {
|
|
199
215
|
context.result.options[name] = rawV;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
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 };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -218,4 +218,4 @@ declare class BreadcError extends Error {
|
|
|
218
218
|
declare class ParseError extends Error {
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
export { AppOption, Argument, Breadc, BreadcError, Command, Option, ParseError, Plugin, breadc, definePlugin, makeTreeNode };
|
|
221
|
+
export { type AppOption, type Argument, type Breadc, BreadcError, type Command, type Option, ParseError, type Plugin, breadc, definePlugin, makeTreeNode };
|
package/dist/index.mjs
CHANGED
|
@@ -80,8 +80,16 @@ const initContextOptions = (options, context) => {
|
|
|
80
80
|
}
|
|
81
81
|
};
|
|
82
82
|
|
|
83
|
+
var __defProp = Object.defineProperty;
|
|
84
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
85
|
+
var __publicField = (obj, key, value) => {
|
|
86
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
87
|
+
return value;
|
|
88
|
+
};
|
|
83
89
|
class Token {
|
|
84
90
|
constructor(text) {
|
|
91
|
+
__publicField(this, "text");
|
|
92
|
+
__publicField(this, "_type");
|
|
85
93
|
this.text = text;
|
|
86
94
|
}
|
|
87
95
|
/**
|
|
@@ -129,7 +137,8 @@ class Token {
|
|
|
129
137
|
}
|
|
130
138
|
class Lexer {
|
|
131
139
|
constructor(rawArgs) {
|
|
132
|
-
this
|
|
140
|
+
__publicField(this, "rawArgs");
|
|
141
|
+
__publicField(this, "cursor", 0);
|
|
133
142
|
this.rawArgs = rawArgs;
|
|
134
143
|
}
|
|
135
144
|
next() {
|
|
@@ -191,7 +200,14 @@ function parseOption(cursor, token, context) {
|
|
|
191
200
|
if (option.parse) {
|
|
192
201
|
return option.parse(cursor, token, context);
|
|
193
202
|
} else if (option.type === "boolean") {
|
|
194
|
-
|
|
203
|
+
const negative = key.startsWith("no-");
|
|
204
|
+
if (rawV === void 0 || ["true", "yes", "t", "y"].includes(rawV.toLowerCase())) {
|
|
205
|
+
context.result.options[name] = !negative ? true : false;
|
|
206
|
+
} else if (["false", "no", "f", "n"].includes(rawV.toLowerCase())) {
|
|
207
|
+
context.result.options[name] = !negative ? false : true;
|
|
208
|
+
} else {
|
|
209
|
+
throw new ParseError(`Unexpected value ${rawV} for ${option.format}`);
|
|
210
|
+
}
|
|
195
211
|
} else if (option.type === "string") {
|
|
196
212
|
if (rawV !== void 0) {
|
|
197
213
|
context.result.options[name] = rawV;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "breadc",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.7",
|
|
4
4
|
"description": "Yet another Command Line Application Framework with fully strong TypeScript support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"breadc",
|
|
@@ -35,13 +35,13 @@
|
|
|
35
35
|
"dist"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@breadc/color": "0.9.
|
|
38
|
+
"@breadc/color": "0.9.7"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@types/node": "^
|
|
42
|
-
"@vitest/coverage-
|
|
41
|
+
"@types/node": "^20.8.7",
|
|
42
|
+
"@vitest/coverage-v8": "^0.34.6",
|
|
43
43
|
"cac": "^6.7.14",
|
|
44
|
-
"vitest": "0.
|
|
44
|
+
"vitest": "0.34.6"
|
|
45
45
|
},
|
|
46
46
|
"size-limit": [
|
|
47
47
|
{
|