goke 6.5.1 → 6.6.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/README.md +39 -9
- package/dist/__test__/index.test.js +16 -6
- package/dist/__test__/readme-examples.test.d.ts +5 -0
- package/dist/__test__/readme-examples.test.d.ts.map +1 -0
- package/dist/__test__/readme-examples.test.js +169 -0
- package/dist/__test__/types.test-d.js +242 -1
- package/dist/goke.d.ts +122 -17
- package/dist/goke.d.ts.map +1 -1
- package/dist/goke.js +35 -5
- package/dist/runtime-node.js +2 -2
- package/package.json +2 -2
- package/src/__test__/index.test.ts +16 -6
- package/src/__test__/readme-examples.test.ts +225 -0
- package/src/__test__/types.test-d.ts +271 -1
- package/src/goke.ts +186 -20
- package/src/runtime-node.ts +2 -2
package/dist/goke.d.ts
CHANGED
|
@@ -73,6 +73,86 @@ type OptionEntry<RawName extends string, Schema> = IsOptionalOption<RawName> ext
|
|
|
73
73
|
} : {
|
|
74
74
|
[K in ExtractOptionName<RawName>]: InferSchemaOutput<Schema>;
|
|
75
75
|
};
|
|
76
|
+
/**
|
|
77
|
+
* Infer the raw runtime value shape for an option declared without a schema.
|
|
78
|
+
*
|
|
79
|
+
* Required value options (`--port <port>`) always reach actions as strings.
|
|
80
|
+
* Optional value options (`--host [host]`) reach actions as strings: the
|
|
81
|
+
* empty string `''` when the flag is passed bare (`--host`), the given
|
|
82
|
+
* value when passed with one (`--host example.com`), and `undefined` when
|
|
83
|
+
* the flag is omitted entirely. This lets callers use a single `typeof`
|
|
84
|
+
* check and, if they really care, distinguish "omitted" from "present but
|
|
85
|
+
* empty" via `=== undefined` vs `=== ''`.
|
|
86
|
+
* Plain flags (`--verbose`) are booleans.
|
|
87
|
+
*/
|
|
88
|
+
type UntypedOptionValue<RawName extends string> = RawName extends `${string}<${string}>` ? string : RawName extends `${string}[${string}]` ? string : boolean | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* Build the option type entry for a `.option()` call that uses a plain
|
|
91
|
+
* description (no schema).
|
|
92
|
+
*/
|
|
93
|
+
type UntypedOptionEntry<RawName extends string> = RawName extends `${string}<${string}>` ? {
|
|
94
|
+
[K in ExtractOptionName<RawName>]: UntypedOptionValue<RawName>;
|
|
95
|
+
} : {
|
|
96
|
+
[K in ExtractOptionName<RawName>]?: UntypedOptionValue<RawName>;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Tokenize a command raw name by splitting on whitespace.
|
|
100
|
+
* "mcp getNodeXml <id>" → ["mcp", "getNodeXml", "<id>"]
|
|
101
|
+
* "" → []
|
|
102
|
+
*/
|
|
103
|
+
type TokenizeName<S extends string, Acc extends readonly string[] = []> = S extends `${infer Head} ${infer Rest}` ? TokenizeName<Rest, [...Acc, Head]> : S extends '' ? Acc : [...Acc, S];
|
|
104
|
+
/**
|
|
105
|
+
* Given a single token, return the corresponding positional arg type or
|
|
106
|
+
* `never` if the token is not a bracketed arg.
|
|
107
|
+
*
|
|
108
|
+
* `<id>` → string (required)
|
|
109
|
+
* `[id]` → string | undefined (optional)
|
|
110
|
+
* `<...files>` → string[] (variadic required)
|
|
111
|
+
* `[...files]` → string[] (variadic optional)
|
|
112
|
+
* Anything else → never (filtered out by ExtractCommandArgs)
|
|
113
|
+
*/
|
|
114
|
+
type TokenToArgType<T extends string> = T extends `<...${string}>` ? string[] : T extends `[...${string}]` ? string[] : T extends `<${string}>` ? string : T extends `[${string}]` ? string | undefined : never;
|
|
115
|
+
/**
|
|
116
|
+
* Filter a tokenized command raw name down to the positional arg tokens
|
|
117
|
+
* and map each to its inferred type.
|
|
118
|
+
*/
|
|
119
|
+
type ExtractCommandArgs<T extends readonly string[]> = T extends readonly [infer Head extends string, ...infer Tail extends string[]] ? [TokenToArgType<Head>] extends [never] ? ExtractCommandArgs<Tail> : [TokenToArgType<Head>, ...ExtractCommandArgs<Tail>] : [];
|
|
120
|
+
/**
|
|
121
|
+
* Extract the tuple of positional arg types from a command raw name.
|
|
122
|
+
*
|
|
123
|
+
* "mcp getNodeXml <id>" → [string]
|
|
124
|
+
* "convert <input> <output>" → [string, string]
|
|
125
|
+
* "run [script]" → [string | undefined]
|
|
126
|
+
* "exec [...args]" → [string[]]
|
|
127
|
+
* "deploy" → []
|
|
128
|
+
*/
|
|
129
|
+
type ExtractPositionalArgs<RawName extends string> = ExtractCommandArgs<TokenizeName<RawName>>;
|
|
130
|
+
/**
|
|
131
|
+
* Everything after a literal `--` on the command line is collected into
|
|
132
|
+
* `options['--']` as a string array (empty when `--` is absent). This key
|
|
133
|
+
* is always present at runtime, so it's merged into every action's options
|
|
134
|
+
* type regardless of which options the user declared.
|
|
135
|
+
*/
|
|
136
|
+
type DoubleDashOptions = {
|
|
137
|
+
'--': string[];
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Build the full argument tuple passed to a command's action callback.
|
|
141
|
+
*
|
|
142
|
+
* Format: [...positionalArgs, options, executionContext]
|
|
143
|
+
*
|
|
144
|
+
* This matches the runtime behavior in Goke.runMatchedCommand(): the action
|
|
145
|
+
* is called with positional args from the parsed command, then the parsed
|
|
146
|
+
* options object, then the injected GokeExecutionContext.
|
|
147
|
+
*
|
|
148
|
+
* The options type is always extended with `{ '--': string[] }` because the
|
|
149
|
+
* parser always populates that key (see `Goke.parse()`).
|
|
150
|
+
*/
|
|
151
|
+
type ActionArgs<RawName extends string, Opts> = [
|
|
152
|
+
...ExtractPositionalArgs<RawName>,
|
|
153
|
+
Opts & DoubleDashOptions,
|
|
154
|
+
GokeExecutionContext
|
|
155
|
+
];
|
|
76
156
|
interface CommandArg {
|
|
77
157
|
required: boolean;
|
|
78
158
|
value: string;
|
|
@@ -88,8 +168,8 @@ interface CommandConfig {
|
|
|
88
168
|
}
|
|
89
169
|
type HelpCallback = (sections: HelpSection[]) => void | HelpSection[];
|
|
90
170
|
type CommandExample = ((bin: string) => string) | string;
|
|
91
|
-
declare class Command {
|
|
92
|
-
rawName:
|
|
171
|
+
declare class Command<RawName extends string = string, Opts = {}> {
|
|
172
|
+
rawName: RawName;
|
|
93
173
|
description: string;
|
|
94
174
|
config: CommandConfig;
|
|
95
175
|
cli: Goke<any>;
|
|
@@ -104,7 +184,7 @@ declare class Command {
|
|
|
104
184
|
helpCallback?: HelpCallback;
|
|
105
185
|
globalCommand?: GlobalCommand;
|
|
106
186
|
_hidden?: boolean;
|
|
107
|
-
constructor(rawName:
|
|
187
|
+
constructor(rawName: RawName, description: string, config: CommandConfig | undefined, cli: Goke<any>);
|
|
108
188
|
usage(text: string): this;
|
|
109
189
|
allowUnknownOptions(): this;
|
|
110
190
|
ignoreOptionDefaultValue(): this;
|
|
@@ -115,7 +195,9 @@ declare class Command {
|
|
|
115
195
|
*
|
|
116
196
|
* The second argument is either a description string or a StandardJSONSchemaV1
|
|
117
197
|
* schema. When a schema is provided, description and default are extracted from
|
|
118
|
-
* the JSON Schema automatically
|
|
198
|
+
* the JSON Schema automatically, and the option's type is tracked on the
|
|
199
|
+
* Command's `Opts` type parameter so that subsequent `.action()` callbacks
|
|
200
|
+
* receive a fully-typed options object.
|
|
119
201
|
*
|
|
120
202
|
* @example
|
|
121
203
|
* ```ts
|
|
@@ -126,13 +208,26 @@ declare class Command {
|
|
|
126
208
|
* cmd.option('--verbose', 'Verbose output')
|
|
127
209
|
* ```
|
|
128
210
|
*/
|
|
129
|
-
option<
|
|
130
|
-
|
|
131
|
-
};
|
|
132
|
-
option(rawName: string, descriptionOrSchema?: string | StandardJSONSchemaV1): this;
|
|
211
|
+
option<OptionRawName extends string, S extends StandardJSONSchemaV1>(rawName: OptionRawName, schema: S): Command<RawName, Opts & OptionEntry<OptionRawName, S>>;
|
|
212
|
+
option<OptionRawName extends string>(rawName: OptionRawName, description?: string): Command<RawName, Opts & UntypedOptionEntry<OptionRawName>>;
|
|
133
213
|
alias(name: string): this;
|
|
134
214
|
hidden(): this;
|
|
135
|
-
|
|
215
|
+
/**
|
|
216
|
+
* Register the action callback that runs when this command is matched.
|
|
217
|
+
*
|
|
218
|
+
* The callback receives positional args extracted from the command's raw name,
|
|
219
|
+
* followed by the parsed options object and the injected GokeExecutionContext.
|
|
220
|
+
*
|
|
221
|
+
* Positional arg types are inferred from the raw name at the type level:
|
|
222
|
+
* `command('convert <input> <output>')` → `(input: string, output: string, options, ctx)`
|
|
223
|
+
* `command('run [script]')` → `(script: string | undefined, options, ctx)`
|
|
224
|
+
* `command('exec [...args]')` → `(args: string[], options, ctx)`
|
|
225
|
+
*
|
|
226
|
+
* The options object is typed according to every `.option()` call chained
|
|
227
|
+
* on this command, plus any global options declared on the parent Goke
|
|
228
|
+
* instance before `.command()` was called.
|
|
229
|
+
*/
|
|
230
|
+
action(callback: (...args: ActionArgs<RawName, Opts>) => unknown | Promise<unknown>): this;
|
|
136
231
|
isMatched(args: string[]): {
|
|
137
232
|
matched: boolean;
|
|
138
233
|
consumedArgs: number;
|
|
@@ -242,17 +337,17 @@ interface ParsedArgv {
|
|
|
242
337
|
[k: string]: any;
|
|
243
338
|
};
|
|
244
339
|
}
|
|
245
|
-
declare class Goke<Opts
|
|
340
|
+
declare class Goke<Opts = {}> extends EventEmitter {
|
|
246
341
|
#private;
|
|
247
342
|
/** The program name to display in help and version message */
|
|
248
343
|
name: string;
|
|
249
|
-
commands: Command[];
|
|
344
|
+
commands: Command<any, any>[];
|
|
250
345
|
/** Middleware functions that run before the matched command action, in registration order */
|
|
251
346
|
middlewares: Array<{
|
|
252
347
|
action: (options: any, context: GokeExecutionContext) => void | Promise<void>;
|
|
253
348
|
}>;
|
|
254
349
|
globalCommand: GlobalCommand;
|
|
255
|
-
matchedCommand?: Command
|
|
350
|
+
matchedCommand?: Command<any, any>;
|
|
256
351
|
matchedCommandName?: string;
|
|
257
352
|
/**
|
|
258
353
|
* Raw CLI arguments
|
|
@@ -303,19 +398,29 @@ declare class Goke<Opts extends Record<string, any> = {}> extends EventEmitter {
|
|
|
303
398
|
*/
|
|
304
399
|
usage(text: string): this;
|
|
305
400
|
/**
|
|
306
|
-
* Add a sub-command
|
|
401
|
+
* Add a sub-command.
|
|
402
|
+
*
|
|
403
|
+
* The returned Command is parameterized by the literal `rawName` (so positional
|
|
404
|
+
* args can be inferred at the type level) and by this Goke's accumulated global
|
|
405
|
+
* `Opts` (so global options declared before `.command()` are visible inside
|
|
406
|
+
* `.action()` callbacks alongside the command's own options).
|
|
307
407
|
*/
|
|
308
|
-
command(rawName:
|
|
408
|
+
command<CommandRawName extends string>(rawName: CommandRawName, description?: string, config?: CommandConfig): Command<CommandRawName, Opts>;
|
|
309
409
|
/**
|
|
310
410
|
* Add a global CLI option.
|
|
311
411
|
*
|
|
312
412
|
* Which is also applied to sub-commands.
|
|
313
413
|
*
|
|
314
414
|
* When a StandardJSONSchemaV1 schema is provided, the return type is narrowed
|
|
315
|
-
* to include the inferred option type — enabling type-safe `.use()` callbacks
|
|
415
|
+
* to include the inferred option type — enabling type-safe `.use()` callbacks
|
|
416
|
+
* and typed `options` params inside command `.action()` handlers.
|
|
417
|
+
*
|
|
418
|
+
* When a plain description string is provided, the option is still tracked on
|
|
419
|
+
* the Goke's `Opts` type, but with a loose `string | boolean | undefined` value
|
|
420
|
+
* type (since no coercion schema is available).
|
|
316
421
|
*/
|
|
317
422
|
option<RawName extends string, S extends StandardJSONSchemaV1>(rawName: RawName, schema: S): Goke<Opts & OptionEntry<RawName, S>>;
|
|
318
|
-
option(rawName:
|
|
423
|
+
option<RawName extends string>(rawName: RawName, description?: string): Goke<Opts & UntypedOptionEntry<RawName>>;
|
|
319
424
|
/**
|
|
320
425
|
* Register a middleware function that runs before the matched command action.
|
|
321
426
|
*
|
|
@@ -338,7 +443,7 @@ declare class Goke<Opts extends Record<string, any> = {}> extends EventEmitter {
|
|
|
338
443
|
* })
|
|
339
444
|
* ```
|
|
340
445
|
*/
|
|
341
|
-
use(callback: (options: Opts, context: GokeExecutionContext) => void | Promise<void>): this;
|
|
446
|
+
use(callback: (options: Opts & DoubleDashOptions, context: GokeExecutionContext) => void | Promise<void>): this;
|
|
342
447
|
/**
|
|
343
448
|
* Show help message when `-h, --help` flags appear.
|
|
344
449
|
*
|
package/dist/goke.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"goke.d.ts","sourceRoot":"","sources":["../src/goke.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"goke.d.ts","sourceRoot":"","sources":["../src/goke.ts"],"names":[],"mappings":"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"}
|
package/dist/goke.js
CHANGED
|
@@ -306,6 +306,21 @@ class Command {
|
|
|
306
306
|
this._hidden = true;
|
|
307
307
|
return this;
|
|
308
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Register the action callback that runs when this command is matched.
|
|
311
|
+
*
|
|
312
|
+
* The callback receives positional args extracted from the command's raw name,
|
|
313
|
+
* followed by the parsed options object and the injected GokeExecutionContext.
|
|
314
|
+
*
|
|
315
|
+
* Positional arg types are inferred from the raw name at the type level:
|
|
316
|
+
* `command('convert <input> <output>')` → `(input: string, output: string, options, ctx)`
|
|
317
|
+
* `command('run [script]')` → `(script: string | undefined, options, ctx)`
|
|
318
|
+
* `command('exec [...args]')` → `(args: string[], options, ctx)`
|
|
319
|
+
*
|
|
320
|
+
* The options object is typed according to every `.option()` call chained
|
|
321
|
+
* on this command, plus any global options declared on the parent Goke
|
|
322
|
+
* instance before `.command()` was called.
|
|
323
|
+
*/
|
|
309
324
|
action(callback) {
|
|
310
325
|
this.commandAction = callback;
|
|
311
326
|
return this;
|
|
@@ -745,7 +760,12 @@ class Goke extends EventEmitter {
|
|
|
745
760
|
return this;
|
|
746
761
|
}
|
|
747
762
|
/**
|
|
748
|
-
* Add a sub-command
|
|
763
|
+
* Add a sub-command.
|
|
764
|
+
*
|
|
765
|
+
* The returned Command is parameterized by the literal `rawName` (so positional
|
|
766
|
+
* args can be inferred at the type level) and by this Goke's accumulated global
|
|
767
|
+
* `Opts` (so global options declared before `.command()` are visible inside
|
|
768
|
+
* `.action()` callbacks alongside the command's own options).
|
|
749
769
|
*/
|
|
750
770
|
command(rawName, description, config) {
|
|
751
771
|
const command = new Command(rawName, description || '', config, this);
|
|
@@ -1082,7 +1102,9 @@ class Goke extends EventEmitter {
|
|
|
1082
1102
|
//
|
|
1083
1103
|
// When mri returns `true` for value-taking options, it means "flag present, no value given".
|
|
1084
1104
|
// For required options (<...>), the sentinel is preserved so checkOptionValue() throws.
|
|
1085
|
-
// For optional options ([...])
|
|
1105
|
+
// For optional options ([...]) we want a single, uniform shape: `string`
|
|
1106
|
+
// with `''` meaning "flag present but no value" — callers get clean
|
|
1107
|
+
// `string | undefined` types instead of `string | boolean | undefined`.
|
|
1086
1108
|
const requiredValueOptions = new Set();
|
|
1087
1109
|
const optionalValueOptions = new Set();
|
|
1088
1110
|
for (const cliOption of cliOptions) {
|
|
@@ -1107,21 +1129,29 @@ class Goke extends EventEmitter {
|
|
|
1107
1129
|
// When value is boolean `true` and the option takes a value, it's mri's sentinel
|
|
1108
1130
|
// for "flag present, no value given":
|
|
1109
1131
|
// - Required options (<...>): preserve `true` so checkOptionValue() throws
|
|
1110
|
-
// - Optional options ([...]) with schema: replace with `undefined`
|
|
1111
|
-
//
|
|
1132
|
+
// - Optional options ([...]) with schema: replace with `undefined` so
|
|
1133
|
+
// any `.default(...)` on the schema kicks in (e.g. z.number().default(30)
|
|
1134
|
+
// should produce 30, not try to coerce the `''` empty-string sentinel).
|
|
1112
1135
|
const schemaInfo = schemaMap.get(key);
|
|
1113
1136
|
if (schemaInfo && value !== undefined) {
|
|
1114
1137
|
if (value === true && requiredValueOptions.has(key)) {
|
|
1115
1138
|
// Keep sentinel for checkOptionValue() to detect
|
|
1116
1139
|
}
|
|
1117
1140
|
else if (value === true && optionalValueOptions.has(key)) {
|
|
1118
|
-
// Optional value not given — schema expects a typed value, so return undefined
|
|
1119
1141
|
value = undefined;
|
|
1120
1142
|
}
|
|
1121
1143
|
else {
|
|
1122
1144
|
value = coerceBySchema(value, schemaInfo.jsonSchema, schemaInfo.optionName);
|
|
1123
1145
|
}
|
|
1124
1146
|
}
|
|
1147
|
+
else if (value === true && optionalValueOptions.has(key)) {
|
|
1148
|
+
// Untyped optional-value flag with no schema: normalize bare `true`
|
|
1149
|
+
// to `''` so callers get a clean `string | undefined` shape. `''`
|
|
1150
|
+
// means "flag passed with no argument", distinct from `undefined`
|
|
1151
|
+
// (flag omitted). This matches the new type inference that treats
|
|
1152
|
+
// `[value]` as `string` instead of `string | boolean`.
|
|
1153
|
+
value = '';
|
|
1154
|
+
}
|
|
1125
1155
|
setDotProp(options, keys, value);
|
|
1126
1156
|
}
|
|
1127
1157
|
}
|
package/dist/runtime-node.js
CHANGED
|
@@ -8,7 +8,7 @@ const process = globalThis.process;
|
|
|
8
8
|
const fs = nodeFs;
|
|
9
9
|
function openInBrowser(url) {
|
|
10
10
|
if (!process.stdout.isTTY) {
|
|
11
|
-
process.
|
|
11
|
+
process.stdout.write(url + '\n');
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
try {
|
|
@@ -23,7 +23,7 @@ function openInBrowser(url) {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
catch {
|
|
26
|
-
process.
|
|
26
|
+
process.stdout.write(url + '\n');
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
export { EventEmitter, fs, openInBrowser, process };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goke",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Simple yet powerful framework for building command-line apps. Inspired by cac.",
|
|
6
6
|
"repository": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"types": "./dist/index.d.ts",
|
|
24
24
|
"imports": {
|
|
25
25
|
"#runtime": {
|
|
26
|
-
"types": "./
|
|
26
|
+
"types": "./dist/runtime-node.d.ts",
|
|
27
27
|
"development": "./src/runtime-node.ts",
|
|
28
28
|
"node": "./dist/runtime-node.js",
|
|
29
29
|
"default": "./dist/runtime-browser.js"
|
|
@@ -278,7 +278,9 @@ test('dot-nested options', () => {
|
|
|
278
278
|
`node bin --externals.env.prod production --scale`.split(' ')
|
|
279
279
|
)
|
|
280
280
|
expect(options1.externals).toEqual({ env: { prod: 'production' } })
|
|
281
|
-
|
|
281
|
+
// Bare `--scale` normalizes to `''` (new uniform string-or-undefined shape
|
|
282
|
+
// for untyped optional-value flags).
|
|
283
|
+
expect(options1.scale).toEqual('')
|
|
282
284
|
})
|
|
283
285
|
|
|
284
286
|
describe('schema-based options', () => {
|
|
@@ -418,11 +420,14 @@ describe('no-schema behavior (mri no longer auto-converts)', () => {
|
|
|
418
420
|
expect(options.verbose).toBe(true)
|
|
419
421
|
})
|
|
420
422
|
|
|
421
|
-
test('optional value flag returns
|
|
423
|
+
test('optional value flag returns empty string when no value given', () => {
|
|
424
|
+
// Bare `--format` is normalized from the mri `true` sentinel to `''` so
|
|
425
|
+
// callers see a uniform `string | undefined` shape. `''` still lets them
|
|
426
|
+
// distinguish "flag present but no value" from "flag omitted entirely".
|
|
422
427
|
const cli = goke()
|
|
423
428
|
cli.option('--format [fmt]', 'Format')
|
|
424
429
|
const { options } = cli.parse('node bin --format'.split(' '))
|
|
425
|
-
expect(options.format).toBe(
|
|
430
|
+
expect(options.format).toBe('')
|
|
426
431
|
})
|
|
427
432
|
|
|
428
433
|
test('optional value flag returns string when value given', () => {
|
|
@@ -699,14 +704,19 @@ describe('regression: oracle-found issues', () => {
|
|
|
699
704
|
expect(options.count).toBe(undefined)
|
|
700
705
|
})
|
|
701
706
|
|
|
702
|
-
test('optional value option without schema
|
|
707
|
+
test('optional value option without schema normalizes bare flag to empty string', () => {
|
|
703
708
|
const cli = goke()
|
|
704
709
|
|
|
705
710
|
cli.option('--count [count]', 'Count')
|
|
706
711
|
|
|
707
|
-
//
|
|
712
|
+
// Untyped optional-value flags uniformly expose `string | undefined`:
|
|
713
|
+
// - `--count` → '' (flag present, no value)
|
|
714
|
+
// - `--count 42` → '42' (flag present, with value)
|
|
715
|
+
// - (omitted) → undefined (flag absent)
|
|
716
|
+
// This lets callers use a single `typeof options.count === 'string'`
|
|
717
|
+
// check and distinguish the three cases via `=== ''` if they need to.
|
|
708
718
|
const { options } = cli.parse('node bin --count'.split(' '))
|
|
709
|
-
expect(options.count).toBe(
|
|
719
|
+
expect(options.count).toBe('')
|
|
710
720
|
})
|
|
711
721
|
|
|
712
722
|
test('optional value option with schema coerces when value given', () => {
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smoke tests that keep README examples and documented APIs executable.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from 'vitest'
|
|
6
|
+
import { z } from 'zod'
|
|
7
|
+
import goke, { openInBrowser } from '../index.js'
|
|
8
|
+
import type { GokeOptions, GokeOutputStream } from '../index.js'
|
|
9
|
+
|
|
10
|
+
const ANSI_RE = /\x1B\[[0-9;]*m/g
|
|
11
|
+
|
|
12
|
+
const stripAnsi = (text: string) => text.replace(ANSI_RE, '')
|
|
13
|
+
|
|
14
|
+
function createTestOutputStream(): GokeOutputStream & { lines: string[]; readonly text: string } {
|
|
15
|
+
const lines: string[] = []
|
|
16
|
+
return {
|
|
17
|
+
lines,
|
|
18
|
+
get text() { return stripAnsi(lines.join('')) },
|
|
19
|
+
write(data: string) { lines.push(data) },
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function gokeTestable(name = '', options?: Partial<GokeOptions>) {
|
|
24
|
+
return goke(name, {
|
|
25
|
+
...options,
|
|
26
|
+
exit: () => {},
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('README smoke tests', () => {
|
|
31
|
+
test('intro example runs middleware and both command forms', async () => {
|
|
32
|
+
const stdout = createTestOutputStream()
|
|
33
|
+
const cli = gokeTestable('deploy', { stdout })
|
|
34
|
+
|
|
35
|
+
cli
|
|
36
|
+
.option('--env <env>', z.enum(['staging', 'production']).default('staging').describe('Target environment'))
|
|
37
|
+
.use((options, { console }) => {
|
|
38
|
+
console.log(`Environment: ${options.env}`)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
cli
|
|
42
|
+
.command('up', 'Deploy the app')
|
|
43
|
+
.option('--dry-run', 'Preview without deploying')
|
|
44
|
+
.action((options, { console, process }) => {
|
|
45
|
+
console.log(`Deploying from ${process.cwd} dryRun=${String(options.dryRun)}`)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
cli
|
|
49
|
+
.command('logs <deploymentId>', 'Stream logs')
|
|
50
|
+
.option('--lines <n>', z.number().default(100).describe('Lines to tail'))
|
|
51
|
+
.action((deploymentId, options, { console }) => {
|
|
52
|
+
console.log(`logs ${deploymentId} ${options.lines}`)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
cli.parse(['node', 'bin', '--env', 'production', 'up', '--dry-run'], { run: false })
|
|
56
|
+
await cli.runMatchedCommand()
|
|
57
|
+
|
|
58
|
+
expect(stdout.text).toBe(
|
|
59
|
+
`Environment: production\nDeploying from ${process.cwd()} dryRun=true\n`,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
stdout.lines.length = 0
|
|
63
|
+
|
|
64
|
+
cli.parse(['node', 'bin', 'logs', 'dep_123'], { run: false })
|
|
65
|
+
await cli.runMatchedCommand()
|
|
66
|
+
|
|
67
|
+
expect(stdout.text).toBe('Environment: staging\nlogs dep_123 100\n')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('simple parsing example stays executable and keeps examples in help output', async () => {
|
|
71
|
+
const stdout = createTestOutputStream()
|
|
72
|
+
const cli = gokeTestable('mycli', { stdout })
|
|
73
|
+
|
|
74
|
+
cli.option(
|
|
75
|
+
'--type [type]',
|
|
76
|
+
z.string().default('node').describe('Choose a project type'),
|
|
77
|
+
)
|
|
78
|
+
cli.option('--name <name>', 'Provide your name')
|
|
79
|
+
|
|
80
|
+
cli.command('lint [...files]', 'Lint files').action((files, options, { console, process }) => {
|
|
81
|
+
console.log(JSON.stringify({ files, options, cwd: process.cwd }))
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
cli
|
|
85
|
+
.command('build [entry]', 'Build your app')
|
|
86
|
+
.option('--minify', 'Minify output')
|
|
87
|
+
.example('build src/index.ts')
|
|
88
|
+
.example('build src/index.ts --minify')
|
|
89
|
+
.action(async (entry, options, { console, process }) => {
|
|
90
|
+
console.log(JSON.stringify({ entry, options, nodeEnv: process.env.NODE_ENV }))
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
cli.example((bin) => `${bin} lint src/**/*.ts`)
|
|
94
|
+
cli.help()
|
|
95
|
+
cli.version('0.0.0')
|
|
96
|
+
|
|
97
|
+
expect(stripAnsi(cli.helpText())).toContain('mycli lint src/**/*.ts')
|
|
98
|
+
|
|
99
|
+
cli.parse(['node', 'bin', '--type', 'bun', '--name', 'Tommy', 'build', 'src/index.ts', '--minify'], { run: false })
|
|
100
|
+
await cli.runMatchedCommand()
|
|
101
|
+
|
|
102
|
+
expect(stdout.text).toBe(
|
|
103
|
+
`${JSON.stringify({
|
|
104
|
+
entry: 'src/index.ts',
|
|
105
|
+
options: {
|
|
106
|
+
'--': [],
|
|
107
|
+
type: 'bun',
|
|
108
|
+
name: 'Tommy',
|
|
109
|
+
minify: true,
|
|
110
|
+
},
|
|
111
|
+
nodeEnv: process.env.NODE_ENV,
|
|
112
|
+
})}\n`,
|
|
113
|
+
)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('many-commands README example runs root and nested commands', async () => {
|
|
117
|
+
const stdout = createTestOutputStream()
|
|
118
|
+
const cli = gokeTestable('deploy', { stdout })
|
|
119
|
+
|
|
120
|
+
cli
|
|
121
|
+
.command('', 'Deploy the current project')
|
|
122
|
+
.option('--env <env>', z.string().default('production').describe('Target environment'))
|
|
123
|
+
.option('--dry-run', 'Preview without deploying')
|
|
124
|
+
.action((options, { console, process }) => {
|
|
125
|
+
console.log(`Deploying to ${options.env} from ${process.cwd} dryRun=${String(options.dryRun)}`)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
cli
|
|
129
|
+
.command('logs <deploymentId>', 'Stream logs for a deployment')
|
|
130
|
+
.option('--follow', 'Follow log output')
|
|
131
|
+
.option('--lines <n>', z.number().default(100).describe('Number of lines'))
|
|
132
|
+
.action((deploymentId, options, { console, process }) => {
|
|
133
|
+
console.log(
|
|
134
|
+
`Streaming logs for ${deploymentId} from ${process.cwd} follow=${String(options.follow)} lines=${options.lines}`,
|
|
135
|
+
)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
cli.parse(['node', 'bin', '--env', 'staging', '--dry-run'], { run: false })
|
|
139
|
+
await cli.runMatchedCommand()
|
|
140
|
+
|
|
141
|
+
expect(stdout.text).toBe(
|
|
142
|
+
`Deploying to staging from ${process.cwd()} dryRun=true\n`,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
stdout.lines.length = 0
|
|
146
|
+
|
|
147
|
+
cli.parse(['node', 'bin', 'logs', 'abc123', '--follow'], { run: false })
|
|
148
|
+
await cli.runMatchedCommand()
|
|
149
|
+
|
|
150
|
+
expect(stdout.text).toBe(
|
|
151
|
+
`Streaming logs for abc123 from ${process.cwd()} follow=true lines=100\n`,
|
|
152
|
+
)
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
describe('documented command APIs', () => {
|
|
157
|
+
test('alias runs the same command through a short name', () => {
|
|
158
|
+
const cli = gokeTestable('mycli')
|
|
159
|
+
let seen = ''
|
|
160
|
+
|
|
161
|
+
cli.command('install', 'Install packages').alias('i').action(() => {
|
|
162
|
+
seen = 'install'
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
cli.parse(['node', 'bin', 'i'], { run: true })
|
|
166
|
+
|
|
167
|
+
expect(seen).toBe('install')
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
test('command helpText returns command-specific help without printing', () => {
|
|
171
|
+
const stdout = createTestOutputStream()
|
|
172
|
+
const cli = goke('mycli', { stdout })
|
|
173
|
+
|
|
174
|
+
const command = cli
|
|
175
|
+
.command('deploy <env>', 'Deploy to an environment')
|
|
176
|
+
.option('--dry-run', 'Preview without deploying')
|
|
177
|
+
.example('# Deploy safely first')
|
|
178
|
+
.example('mycli deploy staging --dry-run')
|
|
179
|
+
|
|
180
|
+
cli.help()
|
|
181
|
+
|
|
182
|
+
const help = stripAnsi(command.helpText())
|
|
183
|
+
|
|
184
|
+
expect(help).toContain('$ mycli deploy <env>')
|
|
185
|
+
expect(help).toContain('--dry-run')
|
|
186
|
+
expect(help).toContain('Deploy safely first')
|
|
187
|
+
expect(stdout.text).toBe('')
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
test('openInBrowser prints the URL to stdout in non-tty environments', () => {
|
|
191
|
+
const url = 'https://example.com/dashboard'
|
|
192
|
+
const originalStdoutWrite = process.stdout.write
|
|
193
|
+
const originalStderrWrite = process.stderr.write
|
|
194
|
+
const originalIsTTY = process.stdout.isTTY
|
|
195
|
+
let stdout = ''
|
|
196
|
+
let stderr = ''
|
|
197
|
+
|
|
198
|
+
Object.defineProperty(process.stdout, 'isTTY', {
|
|
199
|
+
configurable: true,
|
|
200
|
+
value: false,
|
|
201
|
+
})
|
|
202
|
+
process.stdout.write = ((chunk: string | Uint8Array) => {
|
|
203
|
+
stdout += String(chunk)
|
|
204
|
+
return true
|
|
205
|
+
}) as typeof process.stdout.write
|
|
206
|
+
process.stderr.write = ((chunk: string | Uint8Array) => {
|
|
207
|
+
stderr += String(chunk)
|
|
208
|
+
return true
|
|
209
|
+
}) as typeof process.stderr.write
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
openInBrowser(url)
|
|
213
|
+
} finally {
|
|
214
|
+
process.stdout.write = originalStdoutWrite
|
|
215
|
+
process.stderr.write = originalStderrWrite
|
|
216
|
+
Object.defineProperty(process.stdout, 'isTTY', {
|
|
217
|
+
configurable: true,
|
|
218
|
+
value: originalIsTTY,
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
expect(stdout).toBe(`${url}\n`)
|
|
223
|
+
expect(stderr).toBe('')
|
|
224
|
+
})
|
|
225
|
+
})
|