feat: add title command line flag

This commit is contained in:
Alejandro Gómez
2025-12-16 20:50:03 +01:00
parent 6126f43e34
commit 63121f6233
37 changed files with 648 additions and 73 deletions

View File

@@ -1,4 +1,6 @@
import { parse as parseShellTokens } from "shell-quote";
import { manPages } from "@/types/man";
import { extractGlobalFlagsFromTokens, type GlobalFlags } from "./global-flags";
export interface ParsedCommand {
commandName: string;
@@ -6,20 +8,57 @@ export interface ParsedCommand {
fullInput: string;
command?: (typeof manPages)[string];
props?: any;
title?: string;
error?: string;
globalFlags?: GlobalFlags;
}
/**
* Parses a command string into its components.
* Returns basic parsing info without executing argParser.
*
* Now supports:
* - Proper quote handling via shell-quote
* - Global flag extraction (--title, etc.)
*/
export function parseCommandInput(input: string): ParsedCommand {
const parts = input.trim().split(/\s+/);
const commandName = parts[0]?.toLowerCase() || "";
const args = parts.slice(1);
const fullInput = input.trim();
// Pre-process: Escape $ to prevent shell-quote from expanding variables
// We use $me and $contacts as literal syntax, not shell variables
const DOLLAR_PLACEHOLDER = "___DOLLAR___";
const escapedInput = fullInput.replace(/\$/g, DOLLAR_PLACEHOLDER);
// Tokenize with quote support (on escaped input)
const rawTokens = parseShellTokens(escapedInput);
// Convert tokens to strings and restore $ characters
const tokens = rawTokens.map((token) => {
const str = typeof token === "string" ? token : String(token);
return str.replace(new RegExp(DOLLAR_PLACEHOLDER, "g"), "$");
});
// Extract global flags before command parsing
let globalFlags: GlobalFlags = {};
let remainingTokens = tokens;
try {
const extracted = extractGlobalFlagsFromTokens(tokens);
globalFlags = extracted.globalFlags;
remainingTokens = extracted.remainingTokens;
} catch (error) {
// Global flag parsing error
return {
commandName: "",
args: [],
fullInput,
error: error instanceof Error ? error.message : "Failed to parse global flags",
};
}
// Parse command from remaining tokens
const commandName = remainingTokens[0]?.toLowerCase() || "";
const args = remainingTokens.slice(1);
const command = commandName && manPages[commandName];
if (!commandName) {
@@ -27,6 +66,7 @@ export function parseCommandInput(input: string): ParsedCommand {
commandName: "",
args: [],
fullInput: "",
globalFlags,
error: "No command provided",
};
}
@@ -36,6 +76,7 @@ export function parseCommandInput(input: string): ParsedCommand {
commandName,
args,
fullInput,
globalFlags,
error: `Unknown command: ${commandName}`,
};
}
@@ -45,6 +86,7 @@ export function parseCommandInput(input: string): ParsedCommand {
args,
fullInput,
command,
globalFlags,
};
}
@@ -65,16 +107,9 @@ export async function executeCommandParser(
? await Promise.resolve(parsed.command.argParser(parsed.args))
: parsed.command.defaultProps || {};
// Generate title
const title =
parsed.args.length > 0
? `${parsed.commandName.toUpperCase()} ${parsed.args.join(" ")}`
: parsed.commandName.toUpperCase();
return {
...parsed,
props,
title,
};
} catch (error) {
return {