@voryx/cli 0.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.
- package/.turbo/turbo-build.log +22 -0
- package/dist/BooleanFlag.d.ts +10 -0
- package/dist/BooleanFlag.d.ts.map +1 -0
- package/dist/Cli.d.ts +37 -0
- package/dist/Cli.d.ts.map +1 -0
- package/dist/FlagSchema.d.ts +14 -0
- package/dist/FlagSchema.d.ts.map +1 -0
- package/dist/NumberFlag.d.ts +10 -0
- package/dist/NumberFlag.d.ts.map +1 -0
- package/dist/Schema.d.ts +14 -0
- package/dist/Schema.d.ts.map +1 -0
- package/dist/StringFlag.d.ts +10 -0
- package/dist/StringFlag.d.ts.map +1 -0
- package/dist/TerminalColor.d.ts +20 -0
- package/dist/TerminalColor.d.ts.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +345 -0
- package/dist/manifest.json +15 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +25 -0
- package/rslib.config.ts +22 -0
- package/src/BooleanFlag.ts +20 -0
- package/src/Cli.ts +180 -0
- package/src/FlagSchema.ts +24 -0
- package/src/NumberFlag.ts +23 -0
- package/src/Schema.ts +109 -0
- package/src/StringFlag.ts +20 -0
- package/src/TerminalColor.ts +70 -0
- package/src/index.ts +9 -0
- package/src/types.ts +50 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
> @voryx/cli@0.0.0 build /Users/cal/Documents/dev/voryx/packages/cli
|
|
4
|
+
> rslib build
|
|
5
|
+
|
|
6
|
+
[1m[38;2;179;250;236mR[39m[38;2;170;245;228ms[39m[38;2;160;240;221ml[39m[38;2;151;235;213mi[39m[38;2;141;230;206mb[39m[38;2;141;230;206m [39m[38;2;131;224;199mv[39m[38;2;122;219;191m0[39m[38;2;112;214;184m.[39m[38;2;103;209;176m1[39m[38;2;93;204;169m9[39m[38;2;84;199;161m.[39m[38;2;74;194;154m6[39m[38;2;74;194;154m
|
|
7
|
+
[39m[22m
|
|
8
|
+
(node:97408) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///Users/cal/Documents/dev/voryx/packages/cli/rslib.config.ts?t=1788725586056 is not specified and it doesn't parse as CommonJS.
|
|
9
|
+
Reparsing as ES module because module syntax was detected. This incurs a performance overhead.
|
|
10
|
+
To eliminate this warning, add "type": "module" to /Users/cal/Documents/dev/voryx/packages/cli/package.json.
|
|
11
|
+
(Use `node --trace-warnings ...` to show where the warning was created)
|
|
12
|
+
[1m[36minfo [39m[22m build started...
|
|
13
|
+
[1m[32mready [39m[22m built in [1m0.13[22m s
|
|
14
|
+
[1m[36mstart [39m[22m generating declaration files... [2m(esm)[22m
|
|
15
|
+
[1m[32mready [39m[22m declaration files generated in [1m0.26[22m s [2m(esm)[22m
|
|
16
|
+
|
|
17
|
+
[34mFile (esm) Size [39m
|
|
18
|
+
[2mdist/[22m[35mmanifest.json[39m 0.17 kB
|
|
19
|
+
[2mdist/[22m[36mindex.mjs[39m 11.0 kB
|
|
20
|
+
|
|
21
|
+
[35mTotal:[39m 11.1 kB
|
|
22
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FlagSchema } from './FlagSchema';
|
|
2
|
+
import type { FlagDefinition } from './types';
|
|
3
|
+
export declare class BooleanFlag<D extends boolean | undefined = boolean | undefined> extends FlagSchema<'boolean', D> {
|
|
4
|
+
constructor(options: Omit<FlagDefinition<'boolean', D>, 'kind'>);
|
|
5
|
+
resolveValue(raw: string | undefined): {
|
|
6
|
+
value: boolean;
|
|
7
|
+
usedValue: boolean;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=BooleanFlag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BooleanFlag.d.ts","sourceRoot":"","sources":["../src/BooleanFlag.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,qBAAa,WAAW,CACtB,CAAC,SAAS,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CACnD,SAAQ,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC;gBACnB,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC;IAIhE,YAAY,CAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG;QACtC,KAAK,EAAE,OAAO,CAAA;QACd,SAAS,EAAE,OAAO,CAAA;KACnB;CAMF"}
|
package/dist/Cli.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Schema } from './Schema';
|
|
2
|
+
import type { CommandCtx, SchemaOptions } from './types';
|
|
3
|
+
export interface Command<S extends SchemaOptions = SchemaOptions> {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
usage: string;
|
|
7
|
+
schema: Schema<S>;
|
|
8
|
+
run: (ctx: CommandCtx<S>) => void | Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
declare function createCommandClass(cli: Cli): {
|
|
11
|
+
new <S extends SchemaOptions = SchemaOptions>(options: {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
usage: string;
|
|
15
|
+
schema: S;
|
|
16
|
+
run: (ctx: CommandCtx<S>) => void | Promise<void>;
|
|
17
|
+
}): {
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
usage: string;
|
|
21
|
+
schema: Schema<S>;
|
|
22
|
+
run: (ctx: CommandCtx<S>) => void | Promise<void>;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export declare class Cli {
|
|
26
|
+
name: string;
|
|
27
|
+
commands: Map<string, Command>;
|
|
28
|
+
Command: ReturnType<typeof createCommandClass>;
|
|
29
|
+
constructor(name: string);
|
|
30
|
+
run(): Promise<void>;
|
|
31
|
+
warn(msg: string): void;
|
|
32
|
+
error(msg: string): void;
|
|
33
|
+
log(msg: string): void;
|
|
34
|
+
exit(code: number): void;
|
|
35
|
+
}
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=Cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Cli.d.ts","sourceRoot":"","sources":["../src/Cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAgExD,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,aAAa,GAAG,aAAa;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;IACjB,GAAG,EAAE,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClD;AAED,iBAAS,kBAAkB,CAAE,GAAG,EAAE,GAAG;SAEjC,CAAC,SAAS,aAAa,2BAOD;QACpB,IAAI,EAAE,MAAM,CAAA;QACZ,WAAW,EAAE,MAAM,CAAA;QACnB,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,CAAC,CAAA;QACT,GAAG,EAAE,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;KAClD;cAXG,MAAM;qBACC,MAAM;eACZ,MAAM;gBACL,MAAM,CAAC,CAAC,CAAC;aACZ,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;;EAgBlD;AAED,qBAAa,GAAG;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,OAAO,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAA;gBAEjC,IAAI,EAAE,MAAM;IAMnB,GAAG;IAqDT,IAAI,CAAE,GAAG,EAAE,MAAM;IAIjB,KAAK,CAAE,GAAG,EAAE,MAAM;IAKlB,GAAG,CAAE,GAAG,EAAE,MAAM;IAIhB,IAAI,CAAE,IAAI,EAAE,MAAM;CAGnB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FlagDefinition, FlagKind, TypeFlag } from './types';
|
|
2
|
+
export declare abstract class FlagSchema<T extends FlagKind = FlagKind, D extends TypeFlag<T> | undefined = TypeFlag<T> | undefined> {
|
|
3
|
+
kind: T;
|
|
4
|
+
long: string;
|
|
5
|
+
short?: string;
|
|
6
|
+
description: string;
|
|
7
|
+
default: D;
|
|
8
|
+
constructor(options: FlagDefinition<T, D>);
|
|
9
|
+
abstract resolveValue(raw: string | undefined): {
|
|
10
|
+
value: TypeFlag<T> | D;
|
|
11
|
+
usedValue: boolean;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=FlagSchema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FlagSchema.d.ts","sourceRoot":"","sources":["../src/FlagSchema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEjE,8BAAsB,UAAU,CAC9B,CAAC,SAAS,QAAQ,GAAG,QAAQ,EAC7B,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS;IAE3D,IAAI,EAAE,CAAC,CAAA;IACP,IAAI,EAAE,MAAM,CAAK;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,CAAC,CAAA;gBAEG,OAAO,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC;IAO1C,QAAQ,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG;QAC9C,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;QACtB,SAAS,EAAE,OAAO,CAAA;KACnB;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FlagSchema } from "./FlagSchema";
|
|
2
|
+
import type { FlagDefinition } from "./types";
|
|
3
|
+
export declare class NumberFlag<D extends number | undefined = number | undefined> extends FlagSchema<'number', D> {
|
|
4
|
+
constructor(options: Omit<FlagDefinition<'number', D>, 'kind'>);
|
|
5
|
+
resolveValue(raw: string | undefined): {
|
|
6
|
+
value: number;
|
|
7
|
+
usedValue: boolean;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=NumberFlag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NumberFlag.d.ts","sourceRoot":"","sources":["../src/NumberFlag.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,qBAAa,UAAU,CACrB,CAAC,SAAS,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CACjD,SAAQ,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAClB,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC;IAI/D,YAAY,CAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE;CAY9E"}
|
package/dist/Schema.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Flags, SchemaOptions } from "./types";
|
|
2
|
+
export declare class Schema<S extends SchemaOptions = SchemaOptions> {
|
|
3
|
+
private byLong;
|
|
4
|
+
private byShort;
|
|
5
|
+
private names;
|
|
6
|
+
constructor(schema: S);
|
|
7
|
+
map<T>(fn: (flag: S[keyof S]) => T): T[];
|
|
8
|
+
forEach(fn: (flag: S[keyof S], index: number) => void): void;
|
|
9
|
+
parse(argv: string[]): {
|
|
10
|
+
flags: Flags<S>;
|
|
11
|
+
unknown: string[];
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=Schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Schema.d.ts","sourceRoot":"","sources":["../src/Schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAEnD,qBAAa,MAAM,CAAC,CAAC,SAAS,aAAa,GAAG,aAAa;IACzD,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,KAAK,CAAa;gBAEb,MAAM,EAAE,CAAC;IAatB,GAAG,CAAC,CAAC,EAAG,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;IAQzC,OAAO,CAAE,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAQ7D,KAAK,CAAE,IAAI,EAAE,MAAM,EAAE,GAAG;QAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE;CAwE/D"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FlagSchema } from "./FlagSchema";
|
|
2
|
+
import type { FlagDefinition } from "./types";
|
|
3
|
+
export declare class StringFlag<D extends string | undefined = string | undefined> extends FlagSchema<'string', D> {
|
|
4
|
+
constructor(options: Omit<FlagDefinition<'string', D>, 'kind'>);
|
|
5
|
+
resolveValue(raw: string | undefined): {
|
|
6
|
+
value: string;
|
|
7
|
+
usedValue: boolean;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=StringFlag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StringFlag.d.ts","sourceRoot":"","sources":["../src/StringFlag.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,qBAAa,UAAU,CACrB,CAAC,SAAS,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CACjD,SAAQ,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAClB,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC;IAI/D,YAAY,CAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE;CAS9E"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare class TerminalColor {
|
|
2
|
+
static red(text: string): string;
|
|
3
|
+
static green(text: string): string;
|
|
4
|
+
static yellow(text: string): string;
|
|
5
|
+
static blue(text: string): string;
|
|
6
|
+
static magenta(text: string): string;
|
|
7
|
+
static cyan(text: string): string;
|
|
8
|
+
static purple(text: string): string;
|
|
9
|
+
static gray(text: string): string;
|
|
10
|
+
static bold(text: string): string;
|
|
11
|
+
static backgroundRed(text: string): string;
|
|
12
|
+
static backgroundGreen(text: string): string;
|
|
13
|
+
static backgroundYellow(text: string): string;
|
|
14
|
+
static backgroundBlue(text: string): string;
|
|
15
|
+
static backgroundMagenta(text: string): string;
|
|
16
|
+
static backgroundCyan(text: string): string;
|
|
17
|
+
static backgroundPurple(text: string): string;
|
|
18
|
+
static backgroundGray(text: string): string;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=TerminalColor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TerminalColor.d.ts","sourceRoot":"","sources":["../src/TerminalColor.ts"],"names":[],"mappings":"AACA,qBAAa,aAAa;IACxB,MAAM,CAAC,GAAG,CAAE,IAAI,EAAE,MAAM;IAIxB,MAAM,CAAC,KAAK,CAAE,IAAI,EAAE,MAAM;IAI1B,MAAM,CAAC,MAAM,CAAE,IAAI,EAAE,MAAM;IAI3B,MAAM,CAAC,IAAI,CAAE,IAAI,EAAE,MAAM;IAIzB,MAAM,CAAC,OAAO,CAAE,IAAI,EAAE,MAAM;IAI5B,MAAM,CAAC,IAAI,CAAE,IAAI,EAAE,MAAM;IAIzB,MAAM,CAAC,MAAM,CAAE,IAAI,EAAE,MAAM;IAI3B,MAAM,CAAC,IAAI,CAAE,IAAI,EAAE,MAAM;IAIzB,MAAM,CAAC,IAAI,CAAE,IAAI,EAAE,MAAM;IAIzB,MAAM,CAAC,aAAa,CAAE,IAAI,EAAE,MAAM;IAIlC,MAAM,CAAC,eAAe,CAAE,IAAI,EAAE,MAAM;IAIpC,MAAM,CAAC,gBAAgB,CAAE,IAAI,EAAE,MAAM;IAIrC,MAAM,CAAC,cAAc,CAAE,IAAI,EAAE,MAAM;IAInC,MAAM,CAAC,iBAAiB,CAAE,IAAI,EAAE,MAAM;IAItC,MAAM,CAAC,cAAc,CAAE,IAAI,EAAE,MAAM;IAInC,MAAM,CAAC,gBAAgB,CAAE,IAAI,EAAE,MAAM;IAIrC,MAAM,CAAC,cAAc,CAAE,IAAI,EAAE,MAAM;CAGpC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './Cli';
|
|
2
|
+
export * from './Schema';
|
|
3
|
+
export * from './types';
|
|
4
|
+
export * from './TerminalColor';
|
|
5
|
+
export * from './BooleanFlag';
|
|
6
|
+
export * from './StringFlag';
|
|
7
|
+
export * from './NumberFlag';
|
|
8
|
+
export * from './FlagSchema';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,OAAO,CAAA;AACrB,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AACvB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
class Schema {
|
|
2
|
+
byLong;
|
|
3
|
+
byShort;
|
|
4
|
+
names;
|
|
5
|
+
constructor(schema){
|
|
6
|
+
this.byLong = new Map();
|
|
7
|
+
this.byShort = new Map();
|
|
8
|
+
this.names = new Set();
|
|
9
|
+
for (const [key, s] of Object.entries(schema)){
|
|
10
|
+
s.long = key;
|
|
11
|
+
this.byLong.set(s.long, s);
|
|
12
|
+
if (s.short) this.byShort.set(s.short, s);
|
|
13
|
+
this.names.add(s.long);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
map(fn) {
|
|
17
|
+
const result = [];
|
|
18
|
+
for (const flag of this.byLong.values())result.push(fn(flag));
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
forEach(fn) {
|
|
22
|
+
let index = 0;
|
|
23
|
+
for (const flag of this.byLong.values()){
|
|
24
|
+
fn(flag, index);
|
|
25
|
+
index++;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
parse(argv) {
|
|
29
|
+
const flags = {};
|
|
30
|
+
const notFound = new Set(this.names);
|
|
31
|
+
const unknown = [];
|
|
32
|
+
for(let i = 0; i < argv.length; i++){
|
|
33
|
+
const a = argv[i];
|
|
34
|
+
if ('--' === a) break;
|
|
35
|
+
if (a.startsWith('--')) {
|
|
36
|
+
const eq = a.indexOf('=');
|
|
37
|
+
const name = -1 === eq ? a.slice(2) : a.slice(2, eq);
|
|
38
|
+
const flag = this.byLong.get(name);
|
|
39
|
+
if (!flag) {
|
|
40
|
+
unknown.push(a);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
notFound.delete(name);
|
|
44
|
+
const value = -1 === eq ? void 0 : a.slice(eq + 1);
|
|
45
|
+
const raw = value ?? argv[i + 1];
|
|
46
|
+
if (void 0 === value) {
|
|
47
|
+
const { value: resolved, usedValue } = flag.resolveValue(raw);
|
|
48
|
+
flags[flag.long] = resolved;
|
|
49
|
+
if (usedValue) i++;
|
|
50
|
+
} else {
|
|
51
|
+
const resolved = flag.resolveValue(value);
|
|
52
|
+
flags[flag.long] = resolved.value;
|
|
53
|
+
}
|
|
54
|
+
} else if (a.startsWith('-') && '-' !== a) {
|
|
55
|
+
const eq = a.indexOf('=');
|
|
56
|
+
const name = -1 === eq ? a.slice(1) : a.slice(1, eq);
|
|
57
|
+
const flag = this.byShort.get(name);
|
|
58
|
+
if (!flag) {
|
|
59
|
+
unknown.push(a);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
notFound.delete(name);
|
|
63
|
+
const value = -1 === eq ? void 0 : a.slice(eq + 1);
|
|
64
|
+
const raw = value ?? argv[i + 1];
|
|
65
|
+
if (void 0 === value) {
|
|
66
|
+
const { value: resolved, usedValue } = flag.resolveValue(raw);
|
|
67
|
+
flags[flag.long] = resolved;
|
|
68
|
+
if (usedValue) i++;
|
|
69
|
+
} else {
|
|
70
|
+
const resolved = flag.resolveValue(value);
|
|
71
|
+
flags[flag.long] = resolved.value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (const name of notFound){
|
|
76
|
+
const flag = this.byLong.get(name);
|
|
77
|
+
if (void 0 !== flag.default) flags[flag.long] = flag.default;
|
|
78
|
+
else if ('boolean' === flag.kind) flags[flag.long] = false;
|
|
79
|
+
else throw new Error(`Missing required flag --${flag.long}`);
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
flags,
|
|
83
|
+
unknown
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
class TerminalColor {
|
|
88
|
+
static red(text) {
|
|
89
|
+
return `\x1b[38;5;196m${text}\x1b[0m`;
|
|
90
|
+
}
|
|
91
|
+
static green(text) {
|
|
92
|
+
return `\x1b[38;5;47m${text}\x1b[0m`;
|
|
93
|
+
}
|
|
94
|
+
static yellow(text) {
|
|
95
|
+
return `\x1b[38;5;226m${text}\x1b[0m`;
|
|
96
|
+
}
|
|
97
|
+
static blue(text) {
|
|
98
|
+
return `\x1b[38;5;27m${text}\x1b[0m`;
|
|
99
|
+
}
|
|
100
|
+
static magenta(text) {
|
|
101
|
+
return `\x1b[38;5;200m${text}\x1b[0m`;
|
|
102
|
+
}
|
|
103
|
+
static cyan(text) {
|
|
104
|
+
return `\x1b[38;5;51m${text}\x1b[0m`;
|
|
105
|
+
}
|
|
106
|
+
static purple(text) {
|
|
107
|
+
return `\x1b[38;5;141m${text}\x1b[0m`;
|
|
108
|
+
}
|
|
109
|
+
static gray(text) {
|
|
110
|
+
return `\x1b[38;5;245m${text}\x1b[0m`;
|
|
111
|
+
}
|
|
112
|
+
static bold(text) {
|
|
113
|
+
return `\x1b[1m${text}\x1b[0m`;
|
|
114
|
+
}
|
|
115
|
+
static backgroundRed(text) {
|
|
116
|
+
return `\x1b[48;5;196m${text}\x1b[0m`;
|
|
117
|
+
}
|
|
118
|
+
static backgroundGreen(text) {
|
|
119
|
+
return `\x1b[48;5;47m${text}\x1b[0m`;
|
|
120
|
+
}
|
|
121
|
+
static backgroundYellow(text) {
|
|
122
|
+
return `\x1b[48;5;226m${text}\x1b[0m`;
|
|
123
|
+
}
|
|
124
|
+
static backgroundBlue(text) {
|
|
125
|
+
return `\x1b[48;5;27m${text}\x1b[0m`;
|
|
126
|
+
}
|
|
127
|
+
static backgroundMagenta(text) {
|
|
128
|
+
return `\x1b[48;5;200m${text}\x1b[0m`;
|
|
129
|
+
}
|
|
130
|
+
static backgroundCyan(text) {
|
|
131
|
+
return `\x1b[48;5;51m${text}\x1b[0m`;
|
|
132
|
+
}
|
|
133
|
+
static backgroundPurple(text) {
|
|
134
|
+
return `\x1b[48;5;141m${text}\x1b[0m`;
|
|
135
|
+
}
|
|
136
|
+
static backgroundGray(text) {
|
|
137
|
+
return `\x1b[48;5;245m${text}\x1b[0m`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function exit(code) {
|
|
141
|
+
process.exitCode = code;
|
|
142
|
+
throw code;
|
|
143
|
+
}
|
|
144
|
+
function formatError(msg) {
|
|
145
|
+
return `Error: ${msg}`;
|
|
146
|
+
}
|
|
147
|
+
function padRight(s, n) {
|
|
148
|
+
return s.length >= n ? s : s + ' '.repeat(n - s.length);
|
|
149
|
+
}
|
|
150
|
+
function printHelp(appName, commands) {
|
|
151
|
+
const lines = [];
|
|
152
|
+
lines.push(`${TerminalColor.backgroundPurple(appName)} — CLI`);
|
|
153
|
+
lines.push('');
|
|
154
|
+
lines.push('Usage:');
|
|
155
|
+
lines.push(` ${appName} ${TerminalColor.cyan('<command>')} [options]`);
|
|
156
|
+
lines.push('');
|
|
157
|
+
lines.push('Commands:');
|
|
158
|
+
const w = Math.max(...commands.map((c)=>c.name.length), 7);
|
|
159
|
+
for (const c of commands)lines.push(` ${padRight(c.name, w)} ${c.description}`);
|
|
160
|
+
lines.push('');
|
|
161
|
+
lines.push(`Run "${appName} <command> --help" for command options.`);
|
|
162
|
+
process.stdout.write(lines.join('\n') + '\n');
|
|
163
|
+
}
|
|
164
|
+
function printCommandHelp(appName, cmd) {
|
|
165
|
+
const lines = [];
|
|
166
|
+
lines.push(`${TerminalColor.backgroundPurple(appName)} ${TerminalColor.cyan(cmd.name)}`);
|
|
167
|
+
lines.push('');
|
|
168
|
+
lines.push(cmd.description);
|
|
169
|
+
lines.push('');
|
|
170
|
+
lines.push('Usage:');
|
|
171
|
+
lines.push(` ${appName} ${cmd.usage}`);
|
|
172
|
+
if (cmd.schema) {
|
|
173
|
+
lines.push('');
|
|
174
|
+
lines.push('Options:');
|
|
175
|
+
const left = cmd.schema.map((f)=>{
|
|
176
|
+
const short = 'short' in f && f.short ? `-${f.short}, ` : ' ';
|
|
177
|
+
const long = `--${f.long}`;
|
|
178
|
+
const value = 'boolean' === f.kind ? '' : `=<${'placeholder' in f && f.placeholder ? f.placeholder : f.kind}>`;
|
|
179
|
+
return `${short}${long}${value}`;
|
|
180
|
+
});
|
|
181
|
+
const w = Math.max(...left.map((s)=>s.length), 10);
|
|
182
|
+
cmd.schema.forEach((f, i)=>{
|
|
183
|
+
lines.push(` ${padRight(left[i], w)} ${f.description}`);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
lines.push('');
|
|
187
|
+
process.stdout.write(lines.join('\n') + '\n');
|
|
188
|
+
}
|
|
189
|
+
function createCommandClass(cli) {
|
|
190
|
+
return class {
|
|
191
|
+
name;
|
|
192
|
+
description;
|
|
193
|
+
usage;
|
|
194
|
+
schema;
|
|
195
|
+
run;
|
|
196
|
+
constructor(options){
|
|
197
|
+
this.name = options.name;
|
|
198
|
+
this.description = options.description;
|
|
199
|
+
this.usage = options.usage;
|
|
200
|
+
this.schema = new Schema(options.schema);
|
|
201
|
+
this.run = options.run;
|
|
202
|
+
cli.commands.set(this.name, this);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
class Cli {
|
|
207
|
+
name;
|
|
208
|
+
commands;
|
|
209
|
+
Command;
|
|
210
|
+
constructor(name){
|
|
211
|
+
this.name = name;
|
|
212
|
+
this.commands = new Map();
|
|
213
|
+
this.Command = createCommandClass(this);
|
|
214
|
+
}
|
|
215
|
+
async run() {
|
|
216
|
+
const argv = process.argv.slice(2);
|
|
217
|
+
const [maybeCommand, ...rest] = argv;
|
|
218
|
+
if (!maybeCommand || '-h' === maybeCommand || '--help' === maybeCommand) return void printHelp(this.name, [
|
|
219
|
+
...this.commands.values()
|
|
220
|
+
]);
|
|
221
|
+
const cmd = this.commands.get(maybeCommand);
|
|
222
|
+
if (!cmd) {
|
|
223
|
+
process.stderr.write(formatError(`Unknown command "${maybeCommand}".`) + '\n\n');
|
|
224
|
+
printHelp(this.name, [
|
|
225
|
+
...this.commands.values()
|
|
226
|
+
]);
|
|
227
|
+
exit(1);
|
|
228
|
+
}
|
|
229
|
+
if (rest.includes('--help') || rest.includes('-h')) return void printCommandHelp(this.name, cmd);
|
|
230
|
+
const parsed = cmd.schema.parse(rest);
|
|
231
|
+
if (parsed.unknown.length) {
|
|
232
|
+
process.stderr.write(formatError(`Unknown option(s): ${parsed.unknown.join(', ')}`) + '\n');
|
|
233
|
+
process.stderr.write(`Run "${this.name} ${cmd.name} --help" for options.\n`);
|
|
234
|
+
exit(1);
|
|
235
|
+
}
|
|
236
|
+
process.stdout.write(`${TerminalColor.backgroundPurple(this.name)} - <${TerminalColor.cyan(cmd.name)}>...\n\n`);
|
|
237
|
+
const timeNow = performance.now();
|
|
238
|
+
await cmd.run({
|
|
239
|
+
argv: rest,
|
|
240
|
+
flags: parsed.flags,
|
|
241
|
+
stdout: process.stdout,
|
|
242
|
+
stderr: process.stderr,
|
|
243
|
+
exit: exit
|
|
244
|
+
});
|
|
245
|
+
const timeEnd = performance.now();
|
|
246
|
+
process.stdout.write(`\n${TerminalColor.backgroundPurple(this.name)} - <${TerminalColor.cyan(cmd.name)}>\n completed in ${TerminalColor.green((timeEnd - timeNow).toFixed(2))}ms\n\n`);
|
|
247
|
+
}
|
|
248
|
+
warn(msg) {
|
|
249
|
+
process.stdout.write(` ${TerminalColor.yellow('Warning:')} ${msg.split('\n').join('\n ')}\n`);
|
|
250
|
+
}
|
|
251
|
+
error(msg) {
|
|
252
|
+
process.stderr.write(`${TerminalColor.red('Error:')} ${msg}\n`);
|
|
253
|
+
exit(1);
|
|
254
|
+
}
|
|
255
|
+
log(msg) {
|
|
256
|
+
process.stdout.write(` ${msg.split('\n').join('\n ')}\n`);
|
|
257
|
+
}
|
|
258
|
+
exit(code) {
|
|
259
|
+
exit(code);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
class FlagSchema {
|
|
263
|
+
kind;
|
|
264
|
+
long = '';
|
|
265
|
+
short;
|
|
266
|
+
description;
|
|
267
|
+
default;
|
|
268
|
+
constructor(options){
|
|
269
|
+
this.kind = options.kind;
|
|
270
|
+
this.short = options.short;
|
|
271
|
+
this.description = options.description;
|
|
272
|
+
this.default = options.default;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
class BooleanFlag extends FlagSchema {
|
|
276
|
+
constructor(options){
|
|
277
|
+
super({
|
|
278
|
+
...options,
|
|
279
|
+
kind: 'boolean'
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
resolveValue(raw) {
|
|
283
|
+
if (void 0 === raw) return {
|
|
284
|
+
value: true,
|
|
285
|
+
usedValue: false
|
|
286
|
+
};
|
|
287
|
+
if ('true' === raw.toLowerCase()) return {
|
|
288
|
+
value: true,
|
|
289
|
+
usedValue: true
|
|
290
|
+
};
|
|
291
|
+
if ('false' === raw.toLowerCase()) return {
|
|
292
|
+
value: false,
|
|
293
|
+
usedValue: true
|
|
294
|
+
};
|
|
295
|
+
return {
|
|
296
|
+
value: true,
|
|
297
|
+
usedValue: false
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
class StringFlag extends FlagSchema {
|
|
302
|
+
constructor(options){
|
|
303
|
+
super({
|
|
304
|
+
...options,
|
|
305
|
+
kind: 'string'
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
resolveValue(raw) {
|
|
309
|
+
if (void 0 === raw) {
|
|
310
|
+
if (void 0 !== this.default) return {
|
|
311
|
+
value: this.default,
|
|
312
|
+
usedValue: false
|
|
313
|
+
};
|
|
314
|
+
throw new Error(`Missing value for --${this.long}`);
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
value: raw,
|
|
318
|
+
usedValue: true
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
class NumberFlag extends FlagSchema {
|
|
323
|
+
constructor(options){
|
|
324
|
+
super({
|
|
325
|
+
...options,
|
|
326
|
+
kind: 'number'
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
resolveValue(raw) {
|
|
330
|
+
if (void 0 === raw) {
|
|
331
|
+
if (void 0 !== this.default) return {
|
|
332
|
+
value: this.default,
|
|
333
|
+
usedValue: false
|
|
334
|
+
};
|
|
335
|
+
throw new Error(`Missing value for --${this.long}`);
|
|
336
|
+
}
|
|
337
|
+
const n = Number(raw);
|
|
338
|
+
if (!Number.isFinite(n)) throw new Error(`Invalid number for --${this.long}: ${raw}`);
|
|
339
|
+
return {
|
|
340
|
+
value: n,
|
|
341
|
+
usedValue: true
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
export { BooleanFlag, Cli, FlagSchema, NumberFlag, Schema, StringFlag, TerminalColor };
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FlagSchema } from "./FlagSchema";
|
|
2
|
+
export type TypeFlag<T extends 'boolean' | 'string' | 'number'> = T extends 'boolean' ? boolean : T extends 'string' ? string : T extends 'number' ? number : never;
|
|
3
|
+
export type FlagKind = 'boolean' | 'string' | 'number';
|
|
4
|
+
export type FlagDefinition<T extends FlagKind, D extends TypeFlag<T> | undefined = TypeFlag<T> | undefined> = {
|
|
5
|
+
kind: T;
|
|
6
|
+
short?: string;
|
|
7
|
+
description: string;
|
|
8
|
+
default: D;
|
|
9
|
+
};
|
|
10
|
+
export type ShouldRequire<T extends FlagDefinition<FlagKind>> = T['default'] extends undefined ? T['kind'] extends 'boolean' ? true : false : true;
|
|
11
|
+
export type Flags<S extends SchemaOptions> = {
|
|
12
|
+
[K in keyof S as ShouldRequire<S[K]> extends true ? K : never]: TypeFlag<S[K]['kind']>;
|
|
13
|
+
} & {
|
|
14
|
+
[K in keyof S as ShouldRequire<S[K]> extends false ? K : never]?: TypeFlag<S[K]['kind']>;
|
|
15
|
+
};
|
|
16
|
+
export interface SchemaOptions {
|
|
17
|
+
[key: string]: FlagSchema;
|
|
18
|
+
}
|
|
19
|
+
export type CommandCtx<S extends SchemaOptions = SchemaOptions> = {
|
|
20
|
+
argv: string[];
|
|
21
|
+
flags: Flags<S>;
|
|
22
|
+
stdout: NodeJS.WriteStream;
|
|
23
|
+
stderr: NodeJS.WriteStream;
|
|
24
|
+
exit: (code: number) => never;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,SAAS,GAAG,QAAQ,GAAG,QAAQ,IAAI,CAAC,SAAS,SAAS,GACjF,OAAO,GACP,CAAC,SAAS,QAAQ,GAClB,MAAM,GACN,CAAC,SAAS,QAAQ,GAClB,MAAM,GACN,KAAK,CAAA;AAET,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAEtD,MAAM,MAAM,cAAc,CACxB,CAAC,SAAS,QAAQ,EAClB,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,IACzD;IACF,IAAI,EAAE,CAAC,CAAA;IACP,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,CAAC,CAAA;CACX,CAAA;AAED,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,cAAc,CAAC,QAAQ,CAAC,IAC1D,CAAC,CAAC,SAAS,CAAC,SAAS,SAAS,GAC1B,CAAC,CAAC,MAAM,CAAC,SAAS,SAAS,GACzB,IAAI,GACJ,KAAK,GACP,IAAI,CAAA;AAEV,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,aAAa,IAAI;KAC1C,CAAC,IAAI,MAAM,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,CAAC,GAAG,KAAK,GAAG,QAAQ,CACtE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CACb;CACF,GAAG;KACD,CAAC,IAAI,MAAM,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,EAAE,QAAQ,CACxE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CACb;CACF,CAAA;AAED,MAAM,WAAW,aAAa;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;CAC1B;AAED,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,aAAa,GAAG,aAAa,IAAI;IAChE,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;IACf,MAAM,EAAE,MAAM,CAAC,WAAW,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAC,WAAW,CAAA;IAC1B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAA;CAC9B,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@voryx/cli",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@rslib/core": "^0.19.6",
|
|
15
|
+
"@types/node": "^22.15.3",
|
|
16
|
+
"ts-standard": "^12.0.2",
|
|
17
|
+
"typescript": "5.9.2",
|
|
18
|
+
"@voryx/tsconfig": "0.0.0"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"lint": "ts-standard",
|
|
22
|
+
"build": "rslib build",
|
|
23
|
+
"check-types": "tsc --noEmit"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/rslib.config.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
import { defineConfig } from '@rslib/core'
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
lib: [
|
|
6
|
+
{
|
|
7
|
+
format: 'esm',
|
|
8
|
+
syntax: 'es2023',
|
|
9
|
+
bundle: true,
|
|
10
|
+
dts: true
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
output: {
|
|
14
|
+
target: 'node',
|
|
15
|
+
manifest: true
|
|
16
|
+
},
|
|
17
|
+
source: {
|
|
18
|
+
entry: {
|
|
19
|
+
index: 'src/index.ts'
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FlagSchema } from './FlagSchema'
|
|
2
|
+
import type { FlagDefinition } from './types'
|
|
3
|
+
|
|
4
|
+
export class BooleanFlag<
|
|
5
|
+
D extends boolean | undefined = boolean | undefined
|
|
6
|
+
> extends FlagSchema<'boolean', D> {
|
|
7
|
+
constructor (options: Omit<FlagDefinition<'boolean', D>, 'kind'>) {
|
|
8
|
+
super({ ...options, kind: 'boolean' })
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
resolveValue (raw: string | undefined): {
|
|
12
|
+
value: boolean
|
|
13
|
+
usedValue: boolean
|
|
14
|
+
} {
|
|
15
|
+
if (raw === undefined) return { value: true, usedValue: false }
|
|
16
|
+
if (raw.toLowerCase() === 'true') return { value: true, usedValue: true }
|
|
17
|
+
if (raw.toLowerCase() === 'false') return { value: false, usedValue: true }
|
|
18
|
+
return { value: true, usedValue: false }
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/Cli.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { Schema } from './Schema'
|
|
2
|
+
import type { CommandCtx, SchemaOptions } from './types'
|
|
3
|
+
import { TerminalColor } from './TerminalColor'
|
|
4
|
+
|
|
5
|
+
function exit (code: number): never {
|
|
6
|
+
process.exitCode = code
|
|
7
|
+
// eslint-disable-next-line no-throw-literal
|
|
8
|
+
throw code // allows early exits inside async without extra plumbing
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function formatError (msg: string) {
|
|
12
|
+
return `Error: ${msg}`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function padRight (s: string, n: number) {
|
|
16
|
+
return s.length >= n ? s : s + ' '.repeat(n - s.length)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function printHelp (appName: string, commands: Command[]) {
|
|
20
|
+
const lines: string[] = []
|
|
21
|
+
lines.push(`${TerminalColor.backgroundPurple(appName)} — CLI`)
|
|
22
|
+
lines.push('')
|
|
23
|
+
lines.push('Usage:')
|
|
24
|
+
lines.push(` ${appName} ${TerminalColor.cyan('<command>')} [options]`)
|
|
25
|
+
lines.push('')
|
|
26
|
+
lines.push('Commands:')
|
|
27
|
+
const w = Math.max(...commands.map(c => c.name.length), 7)
|
|
28
|
+
for (const c of commands) {
|
|
29
|
+
lines.push(` ${padRight(c.name, w)} ${c.description}`)
|
|
30
|
+
}
|
|
31
|
+
lines.push('')
|
|
32
|
+
lines.push(`Run "${appName} <command> --help" for command options.`)
|
|
33
|
+
process.stdout.write(lines.join('\n') + '\n')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function printCommandHelp (appName: string, cmd: Command) {
|
|
37
|
+
const lines: string[] = []
|
|
38
|
+
lines.push(`${TerminalColor.backgroundPurple(appName)} ${TerminalColor.cyan(cmd.name)}`)
|
|
39
|
+
lines.push('')
|
|
40
|
+
lines.push(cmd.description)
|
|
41
|
+
lines.push('')
|
|
42
|
+
lines.push('Usage:')
|
|
43
|
+
lines.push(` ${appName} ${cmd.usage}`)
|
|
44
|
+
if (cmd.schema) {
|
|
45
|
+
lines.push('')
|
|
46
|
+
lines.push('Options:')
|
|
47
|
+
const left = cmd.schema.map(f => {
|
|
48
|
+
const short = 'short' in f && f.short ? `-${f.short}, ` : ' '
|
|
49
|
+
const long = `--${f.long}`
|
|
50
|
+
const value =
|
|
51
|
+
f.kind === 'boolean'
|
|
52
|
+
? ''
|
|
53
|
+
: `=<${'placeholder' in f && f.placeholder ? f.placeholder : f.kind}>`
|
|
54
|
+
return `${short}${long}${value}`
|
|
55
|
+
})
|
|
56
|
+
const w = Math.max(...left.map(s => s.length), 10)
|
|
57
|
+
|
|
58
|
+
cmd.schema.forEach((f, i) => {
|
|
59
|
+
lines.push(` ${padRight(left[i]!, w)} ${f.description}`)
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
lines.push('')
|
|
63
|
+
process.stdout.write(lines.join('\n') + '\n')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface Command<S extends SchemaOptions = SchemaOptions> {
|
|
67
|
+
name: string
|
|
68
|
+
description: string
|
|
69
|
+
usage: string
|
|
70
|
+
schema: Schema<S>
|
|
71
|
+
run: (ctx: CommandCtx<S>) => void | Promise<void>
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function createCommandClass (cli: Cli) {
|
|
75
|
+
return class CliCommand<
|
|
76
|
+
S extends SchemaOptions = SchemaOptions
|
|
77
|
+
> implements Command<S> {
|
|
78
|
+
name: string
|
|
79
|
+
description: string
|
|
80
|
+
usage: string
|
|
81
|
+
schema: Schema<S>
|
|
82
|
+
run: (ctx: CommandCtx<S>) => void | Promise<void>
|
|
83
|
+
constructor (options: {
|
|
84
|
+
name: string
|
|
85
|
+
description: string
|
|
86
|
+
usage: string
|
|
87
|
+
schema: S
|
|
88
|
+
run: (ctx: CommandCtx<S>) => void | Promise<void>
|
|
89
|
+
}) {
|
|
90
|
+
this.name = options.name
|
|
91
|
+
this.description = options.description
|
|
92
|
+
this.usage = options.usage
|
|
93
|
+
this.schema = new Schema(options.schema)
|
|
94
|
+
this.run = options.run
|
|
95
|
+
cli.commands.set(this.name, this as Command<any>)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export class Cli {
|
|
101
|
+
name: string
|
|
102
|
+
commands: Map<string, Command>
|
|
103
|
+
Command: ReturnType<typeof createCommandClass>
|
|
104
|
+
|
|
105
|
+
constructor (name: string) {
|
|
106
|
+
this.name = name
|
|
107
|
+
this.commands = new Map()
|
|
108
|
+
this.Command = createCommandClass(this)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async run () {
|
|
112
|
+
// remove node + script path
|
|
113
|
+
const argv = process.argv.slice(2)
|
|
114
|
+
|
|
115
|
+
const [maybeCommand, ...rest] = argv
|
|
116
|
+
|
|
117
|
+
if (!maybeCommand || maybeCommand === '-h' || maybeCommand === '--help') {
|
|
118
|
+
printHelp(this.name, [...this.commands.values()])
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const cmd = this.commands.get(maybeCommand)
|
|
123
|
+
if (!cmd) {
|
|
124
|
+
process.stderr.write(
|
|
125
|
+
formatError(`Unknown command "${maybeCommand}".`) + '\n\n'
|
|
126
|
+
)
|
|
127
|
+
printHelp(this.name, [...this.commands.values()])
|
|
128
|
+
exit(1)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// command-level help
|
|
132
|
+
if (rest.includes('--help') || rest.includes('-h')) {
|
|
133
|
+
printCommandHelp(this.name, cmd)
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const parsed = cmd.schema.parse(rest)
|
|
138
|
+
|
|
139
|
+
if (parsed.unknown.length) {
|
|
140
|
+
process.stderr.write(
|
|
141
|
+
formatError(`Unknown option(s): ${parsed.unknown.join(', ')}`) + '\n'
|
|
142
|
+
)
|
|
143
|
+
process.stderr.write(
|
|
144
|
+
`Run "${this.name} ${cmd.name} --help" for options.\n`
|
|
145
|
+
)
|
|
146
|
+
exit(1)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
process.stdout.write(`${TerminalColor.backgroundPurple(this.name)} - <${TerminalColor.cyan(cmd.name)}>...\n\n`)
|
|
150
|
+
const timeNow = performance.now()
|
|
151
|
+
|
|
152
|
+
await cmd.run({
|
|
153
|
+
argv: rest,
|
|
154
|
+
flags: parsed.flags,
|
|
155
|
+
stdout: process.stdout,
|
|
156
|
+
stderr: process.stderr,
|
|
157
|
+
exit
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
const timeEnd = performance.now()
|
|
161
|
+
process.stdout.write(`\n${TerminalColor.backgroundPurple(this.name)} - <${TerminalColor.cyan(cmd.name)}>\n completed in ${TerminalColor.green((timeEnd - timeNow).toFixed(2))}ms\n\n`)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
warn (msg: string) {
|
|
165
|
+
process.stdout.write(` ${TerminalColor.yellow('Warning:')} ${msg.split('\n').join('\n ')}\n`)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
error (msg: string) {
|
|
169
|
+
process.stderr.write(`${TerminalColor.red('Error:')} ${msg}\n`)
|
|
170
|
+
exit(1)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
log (msg: string) {
|
|
174
|
+
process.stdout.write(` ${msg.split('\n').join('\n ')}\n`)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
exit (code: number) {
|
|
178
|
+
exit(code)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { FlagDefinition, FlagKind, TypeFlag } from './types'
|
|
2
|
+
|
|
3
|
+
export abstract class FlagSchema<
|
|
4
|
+
T extends FlagKind = FlagKind,
|
|
5
|
+
D extends TypeFlag<T> | undefined = TypeFlag<T> | undefined
|
|
6
|
+
> {
|
|
7
|
+
kind: T
|
|
8
|
+
long: string = ''
|
|
9
|
+
short?: string
|
|
10
|
+
description: string
|
|
11
|
+
default: D
|
|
12
|
+
|
|
13
|
+
constructor (options: FlagDefinition<T, D>) {
|
|
14
|
+
this.kind = options.kind
|
|
15
|
+
this.short = options.short
|
|
16
|
+
this.description = options.description
|
|
17
|
+
this.default = options.default
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
abstract resolveValue(raw: string | undefined): {
|
|
21
|
+
value: TypeFlag<T> | D
|
|
22
|
+
usedValue: boolean
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FlagSchema } from "./FlagSchema"
|
|
2
|
+
import type { FlagDefinition } from "./types"
|
|
3
|
+
|
|
4
|
+
export class NumberFlag<
|
|
5
|
+
D extends number | undefined = number | undefined
|
|
6
|
+
> extends FlagSchema<'number', D> {
|
|
7
|
+
constructor (options: Omit<FlagDefinition<'number', D>, 'kind'>) {
|
|
8
|
+
super({ ...options, kind: 'number' })
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
resolveValue (raw: string | undefined): { value: number; usedValue: boolean } {
|
|
12
|
+
if (raw === undefined) {
|
|
13
|
+
if (this.default !== undefined)
|
|
14
|
+
return { value: this.default, usedValue: false }
|
|
15
|
+
throw new Error(`Missing value for --${this.long}`)
|
|
16
|
+
}
|
|
17
|
+
const n = Number(raw)
|
|
18
|
+
if (!Number.isFinite(n)) {
|
|
19
|
+
throw new Error(`Invalid number for --${this.long}: ${raw}`)
|
|
20
|
+
}
|
|
21
|
+
return { value: n, usedValue: true }
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/Schema.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { Flags, SchemaOptions } from "./types"
|
|
2
|
+
|
|
3
|
+
export class Schema<S extends SchemaOptions = SchemaOptions> {
|
|
4
|
+
private byLong: Map<string, S[keyof S]>
|
|
5
|
+
private byShort: Map<string, S[keyof S]>
|
|
6
|
+
private names: Set<string>
|
|
7
|
+
|
|
8
|
+
constructor (schema: S) {
|
|
9
|
+
this.byLong = new Map()
|
|
10
|
+
this.byShort = new Map()
|
|
11
|
+
this.names = new Set()
|
|
12
|
+
|
|
13
|
+
for (const [key, s] of Object.entries(schema)) {
|
|
14
|
+
s.long = key
|
|
15
|
+
this.byLong.set(s.long, s as S[keyof S])
|
|
16
|
+
if (s.short) this.byShort.set(s.short, s as S[keyof S])
|
|
17
|
+
this.names.add(s.long)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
map<T> (fn: (flag: S[keyof S]) => T): T[] {
|
|
22
|
+
const result: T[] = []
|
|
23
|
+
for (const flag of this.byLong.values()) {
|
|
24
|
+
result.push(fn(flag))
|
|
25
|
+
}
|
|
26
|
+
return result
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
forEach (fn: (flag: S[keyof S], index: number) => void): void {
|
|
30
|
+
let index = 0
|
|
31
|
+
for (const flag of this.byLong.values()) {
|
|
32
|
+
fn(flag, index)
|
|
33
|
+
index++
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
parse (argv: string[]): { flags: Flags<S>; unknown: string[] } {
|
|
38
|
+
const flags = {} as Flags<S>
|
|
39
|
+
const notFound: Set<string> = new Set(this.names)
|
|
40
|
+
const unknown: string[] = []
|
|
41
|
+
|
|
42
|
+
for (let i = 0; i < argv.length; i++) {
|
|
43
|
+
const a = argv[i]!
|
|
44
|
+
|
|
45
|
+
if (a === '--') {
|
|
46
|
+
break
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (a.startsWith('--')) {
|
|
50
|
+
const eq = a.indexOf('=')
|
|
51
|
+
const name = eq === -1 ? a.slice(2) : a.slice(2, eq)
|
|
52
|
+
const flag = this.byLong.get(name)
|
|
53
|
+
|
|
54
|
+
if (!flag) {
|
|
55
|
+
unknown.push(a)
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
notFound.delete(name)
|
|
60
|
+
const value = eq === -1 ? undefined : a.slice(eq + 1)
|
|
61
|
+
const raw = value ?? argv[i + 1]
|
|
62
|
+
|
|
63
|
+
if (value === undefined) {
|
|
64
|
+
const { value: resolved, usedValue } = flag.resolveValue(raw)
|
|
65
|
+
flags[flag.long as keyof Flags<S>] = resolved as any
|
|
66
|
+
if (usedValue) i++
|
|
67
|
+
} else {
|
|
68
|
+
const resolved = flag.resolveValue(value)
|
|
69
|
+
flags[flag.long as keyof Flags<S>] = resolved.value as any
|
|
70
|
+
}
|
|
71
|
+
} else if (a.startsWith('-') && a !== '-') {
|
|
72
|
+
const eq = a.indexOf('=')
|
|
73
|
+
const name = eq === -1 ? a.slice(1) : a.slice(1, eq)
|
|
74
|
+
const flag = this.byShort.get(name)
|
|
75
|
+
|
|
76
|
+
if (!flag) {
|
|
77
|
+
unknown.push(a)
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
notFound.delete(name)
|
|
82
|
+
const value = eq === -1 ? undefined : a.slice(eq + 1)
|
|
83
|
+
const raw = value ?? argv[i + 1]
|
|
84
|
+
|
|
85
|
+
if (value === undefined) {
|
|
86
|
+
const { value: resolved, usedValue } = flag.resolveValue(raw)
|
|
87
|
+
flags[flag.long as keyof Flags<S>] = resolved as any
|
|
88
|
+
if (usedValue) i++
|
|
89
|
+
} else {
|
|
90
|
+
const resolved = flag.resolveValue(value)
|
|
91
|
+
flags[flag.long as keyof Flags<S>] = resolved.value as any
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
for (const name of notFound) {
|
|
97
|
+
const flag = this.byLong.get(name)!
|
|
98
|
+
if (flag.default !== undefined) {
|
|
99
|
+
flags[flag.long as keyof Flags<S>] = flag.default as any
|
|
100
|
+
} else if (flag.kind === 'boolean') {
|
|
101
|
+
flags[flag.long as keyof Flags<S>] = false as any
|
|
102
|
+
} else {
|
|
103
|
+
throw new Error(`Missing required flag --${flag.long}`)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return { flags, unknown }
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FlagSchema } from "./FlagSchema"
|
|
2
|
+
import type { FlagDefinition } from "./types"
|
|
3
|
+
|
|
4
|
+
export class StringFlag<
|
|
5
|
+
D extends string | undefined = string | undefined
|
|
6
|
+
> extends FlagSchema<'string', D> {
|
|
7
|
+
constructor (options: Omit<FlagDefinition<'string', D>, 'kind'>) {
|
|
8
|
+
super({ ...options, kind: 'string' })
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
resolveValue (raw: string | undefined): { value: string; usedValue: boolean } {
|
|
12
|
+
if (raw === undefined) {
|
|
13
|
+
if (this.default !== undefined)
|
|
14
|
+
return { value: this.default, usedValue: false }
|
|
15
|
+
throw new Error(`Missing value for --${this.long}`)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return { value: raw, usedValue: true }
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
|
|
2
|
+
export class TerminalColor {
|
|
3
|
+
static red (text: string) {
|
|
4
|
+
return `\x1b[38;5;196m${text}\x1b[0m`
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
static green (text: string) {
|
|
8
|
+
return `\x1b[38;5;47m${text}\x1b[0m`
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static yellow (text: string) {
|
|
12
|
+
return `\x1b[38;5;226m${text}\x1b[0m`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static blue (text: string) {
|
|
16
|
+
return `\x1b[38;5;27m${text}\x1b[0m`
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static magenta (text: string) {
|
|
20
|
+
return `\x1b[38;5;200m${text}\x1b[0m`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static cyan (text: string) {
|
|
24
|
+
return `\x1b[38;5;51m${text}\x1b[0m`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static purple (text: string) {
|
|
28
|
+
return `\x1b[38;5;141m${text}\x1b[0m`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static gray (text: string) {
|
|
32
|
+
return `\x1b[38;5;245m${text}\x1b[0m`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static bold (text: string) {
|
|
36
|
+
return `\x1b[1m${text}\x1b[0m`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static backgroundRed (text: string) {
|
|
40
|
+
return `\x1b[48;5;196m${text}\x1b[0m`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static backgroundGreen (text: string) {
|
|
44
|
+
return `\x1b[48;5;47m${text}\x1b[0m`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static backgroundYellow (text: string) {
|
|
48
|
+
return `\x1b[48;5;226m${text}\x1b[0m`
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static backgroundBlue (text: string) {
|
|
52
|
+
return `\x1b[48;5;27m${text}\x1b[0m`
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static backgroundMagenta (text: string) {
|
|
56
|
+
return `\x1b[48;5;200m${text}\x1b[0m`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static backgroundCyan (text: string) {
|
|
60
|
+
return `\x1b[48;5;51m${text}\x1b[0m`
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static backgroundPurple (text: string) {
|
|
64
|
+
return `\x1b[48;5;141m${text}\x1b[0m`
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static backgroundGray (text: string) {
|
|
68
|
+
return `\x1b[48;5;245m${text}\x1b[0m`
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/index.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { FlagSchema } from "./FlagSchema"
|
|
2
|
+
|
|
3
|
+
export type TypeFlag<T extends 'boolean' | 'string' | 'number'> = T extends 'boolean'
|
|
4
|
+
? boolean
|
|
5
|
+
: T extends 'string'
|
|
6
|
+
? string
|
|
7
|
+
: T extends 'number'
|
|
8
|
+
? number
|
|
9
|
+
: never
|
|
10
|
+
|
|
11
|
+
export type FlagKind = 'boolean' | 'string' | 'number'
|
|
12
|
+
|
|
13
|
+
export type FlagDefinition<
|
|
14
|
+
T extends FlagKind,
|
|
15
|
+
D extends TypeFlag<T> | undefined = TypeFlag<T> | undefined
|
|
16
|
+
> = {
|
|
17
|
+
kind: T
|
|
18
|
+
short?: string
|
|
19
|
+
description: string
|
|
20
|
+
default: D
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type ShouldRequire<T extends FlagDefinition<FlagKind>> =
|
|
24
|
+
T['default'] extends undefined
|
|
25
|
+
? T['kind'] extends 'boolean'
|
|
26
|
+
? true
|
|
27
|
+
: false
|
|
28
|
+
: true
|
|
29
|
+
|
|
30
|
+
export type Flags<S extends SchemaOptions> = {
|
|
31
|
+
[K in keyof S as ShouldRequire<S[K]> extends true ? K : never]: TypeFlag<
|
|
32
|
+
S[K]['kind']
|
|
33
|
+
>
|
|
34
|
+
} & {
|
|
35
|
+
[K in keyof S as ShouldRequire<S[K]> extends false ? K : never]?: TypeFlag<
|
|
36
|
+
S[K]['kind']
|
|
37
|
+
>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface SchemaOptions {
|
|
41
|
+
[key: string]: FlagSchema
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type CommandCtx<S extends SchemaOptions = SchemaOptions> = {
|
|
45
|
+
argv: string[]
|
|
46
|
+
flags: Flags<S>
|
|
47
|
+
stdout: NodeJS.WriteStream
|
|
48
|
+
stderr: NodeJS.WriteStream
|
|
49
|
+
exit: (code: number) => never
|
|
50
|
+
}
|