Build complex CLIs with type safety and no dependencies
Comprehensive localization system with customizable text for all user-facing strings including help text, error messages, and documentation.
import { text_en } from "@stricli/core";
const app = buildApplication(command, {
name: "myapp",
localization: {
defaultLocale: "en",
loadText: (locale) => {
if (locale.startsWith("es")) return text_es;
if (locale.startsWith("en")) return text_en;
return undefined;
}
}
});
await run(app, inputs, { process, locale: process.env.LANG });Full set of static text and text-returning callbacks necessary for Stricli output. Extends error formatting and documentation text.
/**
* Complete text interface for Stricli output
* Includes error messages, documentation text, and version warnings
*/
interface ApplicationText extends ApplicationErrorFormatting, DocumentationText {
/**
* Generate warning when latest version is not installed
* @param args - Current version, latest version, upgrade command, color flag
* @returns Formatted warning message
*/
readonly currentVersionIsNotLatest: (args: {
readonly currentVersion: string;
readonly latestVersion: string;
readonly upgradeCommand?: string;
readonly ansiColor: boolean;
}) => string;
}
/**
* Default English text implementation
*/
const text_en: ApplicationText;
interface DocumentationKeywords {
default: string;
separator: string;
}
interface DocumentationHeaders {
usage: string;
aliases: string;
commands: string;
flags: string;
arguments: string;
}
interface DocumentationBriefs {
help: string;
helpAll: string;
version: string;
argumentEscapeSequence: string;
}
interface ApplicationErrorFormatting extends CommandErrorFormatting {
noCommandRegisteredForInput: (args: {
input: string;
corrections: readonly string[];
ansiColor: boolean;
}) => string;
noTextAvailableForLocale: (args: {
requestedLocale: string;
defaultLocale: string;
ansiColor: boolean;
}) => string;
}
interface CommandErrorFormatting {
exceptionWhileParsingArguments: (exc: unknown, ansiColor: boolean) => string;
exceptionWhileLoadingCommandFunction: (exc: unknown, ansiColor: boolean) => string;
exceptionWhileLoadingCommandContext: (exc: unknown, ansiColor: boolean) => string;
exceptionWhileRunningCommand: (exc: unknown, ansiColor: boolean) => string;
commandErrorResult: (err: Error, ansiColor: boolean) => string;
}import { text_en } from "@stricli/core";
const app = buildApplication(command, {
name: "myapp",
localization: { defaultLocale: "en", loadText: (locale) => text_en }
});import { ApplicationText, text_en } from "@stricli/core";
const text_es: ApplicationText = {
// Inherit English text as base
...text_en,
// Override with Spanish text
headers: {
usage: "USO",
aliases: "ALIAS",
commands: "COMANDOS",
flags: "OPCIONES",
arguments: "ARGUMENTOS"
},
keywords: {
default: "predeterminado =",
separator: "separador ="
},
briefs: {
help: "Mostrar información de ayuda y salir",
helpAll: "Mostrar toda la información de ayuda y salir",
version: "Mostrar información de versión y salir",
argumentEscapeSequence: "Todas las entradas posteriores deben interpretarse como argumentos"
},
noCommandRegisteredForInput: ({ input, corrections }) => {
const mensaje = `No hay comando registrado para \`${input}\``;
if (corrections.length > 0) {
return `${mensaje}, ¿quiso decir ${corrections.join(" o ")}?`;
}
return mensaje;
},
noTextAvailableForLocale: ({ requestedLocale, defaultLocale }) => {
return `La aplicación no admite el idioma "${requestedLocale}", usando "${defaultLocale}" por defecto`;
},
exceptionWhileParsingArguments: (exc) => {
return `No se pudieron analizar los argumentos: ${exc}`;
},
exceptionWhileLoadingCommandFunction: (exc) => {
return `No se pudo cargar la función de comando: ${exc}`;
},
exceptionWhileLoadingCommandContext: (exc) => {
return `No se pudo cargar el contexto del comando: ${exc}`;
},
exceptionWhileRunningCommand: (exc) => {
return `El comando falló: ${exc}`;
},
commandErrorResult: (err) => {
return err.message;
},
currentVersionIsNotLatest: ({ currentVersion, latestVersion, upgradeCommand }) => {
if (upgradeCommand) {
return `La última versión disponible es ${latestVersion} (actualmente ejecutando ${currentVersion}), actualice con "${upgradeCommand}"`;
}
return `La última versión disponible es ${latestVersion} (actualmente ejecutando ${currentVersion})`;
}
};
// Use in application
const app = buildApplication(command, {
name: "myapp",
localization: {
defaultLocale: "es",
loadText: (locale) => {
if (locale.startsWith("es")) {
return text_es;
}
if (locale.startsWith("en")) {
return text_en;
}
return undefined;
}
}
});import { ApplicationText, text_en } from "@stricli/core";
const text_fr: ApplicationText = {
...text_en,
headers: {
usage: "UTILISATION",
aliases: "ALIAS",
commands: "COMMANDES",
flags: "OPTIONS",
arguments: "ARGUMENTS"
},
keywords: {
default: "par défaut =",
separator: "séparateur ="
},
briefs: {
help: "Afficher les informations d'aide et quitter",
helpAll: "Afficher toutes les informations d'aide et quitter",
version: "Afficher les informations de version et quitter",
argumentEscapeSequence: "Toutes les entrées suivantes doivent être interprétées comme des arguments"
},
noCommandRegisteredForInput: ({ input, corrections }) => {
const message = `Aucune commande enregistrée pour \`${input}\``;
if (corrections.length > 0) {
return `${message}, vouliez-vous dire ${corrections.join(" ou ")} ?`;
}
return message;
},
noTextAvailableForLocale: ({ requestedLocale, defaultLocale }) => {
return `L'application ne prend pas en charge la locale "${requestedLocale}", utilisation de "${defaultLocale}" par défaut`;
},
exceptionWhileParsingArguments: (exc) => {
return `Impossible d'analyser les arguments : ${exc}`;
},
exceptionWhileLoadingCommandFunction: (exc) => {
return `Impossible de charger la fonction de commande : ${exc}`;
},
exceptionWhileLoadingCommandContext: (exc) => {
return `Impossible de charger le contexte de la commande : ${exc}`;
},
exceptionWhileRunningCommand: (exc) => {
return `La commande a échoué : ${exc}`;
},
commandErrorResult: (err) => {
return err.message;
},
currentVersionIsNotLatest: ({ currentVersion, latestVersion, upgradeCommand }) => {
if (upgradeCommand) {
return `La dernière version disponible est ${latestVersion} (actuellement ${currentVersion}), mise à jour avec "${upgradeCommand}"`;
}
return `La dernière version disponible est ${latestVersion} (actuellement ${currentVersion})`;
}
};import { buildApplication, text_en, ApplicationText } from "@stricli/core";
// Lazy-load translations
async function loadLocale(locale: string): Promise<ApplicationText | undefined> {
if (locale.startsWith("en")) {
return text_en;
}
try {
// Dynamic import based on locale
const module = await import(`./locales/${locale}.js`);
return module.default;
} catch {
// Locale not available
return undefined;
}
}
const app = buildApplication(command, {
name: "myapp",
localization: {
defaultLocale: "en",
loadText: (locale) => {
// Synchronous loading from cache or return undefined
const cached = localeCache.get(locale);
if (cached) {
return cached;
}
// For runtime, we need synchronous loading
// Pre-load locales during build
return undefined;
}
}
});
// Pre-load common locales
const localeCache = new Map<string, ApplicationText>();
localeCache.set("en", text_en);
(async () => {
const es = await loadLocale("es");
if (es) localeCache.set("es", es);
const fr = await loadLocale("fr");
if (fr) localeCache.set("fr", fr);
})();import { ApplicationText, text_en, formatMessageForArgumentScannerError, ArgumentScannerError } from "@stricli/core";
const customText: ApplicationText = {
...text_en,
exceptionWhileParsingArguments: (exc, ansiColor) => {
if (exc instanceof ArgumentScannerError) {
// Custom formatting for specific errors
return formatMessageForArgumentScannerError(exc, {
FlagNotFoundError: (err) => {
const flag = ansiColor ? `\x1B[1m${err.input}\x1B[22m` : err.input;
if (err.corrections.length > 0) {
return `Unknown option: ${flag}\nDid you mean: ${err.corrections.join(", ")}?`;
}
return `Unknown option: ${flag}`;
},
ArgumentParseError: (err) => {
const value = ansiColor ? `\x1B[1m${err.input}\x1B[22m` : err.input;
const param = ansiColor
? `\x1B[1m${err.externalFlagNameOrPlaceholder}\x1B[22m`
: err.externalFlagNameOrPlaceholder;
return `Invalid value ${value} for ${param}`;
}
});
}
return text_en.exceptionWhileParsingArguments(exc, ansiColor);
},
exceptionWhileRunningCommand: (exc, ansiColor) => {
// Add more context to errors
const prefix = ansiColor ? "\x1B[31mError:\x1B[39m" : "Error:";
if (exc instanceof Error) {
return `${prefix} ${exc.message}\n\nRun with --help for usage information.`;
}
return `${prefix} ${String(exc)}`;
}
};import { ApplicationText, text_en } from "@stricli/core";
const brandedText: ApplicationText = {
...text_en,
currentVersionIsNotLatest: ({ currentVersion, latestVersion, upgradeCommand }) => {
// Custom branding
const message = `🎉 New version available!\n` +
` Current: v${currentVersion}\n` +
` Latest: v${latestVersion}\n`;
if (upgradeCommand) {
return message + ` Run: ${upgradeCommand}`;
}
return message;
},
exceptionWhileRunningCommand: (exc, ansiColor) => {
// Friendly error messages
const prefix = ansiColor ? "😞 \x1B[31mOops!\x1B[39m" : "Oops!";
if (exc instanceof Error) {
return `${prefix} Something went wrong:\n\n ${exc.message}\n\n` +
`💡 Tip: Run with --help for usage information`;
}
return `${prefix} ${String(exc)}`;
}
};const locales = { en: text_en, es: text_es, fr: text_fr };
const app = buildApplication(command, {
name: "myapp",
localization: {
defaultLocale: "en",
loadText: (locale) => {
// Try exact match
if (locale in locales) return locales[locale];
// Try language prefix (e.g., "en-US" -> "en")
const lang = locale.split("-")[0];
if (lang in locales) return locales[lang];
// Fall back to English
return text_en;
}
}
});
await run(app, inputs, {
process,
locale: process.env.LANG || "en"
});Here's a comprehensive example with multiple locales:
import {
buildApplication,
buildCommand,
run,
text_en,
type ApplicationText
} from "@stricli/core";
// Define Spanish text
const text_es: ApplicationText = {
...text_en,
headers: {
usage: "USO",
aliases: "ALIAS",
commands: "COMANDOS",
flags: "OPCIONES",
arguments: "ARGUMENTOS"
},
briefs: {
help: "Mostrar ayuda",
helpAll: "Mostrar toda la ayuda",
version: "Mostrar versión",
argumentEscapeSequence: "Interpretar como argumentos"
},
// ... other translations
};
// Build application with localization
const app = buildApplication(
buildCommand({
func: async function(flags) {
if (flags.verbose) {
this.process.stdout.write("Modo detallado activado\n");
}
this.process.stdout.write("¡Hola mundo!\n");
},
parameters: {
flags: {
verbose: {
kind: "boolean",
brief: "Salida detallada"
}
}
},
docs: {
brief: "Comando de saludo"
}
}),
{
name: "myapp",
versionInfo: {
currentVersion: "1.0.0"
},
localization: {
defaultLocale: "en",
loadText: (locale) => {
if (locale.startsWith("es")) {
return text_es;
}
if (locale.startsWith("en")) {
return text_en;
}
return undefined;
}
}
}
);
// Run with locale from environment
await run(app, process.argv.slice(2), {
process,
locale: process.env.LANG?.split(".")[0] || "en"
});Install with Tessl CLI
npx tessl i tessl/npm-stricli--core