From 8bbcca299175ae1968ded08b7afaf5f9fbb9f303 Mon Sep 17 00:00:00 2001 From: AmirSa12 Date: Fri, 8 Aug 2025 06:42:33 +0330 Subject: [PATCH 1/2] fix: lint --- src/cac.ts | 4 ++-- src/citty.ts | 58 ++-------------------------------------------------- src/index.ts | 2 +- src/t.ts | 18 +++++----------- 4 files changed, 10 insertions(+), 72 deletions(-) diff --git a/src/cac.ts b/src/cac.ts index 72bbdd5..a0d66f5 100644 --- a/src/cac.ts +++ b/src/cac.ts @@ -5,7 +5,7 @@ import * as powershell from './powershell'; import type { CAC } from 'cac'; import { assertDoubleDashes } from './shared'; import { CompletionConfig } from './shared'; -import t from './t'; +import t, { RootCommand } from './t'; const execPath = process.execPath; const processArgs = process.argv.slice(1); @@ -22,7 +22,7 @@ function quoteIfNeeded(path: string): string { export default async function tab( instance: CAC, completionConfig?: CompletionConfig -): Promise { +): Promise { // Add all commands and their options for (const cmd of [instance.globalCommand, ...instance.commands]) { if (cmd.name === 'complete') continue; // Skip completion command diff --git a/src/citty.ts b/src/citty.ts index 4ff5e21..d3eb1a3 100644 --- a/src/citty.ts +++ b/src/citty.ts @@ -11,8 +11,7 @@ import type { } from 'citty'; import { generateFigSpec } from './fig'; import { CompletionConfig, assertDoubleDashes } from './shared'; -import { OptionHandler, Command, Option, OptionsMap } from './t'; -import t from './t'; +import t, { RootCommand } from './t'; function quoteIfNeeded(path: string) { return path.includes(' ') ? `'${path}'` : path; @@ -32,59 +31,6 @@ function isConfigPositional(config: CommandDef) { ); } -// Convert Handler from index.ts to OptionHandler from t.ts -function convertOptionHandler(handler: any): OptionHandler { - return function ( - this: Option, - complete: (value: string, description: string) => void, - options: OptionsMap, - previousArgs?: string[], - toComplete?: string, - endsWithSpace?: boolean - ) { - // For short flags with equals sign and a value, don't complete (citty behavior) - // Check if this is a short flag option and if the toComplete looks like a value - if ( - this.alias && - toComplete && - toComplete !== '' && - !toComplete.startsWith('-') - ) { - // This might be a short flag with equals sign and a value - // Check if the previous args contain a short flag with equals sign - if (previousArgs && previousArgs.length > 0) { - const lastArg = previousArgs[previousArgs.length - 1]; - if (lastArg.includes('=')) { - const [flag, value] = lastArg.split('='); - if (flag.startsWith('-') && !flag.startsWith('--') && value !== '') { - return; // Don't complete short flags with equals sign and value - } - } - } - } - - // Call the old handler with the proper context - const result = handler( - previousArgs || [], - toComplete || '', - endsWithSpace || false - ); - - if (Array.isArray(result)) { - result.forEach((item: any) => - complete(item.value, item.description || '') - ); - } else if (result && typeof result.then === 'function') { - // Handle async handlers - result.then((items: any[]) => { - items.forEach((item: any) => - complete(item.value, item.description || '') - ); - }); - } - }; -} - async function handleSubCommands( subCommands: SubCommandsDef, parentCmd?: string, @@ -169,7 +115,7 @@ async function handleSubCommands( export default async function tab( instance: CommandDef, completionConfig?: CompletionConfig -): Promise { +): Promise { const meta = await resolve(instance.meta); if (!meta) { diff --git a/src/index.ts b/src/index.ts index bcd16d9..6bcca5f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,7 @@ import { Completion as CompletionItem } from './t'; const DEBUG = false; -function debugLog(...args: any[]) { +function debugLog(...args: unknown[]) { if (DEBUG) { console.error('[DEBUG]', ...args); } diff --git a/src/t.ts b/src/t.ts index 29321a4..105cd5d 100644 --- a/src/t.ts +++ b/src/t.ts @@ -88,16 +88,7 @@ export class Command { this.description = description; } - // Function overloads for better UX - option(value: string, description: string): Command; - option(value: string, description: string, alias: string): Command; - option(value: string, description: string, handler: OptionHandler): Command; - option( - value: string, - description: string, - handler: OptionHandler, - alias: string - ): Command; + // Function overloads for better UX - combined into single signature with optional parameters option( value: string, description: string, @@ -211,7 +202,7 @@ export class RootCommand extends Command { args = this.stripOptions(args); const parts: string[] = []; let remaining: string[] = []; - let matched: Command = this; + let matchedCommand: Command | null = null; for (let i = 0; i < args.length; i++) { const k = args[i]; @@ -219,14 +210,15 @@ export class RootCommand extends Command { const potential = this.commands.get(parts.join(' ')); if (potential) { - matched = potential; + matchedCommand = potential; } else { remaining = args.slice(i, args.length); break; } } - return [matched, remaining]; + // If no command was matched, use the root command (this) + return [matchedCommand || this, remaining]; } // Determine if we should complete flags From a930f328916187119b8c4ad2a6a92b5cd04f8edc Mon Sep 17 00:00:00 2001 From: AmirSa12 Date: Fri, 8 Aug 2025 06:52:04 +0330 Subject: [PATCH 2/2] fix: types --- bin/completion-handlers.ts | 57 -------------------------------------- examples/demo.cac.ts | 2 +- examples/demo.t.ts | 5 ++-- src/t.ts | 8 ++---- 4 files changed, 6 insertions(+), 66 deletions(-) diff --git a/bin/completion-handlers.ts b/bin/completion-handlers.ts index 20b8496..08bde66 100644 --- a/bin/completion-handlers.ts +++ b/bin/completion-handlers.ts @@ -1,62 +1,5 @@ // TODO: i do not see any completion functionality in this file. nothing is being provided for the defined commands of these package managers. this is a blocker for release. every each of them should be handled. import { Completion } from '../src/index.js'; -import { execSync } from 'child_process'; - -const DEBUG = false; // for debugging purposes - -function debugLog(...args: any[]) { - if (DEBUG) { - console.error('[DEBUG]', ...args); - } -} - -async function checkCliHasCompletions( - cliName: string, - packageManager: string -): Promise { - try { - debugLog(`Checking if ${cliName} has completions via ${packageManager}`); - const command = `${packageManager} ${cliName} complete --`; - const result = execSync(command, { - encoding: 'utf8', - stdio: ['pipe', 'pipe', 'ignore'], - timeout: 1000, // AMIR: we still havin issues with this, it still hangs if a cli doesn't have completions. longer timeout needed for shell completion system (shell → node → package manager → cli) - }); - const hasCompletions = !!result.trim(); - debugLog(`${cliName} supports completions: ${hasCompletions}`); - return hasCompletions; - } catch (error) { - debugLog(`Error checking completions for ${cliName}:`, error); - return false; - } -} - -async function getCliCompletions( - cliName: string, - packageManager: string, - args: string[] -): Promise { - try { - const completeArgs = args.map((arg) => - arg.includes(' ') ? `"${arg}"` : arg - ); - const completeCommand = `${packageManager} ${cliName} complete -- ${completeArgs.join(' ')}`; - debugLog(`Getting completions with command: ${completeCommand}`); - - const result = execSync(completeCommand, { - encoding: 'utf8', - stdio: ['pipe', 'pipe', 'ignore'], - timeout: 1000, // same: longer timeout needed for shell completion system (shell → node → package manager → cli) - }); - - const completions = result.trim().split('\n').filter(Boolean); - debugLog(`Got ${completions.length} completions from ${cliName}`); - return completions; - } catch (error) { - debugLog(`Error getting completions from ${cliName}:`, error); - return []; - } -} export function setupCompletionForPackageManager( packageManager: string, diff --git a/examples/demo.cac.ts b/examples/demo.cac.ts index ff95a1a..2d4178c 100644 --- a/examples/demo.cac.ts +++ b/examples/demo.cac.ts @@ -1,6 +1,6 @@ import cac from 'cac'; import tab from '../src/cac'; -import type { Command, Option, OptionsMap } from '../src/t'; +import type { Option, OptionsMap } from '../src/t'; const cli = cac('vite'); diff --git a/examples/demo.t.ts b/examples/demo.t.ts index 38f3569..508302c 100644 --- a/examples/demo.t.ts +++ b/examples/demo.t.ts @@ -96,8 +96,7 @@ t.command('dev build', 'Build project'); t.command('dev start', 'Start development server'); // Copy command with multiple arguments -const copyCmd = t - .command('copy', 'Copy files') +t.command('copy', 'Copy files') .argument('source', function (complete) { complete('src/', 'Source directory'); complete('dist/', 'Distribution directory'); @@ -110,7 +109,7 @@ const copyCmd = t }); // Lint command with variadic arguments -const lintCmd = t.command('lint', 'Lint project').argument( +t.command('lint', 'Lint project').argument( 'files', function (complete) { complete('main.ts', 'Main file'); diff --git a/src/t.ts b/src/t.ts index 105cd5d..a38b215 100644 --- a/src/t.ts +++ b/src/t.ts @@ -274,12 +274,10 @@ export class RootCommand extends Command { ) { // Handle flag value completion let optionName: string | undefined; - let valueToComplete = toComplete; if (toComplete.includes('=')) { - const [flag, value] = toComplete.split('='); + const [flag] = toComplete.split('='); optionName = flag; - valueToComplete = value || ''; } else if (lastPrevArg?.startsWith('-')) { optionName = lastPrevArg; } @@ -342,7 +340,7 @@ export class RootCommand extends Command { if (option) return option; // Try by short alias - for (const [name, opt] of command.options) { + for (const [_name, opt] of command.options) { if (opt.alias && `-${opt.alias}` === optionName) { return opt; } @@ -391,7 +389,7 @@ export class RootCommand extends Command { if (currentArgIndex < argumentEntries.length) { // We're within the defined arguments - const [argName, argument] = argumentEntries[currentArgIndex]; + const [_argName, argument] = argumentEntries[currentArgIndex]; targetArgument = argument; } else { // We're beyond the defined arguments, check if the last argument is variadic