-
-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathcac.ts
More file actions
152 lines (134 loc) · 4.92 KB
/
cac.ts
File metadata and controls
152 lines (134 loc) · 4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import * as zsh from './zsh';
import * as bash from './bash';
import * as fish from './fish';
import * as powershell from './powershell';
import type { CAC } from 'cac';
import { assertDoubleDashes } from './shared';
import { OptionHandler, noopHandler } from './t';
import { CompletionConfig } from './shared';
import t from './t';
const execPath = process.execPath;
const processArgs = process.argv.slice(1);
const quotedExecPath = quoteIfNeeded(execPath);
const quotedProcessArgs = processArgs.map(quoteIfNeeded);
const quotedProcessExecArgs = process.execArgv.map(quoteIfNeeded);
const x = `${quotedExecPath} ${quotedProcessExecArgs.join(' ')} ${quotedProcessArgs[0]}`;
function quoteIfNeeded(path: string): string {
return path.includes(' ') ? `'${path}'` : path;
}
export default async function tab(
instance: CAC,
completionConfig?: CompletionConfig
): Promise<any> {
// Add all commands and their options
for (const cmd of [instance.globalCommand, ...instance.commands]) {
if (cmd.name === 'complete') continue; // Skip completion command
// Get positional args info from command usage
const args = (cmd.rawName.match(/\[.*?\]|<.*?>/g) || []).map((arg) =>
arg.startsWith('[')
); // true if optional (wrapped in [])
const isRootCommand = cmd.name === '@@global@@';
const commandCompletionConfig = isRootCommand
? completionConfig
: completionConfig?.subCommands?.[cmd.name];
// Add command to completion using t.ts API
const commandName = isRootCommand ? '' : cmd.name;
const command = isRootCommand
? t
: t.command(commandName, cmd.description || '');
// Set args for the command
if (command) {
// Extract argument names from command usage
const argMatches =
cmd.rawName.match(/<([^>]+)>|\[\.\.\.([^\]]+)\]/g) || [];
const argNames = argMatches.map((match) => {
if (match.startsWith('<') && match.endsWith('>')) {
return match.slice(1, -1); // Remove < >
} else if (match.startsWith('[...') && match.endsWith(']')) {
return match.slice(4, -1); // Remove [... ]
}
return match;
});
args.forEach((variadic, index) => {
const argName = argNames[index] || `arg${index}`;
const argHandler = commandCompletionConfig?.args?.[argName];
if (argHandler) {
command.argument(argName, argHandler, variadic);
} else {
command.argument(argName, undefined, variadic);
}
});
}
// Add command options
for (const option of [...instance.globalCommand.options, ...cmd.options]) {
// Extract short flag from the name if it exists (e.g., "-c, --config" -> "c")
const shortFlag = option.name.match(/^-([a-zA-Z]), --/)?.[1];
const argName = option.name.replace(/^-[a-zA-Z], --/, '');
// Detect if this is a boolean option by checking if it has <value> or [value] in the raw definition
const isBoolean =
!option.rawName.includes('<') && !option.rawName.includes('[');
// Add option using t.ts API
const targetCommand = isRootCommand ? t : command;
if (targetCommand) {
// Auto-detection handles boolean vs value options based on handler presence
const customHandler = commandCompletionConfig?.options?.[argName];
const handler = isBoolean ? noopHandler : customHandler;
if (shortFlag) {
if (handler) {
targetCommand.option(
argName,
option.description || '',
handler,
shortFlag
);
} else {
targetCommand.option(argName, option.description || '', shortFlag);
}
} else {
if (handler) {
targetCommand.option(argName, option.description || '', handler);
} else {
targetCommand.option(argName, option.description || '');
}
}
}
}
}
instance.command('complete [shell]').action(async (shell, extra) => {
switch (shell) {
case 'zsh': {
const script = zsh.generate(instance.name, x);
console.log(script);
break;
}
case 'bash': {
const script = bash.generate(instance.name, x);
console.log(script);
break;
}
case 'fish': {
const script = fish.generate(instance.name, x);
console.log(script);
break;
}
case 'powershell': {
const script = powershell.generate(instance.name, x);
console.log(script);
break;
}
default: {
assertDoubleDashes(instance.name);
const args: string[] = extra['--'] || [];
instance.showHelpOnExit = false;
// Parse current command context
instance.unsetMatchedCommand();
instance.parse([execPath, processArgs[0], ...args], {
run: false,
});
// Use t.ts parse method instead of completion.parse
return t.parse(args);
}
}
});
return t;
}