cli-kiss 0.2.4 → 0.2.6
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/dist/index.d.ts +159 -167
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/docs/.vitepress/config.mts +3 -2
- package/docs/.vitepress/theme/style.css +6 -2
- package/docs/guide/01_getting_started.md +12 -13
- package/docs/guide/02_commands.md +12 -29
- package/docs/guide/03_options.md +16 -25
- package/docs/guide/04_positionals.md +45 -55
- package/docs/guide/05_input_types.md +134 -0
- package/docs/guide/06_run_as_cli.md +143 -0
- package/docs/index.md +3 -2
- package/docs/public/favicon.ico +0 -0
- package/docs/public/hero.png +0 -0
- package/package.json +1 -1
- package/src/index.ts +0 -2
- package/src/lib/Command.ts +14 -35
- package/src/lib/Operation.ts +13 -4
- package/src/lib/Option.ts +118 -162
- package/src/lib/Positional.ts +37 -62
- package/src/lib/Reader.ts +3 -3
- package/src/lib/Run.ts +76 -49
- package/src/lib/Type.ts +227 -143
- package/src/lib/Typo.ts +55 -23
- package/src/lib/Usage.ts +30 -45
- package/tests/unit.Reader.parsings.ts +50 -0
- package/tests/unit.command.execute.ts +13 -13
- package/tests/unit.command.usage.ts +60 -54
- package/tests/unit.runner.colors.ts +199 -0
- package/tests/unit.runner.cycle.ts +69 -55
- package/tests/unit.runner.errors.ts +12 -20
- package/docs/guide/05_types.md +0 -132
- package/docs/guide/06_run.md +0 -160
package/docs/guide/05_types.md
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
# Types
|
|
2
|
-
|
|
3
|
-
A `Type<Value>` converts a raw CLI string into a typed value: a `content` label paired with a `decoder`.
|
|
4
|
-
|
|
5
|
-
## Built-in types
|
|
6
|
-
|
|
7
|
-
| Export | TypeScript type | Accepts |
|
|
8
|
-
| ------------- | --------------- | ---------------------------------------------------------- |
|
|
9
|
-
| `typeString` | `string` | Any string |
|
|
10
|
-
| `typeBoolean` | `boolean` | `true/yes/on/1/y/t` → true, `false/no/off/0/n/f` → false (case-insensitive) |
|
|
11
|
-
| `typeNumber` | `number` | Integers, floats, scientific notation |
|
|
12
|
-
| `typeInteger` | `bigint` | Integer strings only |
|
|
13
|
-
| `typeDate` | `Date` | Any format accepted by `Date.parse` (ISO 8601 recommended) |
|
|
14
|
-
| `typeUrl` | `URL` | Absolute URLs |
|
|
15
|
-
|
|
16
|
-
```ts
|
|
17
|
-
import {
|
|
18
|
-
typeBoolean,
|
|
19
|
-
typeDate,
|
|
20
|
-
typeInteger,
|
|
21
|
-
typeNumber,
|
|
22
|
-
typeString,
|
|
23
|
-
typeUrl,
|
|
24
|
-
} from "cli-kiss";
|
|
25
|
-
|
|
26
|
-
typeString.decoder("hello"); // → "hello"
|
|
27
|
-
typeBoolean.decoder("yes"); // → true
|
|
28
|
-
typeNumber.decoder("3.14"); // → 3.14
|
|
29
|
-
typeInteger.decoder("9007199254740993"); // → 9007199254740993n
|
|
30
|
-
typeDate.decoder("2024-01-15"); // → Date object
|
|
31
|
-
typeUrl.decoder("https://example.com/path"); // → URL object
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## `typeOneOf` — string enum
|
|
35
|
-
|
|
36
|
-
Accepts only a fixed set of strings:
|
|
37
|
-
|
|
38
|
-
```ts
|
|
39
|
-
import { typeOneOf } from "cli-kiss";
|
|
40
|
-
|
|
41
|
-
const typeEnv = typeOneOf("Environment", ["dev", "staging", "prod"]);
|
|
42
|
-
|
|
43
|
-
typeEnv.decoder("prod"); // → "prod"
|
|
44
|
-
typeEnv.decoder("unknown");
|
|
45
|
-
// Error: Invalid value: "unknown" (expected one of: "dev" | "staging" | "prod")
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## `typeMapped` — transform an existing type
|
|
49
|
-
|
|
50
|
-
Chain a `before` type with an `after` transformation:
|
|
51
|
-
|
|
52
|
-
```ts
|
|
53
|
-
import { typeMapped, typeNumber } from "cli-kiss";
|
|
54
|
-
|
|
55
|
-
const typePort = typeMapped(typeNumber, {
|
|
56
|
-
content: "Port",
|
|
57
|
-
decoder: (n) => {
|
|
58
|
-
if (n < 1 || n > 65535) throw new Error("Out of range");
|
|
59
|
-
return n;
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
// "--port 8080" → 8080
|
|
63
|
-
// "--port 99999" → Error: --port: <PORT>: Port: Out of range
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Errors from the `before` decoder are prefixed with `from: <content>`.
|
|
67
|
-
|
|
68
|
-
## `typeTuple` — fixed-length delimited value
|
|
69
|
-
|
|
70
|
-
Splits a string into a fixed-length typed tuple:
|
|
71
|
-
|
|
72
|
-
```ts
|
|
73
|
-
import { typeTuple, typeNumber } from "cli-kiss";
|
|
74
|
-
|
|
75
|
-
const typePoint = typeTuple([typeNumber, typeNumber]);
|
|
76
|
-
|
|
77
|
-
typePoint.decoder("3.14,2.71"); // → [3.14, 2.71]
|
|
78
|
-
typePoint.decoder("x,2"); // → Error: at 0: Number: Unable to parse: "x"
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
The default separator is `","`. Pass a second argument to change it:
|
|
82
|
-
|
|
83
|
-
```ts
|
|
84
|
-
typeTuple([typeString, typeNumber], ":");
|
|
85
|
-
// "foo:42" → ["foo", 42]
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## `typeList` — variable-length delimited value
|
|
89
|
-
|
|
90
|
-
Splits a string into an array of typed values:
|
|
91
|
-
|
|
92
|
-
```ts
|
|
93
|
-
import { typeList, typeNumber } from "cli-kiss";
|
|
94
|
-
|
|
95
|
-
const typeNumbers = typeList(typeNumber);
|
|
96
|
-
|
|
97
|
-
typeNumbers.decoder("1,2,3"); // → [1, 2, 3]
|
|
98
|
-
typeNumbers.decoder("1,x,3"); // → Error: at 1: Number: Unable to parse: "x"
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Custom separator:
|
|
102
|
-
|
|
103
|
-
```ts
|
|
104
|
-
const typePaths = typeList(typeString, ":");
|
|
105
|
-
typePaths.decoder("/usr/bin:/usr/local/bin"); // → ["/usr/bin", "/usr/local/bin"]
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
::: tip Prefer
|
|
109
|
-
[`optionRepeatable`](/guide/03_options#optionrepeatable-collect-multiple-values)
|
|
110
|
-
over `typeList` when users should pass multiple values as separate flags
|
|
111
|
-
(`--file a --file b` rather than `--files a,b`).
|
|
112
|
-
|
|
113
|
-
:::
|
|
114
|
-
|
|
115
|
-
## Custom types
|
|
116
|
-
|
|
117
|
-
Implement the `Type<Value>` interface directly:
|
|
118
|
-
|
|
119
|
-
```ts
|
|
120
|
-
import type { Type } from "cli-kiss";
|
|
121
|
-
|
|
122
|
-
const typeHexColor: Type<string> = {
|
|
123
|
-
content: "HexColor",
|
|
124
|
-
decoder(value) {
|
|
125
|
-
if (/^#[0-9a-fA-F]{6}$/.test(value)) return value;
|
|
126
|
-
throw new Error(`Not a valid value: "${value}"`);
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
// "--color #ff0000" → "#ff0000"
|
|
131
|
-
// "--color red" → Error: --color: <HEXCOLOR>: HexColor: Not a valid value: "red"
|
|
132
|
-
```
|
package/docs/guide/06_run.md
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
# Running your CLI
|
|
2
|
-
|
|
3
|
-
## `runAndExit`
|
|
4
|
-
|
|
5
|
-
`runAndExit` parses arguments, runs the matched command, and exits.
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
await runAndExit(cliName, cliArgs, context, command, options?);
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
| Parameter | Type | Description |
|
|
12
|
-
| --------- | ---------------------------------- | ------------------------------------------------- |
|
|
13
|
-
| `cliName` | `Lowercase<string>` | Program name used in help and `--version` output |
|
|
14
|
-
| `cliArgs` | `ReadonlyArray<string>` | Raw arguments — typically `process.argv.slice(2)` |
|
|
15
|
-
| `context` | `Context` | Value forwarded to every command handler |
|
|
16
|
-
| `command` | `Command<Context, void>` | The root command |
|
|
17
|
-
| `options` | `object?` | See below |
|
|
18
|
-
|
|
19
|
-
### Options
|
|
20
|
-
|
|
21
|
-
| Option | Type | Default | Description |
|
|
22
|
-
| -------------- | -------------------------- | -------------- | ----------------------------------------------------------- |
|
|
23
|
-
| `buildVersion` | `string?` | — | Enables `--version` flag; prints `<cliName> <buildVersion>` |
|
|
24
|
-
| `usageOnHelp` | `boolean?` | `true` | Enables `--help` flag |
|
|
25
|
-
| `usageOnError` | `boolean?` | `true` | Prints usage to stderr when parsing fails |
|
|
26
|
-
| `useTtyColors` | `boolean \| "mock"?` | auto | Controls ANSI color output |
|
|
27
|
-
| `onError` | `(error: unknown) => void` | — | Custom handler for parse and execution errors |
|
|
28
|
-
| `onExit` | `(code: number) => never` | `process.exit` | Override for testing |
|
|
29
|
-
|
|
30
|
-
### Exit codes
|
|
31
|
-
|
|
32
|
-
| Code | Reason |
|
|
33
|
-
| ---- | --------------------------------------- |
|
|
34
|
-
| `0` | Success, `--help`, or `--version` |
|
|
35
|
-
| `1` | Parse error or uncaught execution error |
|
|
36
|
-
|
|
37
|
-
## Full example
|
|
38
|
-
|
|
39
|
-
```ts
|
|
40
|
-
import {
|
|
41
|
-
command,
|
|
42
|
-
commandWithSubcommands,
|
|
43
|
-
operation,
|
|
44
|
-
optionFlag,
|
|
45
|
-
optionSingleValue,
|
|
46
|
-
positionalRequired,
|
|
47
|
-
runAndExit,
|
|
48
|
-
typeString,
|
|
49
|
-
typeUrl,
|
|
50
|
-
} from "cli-kiss";
|
|
51
|
-
|
|
52
|
-
type Ctx = { db: string };
|
|
53
|
-
|
|
54
|
-
const deployCmd = command(
|
|
55
|
-
{ description: "Deploy to production" },
|
|
56
|
-
operation(
|
|
57
|
-
{
|
|
58
|
-
options: {
|
|
59
|
-
dryRun: optionFlag({ long: "dry-run", description: "Simulate only" }),
|
|
60
|
-
},
|
|
61
|
-
positionals: [],
|
|
62
|
-
},
|
|
63
|
-
async ({ db }, { options: { dryRun } }) => {
|
|
64
|
-
if (dryRun) {
|
|
65
|
-
console.log(`[dry-run] would deploy with DB: ${db}`);
|
|
66
|
-
} else {
|
|
67
|
-
console.log(`Deploying with DB: ${db}`);
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
),
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
const rootCmd = commandWithSubcommands(
|
|
74
|
-
{ description: "My deployment CLI" },
|
|
75
|
-
operation(
|
|
76
|
-
{
|
|
77
|
-
options: {
|
|
78
|
-
dbUrl: optionSingleValue({
|
|
79
|
-
long: "db",
|
|
80
|
-
type: typeUrl,
|
|
81
|
-
description: "Database URL",
|
|
82
|
-
default: () => new URL("postgres://localhost/mydb"),
|
|
83
|
-
}),
|
|
84
|
-
},
|
|
85
|
-
positionals: [],
|
|
86
|
-
},
|
|
87
|
-
async (_ctx, { options: { dbUrl } }): Promise<Ctx> => ({
|
|
88
|
-
db: dbUrl.toString(),
|
|
89
|
-
}),
|
|
90
|
-
),
|
|
91
|
-
{ deploy: deployCmd },
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
await runAndExit("my-cli", process.argv.slice(2), undefined, rootCmd, {
|
|
95
|
-
buildVersion: "2.0.0",
|
|
96
|
-
});
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Check it
|
|
100
|
-
|
|
101
|
-
```sh
|
|
102
|
-
my-cli --help
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
```text
|
|
106
|
-
Usage: my-cli <SUBCOMMAND>
|
|
107
|
-
|
|
108
|
-
My deployment CLI
|
|
109
|
-
|
|
110
|
-
Subcommands:
|
|
111
|
-
deploy Deploy to production
|
|
112
|
-
|
|
113
|
-
Options:
|
|
114
|
-
--db <URL> Database URL
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Try it
|
|
118
|
-
|
|
119
|
-
```sh
|
|
120
|
-
my-cli deploy --dry-run
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
```text
|
|
124
|
-
[dry-run] would deploy with DB: postgres://localhost/mydb
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## Color control
|
|
128
|
-
|
|
129
|
-
Colors are auto-detected. Override:
|
|
130
|
-
|
|
131
|
-
```ts
|
|
132
|
-
// Force colors on
|
|
133
|
-
await runAndExit("my-cli", args, ctx, cmd, { useTtyColors: true });
|
|
134
|
-
|
|
135
|
-
// Force colors off (useful in CI)
|
|
136
|
-
await runAndExit("my-cli", args, ctx, cmd, { useTtyColors: false });
|
|
137
|
-
|
|
138
|
-
// Deterministic mock output (useful in snapshot tests)
|
|
139
|
-
await runAndExit("my-cli", args, ctx, cmd, { useTtyColors: "mock" });
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## Testing your CLI
|
|
143
|
-
|
|
144
|
-
Override `onExit` to prevent process exit during tests:
|
|
145
|
-
|
|
146
|
-
```ts
|
|
147
|
-
import { runAndExit } from "cli-kiss";
|
|
148
|
-
|
|
149
|
-
const exitCodes: number[] = [];
|
|
150
|
-
|
|
151
|
-
await runAndExit("my-cli", ["--help"], undefined, myCommand, {
|
|
152
|
-
useTtyColors: false,
|
|
153
|
-
onExit: (code) => {
|
|
154
|
-
exitCodes.push(code);
|
|
155
|
-
return undefined as never;
|
|
156
|
-
},
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
console.assert(exitCodes[0] === 0, "expected exit 0 for --help");
|
|
160
|
-
```
|