Add custom path support for cli/tui in terminal config

This commit is contained in:
2026-06-01 11:49:23 +02:00
parent 5e9c6db412
commit b30243f674
12 changed files with 264 additions and 42 deletions

View File

@@ -23,6 +23,7 @@ import {
existsSync,
readFileSync,
appendFileSync,
mkdirSync,
} from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
@@ -193,7 +194,7 @@ export function generateFishCompletions(binName: string): string {
return loadAndProcessTemplate("fish.fish", binName);
}
type ShellType = "bash" | "zsh" | "fish";
export type ShellType = "bash" | "zsh" | "fish";
const generators: Record<ShellType, (binName: string) => string> = {
bash: generateBashCompletions,
@@ -202,51 +203,74 @@ const generators: Record<ShellType, (binName: string) => string> = {
};
/**
* Shell config file paths and eval commands for each shell type.
* Shell config file paths and startup commands for each shell type.
*/
const shellConfigs: Record<
ShellType,
{ configFile: string; evalCommand: (binName: string) => string }
{
configFile: string;
configDirCommand: string;
configDirPattern: RegExp;
evalCommand: (binName: string) => string;
}
> = {
bash: {
configFile: join(homedir(), ".bashrc"),
configDirCommand: 'export XO_CONFIG_DIR="${XO_CONFIG_DIR:-$HOME/.config/xo-cli}"',
configDirPattern: /^\s*(?:export\s+)?XO_CONFIG_DIR=/m,
evalCommand: (binName) => `eval "$(${binName} completions bash)"`,
},
zsh: {
configFile: join(homedir(), ".zshrc"),
configDirCommand: 'export XO_CONFIG_DIR="${XO_CONFIG_DIR:-$HOME/.config/xo-cli}"',
configDirPattern: /^\s*(?:export\s+)?XO_CONFIG_DIR=/m,
evalCommand: (binName) => `eval "$(${binName} completions zsh)"`,
},
fish: {
configFile: join(homedir(), ".config", "fish", "config.fish"),
configDirCommand:
'set -q XO_CONFIG_DIR; or set -gx XO_CONFIG_DIR "$HOME/.config/xo-cli"',
configDirPattern: /^\s*set\b[^\n]*\bXO_CONFIG_DIR\b/m,
evalCommand: (binName) => `${binName} completions fish | source`,
},
};
/**
* Installs completions to the user's shell config file.
* Adds the eval command if not already present.
* Adds a default config directory and the eval command if not already present.
* @param shell - The shell type
* @param binName - The CLI binary name
* @returns true if installed, false if already present
*/
function installCompletions(shell: ShellType, binName: string): boolean {
const config = shellConfigs[shell];
export function installCompletions(
shell: ShellType,
binName: string,
configFile: string = shellConfigs[shell].configFile,
): boolean {
const config = { ...shellConfigs[shell], configFile };
const evalCommand = config.evalCommand(binName);
// Check if config file exists and already has the completion line
let existingContent = "";
if (existsSync(config.configFile)) {
existingContent = readFileSync(config.configFile, "utf8");
if (existingContent.includes(evalCommand)) {
return false; // Already installed
}
}
// Append the completion line
const commands: string[] = [];
if (!config.configDirPattern.test(existingContent)) {
commands.push(config.configDirCommand);
}
if (!existingContent.includes(evalCommand)) {
commands.push(evalCommand);
}
if (commands.length === 0) {
return false;
}
const newLine =
existingContent.endsWith("\n") || existingContent === "" ? "" : "\n";
const completionBlock = `${newLine}\n# ${binName} shell completions\n${evalCommand}\n`;
const completionBlock = `${newLine}\n# ${binName} shell completions\n${commands.join("\n")}\n`;
mkdirSync(dirname(config.configFile), { recursive: true });
appendFileSync(config.configFile, completionBlock);
return true;
}