Skip to content

Commit d76f4e1

Browse files
committed
boolean flags
1 parent 09dd61d commit d76f4e1

File tree

4 files changed

+67
-62
lines changed

4 files changed

+67
-62
lines changed

examples/demo.t.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ devCmd.option(
6262
'p'
6363
);
6464

65-
devCmd.option('verbose', 'Enable verbose logging', 'v', true);
65+
devCmd.option('verbose', 'Enable verbose logging', 'v');
6666

6767
// Serve command
6868
const serveCmd = t.command('serve', 'Start the server');

src/cac.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@ 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 } from './t';
7+
import { OptionHandler, noopHandler } from './t';
88
import { CompletionConfig } from './shared';
99
import t from './t';
1010

11-
const noopOptionHandler: OptionHandler = function () {};
12-
1311
const execPath = process.execPath;
1412
const processArgs = process.argv.slice(1);
1513
const quotedExecPath = quoteIfNeeded(execPath);
@@ -84,13 +82,28 @@ export default async function tab(
8482
// Add option using t.ts API
8583
const targetCommand = isRootCommand ? t : command;
8684
if (targetCommand) {
87-
targetCommand.option(
88-
argName, // Store just the option name without -- prefix
89-
option.description || '',
90-
commandCompletionConfig?.options?.[argName] ?? noopOptionHandler,
91-
shortFlag,
92-
isBoolean
93-
);
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) {
91+
targetCommand.option(
92+
argName,
93+
option.description || '',
94+
handler,
95+
shortFlag
96+
);
97+
} else {
98+
targetCommand.option(argName, option.description || '', shortFlag);
99+
}
100+
} else {
101+
if (handler) {
102+
targetCommand.option(argName, option.description || '', handler);
103+
} else {
104+
targetCommand.option(argName, option.description || '');
105+
}
106+
}
94107
}
95108
}
96109
}

src/citty.ts

Lines changed: 30 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 } from './t';
14+
import { OptionHandler, Command, Option, OptionsMap, noopHandler } from './t';
1515
import t from './t';
1616

1717
function quoteIfNeeded(path: string) {
@@ -85,8 +85,6 @@ function convertOptionHandler(handler: any): OptionHandler {
8585
};
8686
}
8787

88-
const noopOptionHandler: OptionHandler = function () {};
89-
9088
async function handleSubCommands(
9189
subCommands: SubCommandsDef,
9290
parentCmd?: string,
@@ -146,15 +144,25 @@ async function handleSubCommands(
146144
: conf.alias
147145
: undefined;
148146

149-
// Add option using t.ts API - store without -- prefix
147+
// Detect boolean options and use appropriate handler
150148
const isBoolean = conf.type === 'boolean';
151-
command.option(
152-
argName,
153-
conf.description ?? '',
154-
subCompletionConfig?.options?.[argName] ?? noopOptionHandler,
155-
shortFlag,
156-
isBoolean
157-
);
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) {
155+
command.option(argName, conf.description ?? '', handler, shortFlag);
156+
} else {
157+
command.option(argName, conf.description ?? '', shortFlag);
158+
}
159+
} else {
160+
if (handler) {
161+
command.option(argName, conf.description ?? '', handler);
162+
} else {
163+
command.option(argName, conf.description ?? '');
164+
}
165+
}
158166
}
159167
}
160168
}
@@ -206,15 +214,18 @@ export default async function tab<TArgs extends ArgsDef>(
206214
if (instance.args) {
207215
for (const [argName, argConfig] of Object.entries(instance.args)) {
208216
const conf = argConfig as ArgDef;
209-
// Add option using t.ts API - store without -- prefix
217+
218+
// Detect boolean options and use appropriate handler
210219
const isBoolean = conf.type === 'boolean';
211-
t.option(
212-
argName,
213-
conf.description ?? '',
214-
completionConfig?.options?.[argName] ?? noopOptionHandler,
215-
undefined,
216-
isBoolean
217-
);
220+
const customHandler = completionConfig?.options?.[argName];
221+
const handler = isBoolean ? noopHandler : customHandler;
222+
223+
// Add option using t.ts API - auto-detection handles boolean vs value options
224+
if (handler) {
225+
t.option(argName, conf.description ?? '', handler);
226+
} else {
227+
t.option(argName, conf.description ?? '');
228+
}
218229
}
219230
}
220231

src/t.ts

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

23-
// Default no-op handler for options (internal)
24-
const noopHandler: OptionHandler = function () { };
23+
// Default no-op handler for options (exported for integrations)
24+
export const noopHandler: OptionHandler = function () { };
2525

2626
// Completion result types
2727
export type Completion = {
@@ -94,61 +94,42 @@ export class Command {
9494
// Function overloads for better UX
9595
option(value: string, description: string): Command;
9696
option(value: string, description: string, alias: string): Command;
97-
option(
98-
value: string,
99-
description: string,
100-
alias: string,
101-
isBoolean: boolean
102-
): Command;
10397
option(value: string, description: string, handler: OptionHandler): Command;
10498
option(
10599
value: string,
106100
description: string,
107101
handler: OptionHandler,
108102
alias: string
109103
): Command;
110-
option(
111-
value: string,
112-
description: string,
113-
handler: OptionHandler,
114-
alias: string,
115-
isBoolean: boolean
116-
): Command;
117104
option(
118105
value: string,
119106
description: string,
120107
handlerOrAlias?: OptionHandler | string,
121-
aliasOrIsBoolean?: string | boolean,
122-
isBoolean?: boolean
108+
alias?: string
123109
): Command {
124110
let handler: OptionHandler = noopHandler;
125-
let alias: string | undefined;
126-
let isBooleanFlag: boolean | undefined;
111+
let aliasValue: string | undefined;
127112

128113
// Parse arguments based on types
129114
if (typeof handlerOrAlias === 'function') {
130-
// handler provided
115+
// handler provided, value option
131116
handler = handlerOrAlias;
132-
alias = aliasOrIsBoolean as string;
133-
isBooleanFlag = isBoolean;
117+
aliasValue = alias;
134118
} else if (typeof handlerOrAlias === 'string') {
135-
// alias provided (no handler)
136-
alias = handlerOrAlias;
137-
isBooleanFlag = aliasOrIsBoolean as boolean;
138-
} else if (handlerOrAlias === undefined) {
139-
// neither handler nor alias provided
140-
if (typeof aliasOrIsBoolean === 'boolean') {
141-
isBooleanFlag = aliasOrIsBoolean;
142-
}
119+
// alias provided, no handler, boolean flag
120+
aliasValue = handlerOrAlias;
143121
}
144122

123+
// if no custom handler provided, it's a boolean flag
124+
const isBoolean = handler === noopHandler;
125+
145126
const option = new Option(
146127
this,
147128
value,
148129
description,
149130
handler,
150-
alias,
151-
isBooleanFlag
131+
aliasValue,
132+
isBoolean
152133
);
153134
this.options.set(value, option);
154135
return this;

0 commit comments

Comments
 (0)