Skip to content

Commit b75dd11

Browse files
committed
update
1 parent 5ec5ccc commit b75dd11

File tree

8 files changed

+174
-63
lines changed

8 files changed

+174
-63
lines changed

examples/demo.cac.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ cli
1414
.option('-H, --host [host]', `Specify hostname`)
1515
.option('-p, --port <port>', `Specify port`)
1616
.option('-v, --verbose', `Enable verbose logging`)
17+
.option('--quiet', `Suppress output`)
1718
.action((options) => {});
1819

1920
cli

examples/demo.citty.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ const devCommand = defineCommand({
5858
description: 'Enable verbose logging',
5959
alias: 'v',
6060
},
61+
quiet: {
62+
type: 'boolean',
63+
description: 'Suppress output',
64+
},
6165
},
6266
run: () => {},
6367
});

examples/demo.t.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ devCmd.option(
6464

6565
devCmd.option('verbose', 'Enable verbose logging', 'v');
6666

67+
// Add a simple quiet option to test basic option API (no handler, no alias)
68+
devCmd.option('quiet', 'Suppress output');
69+
6770
// Serve command
6871
const serveCmd = t.command('serve', 'Start the server');
6972
serveCmd.option(

src/cac.ts

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import * as fish from './fish';
44
import * as powershell from './powershell';
55
import type { CAC } from 'cac';
66
import { assertDoubleDashes } from './shared';
7-
import { OptionHandler, noopHandler } from './t';
87
import { CompletionConfig } from './shared';
98
import t from './t';
109

@@ -71,35 +70,30 @@ export default async function tab(
7170

7271
// Add command options
7372
for (const option of [...instance.globalCommand.options, ...cmd.options]) {
74-
// Extract short flag from the name if it exists (e.g., "-c, --config" -> "c")
75-
const shortFlag = option.name.match(/^-([a-zA-Z]), --/)?.[1];
76-
const argName = option.name.replace(/^-[a-zA-Z], --/, '');
77-
78-
// Detect if this is a boolean option by checking if it has <value> or [value] in the raw definition
79-
const isBoolean =
80-
!option.rawName.includes('<') && !option.rawName.includes('[');
73+
// Extract short flag from the rawName if it exists (e.g., "-c, --config" -> "c")
74+
const shortFlag = option.rawName.match(/^-([a-zA-Z]), --/)?.[1];
75+
const argName = option.name; // option.name is already clean (e.g., "config")
8176

8277
// Add option using t.ts API
8378
const targetCommand = isRootCommand ? t : command;
8479
if (targetCommand) {
85-
// Auto-detection handles boolean vs value options based on handler presence
86-
const customHandler = commandCompletionConfig?.options?.[argName];
87-
const handler = isBoolean ? noopHandler : customHandler;
88-
89-
if (shortFlag) {
90-
if (handler) {
80+
const handler = commandCompletionConfig?.options?.[argName];
81+
if (handler) {
82+
// Has custom handler → value option
83+
if (shortFlag) {
9184
targetCommand.option(
9285
argName,
9386
option.description || '',
9487
handler,
9588
shortFlag
9689
);
9790
} else {
98-
targetCommand.option(argName, option.description || '', shortFlag);
91+
targetCommand.option(argName, option.description || '', handler);
9992
}
10093
} else {
101-
if (handler) {
102-
targetCommand.option(argName, option.description || '', handler);
94+
// No custom handler → boolean flag
95+
if (shortFlag) {
96+
targetCommand.option(argName, option.description || '', shortFlag);
10397
} else {
10498
targetCommand.option(argName, option.description || '');
10599
}

src/citty.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type {
1111
} from 'citty';
1212
import { generateFigSpec } from './fig';
1313
import { CompletionConfig, assertDoubleDashes } from './shared';
14-
import { OptionHandler, Command, Option, OptionsMap, noopHandler } from './t';
14+
import { OptionHandler, Command, Option, OptionsMap } from './t';
1515
import t from './t';
1616

1717
function quoteIfNeeded(path: string) {
@@ -144,21 +144,19 @@ async function handleSubCommands(
144144
: conf.alias
145145
: undefined;
146146

147-
// Detect boolean options and use appropriate handler
148-
const isBoolean = conf.type === 'boolean';
149-
const customHandler = subCompletionConfig?.options?.[argName];
150-
const handler = isBoolean ? noopHandler : customHandler;
151-
152-
// Add option using t.ts API - auto-detection handles boolean vs value options
153-
if (shortFlag) {
154-
if (handler) {
147+
// Add option using t.ts API - store without -- prefix
148+
const handler = subCompletionConfig?.options?.[argName];
149+
if (handler) {
150+
// Has custom handler → value option
151+
if (shortFlag) {
155152
command.option(argName, conf.description ?? '', handler, shortFlag);
156153
} else {
157-
command.option(argName, conf.description ?? '', shortFlag);
154+
command.option(argName, conf.description ?? '', handler);
158155
}
159156
} else {
160-
if (handler) {
161-
command.option(argName, conf.description ?? '', handler);
157+
// No custom handler → boolean flag
158+
if (shortFlag) {
159+
command.option(argName, conf.description ?? '', shortFlag);
162160
} else {
163161
command.option(argName, conf.description ?? '');
164162
}
@@ -215,16 +213,30 @@ export default async function tab<TArgs extends ArgsDef>(
215213
for (const [argName, argConfig] of Object.entries(instance.args)) {
216214
const conf = argConfig as ArgDef;
217215

218-
// Detect boolean options and use appropriate handler
219-
const isBoolean = conf.type === 'boolean';
220-
const customHandler = completionConfig?.options?.[argName];
221-
const handler = isBoolean ? noopHandler : customHandler;
216+
// Extract alias (same logic as subcommands)
217+
const shortFlag =
218+
typeof conf === 'object' && 'alias' in conf
219+
? Array.isArray(conf.alias)
220+
? conf.alias[0]
221+
: conf.alias
222+
: undefined;
222223

223-
// Add option using t.ts API - auto-detection handles boolean vs value options
224+
// Add option using t.ts API - store without -- prefix
225+
const handler = completionConfig?.options?.[argName];
224226
if (handler) {
225-
t.option(argName, conf.description ?? '', handler);
227+
// Has custom handler → value option
228+
if (shortFlag) {
229+
t.option(argName, conf.description ?? '', handler, shortFlag);
230+
} else {
231+
t.option(argName, conf.description ?? '', handler);
232+
}
226233
} else {
227-
t.option(argName, conf.description ?? '');
234+
// No custom handler → boolean flag
235+
if (shortFlag) {
236+
t.option(argName, conf.description ?? '', shortFlag);
237+
} else {
238+
t.option(argName, conf.description ?? '');
239+
}
228240
}
229241
}
230242
}

src/t.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ export type OptionHandler = (
2020
options: OptionsMap
2121
) => void;
2222

23-
// Default no-op handler for options (exported for integrations)
24-
export const noopHandler: OptionHandler = function () {};
25-
2623
// Completion result types
2724
export type Completion = {
2825
description?: string;
@@ -107,28 +104,31 @@ export class Command {
107104
handlerOrAlias?: OptionHandler | string,
108105
alias?: string
109106
): Command {
110-
let handler: OptionHandler = noopHandler;
111-
let aliasValue: string | undefined;
107+
let handler: OptionHandler | undefined;
108+
let aliasStr: string | undefined;
109+
let isBoolean: boolean;
112110

113111
// Parse arguments based on types
114112
if (typeof handlerOrAlias === 'function') {
115-
// handler provided, value option
116113
handler = handlerOrAlias;
117-
aliasValue = alias;
114+
aliasStr = alias;
115+
isBoolean = false;
118116
} else if (typeof handlerOrAlias === 'string') {
119-
// alias provided, no handler, boolean flag
120-
aliasValue = handlerOrAlias;
117+
handler = undefined;
118+
aliasStr = handlerOrAlias;
119+
isBoolean = true;
120+
} else {
121+
handler = undefined;
122+
aliasStr = undefined;
123+
isBoolean = true;
121124
}
122125

123-
// if no custom handler provided, it's a boolean flag
124-
const isBoolean = handler === noopHandler;
125-
126126
const option = new Option(
127127
this,
128128
value,
129129
description,
130130
handler,
131-
aliasValue,
131+
aliasStr,
132132
isBoolean
133133
);
134134
this.options.set(value, option);

tests/__snapshots__/cli.test.ts.snap

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,16 @@ vite.config.js Vite config file
2222
`;
2323

2424
exports[`cli completion tests for cac > --config option tests > should complete short flag -c option values 1`] = `
25-
":4
25+
"vite.config.ts Vite config file
26+
vite.config.js Vite config file
27+
:4
2628
"
2729
`;
2830

2931
exports[`cli completion tests for cac > --config option tests > should complete short flag -c option with partial input 1`] = `
30-
":4
32+
"vite.config.ts Vite config file
33+
vite.config.js Vite config file
34+
:4
3135
"
3236
`;
3337

@@ -46,12 +50,14 @@ exports[`cli completion tests for cac > cli option completion tests > should com
4650
`;
4751

4852
exports[`cli completion tests for cac > cli option completion tests > should complete option for partial input '{ partial: '-H', expected: '-H' }' 1`] = `
49-
":4
53+
"-H Specify hostname
54+
:4
5055
"
5156
`;
5257

5358
exports[`cli completion tests for cac > cli option completion tests > should complete option for partial input '{ partial: '-p', expected: '-p' }' 1`] = `
54-
":4
59+
"-p Specify port
60+
:4
5561
"
5662
`;
5763

@@ -249,27 +255,36 @@ exports[`cli completion tests for cac > root command option tests > should compl
249255
`;
250256

251257
exports[`cli completion tests for cac > root command option tests > should complete root command short flag -l option values 1`] = `
252-
":4
258+
"info Info level
259+
warn Warn level
260+
error Error level
261+
silent Silent level
262+
:4
253263
"
254264
`;
255265

256266
exports[`cli completion tests for cac > root command option tests > should complete root command short flag -m option values 1`] = `
257-
":4
267+
"development Development mode
268+
production Production mode
269+
:4
258270
"
259271
`;
260272

261273
exports[`cli completion tests for cac > short flag handling > should handle global short flags 1`] = `
262-
":4
274+
"-c Use specified config file
275+
:4
263276
"
264277
`;
265278

266279
exports[`cli completion tests for cac > short flag handling > should handle short flag value completion 1`] = `
267-
":4
280+
"-p Specify port
281+
:4
268282
"
269283
`;
270284

271285
exports[`cli completion tests for cac > short flag handling > should handle short flag with equals sign 1`] = `
272-
":4
286+
"-p=3000 Development server port
287+
:4
273288
"
274289
`;
275290

@@ -312,12 +327,16 @@ vite.config.js Vite config file
312327
`;
313328

314329
exports[`cli completion tests for citty > --config option tests > should complete short flag -c option values 1`] = `
315-
":4
330+
"vite.config.ts Vite config file
331+
vite.config.js Vite config file
332+
:4
316333
"
317334
`;
318335

319336
exports[`cli completion tests for citty > --config option tests > should complete short flag -c option with partial input 1`] = `
320-
":4
337+
"vite.config.ts Vite config file
338+
vite.config.js Vite config file
339+
:4
321340
"
322341
`;
323342

@@ -548,17 +567,24 @@ exports[`cli completion tests for citty > root command option tests > should com
548567
`;
549568

550569
exports[`cli completion tests for citty > root command option tests > should complete root command short flag -l option values 1`] = `
551-
":4
570+
"info Info level
571+
warn Warn level
572+
error Error level
573+
silent Silent level
574+
:4
552575
"
553576
`;
554577

555578
exports[`cli completion tests for citty > root command option tests > should complete root command short flag -m option values 1`] = `
556-
":4
579+
"development Development mode
580+
production Production mode
581+
:4
557582
"
558583
`;
559584

560585
exports[`cli completion tests for citty > short flag handling > should handle global short flags 1`] = `
561-
":4
586+
"-c Use specified config file
587+
:4
562588
"
563589
`;
564590

0 commit comments

Comments
 (0)