cli-kiss 0.2.3 → 0.2.4
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.d.ts +643 -667
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/docs/.vitepress/config.mts +1 -3
- package/docs/.vitepress/theme/index.ts +4 -0
- package/docs/.vitepress/theme/style.css +4 -0
- package/docs/guide/02_commands.md +67 -31
- package/docs/guide/03_options.md +11 -10
- package/docs/guide/05_types.md +1 -1
- package/docs/guide/06_run.md +1 -1
- package/docs/index.md +8 -3
- package/docs/public/hero.png +0 -0
- package/package.json +1 -1
- package/src/lib/Command.ts +36 -93
- package/src/lib/Operation.ts +12 -30
- package/src/lib/Option.ts +120 -96
- package/src/lib/Positional.ts +9 -34
- package/src/lib/Reader.ts +122 -98
- package/src/lib/Run.ts +30 -17
- package/src/lib/Type.ts +26 -22
- package/src/lib/Typo.ts +62 -83
- package/src/lib/Usage.ts +174 -78
- package/tests/unit.Reader.aliases.ts +31 -15
- package/tests/unit.Reader.commons.ts +99 -43
- package/tests/unit.Reader.shortBig.ts +75 -31
- package/tests/unit.command.execute.ts +76 -33
- package/tests/unit.command.usage.ts +35 -35
- package/tests/unit.runner.cycle.ts +13 -13
- package/tests/unit.runner.errors.ts +19 -3
|
@@ -14,9 +14,7 @@ export default defineConfig({
|
|
|
14
14
|
],
|
|
15
15
|
],
|
|
16
16
|
themeConfig: {
|
|
17
|
-
search: {
|
|
18
|
-
provider: "local",
|
|
19
|
-
},
|
|
17
|
+
search: { provider: "local", options: { detailedView: true } },
|
|
20
18
|
nav: [
|
|
21
19
|
{ text: "Guide", link: "/guide/01_getting_started" },
|
|
22
20
|
{ text: "npm", link: "https://www.npmjs.com/package/cli-kiss" },
|
|
@@ -16,37 +16,13 @@ const greet = command(
|
|
|
16
16
|
options: {},
|
|
17
17
|
positionals: [positionalRequired({ type: typeString, label: "NAME" })],
|
|
18
18
|
},
|
|
19
|
-
async (_ctx, { positionals: [name] })
|
|
19
|
+
async function (_ctx, { positionals: [name] }) {
|
|
20
20
|
console.log(`Hello, ${name}!`);
|
|
21
21
|
},
|
|
22
22
|
),
|
|
23
23
|
);
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
### `CommandInformation`
|
|
27
|
-
|
|
28
|
-
Every command accepts a metadata object:
|
|
29
|
-
|
|
30
|
-
| Field | Type | Description |
|
|
31
|
-
| ------------- | ----------- | ------------------------------------------------- |
|
|
32
|
-
| `description` | `string` | Short description shown in help output |
|
|
33
|
-
| `hint` | `string?` | Note shown in parentheses next to the description |
|
|
34
|
-
| `details` | `string[]?` | Extra lines printed below the description |
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
command(
|
|
38
|
-
{
|
|
39
|
-
description: "Deploy the application",
|
|
40
|
-
hint: "experimental",
|
|
41
|
-
details: [
|
|
42
|
-
"Pushes to the configured remote.",
|
|
43
|
-
"Runs migrations after push.",
|
|
44
|
-
],
|
|
45
|
-
},
|
|
46
|
-
deployOperation,
|
|
47
|
-
);
|
|
48
|
-
```
|
|
49
|
-
|
|
50
26
|
## `commandWithSubcommands` — dispatch to a subcommand
|
|
51
27
|
|
|
52
28
|
User must pick one of several sub-actions.
|
|
@@ -63,19 +39,19 @@ const rootCmd = commandWithSubcommands(
|
|
|
63
39
|
{ description: "My deployment CLI" },
|
|
64
40
|
// This operation runs before the subcommand is selected.
|
|
65
41
|
// Its return value becomes the subcommand's context.
|
|
66
|
-
operation({ options: {}, positionals: [] }, async (_ctx)
|
|
67
|
-
db: "postgres://localhost/mydb"
|
|
68
|
-
})
|
|
42
|
+
operation({ options: {}, positionals: [] }, async function (_ctx) {
|
|
43
|
+
return { db: "postgres://localhost/mydb" };
|
|
44
|
+
}),
|
|
69
45
|
{
|
|
70
46
|
deploy: command(
|
|
71
47
|
{ description: "Deploy the latest build" },
|
|
72
|
-
operation({ options: {}, positionals: [] }, async (ctx)
|
|
48
|
+
operation({ options: {}, positionals: [] }, async function (ctx) {
|
|
73
49
|
console.log(`Deploying with DB: ${ctx.db}`);
|
|
74
50
|
}),
|
|
75
51
|
),
|
|
76
52
|
rollback: command(
|
|
77
53
|
{ description: "Rollback to the previous release" },
|
|
78
|
-
operation({ options: {}, positionals: [] }, async (ctx)
|
|
54
|
+
operation({ options: {}, positionals: [] }, async function (ctx) {
|
|
79
55
|
console.log(`Rolling back, DB: ${ctx.db}`);
|
|
80
56
|
}),
|
|
81
57
|
),
|
|
@@ -128,7 +104,7 @@ const authenticatedDeploy = commandChained(
|
|
|
128
104
|
long: "token",
|
|
129
105
|
type: typeString,
|
|
130
106
|
description: "API token",
|
|
131
|
-
default: ()
|
|
107
|
+
default: function () {
|
|
132
108
|
const t = process.env.API_TOKEN;
|
|
133
109
|
if (!t) throw new Error("API_TOKEN env var is required");
|
|
134
110
|
return t;
|
|
@@ -150,3 +126,63 @@ const authenticatedDeploy = commandChained(
|
|
|
150
126
|
```
|
|
151
127
|
|
|
152
128
|
All stages share a single flat usage — users see one combined command.
|
|
129
|
+
|
|
130
|
+
## `CommandInformation`
|
|
131
|
+
|
|
132
|
+
Every command accepts a metadata object:
|
|
133
|
+
|
|
134
|
+
| Field | Type | Description |
|
|
135
|
+
| ------------- | ------------ | ------------------------------------------------- |
|
|
136
|
+
| `description` | `string` | Short description shown in help output |
|
|
137
|
+
| `hint` | `string?` | Note shown in parentheses next to the description |
|
|
138
|
+
| `details` | `string[]?` | Extra lines printed below the description |
|
|
139
|
+
| `examples` | `Example[]?` | Usage examples shown in the `Examples:` section |
|
|
140
|
+
|
|
141
|
+
Each `Example` entry has:
|
|
142
|
+
|
|
143
|
+
| Field | Type | Description |
|
|
144
|
+
| ------------- | -------------- | ------------------------------------------------------- |
|
|
145
|
+
| `explanation` | `string` | Comment line shown above the example command |
|
|
146
|
+
| `commandArgs` | `CommandArg[]` | Ordered list of arguments to render on the command line |
|
|
147
|
+
|
|
148
|
+
Each `CommandArg` is one of:
|
|
149
|
+
|
|
150
|
+
| Shape | Renders as |
|
|
151
|
+
| ----------------------------------------------- | ---------------- |
|
|
152
|
+
| `string` | literal text |
|
|
153
|
+
| `{ positional: string }` | positional label |
|
|
154
|
+
| `{ subcommand: string }` | subcommand name |
|
|
155
|
+
| `{ option: { long: string; value?: string } }` | `--long[=value]` |
|
|
156
|
+
| `{ option: { short: string; value?: string } }` | `-s[=value]` |
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
command(
|
|
160
|
+
{
|
|
161
|
+
description: "Deploy the application",
|
|
162
|
+
hint: "experimental",
|
|
163
|
+
details: [
|
|
164
|
+
"Pushes to the configured remote.",
|
|
165
|
+
"Runs migrations after push.",
|
|
166
|
+
],
|
|
167
|
+
examples: [
|
|
168
|
+
{
|
|
169
|
+
explanation: "Deploy with a specific tag",
|
|
170
|
+
commandArgs: [
|
|
171
|
+
{ positional: "v1.2.3" },
|
|
172
|
+
{ option: { long: "dry-run" } },
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
deployOperation,
|
|
178
|
+
);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The `Examples:` section in `--help` output renders each entry as a comment
|
|
182
|
+
followed by the reconstructed command line:
|
|
183
|
+
|
|
184
|
+
```text
|
|
185
|
+
Examples:
|
|
186
|
+
# Deploy with a specific tag
|
|
187
|
+
deploy v1.2.3 --dry-run
|
|
188
|
+
```
|
package/docs/guide/03_options.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# Options
|
|
2
2
|
|
|
3
|
-
Named `--` arguments (or `-` for short forms). Declared in the `options` map of
|
|
3
|
+
Named `--` arguments (or `-` for short forms). Declared in the `options` map of
|
|
4
|
+
[`operation`](/guide/02_commands).
|
|
4
5
|
|
|
5
6
|
## `optionFlag` — boolean toggle
|
|
6
7
|
|
|
7
|
-
Present or absent. Also accepts `--flag=
|
|
8
|
+
Present or absent. Also accepts `--flag=true` / `--flag=no` / `--no-flag`.
|
|
8
9
|
|
|
9
10
|
```ts
|
|
10
11
|
import { optionFlag } from "cli-kiss";
|
|
@@ -20,14 +21,14 @@ const verbose = optionFlag({
|
|
|
20
21
|
// (absent) → false
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
| Parameter | Type | Description
|
|
24
|
-
| ------------- | --------------------- |
|
|
25
|
-
| `long` | `Lowercase<string>` | Long flag name (without `--`)
|
|
26
|
-
| `short` | `string?` | Short flag name (without `-`)
|
|
27
|
-
| `description` | `string?` | Help text
|
|
28
|
-
| `hint` | `string?` | Short note in parentheses
|
|
29
|
-
| `default` | `
|
|
30
|
-
| `aliases` | `{ longs?, shorts? }` | Additional names for the flag
|
|
24
|
+
| Parameter | Type | Description |
|
|
25
|
+
| ------------- | --------------------- | ------------------------------------ |
|
|
26
|
+
| `long` | `Lowercase<string>` | Long flag name (without `--`) |
|
|
27
|
+
| `short` | `string?` | Short flag name (without `-`) |
|
|
28
|
+
| `description` | `string?` | Help text |
|
|
29
|
+
| `hint` | `string?` | Short note in parentheses |
|
|
30
|
+
| `default` | `boolean? ` | Value when absent (default: `false`) |
|
|
31
|
+
| `aliases` | `{ longs?, shorts? }` | Additional names for the flag |
|
|
31
32
|
|
|
32
33
|
::: tip A flag specified more than once triggers a parse error. Use
|
|
33
34
|
[`optionRepeatable`](#optionrepeatable-collect-multiple-values) if you need
|
package/docs/guide/05_types.md
CHANGED
|
@@ -7,7 +7,7 @@ A `Type<Value>` converts a raw CLI string into a typed value: a `content` label
|
|
|
7
7
|
| Export | TypeScript type | Accepts |
|
|
8
8
|
| ------------- | --------------- | ---------------------------------------------------------- |
|
|
9
9
|
| `typeString` | `string` | Any string |
|
|
10
|
-
| `typeBoolean` | `boolean` | `true
|
|
10
|
+
| `typeBoolean` | `boolean` | `true/yes/on/1/y/t` → true, `false/no/off/0/n/f` → false (case-insensitive) |
|
|
11
11
|
| `typeNumber` | `number` | Integers, floats, scientific notation |
|
|
12
12
|
| `typeInteger` | `bigint` | Integer strings only |
|
|
13
13
|
| `typeDate` | `Date` | Any format accepted by `Date.parse` (ISO 8601 recommended) |
|
package/docs/guide/06_run.md
CHANGED
|
@@ -24,7 +24,7 @@ await runAndExit(cliName, cliArgs, context, command, options?);
|
|
|
24
24
|
| `usageOnHelp` | `boolean?` | `true` | Enables `--help` flag |
|
|
25
25
|
| `usageOnError` | `boolean?` | `true` | Prints usage to stderr when parsing fails |
|
|
26
26
|
| `useTtyColors` | `boolean \| "mock"?` | auto | Controls ANSI color output |
|
|
27
|
-
| `onError` | `(error: unknown) => void` | — | Custom handler for execution errors
|
|
27
|
+
| `onError` | `(error: unknown) => void` | — | Custom handler for parse and execution errors |
|
|
28
28
|
| `onExit` | `(code: number) => never` | `process.exit` | Override for testing |
|
|
29
29
|
|
|
30
30
|
### Exit codes
|
package/docs/index.md
CHANGED
|
@@ -4,10 +4,12 @@ layout: home
|
|
|
4
4
|
hero:
|
|
5
5
|
name: CLI-kiss
|
|
6
6
|
text: CLI for TypeScript.
|
|
7
|
+
|
|
7
8
|
tagline:
|
|
8
|
-
No bloat, no dependencies.<br/>Standard behavior users expect.<br
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
No bloat, no dependencies.<br/>Standard behavior users expect.<br/>Keep It Simple and Stupid, it just does the job.
|
|
10
|
+
|
|
11
|
+
image:
|
|
12
|
+
src: /hero.png
|
|
11
13
|
|
|
12
14
|
actions:
|
|
13
15
|
- theme: brand
|
|
@@ -19,12 +21,15 @@ hero:
|
|
|
19
21
|
|
|
20
22
|
features:
|
|
21
23
|
- title: Zero dependencies
|
|
24
|
+
icon: 📦
|
|
22
25
|
details:
|
|
23
26
|
Ships with no runtime dependencies.<br/>Pure TypeScript, 5kb bundled.
|
|
24
27
|
- title: Fully typed
|
|
28
|
+
icon: 🧠
|
|
25
29
|
details:
|
|
26
30
|
TypeScript first.<br/>Options and positionals inputs strongly typed.
|
|
27
31
|
- title: Composable
|
|
32
|
+
icon: 🧩
|
|
28
33
|
details:
|
|
29
34
|
Easily create nested subcommands.<br/>Build complex CLIs by nesting logic.
|
|
30
35
|
---
|
|
Binary file
|
package/package.json
CHANGED
package/src/lib/Command.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { Operation } from "./Operation";
|
|
2
|
-
import { OptionUsage } from "./Option";
|
|
3
|
-
import { PositionalUsage } from "./Positional";
|
|
4
2
|
import { ReaderArgs } from "./Reader";
|
|
5
3
|
import {
|
|
6
4
|
TypoError,
|
|
@@ -9,22 +7,21 @@ import {
|
|
|
9
7
|
typoStyleUserInput,
|
|
10
8
|
TypoText,
|
|
11
9
|
} from "./Typo";
|
|
10
|
+
import { UsageCommand, UsageSegment } from "./Usage";
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
|
-
* A CLI command
|
|
15
|
-
* Created with {@link command}, {@link commandWithSubcommands}, or {@link commandChained}.
|
|
16
|
-
* Usually passed through {@link runAndExit} to run.
|
|
13
|
+
* A CLI command. Created with {@link command}, {@link commandWithSubcommands}, or {@link commandChained}.
|
|
17
14
|
*
|
|
18
|
-
* @typeParam Context - Injected at execution
|
|
19
|
-
* @typeParam Result -
|
|
15
|
+
* @typeParam Context - Injected at execution; forwarded to handlers.
|
|
16
|
+
* @typeParam Result - Produced on execution; typically `void`.
|
|
20
17
|
*/
|
|
21
18
|
export type Command<Context, Result> = {
|
|
22
19
|
/**
|
|
23
|
-
* Returns
|
|
20
|
+
* Returns static metadata.
|
|
24
21
|
*/
|
|
25
22
|
getInformation(): CommandInformation;
|
|
26
23
|
/**
|
|
27
|
-
*
|
|
24
|
+
* Registers options/positionals on `readerArgs`; returns a {@link CommandDecoder}.
|
|
28
25
|
*/
|
|
29
26
|
consumeAndMakeDecoder(
|
|
30
27
|
readerArgs: ReaderArgs,
|
|
@@ -39,10 +36,9 @@ export type Command<Context, Result> = {
|
|
|
39
36
|
*/
|
|
40
37
|
export type CommandDecoder<Context, Result> = {
|
|
41
38
|
/**
|
|
42
|
-
*
|
|
43
|
-
* Used for `--help` and `usageOnError`.
|
|
39
|
+
* Returns {@link UsageCommand} for the current command path.
|
|
44
40
|
*/
|
|
45
|
-
generateUsage():
|
|
41
|
+
generateUsage(): UsageCommand;
|
|
46
42
|
/**
|
|
47
43
|
* Creates a ready-to-execute {@link CommandInterpreter}.
|
|
48
44
|
*
|
|
@@ -54,7 +50,7 @@ export type CommandDecoder<Context, Result> = {
|
|
|
54
50
|
/**
|
|
55
51
|
* A fully parsed, decoded and ready-to-execute command.
|
|
56
52
|
*
|
|
57
|
-
* @typeParam Context -
|
|
53
|
+
* @typeParam Context - Context passed to the handler.
|
|
58
54
|
* @typeParam Result - Value produced on success.
|
|
59
55
|
*/
|
|
60
56
|
export type CommandInterpreter<Context, Result> = {
|
|
@@ -65,15 +61,15 @@ export type CommandInterpreter<Context, Result> = {
|
|
|
65
61
|
};
|
|
66
62
|
|
|
67
63
|
/**
|
|
68
|
-
*
|
|
64
|
+
* Command metadata shown in `--help` output.
|
|
69
65
|
*/
|
|
70
66
|
export type CommandInformation = {
|
|
71
67
|
/**
|
|
72
|
-
*
|
|
68
|
+
* Shown in the usage header.
|
|
73
69
|
*/
|
|
74
70
|
description: string;
|
|
75
71
|
/**
|
|
76
|
-
*
|
|
72
|
+
* Shown in parentheses, e.g. `"deprecated"`, `"experimental"`.
|
|
77
73
|
*/
|
|
78
74
|
hint?: string;
|
|
79
75
|
/**
|
|
@@ -81,7 +77,7 @@ export type CommandInformation = {
|
|
|
81
77
|
*/
|
|
82
78
|
details?: Array<string>;
|
|
83
79
|
/**
|
|
84
|
-
*
|
|
80
|
+
* Shown in the `Examples:` section.
|
|
85
81
|
*/
|
|
86
82
|
examples?: Array<{
|
|
87
83
|
/**
|
|
@@ -89,7 +85,7 @@ export type CommandInformation = {
|
|
|
89
85
|
*/
|
|
90
86
|
explanation: string;
|
|
91
87
|
/**
|
|
92
|
-
*
|
|
88
|
+
* Example command args.
|
|
93
89
|
*/
|
|
94
90
|
commandArgs: Array<
|
|
95
91
|
| string
|
|
@@ -104,66 +100,14 @@ export type CommandInformation = {
|
|
|
104
100
|
}>;
|
|
105
101
|
};
|
|
106
102
|
|
|
107
|
-
/**
|
|
108
|
-
* Full usage/help model.
|
|
109
|
-
* Produced by {@link CommandDecoder.generateUsage},
|
|
110
|
-
* Consumed by {@link usageToStyledLines}.
|
|
111
|
-
*/
|
|
112
|
-
export type CommandUsage = {
|
|
113
|
-
/**
|
|
114
|
-
* Segments forming the usage line
|
|
115
|
-
* (e.g. `my-cli <POSITIONAL> subcommand <ANOTHER_POSITIONAL>`).
|
|
116
|
-
*/
|
|
117
|
-
segments: Array<CommandUsageSegment>;
|
|
118
|
-
/**
|
|
119
|
-
* Command's static metadata.
|
|
120
|
-
*/
|
|
121
|
-
information: CommandInformation;
|
|
122
|
-
/**
|
|
123
|
-
* Positionals in declaration order.
|
|
124
|
-
*/
|
|
125
|
-
positionals: Array<PositionalUsage>;
|
|
126
|
-
/**
|
|
127
|
-
* Available subcommands. Non-empty when subcommand was not specified.
|
|
128
|
-
*/
|
|
129
|
-
subcommands: Array<CommandUsageSubcommand>;
|
|
130
|
-
/**
|
|
131
|
-
* Options in registration order.
|
|
132
|
-
*/
|
|
133
|
-
options: Array<OptionUsage>;
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* One element in the usage segment trail.
|
|
138
|
-
*/
|
|
139
|
-
export type CommandUsageSegment = { positional: string } | { command: string };
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Subcommand entry shown in the `Subcommands:` section of the usage output.
|
|
143
|
-
*/
|
|
144
|
-
export type CommandUsageSubcommand = {
|
|
145
|
-
/**
|
|
146
|
-
* Literal token the user types (e.g. `"deploy"`).
|
|
147
|
-
*/
|
|
148
|
-
name: string;
|
|
149
|
-
/**
|
|
150
|
-
* Short description from the subcommand's {@link CommandInformation}.
|
|
151
|
-
*/
|
|
152
|
-
description: string | undefined;
|
|
153
|
-
/**
|
|
154
|
-
* Hint from the subcommand's {@link CommandInformation}.
|
|
155
|
-
*/
|
|
156
|
-
hint: string | undefined;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
103
|
/**
|
|
160
104
|
* Creates a leaf command that directly executes an {@link Operation}.
|
|
161
105
|
*
|
|
162
106
|
* @typeParam Context - Context forwarded to the handler.
|
|
163
107
|
* @typeParam Result - Value returned by the handler.
|
|
164
108
|
*
|
|
165
|
-
* @param information - Command metadata
|
|
166
|
-
* @param operation -
|
|
109
|
+
* @param information - Command metadata.
|
|
110
|
+
* @param operation - Options, positionals, and handler.
|
|
167
111
|
* @returns A {@link Command}.
|
|
168
112
|
*
|
|
169
113
|
* @example
|
|
@@ -198,7 +142,7 @@ export function command<Context, Result>(
|
|
|
198
142
|
);
|
|
199
143
|
}
|
|
200
144
|
return {
|
|
201
|
-
generateUsage: () =>
|
|
145
|
+
generateUsage: () => generateUsageLeaf(information, operation),
|
|
202
146
|
decodeAndMakeInterpreter() {
|
|
203
147
|
const operationInterpreter =
|
|
204
148
|
operationDecoder.decodeAndMakeInterpreter();
|
|
@@ -211,7 +155,7 @@ export function command<Context, Result>(
|
|
|
211
155
|
};
|
|
212
156
|
} catch (error) {
|
|
213
157
|
return {
|
|
214
|
-
generateUsage: () =>
|
|
158
|
+
generateUsage: () => generateUsageLeaf(information, operation),
|
|
215
159
|
decodeAndMakeInterpreter() {
|
|
216
160
|
throw error;
|
|
217
161
|
},
|
|
@@ -222,17 +166,16 @@ export function command<Context, Result>(
|
|
|
222
166
|
}
|
|
223
167
|
|
|
224
168
|
/**
|
|
225
|
-
* Creates a command that runs
|
|
226
|
-
* then dispatches to a named subcommand based on the next positional token.
|
|
169
|
+
* Creates a command that runs `operation` first, then dispatches to a named subcommand.
|
|
227
170
|
*
|
|
228
171
|
* @typeParam Context - Context accepted by `operation`.
|
|
229
172
|
* @typeParam Payload - Output of `operation`; becomes the subcommand's context.
|
|
230
173
|
* @typeParam Result - Value produced by the selected subcommand.
|
|
231
174
|
*
|
|
232
|
-
* @param information - Command metadata
|
|
233
|
-
* @param operation -
|
|
175
|
+
* @param information - Command metadata.
|
|
176
|
+
* @param operation - Runs first; output becomes the subcommand's context.
|
|
234
177
|
* @param subcommands - Map of subcommand names to their {@link Command}s.
|
|
235
|
-
* @returns A {@link Command}
|
|
178
|
+
* @returns A dispatching {@link Command}.
|
|
236
179
|
*
|
|
237
180
|
* @example
|
|
238
181
|
* ```ts
|
|
@@ -283,8 +226,8 @@ export function commandWithSubcommands<Context, Payload, Result>(
|
|
|
283
226
|
return {
|
|
284
227
|
generateUsage() {
|
|
285
228
|
const subcommandUsage = subcommandDecoder.generateUsage();
|
|
286
|
-
const currentUsage =
|
|
287
|
-
currentUsage.segments.push(
|
|
229
|
+
const currentUsage = generateUsageLeaf(information, operation);
|
|
230
|
+
currentUsage.segments.push(segmentSubcommand(subcommandName));
|
|
288
231
|
currentUsage.segments.push(...subcommandUsage.segments);
|
|
289
232
|
currentUsage.information = subcommandUsage.information;
|
|
290
233
|
currentUsage.positionals.push(...subcommandUsage.positionals);
|
|
@@ -309,7 +252,7 @@ export function commandWithSubcommands<Context, Payload, Result>(
|
|
|
309
252
|
} catch (error) {
|
|
310
253
|
return {
|
|
311
254
|
generateUsage() {
|
|
312
|
-
const currentUsage =
|
|
255
|
+
const currentUsage = generateUsageLeaf(information, operation);
|
|
313
256
|
currentUsage.segments.push(segmentPositional("<SUBCOMMAND>"));
|
|
314
257
|
for (const [name, subcommand] of Object.entries(subcommands)) {
|
|
315
258
|
const { description, hint } = subcommand.getInformation();
|
|
@@ -334,10 +277,10 @@ export function commandWithSubcommands<Context, Payload, Result>(
|
|
|
334
277
|
* @typeParam Payload - Output of `operation`; becomes `subcommand`'s context.
|
|
335
278
|
* @typeParam Result - Value produced by `subcommand`.
|
|
336
279
|
*
|
|
337
|
-
* @param information - Command metadata
|
|
338
|
-
* @param operation -
|
|
339
|
-
* @param subcommand -
|
|
340
|
-
* @returns A {@link Command}
|
|
280
|
+
* @param information - Command metadata.
|
|
281
|
+
* @param operation - Runs first; output becomes `subcommand`'s context.
|
|
282
|
+
* @param subcommand - Runs after `operation`.
|
|
283
|
+
* @returns A {@link Command} composing both stages.
|
|
341
284
|
*
|
|
342
285
|
* @example
|
|
343
286
|
* ```ts
|
|
@@ -367,7 +310,7 @@ export function commandChained<Context, Payload, Result>(
|
|
|
367
310
|
return {
|
|
368
311
|
generateUsage() {
|
|
369
312
|
const subcommandUsage = subcommandDecoder.generateUsage();
|
|
370
|
-
const currentUsage =
|
|
313
|
+
const currentUsage = generateUsageLeaf(information, operation);
|
|
371
314
|
currentUsage.segments.push(...subcommandUsage.segments);
|
|
372
315
|
currentUsage.information = subcommandUsage.information;
|
|
373
316
|
currentUsage.positionals.push(...subcommandUsage.positionals);
|
|
@@ -392,7 +335,7 @@ export function commandChained<Context, Payload, Result>(
|
|
|
392
335
|
} catch (error) {
|
|
393
336
|
return {
|
|
394
337
|
generateUsage() {
|
|
395
|
-
const currentUsage =
|
|
338
|
+
const currentUsage = generateUsageLeaf(information, operation);
|
|
396
339
|
currentUsage.segments.push(segmentPositional("[REST]..."));
|
|
397
340
|
return currentUsage;
|
|
398
341
|
},
|
|
@@ -405,18 +348,18 @@ export function commandChained<Context, Payload, Result>(
|
|
|
405
348
|
};
|
|
406
349
|
}
|
|
407
350
|
|
|
408
|
-
function segmentPositional(value: string):
|
|
351
|
+
function segmentPositional(value: string): UsageSegment {
|
|
409
352
|
return { positional: value };
|
|
410
353
|
}
|
|
411
354
|
|
|
412
|
-
function
|
|
413
|
-
return {
|
|
355
|
+
function segmentSubcommand(value: string): UsageSegment {
|
|
356
|
+
return { subcommand: value };
|
|
414
357
|
}
|
|
415
358
|
|
|
416
|
-
function
|
|
359
|
+
function generateUsageLeaf(
|
|
417
360
|
information: CommandInformation,
|
|
418
361
|
operation: Operation<any, any>,
|
|
419
|
-
):
|
|
362
|
+
): UsageCommand {
|
|
420
363
|
const { positionals, options } = operation.generateUsage();
|
|
421
364
|
return {
|
|
422
365
|
segments: positionals.map((positional) =>
|
package/src/lib/Operation.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Option, OptionDecoder
|
|
2
|
-
import { Positional, PositionalDecoder
|
|
1
|
+
import { Option, OptionDecoder } from "./Option";
|
|
2
|
+
import { Positional, PositionalDecoder } from "./Positional";
|
|
3
3
|
import { ReaderArgs } from "./Reader";
|
|
4
|
+
import { UsageOperation, UsageOption, UsagePositional } from "./Usage";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Options, positionals, and an async handler that together form the logic of a CLI command.
|
|
@@ -8,14 +9,14 @@ import { ReaderArgs } from "./Reader";
|
|
|
8
9
|
* Created with {@link operation} and passed to {@link command},
|
|
9
10
|
* {@link commandWithSubcommands}, or {@link commandChained}.
|
|
10
11
|
*
|
|
11
|
-
* @typeParam Context - Injected at execution
|
|
12
|
-
* @typeParam Result - Value produced on execution; typically `void
|
|
12
|
+
* @typeParam Context - Injected at execution; forwarded to handlers.
|
|
13
|
+
* @typeParam Result - Value produced on execution; typically `void`.
|
|
13
14
|
*/
|
|
14
15
|
export type Operation<Context, Result> = {
|
|
15
16
|
/**
|
|
16
17
|
* Returns usage metadata without consuming any arguments.
|
|
17
18
|
*/
|
|
18
|
-
generateUsage():
|
|
19
|
+
generateUsage(): UsageOperation;
|
|
19
20
|
/**
|
|
20
21
|
* Consumes args from `readerArgs` and returns an {@link OperationDecoder}.
|
|
21
22
|
*/
|
|
@@ -52,27 +53,10 @@ export type OperationInterpreter<Context, Result> = {
|
|
|
52
53
|
executeWithContext(context: Context): Promise<Result>;
|
|
53
54
|
};
|
|
54
55
|
|
|
55
|
-
/**
|
|
56
|
-
* Usage metadata produced by {@link Operation.generateUsage}.
|
|
57
|
-
* Consumed when building {@link CommandUsage}.
|
|
58
|
-
*/
|
|
59
|
-
export type OperationUsage = {
|
|
60
|
-
/**
|
|
61
|
-
* Usage descriptors for all registered options.
|
|
62
|
-
*/
|
|
63
|
-
options: Array<OptionUsage>;
|
|
64
|
-
/**
|
|
65
|
-
* Usage descriptors for all declared positionals, in order.
|
|
66
|
-
*/
|
|
67
|
-
positionals: Array<PositionalUsage>;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
56
|
/**
|
|
71
57
|
* Creates an {@link Operation} from options, positionals, and an async handler.
|
|
72
58
|
*
|
|
73
|
-
* The `handler` receives
|
|
74
|
-
* `options` (keyed by the same names declared in `inputs.options`) and
|
|
75
|
-
* `positionals` (a tuple in declaration order).
|
|
59
|
+
* The `handler` receives `context` and `inputs` with decoded `options` and `positionals`.
|
|
76
60
|
*
|
|
77
61
|
* @typeParam Context - Context type accepted by the handler.
|
|
78
62
|
* @typeParam Result - Return type of the handler.
|
|
@@ -83,7 +67,7 @@ export type OperationUsage = {
|
|
|
83
67
|
* @param inputs.options - Map of keys to {@link Option} descriptors.
|
|
84
68
|
* @param inputs.positionals - Ordered array of {@link Positional} descriptors.
|
|
85
69
|
* @param handler - Async function implementing the command logic.
|
|
86
|
-
* @returns An {@link Operation}
|
|
70
|
+
* @returns An {@link Operation}.
|
|
87
71
|
*
|
|
88
72
|
* @example
|
|
89
73
|
* ```ts
|
|
@@ -96,7 +80,7 @@ export type OperationUsage = {
|
|
|
96
80
|
* positionalRequired({ type: typeString, label: "NAME", description: "Name to greet" }),
|
|
97
81
|
* ],
|
|
98
82
|
* },
|
|
99
|
-
* async (_ctx, { options: { loud }, positionals: [name] })
|
|
83
|
+
* async function (_ctx, { options: { loud }, positionals: [name] }) {
|
|
100
84
|
* const message = `Hello, ${name}!`;
|
|
101
85
|
* console.log(loud ? message.toUpperCase() : message);
|
|
102
86
|
* },
|
|
@@ -123,14 +107,12 @@ export function operation<
|
|
|
123
107
|
): Operation<Context, Result> {
|
|
124
108
|
return {
|
|
125
109
|
generateUsage() {
|
|
126
|
-
const optionsUsage = new Array<
|
|
110
|
+
const optionsUsage = new Array<UsageOption>();
|
|
127
111
|
for (const optionKey in inputs.options) {
|
|
128
112
|
const optionInput = inputs.options[optionKey]!;
|
|
129
|
-
|
|
130
|
-
optionsUsage.push(optionInput.generateUsage());
|
|
131
|
-
}
|
|
113
|
+
optionsUsage.push(optionInput.generateUsage());
|
|
132
114
|
}
|
|
133
|
-
const positionalsUsage = new Array<
|
|
115
|
+
const positionalsUsage = new Array<UsagePositional>();
|
|
134
116
|
for (const positionalInput of inputs.positionals) {
|
|
135
117
|
positionalsUsage.push(positionalInput.generateUsage());
|
|
136
118
|
}
|