cli-kiss 0.1.9 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli-kiss",
3
- "version": "0.1.9",
3
+ "version": "0.2.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "devDependencies": {
@@ -16,12 +16,12 @@ import {
16
16
  *
17
17
  * A `CommandDescriptor` is the central building block of a `cli-kiss` CLI. You create
18
18
  * one with {@link command}, {@link commandWithSubcommands}, or {@link commandChained},
19
- * and pass it to {@link runAsCliAndExit} to run your CLI.
19
+ * and pass it to {@link runAndExit} to run your CLI.
20
20
  *
21
21
  * @typeParam Context - The value passed into the command when it is executed. It flows
22
- * from {@link runAsCliAndExit}'s `context` argument down through the command chain.
22
+ * from {@link runAndExit}'s `context` argument down through the command chain.
23
23
  * @typeParam Result - The value produced by executing the command. For root commands
24
- * passed to {@link runAsCliAndExit} this is always `void`.
24
+ * passed to {@link runAndExit} this is always `void`.
25
25
  */
26
26
  export type CommandDescriptor<Context, Result> = {
27
27
  /** Returns the static metadata (description, hint, details) for this command. */
@@ -74,7 +74,7 @@ export type CommandInstance<Context, Result> = {
74
74
  /**
75
75
  * Executes the command with the provided context.
76
76
  *
77
- * @param context - Arbitrary value injected by the caller (see {@link runAsCliAndExit}).
77
+ * @param context - Arbitrary value injected by the caller (see {@link runAndExit}).
78
78
  * @returns A promise that resolves to the command's result, or rejects if the
79
79
  * command handler throws.
80
80
  */
@@ -84,8 +84,7 @@ export type CommandInstance<Context, Result> = {
84
84
  /**
85
85
  * Static, human-readable metadata attached to a command.
86
86
  *
87
- * This information is displayed in the usage/help output produced by
88
- * {@link usageToStyledLines}.
87
+ * This information is displayed in the usage/help output produced by {@link usageToStyledLines}.
89
88
  */
90
89
  export type CommandInformation = {
91
90
  /** Short description of what the command does. Shown prominently in the usage header. */
@@ -176,7 +175,7 @@ export type CommandUsageSubcommand = {
176
175
  * @param information - Static metadata (description, hint, details) for the command.
177
176
  * @param operation - The operation that defines options, positionals, and the execution
178
177
  * handler for this command.
179
- * @returns A {@link CommandDescriptor} suitable for passing to {@link runAsCliAndExit}
178
+ * @returns A {@link CommandDescriptor} suitable for passing to {@link runAndExit}
180
179
  * or composing with {@link commandWithSubcommands} / {@link commandChained}.
181
180
  *
182
181
  * @example
@@ -66,7 +66,7 @@ export type OperationInstance<Input, Output> = {
66
66
  * option/positional values.
67
67
  *
68
68
  * @param input - Context from the parent command (or the root context supplied to
69
- * {@link runAsCliAndExit}).
69
+ * {@link runAndExit}).
70
70
  * @returns A promise resolving to the handler's return value.
71
71
  */
72
72
  executeWithContext(input: Input): Promise<Output>;
@@ -89,7 +89,7 @@ export type OperationUsage = {
89
89
  *
90
90
  * The `handler` receives:
91
91
  * - `context` — the value passed down from the parent command (or from
92
- * {@link runAsCliAndExit}).
92
+ * {@link runAndExit}).
93
93
  * - `inputs.options` — an object whose keys match those declared in `inputs.options` and whose values are
94
94
  * the parsed option values.
95
95
  * - `inputs.positionals` — a tuple whose elements match `inputs.positionals` and whose
package/src/lib/Reader.ts CHANGED
@@ -85,7 +85,7 @@ export type ReaderPositionals = {
85
85
  * - End-of-options separator: `--` — all subsequent tokens are treated as positionals.
86
86
  *
87
87
  * In most cases you do not need to use `ReaderArgs` directly; it is created internally
88
- * by {@link runAsCliAndExit}. It is exposed for advanced use cases such as building
88
+ * by {@link runAndExit}. It is exposed for advanced use cases such as building
89
89
  * custom runners.
90
90
  */
91
91
  export class ReaderArgs {
package/src/lib/Run.ts CHANGED
@@ -44,10 +44,8 @@ import { usageToStyledLines } from "./Usage";
44
44
  * stderr before the error message whenever argument parsing fails.
45
45
  * @param options.buildVersion - When provided, registers a `--version` flag that prints
46
46
  * `<cliName> <buildVersion>` to stdout and exits with code `0`.
47
- * @param options.onExecutionError - Custom handler for errors thrown during command
48
- * execution. If omitted, the error is printed to stderr via {@link TypoSupport}.
49
- * @param options.onLogStdOut - Overrides the standard output sink (default: `console.log`).
50
- * @param options.onLogStdErr - Overrides the standard error sink (default: `console.error`).
47
+ * @param options.onError - Custom handler for errors thrown during command execution.
48
+ * If omitted, the error is printed to stderr via {@link TypoSupport}.
51
49
  * @param options.onExit - Overrides the process exit function (default: `process.exit`).
52
50
  * Useful for testing — supply a function that throws or captures the exit code instead
53
51
  * of actually terminating the process.
@@ -56,7 +54,7 @@ import { usageToStyledLines } from "./Usage";
56
54
  *
57
55
  * @example
58
56
  * ```ts
59
- * import { runAsCliAndExit, command, operation, positionalRequired, typeString } from "cli-kiss";
57
+ * import { runAndExit, command, operation, positionalRequired, typeString } from "cli-kiss";
60
58
  *
61
59
  * const greetCommand = command(
62
60
  * { description: "Greet someone" },
@@ -68,12 +66,12 @@ import { usageToStyledLines } from "./Usage";
68
66
  * ),
69
67
  * );
70
68
  *
71
- * await runAsCliAndExit("greet", process.argv.slice(2), undefined, greetCommand, {
69
+ * await runAndExit("greet", process.argv.slice(2), undefined, greetCommand, {
72
70
  * buildVersion: "1.0.0",
73
71
  * });
74
72
  * ```
75
73
  */
76
- export async function runAsCliAndExit<Context>(
74
+ export async function runAndExit<Context>(
77
75
  cliName: Lowercase<string>,
78
76
  cliArgs: ReadonlyArray<string>,
79
77
  context: Context,
@@ -83,9 +81,7 @@ export async function runAsCliAndExit<Context>(
83
81
  usageOnHelp?: boolean | undefined;
84
82
  usageOnError?: boolean | undefined;
85
83
  buildVersion?: string | undefined;
86
- onExecutionError?: ((error: unknown) => void) | undefined;
87
- onLogStdOut?: ((message: string) => void) | undefined;
88
- onLogStdErr?: ((message: string) => void) | undefined;
84
+ onError?: ((error: unknown) => void) | undefined;
89
85
  onExit?: ((code: number) => never) | undefined;
90
86
  },
91
87
  ): Promise<never> {
@@ -114,17 +110,6 @@ export async function runAsCliAndExit<Context>(
114
110
  longs: ["completion"],
115
111
  });
116
112
  */
117
- const typoSupport =
118
- options?.useTtyColors === undefined
119
- ? TypoSupport.inferFromProcess()
120
- : options.useTtyColors === "mock"
121
- ? TypoSupport.mock()
122
- : options.useTtyColors
123
- ? TypoSupport.tty()
124
- : TypoSupport.none();
125
- const onLogStdOut = options?.onLogStdOut ?? console.log;
126
- const onLogStdErr = options?.onLogStdErr ?? console.error;
127
- const onExit = options?.onExit ?? process.exit;
128
113
  const commandFactory = command.createFactory(readerArgs);
129
114
  while (true) {
130
115
  try {
@@ -134,15 +119,24 @@ export async function runAsCliAndExit<Context>(
134
119
  }
135
120
  } catch (_) {}
136
121
  }
122
+ const onExit = options?.onExit ?? process.exit;
123
+ const typoSupport =
124
+ options?.useTtyColors === undefined
125
+ ? TypoSupport.inferFromProcess()
126
+ : options.useTtyColors === "mock"
127
+ ? TypoSupport.mock()
128
+ : options.useTtyColors
129
+ ? TypoSupport.tty()
130
+ : TypoSupport.none();
137
131
  if (usageOnHelp) {
138
132
  if (readerArgs.getOptionValues("--help" as any).length > 0) {
139
- onLogStdOut(computeUsageString(cliName, commandFactory, typoSupport));
133
+ console.log(computeUsageString(cliName, commandFactory, typoSupport));
140
134
  return onExit(0);
141
135
  }
142
136
  }
143
137
  if (buildVersion) {
144
138
  if (readerArgs.getOptionValues("--version" as any).length > 0) {
145
- onLogStdOut([cliName, buildVersion].join(" "));
139
+ console.log([cliName, buildVersion].join(" "));
146
140
  return onExit(0);
147
141
  }
148
142
  }
@@ -152,18 +146,18 @@ export async function runAsCliAndExit<Context>(
152
146
  await commandInstance.executeWithContext(context);
153
147
  return onExit(0);
154
148
  } catch (executionError) {
155
- if (options?.onExecutionError) {
156
- options.onExecutionError(executionError);
149
+ if (options?.onError) {
150
+ options.onError(executionError);
157
151
  } else {
158
- onLogStdErr(typoSupport.computeStyledErrorMessage(executionError));
152
+ console.error(typoSupport.computeStyledErrorMessage(executionError));
159
153
  }
160
154
  return onExit(1);
161
155
  }
162
156
  } catch (parsingError) {
163
157
  if (options?.usageOnError ?? true) {
164
- onLogStdErr(computeUsageString(cliName, commandFactory, typoSupport));
158
+ console.error(computeUsageString(cliName, commandFactory, typoSupport));
165
159
  }
166
- onLogStdErr(typoSupport.computeStyledErrorMessage(parsingError));
160
+ console.error(typoSupport.computeStyledErrorMessage(parsingError));
167
161
  return onExit(1);
168
162
  }
169
163
  }
package/src/lib/Typo.ts CHANGED
@@ -368,9 +368,8 @@ export class TypoError extends Error {
368
368
  * - {@link TypoSupport.inferFromProcess} — auto-detects based on `process.stdout.isTTY`
369
369
  * and the `FORCE_COLOR` / `NO_COLOR` environment variables.
370
370
  *
371
- * `TypoSupport` is consumed by {@link runAsCliAndExit} (via the `useTtyColors` option)
372
- * and can also be used directly when building custom usage renderers with
373
- * {@link usageToStyledLines}.
371
+ * `TypoSupport` is consumed by {@link runAndExit} (via the `useTtyColors` option)
372
+ * and can also be used directly when building custom usage renderers with {@link usageToStyledLines}.
374
373
  */
375
374
  export class TypoSupport {
376
375
  #kind: "none" | "tty" | "mock";
package/src/lib/Usage.ts CHANGED
@@ -39,8 +39,7 @@ import {
39
39
  * in each column sets the width for the entire section.
40
40
  *
41
41
  * @param params.cliName - The CLI program name shown at the start of the usage line.
42
- * @param params.commandUsage - The usage model produced by
43
- * {@link CommandFactory.generateUsage}.
42
+ * @param params.commandUsage - The usage model produced by {@link CommandFactory.generateUsage}.
44
43
  * @param params.typoSupport - Controls color/styling of the output.
45
44
  * @returns An ordered array of strings, one per output line (including a trailing
46
45
  * empty string for the blank line at the end).
@@ -50,7 +49,7 @@ import {
50
49
  * const lines = usageToStyledLines({
51
50
  * cliName: "my-cli",
52
51
  * commandUsage: commandFactory.generateUsage(),
53
- * typoSupport: TypoSupport.none(),
52
+ * typoSupport: TypoSupport.tty(),
54
53
  * });
55
54
  * process.stdout.write(lines.join("\n"));
56
55
  * ```
@@ -9,7 +9,7 @@ import {
9
9
  positionalOptional,
10
10
  positionalRequired,
11
11
  positionalVariadics,
12
- runAsCliAndExit,
12
+ runAndExit,
13
13
  typeConverted,
14
14
  typeOneOf,
15
15
  typeString,
@@ -324,7 +324,7 @@ async function testCase(
324
324
  ],
325
325
  },
326
326
  async () => {
327
- onLogStdOut.call("Has executed root command");
327
+ console.log("Has executed root command");
328
328
  },
329
329
  ),
330
330
  {
@@ -359,17 +359,17 @@ async function testCase(
359
359
  ],
360
360
  },
361
361
  async () => {
362
- onLogStdOut.call("Has executed subcommand");
362
+ console.log("Has executed subcommand");
363
363
  },
364
364
  ),
365
365
  ),
366
366
  },
367
367
  );
368
- await runAsCliAndExit("my-cli", args, null, cmd, {
368
+ console.log = onLogStdOut.call;
369
+ console.error = onLogStdErr.call;
370
+ await runAndExit("my-cli", args, null, cmd, {
369
371
  buildVersion: "1.0.0",
370
372
  useTtyColors: false,
371
- onLogStdOut: onLogStdOut.call,
372
- onLogStdErr: onLogStdErr.call,
373
373
  onExit: onExit.call,
374
374
  });
375
375
  expect({
@@ -5,7 +5,7 @@ import {
5
5
  optionFlag,
6
6
  optionRepeatable,
7
7
  optionSingleValue,
8
- runAsCliAndExit,
8
+ runAndExit,
9
9
  typeNumber,
10
10
  typeUrl,
11
11
  } from "../src";
@@ -34,43 +34,43 @@ it("run", async () => {
34
34
  });
35
35
 
36
36
  async function testCase(args: Array<string>, error: string) {
37
+ const onLogStdOut = makeMocked<string, void>([]);
37
38
  const onLogStdErr = makeMocked<string, void>([null as unknown as void]);
38
39
  const onExit = makeMocked<number, never>([null as never]);
39
- await runAsCliAndExit(
40
- "my-cli",
41
- args,
42
- null,
43
- command<void, void>(
44
- { description: "" },
45
- operation(
46
- {
47
- options: {
48
- optionFlag: optionFlag({ long: "flag" }),
49
- optionSingleValue: optionSingleValue({
50
- label: "LOCATION",
51
- long: "single-value",
52
- type: typeUrl,
53
- default: () => undefined,
54
- }),
55
- optionRepeatable: optionRepeatable({
56
- label: "INDEX",
57
- long: "repeatable",
58
- type: typeNumber,
59
- }),
60
- },
61
- positionals: [],
40
+ const rootCommand = command<void, void>(
41
+ { description: "" },
42
+ operation(
43
+ {
44
+ options: {
45
+ optionFlag: optionFlag({
46
+ long: "flag",
47
+ }),
48
+ optionSingleValue: optionSingleValue({
49
+ label: "LOCATION",
50
+ long: "single-value",
51
+ type: typeUrl,
52
+ default: () => undefined,
53
+ }),
54
+ optionRepeatable: optionRepeatable({
55
+ label: "INDEX",
56
+ long: "repeatable",
57
+ type: typeNumber,
58
+ }),
62
59
  },
63
- async (_, _inputs) => {},
64
- ),
60
+ positionals: [],
61
+ },
62
+ async () => {},
65
63
  ),
66
- {
67
- buildVersion: "1.0.0",
68
- usageOnError: false,
69
- useTtyColors: "mock",
70
- onLogStdErr: onLogStdErr.call,
71
- onExit: onExit.call,
72
- },
73
64
  );
65
+ console.log = onLogStdOut.call;
66
+ console.error = onLogStdErr.call;
67
+ await runAndExit("my-cli", args, null, rootCommand, {
68
+ buildVersion: "1.0.0",
69
+ usageOnError: false,
70
+ useTtyColors: "mock",
71
+ onExit: onExit.call,
72
+ });
73
+ expect(onLogStdOut.history).toEqual([]);
74
74
  expect(onLogStdErr.history).toEqual([error]);
75
75
  expect(onExit.history).toEqual([1]);
76
76
  }