cli-kiss 0.2.4 → 0.2.5

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