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/src/lib/Run.ts CHANGED
@@ -1,8 +1,15 @@
1
1
  import { Command, CommandDecoder } from "./Command";
2
+ import { optionFlag, optionSingleValue } from "./Option";
2
3
  import { ReaderArgs } from "./Reader";
4
+ import { typeChoice } from "./Type";
3
5
  import { TypoSupport } from "./Typo";
4
6
  import { usageToStyledLines } from "./Usage";
5
7
 
8
+ /**
9
+ * Color selection modes availables
10
+ */
11
+ export type RunColorMode = "env" | "always" | "never" | "mock";
12
+
6
13
  /**
7
14
  * Main entry point: parses CLI arguments, executes the matched command, and exits.
8
15
  * Handles `--help`, `--version`, usage-on-error, and exit codes.
@@ -17,8 +24,7 @@ import { usageToStyledLines } from "./Usage";
17
24
  * @param cliArgs - Raw arguments, typically `process.argv.slice(2)`.
18
25
  * @param context - Forwarded to the handler.
19
26
  * @param command - Root {@link Command}.
20
- * @param options.useTtyColors - Color mode: `true` (always), `false` (never),
21
- * `"mock"` (snapshot-friendly), `undefined` (auto-detect from env).
27
+ * @param options.colorSetup - Configures color support; enables `--color` flag if set to `"flag"`.
22
28
  * @param options.usageOnHelp - Enables `--help` flag (default `true`).
23
29
  * @param options.usageOnError - Prints usage to stderr on parse error (default `true`).
24
30
  * @param options.buildVersion - Enables `--version`; prints `<cliName> <buildVersion>`.
@@ -29,12 +35,12 @@ import { usageToStyledLines } from "./Usage";
29
35
  *
30
36
  * @example
31
37
  * ```ts
32
- * import { runAndExit, command, operation, positionalRequired, typeString } from "cli-kiss";
38
+ * import { runAndExit, command, operation, positionalRequired, type } from "cli-kiss";
33
39
  *
34
40
  * const greetCommand = command(
35
41
  * { description: "Greet someone" },
36
42
  * operation(
37
- * { options: {}, positionals: [positionalRequired({ type: typeString, label: "NAME" })] },
43
+ * { options: {}, positionals: [positionalRequired({ type: type("name") })] },
38
44
  * async function (_ctx, { positionals: [name] }) {
39
45
  * console.log(`Hello, ${name}!`);
40
46
  * },
@@ -47,12 +53,12 @@ import { usageToStyledLines } from "./Usage";
47
53
  * ```
48
54
  */
49
55
  export async function runAndExit<Context>(
50
- cliName: Lowercase<string>,
56
+ cliName: string,
51
57
  cliArgs: ReadonlyArray<string>,
52
58
  context: Context,
53
59
  command: Command<Context, void>,
54
60
  options?: {
55
- useTtyColors?: boolean | undefined | "mock"; // TODO - flag setter option
61
+ colorSetup?: "flag" | RunColorMode | undefined;
56
62
  usageOnHelp?: boolean | undefined;
57
63
  usageOnError?: boolean | undefined;
58
64
  buildVersion?: string | undefined;
@@ -61,26 +67,52 @@ export async function runAndExit<Context>(
61
67
  },
62
68
  ): Promise<never> {
63
69
  const readerArgs = new ReaderArgs(cliArgs);
64
- const usageOnHelp = options?.usageOnHelp ?? true;
65
- if (usageOnHelp) {
66
- readerArgs.registerOption({
67
- shorts: [],
68
- longs: ["help"],
69
- parsing: {
70
- consumeShortGroup: false,
71
- consumeNextArg: () => false,
72
- },
70
+ const preprocessors = new Array<
71
+ (commandDecoder: CommandDecoder<Context, void>) => undefined | number
72
+ >();
73
+ let typoSupport = TypoSupport.none();
74
+ const colorSetup = options?.colorSetup ?? "flag";
75
+ if (colorSetup === "flag") {
76
+ const colorOption = optionSingleValue<"auto" | RunColorMode>({
77
+ long: "color",
78
+ type: typeChoice("color-mode", ["auto", "always", "never", "mock"]),
79
+ defaultWhenNotDefined: () => "auto",
80
+ defaultWhenNotInlined: () => "always",
81
+ }).registerAndMakeDecoder(readerArgs);
82
+ preprocessors.push(() => {
83
+ try {
84
+ typoSupport = computeTypoSupport(colorOption.getAndDecodeValue());
85
+ } catch (error) {
86
+ typoSupport = TypoSupport.inferFromEnv();
87
+ throw error;
88
+ }
89
+ return undefined;
90
+ });
91
+ } else {
92
+ typoSupport = computeTypoSupport(colorSetup);
93
+ }
94
+ if (options?.usageOnHelp ?? true) {
95
+ const helpOption = optionFlag({ long: "help" }).registerAndMakeDecoder(
96
+ readerArgs,
97
+ );
98
+ preprocessors.push((commandDecoder) => {
99
+ if (!helpOption.getAndDecodeValue()) {
100
+ return undefined;
101
+ }
102
+ console.log(computeUsageString(cliName, commandDecoder, typoSupport));
103
+ return 0;
73
104
  });
74
105
  }
75
- const buildVersion = options?.buildVersion;
76
- if (buildVersion) {
77
- readerArgs.registerOption({
78
- shorts: [],
79
- longs: ["version"],
80
- parsing: {
81
- consumeShortGroup: false,
82
- consumeNextArg: () => false,
83
- },
106
+ if (options?.buildVersion) {
107
+ const versionOption = optionFlag({
108
+ long: "version",
109
+ }).registerAndMakeDecoder(readerArgs);
110
+ preprocessors.push(() => {
111
+ if (!versionOption.getAndDecodeValue()) {
112
+ return undefined;
113
+ }
114
+ console.log([cliName, options.buildVersion].join(" "));
115
+ return 0;
84
116
  });
85
117
  }
86
118
  /*
@@ -91,7 +123,6 @@ export async function runAndExit<Context>(
91
123
  longs: ["completion"],
92
124
  });
93
125
  */
94
- // TODO - handle color flag ?
95
126
  const commandDecoder = command.consumeAndMakeDecoder(readerArgs);
96
127
  while (true) {
97
128
  try {
@@ -101,21 +132,14 @@ export async function runAndExit<Context>(
101
132
  }
102
133
  } catch (_) {}
103
134
  }
104
- const typoSupport = computeTypoSupport(options?.useTtyColors);
105
135
  const onExit = options?.onExit ?? process.exit;
106
- if (usageOnHelp) {
107
- if (readerArgs.getOptionValues("--help" as any).length > 0) {
108
- console.log(computeUsageString(cliName, commandDecoder, typoSupport));
109
- return onExit(0);
110
- }
111
- }
112
- if (buildVersion) {
113
- if (readerArgs.getOptionValues("--version" as any).length > 0) {
114
- console.log([cliName, buildVersion].join(" "));
115
- return onExit(0);
116
- }
117
- }
118
136
  try {
137
+ for (const preprocessor of preprocessors) {
138
+ const preprocessorResult = preprocessor(commandDecoder);
139
+ if (preprocessorResult !== undefined) {
140
+ return onExit(preprocessorResult);
141
+ }
142
+ }
119
143
  const commandInterpreter = commandDecoder.decodeAndMakeInterpreter();
120
144
  try {
121
145
  await commandInterpreter.executeWithContext(context);
@@ -146,7 +170,7 @@ function handleError(
146
170
  }
147
171
 
148
172
  function computeUsageString<Context, Result>(
149
- cliName: Lowercase<string>,
173
+ cliName: string,
150
174
  commandDecoder: CommandDecoder<Context, Result>,
151
175
  typoSupport: TypoSupport,
152
176
  ) {
@@ -157,14 +181,17 @@ function computeUsageString<Context, Result>(
157
181
  }).join("\n");
158
182
  }
159
183
 
160
- function computeTypoSupport(
161
- useTtyColors: boolean | undefined | "mock",
162
- ): TypoSupport {
163
- return useTtyColors === undefined
164
- ? TypoSupport.inferFromProcess()
165
- : useTtyColors === "mock"
166
- ? TypoSupport.mock()
167
- : useTtyColors
168
- ? TypoSupport.tty()
169
- : TypoSupport.none();
184
+ function computeTypoSupport(colorMode: "auto" | RunColorMode): TypoSupport {
185
+ switch (colorMode) {
186
+ case "auto":
187
+ return TypoSupport.inferFromEnv();
188
+ case "env":
189
+ return TypoSupport.inferFromEnv();
190
+ case "always":
191
+ return TypoSupport.tty();
192
+ case "never":
193
+ return TypoSupport.none();
194
+ case "mock":
195
+ return TypoSupport.mock();
196
+ }
170
197
  }