[READ-ONLY] Mirror of https://github.com/bombshell-dev/tab. shell autocompletions for javascript CLIs
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165import * 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 type { CompletionConfig } from './shared';import t, { type RootCommand } 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]}`;
// Regex to detect if an option takes a value (has <required> or [optional] parameters)const VALUE_OPTION_RE = /<[^>]+>|\[[^\]]+\]/;
function quoteIfNeeded(path: string): string { return path.includes(' ') ? `'${path}'` : path;}
export default async function tab( instance: CAC, completionConfig?: CompletionConfig): Promise<RootCommand> { for (const cmd of [instance.globalCommand, ...instance.commands]) { if (cmd.name === 'complete') continue;
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];
// command const commandName = isRootCommand ? '' : cmd.name; const command = isRootCommand ? t : t.command(commandName, cmd.description || '');
// args (if has positional arguments) if (command) { const argMatches = cmd.rawName.match(/<([^>]+)>|\[\.\.\.([^\]]+)\]/g) || []; const argNames = argMatches.map((match) => { if (match.startsWith('<') && match.endsWith('>')) { return match.slice(1, -1); } else if (match.startsWith('[...') && match.endsWith(']')) { return match.slice(4, -1); } 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); } }); }
// options for (const option of [...instance.globalCommand.options, ...cmd.options]) { // short flag (if exists) const shortFlag = option.rawName.match(/^-([a-zA-Z]), --/)?.[1]; const argName = option.name;
const targetCommand = isRootCommand ? t : command; if (targetCommand) { const handler = commandCompletionConfig?.options?.[argName];
// takes value (if has <> or [] in rawName, or is marked as required) const takesValue = option.required || VALUE_OPTION_RE.test(option.rawName);
if (handler) { if (shortFlag) { targetCommand.option( argName, option.description || '', handler, shortFlag ); } else { targetCommand.option(argName, option.description || '', handler); } } else if (takesValue) { // value option (if takes value but no custom handler) if (shortFlag) { targetCommand.option( argName, option.description || '', async () => [], shortFlag ); } else { targetCommand.option( argName, option.description || '', async () => [] ); } } else { // boolean flag (if no custom handler and doesn't take value) if (shortFlag) { targetCommand.option(argName, option.description || '', shortFlag); } 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;
// command context instance.unsetMatchedCommand(); instance.parse([execPath, processArgs[0], ...args], { run: false, });
return t.parse(args); } } });
return t;}