UNPKG

@codama/cli

Version:

A CLI for setting up and managing Codama IDLs

778 lines (755 loc) 28.5 kB
// src/program.ts import { createCommand } from "commander"; import pico9 from "picocolors"; // src/commands/init.ts import pico6 from "picocolors"; import prompts2 from "prompts"; // src/utils/childCommands.ts import { spawn } from "child_process"; function createChildCommand(command, args = []) { return { command, args }; } function formatChildCommand(childCommand) { const { command, args } = childCommand; return [command, ...args].join(" "); } async function spawnChildCommand(childCommand, options = { quiet: false }) { const { command, args } = childCommand; const childProcess = spawn(command, args, options); childProcess.stdoutString = ""; childProcess.stderrString = ""; childProcess.stdout?.on("data", (chunk) => { childProcess.stdoutString += chunk.toString(); if (!options.quiet) { process.stdout.write(chunk); } }); childProcess.stderr?.on("data", (chunk) => { childProcess.stderrString += chunk.toString(); if (!options.quiet) { process.stderr.write(chunk); } }); const exitCode = await new Promise((resolve, reject) => { childProcess.on("error", () => reject(createChildProcessResultError(childCommand, childProcess))); childProcess.on("close", resolve); }); if (exitCode) { throw createChildProcessResultError(childCommand, childProcess); } return childProcess; } function createChildProcessResultError(childCommand, childProcess) { const error = new Error(`Command [${formatChildCommand(childCommand)}] failed`); error.childProcess = childProcess; return error; } // src/utils/errors.ts var CliError = class extends Error { constructor(message, items = [], options) { super(message, options); this.items = items; this.name = "CliError"; } }; // src/utils/fs.ts import { R_OK, W_OK } from "constants"; import fs from "fs"; import path from "path"; function resolveRelativePath(childPath, relativeDirectory = null) { return path.resolve(relativeDirectory ?? process.cwd(), childPath); } function resolveConfigPath(childPath, configPath) { const configDir = configPath ? path.dirname(configPath) : null; return resolveRelativePath(childPath, configDir); } function isLocalModulePath(modulePath) { return modulePath.startsWith(".") || modulePath.startsWith("/"); } async function readJson(filePath) { return JSON.parse(await readFile(filePath)); } async function readFile(filePath) { return await fs.promises.readFile(filePath, "utf8"); } async function writeFile(filePath, content) { const directory = path.dirname(filePath); if (!await canWrite(directory)) { await fs.promises.mkdir(directory, { recursive: true }); } await fs.promises.writeFile(filePath, content); } async function canRead(p) { try { await fs.promises.access(p, R_OK); return true; } catch { return false; } } async function canWrite(p) { try { await fs.promises.access(p, W_OK); return true; } catch { return false; } } // src/utils/import.ts import { createRequire } from "module"; import pico from "picocolors"; async function importModuleItem(options) { const module = await importModule(options); const moduleItem = pickModuleItem(module, options.item); if (moduleItem === void 0) { const items = getErrorItems(options); throw new CliError(`Failed to load ${options.identifier ?? "module"}.`, items); } return moduleItem; } function pickModuleItem(module, item = "default") { if (item === "default") { return module.default?.default ?? module.default ?? module; } return module[item] ?? module.default?.[item] ?? module.default?.default?.[item]; } async function importModule(options) { if (isLocalModulePath(options.from)) { return await importLocalModule(options); } try { return await importExternalUserModule(options); } catch { return await importExternalModule(options); } } async function importLocalModule(options) { const { from, identifier } = options; if (!await canRead(from)) { const items = getErrorItems(options); throw new CliError(`Cannot access ${identifier ?? "module"}.`, items); } const dotIndex = from.lastIndexOf("."); const extension = dotIndex === -1 ? void 0 : from.slice(dotIndex); const modulePromise = extension === ".json" ? import(from, { with: { type: "json" } }) : import(from); return await handleImportPromise(modulePromise, options); } async function importExternalModule(options) { return await handleImportPromise(import(options.from), options); } async function importExternalUserModule(options) { const userPackageJsonPath = resolveRelativePath("package.json"); const userRequire = createRequire(userPackageJsonPath); const userFrom = userRequire.resolve(options.from); return await importExternalModule({ ...options, from: userFrom }); } async function handleImportPromise(importPromise, options) { try { return await importPromise; } catch (cause) { const items = getErrorItems(options, cause); throw new CliError(`Failed to load ${options.identifier ?? "module"}.`, items, { cause }); } } function getErrorItems(options, cause) { const { from, item } = options; const items = [`${pico.bold("Module")}: ${from}`]; if (item) { items.push(`${pico.bold("Item")}: ${item}`); } const hasCause = !!cause && typeof cause === "object" && "message" in cause && typeof cause.message === "string"; if (hasCause) { items.push(`${pico.bold("Caused by")}: ${cause.message}`); } return items; } // src/utils/logs.ts import pico2 from "picocolors"; function getLogLevelInfo(logLevel) { const identity = (text) => text; const infos = { success: ["\u2714", pico2.green, pico2.green], info: ["\u2192", pico2.blueBright, identity], warning: ["\u25B2", pico2.yellow, pico2.yellow], error: ["\u2716", pico2.red, pico2.red], debug: ["\u2731", pico2.magenta, pico2.magenta] }; return { icon: infos[logLevel][0], color: infos[logLevel][1], messageColor: infos[logLevel][2] }; } var logWrapper = (level) => (message, items) => log({ level, message, items }); var logSuccess = logWrapper("success"); var logError = logWrapper("error"); var logInfo = logWrapper("info"); var logWarning = logWrapper("warning"); var logDebug = logWrapper("debug"); function log({ level, message, items }) { const { icon, color, messageColor } = getLogLevelInfo(level); console.log(color(icon), messageColor(message)); if (items) { logItems(items, color); } } function logItems(items, color) { const colorFn = color ?? ((text) => text); items.forEach((item, index) => { const prefix = index === items.length - 1 ? "\u2514\u2500" : "\u251C\u2500"; console.log(" " + colorFn(prefix), item); }); } function logBanner() { console.log(pico2.bold(codamaColor("Welcome to Codama!"))); } function codamaColor(text) { if (!pico2.isColorSupported) return text; return `\x1B[38;2;231;171;97m${text}\x1B[0m`; } // src/utils/packageInstall.ts import pico4 from "picocolors"; import prompts from "prompts"; // src/utils/packageJson.ts import pico3 from "picocolors"; var packageJson; async function getPackageJson() { if (!packageJson) { const packageJsonPath = resolveRelativePath("package.json"); if (!await canRead(packageJsonPath)) { throw new CliError("Cannot read package.json.", [`${pico3.bold("Path")}: ${packageJsonPath}`]); } packageJson = await readJson(packageJsonPath); } return packageJson; } async function getPackageJsonDependencies(options = {}) { const packageJson2 = await getPackageJson(); return [ ...packageJson2.dependencies ? Object.keys(packageJson2.dependencies) : [], ...options.includeDev && packageJson2.devDependencies ? Object.keys(packageJson2.devDependencies) : [] ]; } // src/utils/packageManager.ts var FALLBACK_PACKAGE_MANAGER = "npm"; var packageManager; async function getPackageManager() { if (!packageManager) { packageManager = await detectPackageManager(); } return packageManager; } async function detectPackageManager() { const fromPackageJson = await detectPackageManagerFromPackageJson(); if (fromPackageJson) return fromPackageJson; const fromLockfile = await detectPackageManagerFromLockfile(); if (fromLockfile) return fromLockfile; const fromInstalledCli = await detectPackageManagerFromInstalledCli(); if (fromInstalledCli) return fromInstalledCli; return FALLBACK_PACKAGE_MANAGER; } async function detectPackageManagerFromPackageJson() { const packageJson2 = await getPackageJson(); if (!packageJson2.packageManager) return void 0; if (packageJson2.packageManager.startsWith("npm@")) return "npm"; if (packageJson2.packageManager.startsWith("pnpm@")) return "pnpm"; if (packageJson2.packageManager.startsWith("yarn@")) return "yarn"; if (packageJson2.packageManager.startsWith("bun@")) return "bun"; return void 0; } async function detectPackageManagerFromLockfile() { const [isYarn, isPnpm, isBun, isNpm] = await Promise.all([ canRead(resolveRelativePath("yarn.lock")), canRead(resolveRelativePath("pnpm-lock.yaml")), canRead(resolveRelativePath("bun.lockb")), canRead(resolveRelativePath("package-lock.json")) ]); if (isYarn) return "yarn"; if (isPnpm) return "pnpm"; if (isBun) return "bun"; if (isNpm) return "npm"; return void 0; } async function detectPackageManagerFromInstalledCli() { const [isPnpm, isYarn, isBun] = await Promise.all([ hasPackageManagerCli("pnpm"), hasPackageManagerCli("yarn"), hasPackageManagerCli("bun") ]); if (isPnpm) return "pnpm"; if (isYarn) return "yarn"; if (isBun) return "bun"; return void 0; } async function hasPackageManagerCli(packageManager2) { return await spawnChildCommand(createChildCommand(packageManager2, ["--version"]), { quiet: true }).then(() => true).catch(() => false); } // src/utils/prompts.ts var PROMPT_OPTIONS = { onCancel: () => { throw new CliError("Operation cancelled."); } }; // src/utils/packageInstall.ts async function getPackageManagerInstallCommand(packages, options = []) { const packageManager2 = await getPackageManager(); const args = [packageManager2 === "yarn" ? "add" : "install", ...packages, ...options]; return createChildCommand(packageManager2, args); } async function installMissingDependencies(message, requiredDependencies) { if (requiredDependencies.length === 0) return true; const installedDependencies = await getPackageJsonDependencies({ includeDev: true }); const missingDependencies = requiredDependencies.filter((dep) => !installedDependencies.includes(dep)); if (missingDependencies.length === 0) return true; return await installDependencies(message, missingDependencies); } async function installDependencies(message, dependencies) { if (dependencies.length === 0) return true; const installCommand = await getPackageManagerInstallCommand(dependencies); const formattedInstallCommand = pico4.yellow(formatChildCommand(installCommand)); if (process.env.CI) { logWarning(message); logWarning(`Skipping installation in CI environment. Please install manually:`); logWarning(formattedInstallCommand); return false; } logWarning(message); logWarning(`Install command: ${formattedInstallCommand}`); const dependencyResult = await prompts( { initial: true, message: "Install dependencies?", name: "installDependencies", type: "confirm" }, PROMPT_OPTIONS ); if (!dependencyResult.installDependencies) { logWarning("Skipping installation."); return false; } try { logInfo(`Installing`, dependencies); await spawnChildCommand(installCommand, { quiet: true }); logSuccess(`Dependencies installed successfully.`); return true; } catch { logError(`Failed to install dependencies. Please try manually:`); logError(formattedInstallCommand); return false; } } // src/utils/nodes.ts async function getRootNodeFromIdl(idl) { if (typeof idl !== "object" || idl === null) { throw new CliError("Unexpected IDL content. Expected an object, got " + typeof idl); } if (isRootNode(idl)) { return idl; } const hasNodesFromAnchor = await installMissingDependencies( "Anchor IDL detected. Additional dependencies are required to process Anchor IDLs.", ["@codama/nodes-from-anchor"] ); if (!hasNodesFromAnchor) { throw new CliError("Cannot proceed without Anchor IDL support."); } const rootNodeFromAnchor = await importModuleItem({ from: "@codama/nodes-from-anchor", item: "rootNodeFromAnchor" }); return rootNodeFromAnchor(idl); } function isRootNode(value) { return typeof value === "object" && value !== null && value.standard === "codama" && value.kind === "rootNode"; } // src/utils/promises.ts function promisify(value) { return Promise.resolve(value); } // src/utils/visitors.ts import { rootNodeVisitor, visit } from "@codama/visitors-core"; import pico5 from "picocolors"; async function getRootNodeVisitors(visitors) { return await Promise.all(visitors.map(getRootNodeVisitor)); } async function getRootNodeVisitor(visitorConfig) { const { item, path: path3 } = visitorConfig; const identifier = getVisitorIdentifier(visitorConfig); const moduleItem = await importModuleItem({ identifier, from: path3, item }); const visitor = await getVisitorFromModuleItem(identifier, moduleItem, visitorConfig); return rootNodeVisitor((root) => { const result = visit(root, visitor); return isRootNode(result) ? result : root; }); } async function getVisitorFromModuleItem(identifier, moduleItem, visitorConfig) { const { args, item, path: path3 } = visitorConfig; if (isRootNodeVisitor(moduleItem)) { return moduleItem; } if (typeof moduleItem === "function") { const result = await promisify(moduleItem(...args)); if (isRootNodeVisitor(result)) { return result; } } throw new CliError(`Invalid visitor. Expected a visitor or a function returning a visitor.`, [ `${pico5.bold("Visitor")}: ${identifier}`, `${pico5.bold("Path")}: ${path3}`, ...item ? [`${pico5.bold("Item")}: ${item}`] : [] ]); } function isRootNodeVisitor(value) { return !!value && typeof value === "object" && "visitRoot" in value; } function getVisitorIdentifier(visitorConfig) { const { index, script } = visitorConfig; let identifier = `visitor #${index}`; identifier += script ? ` in script "${script}"` : ""; return identifier; } // src/commands/init.ts function setInitCommand(program) { program.command("init").argument("[output]", "Optional path used to output the configuration file").option("-d, --default", "Bypass prompts and select all defaults options").option("--force", "Overwrite existing configuration file, if any").option("--js", "Forces the output to be a JavaScript file").option("--gill", "Forces the output to be a gill based JavaScript file").action(doInit); } async function doInit(explicitOutput, options) { const output = getOutputPath(explicitOutput, options); const configFileType = getConfigFileType(output, options); if (!options.force && await canRead(output)) { throw new CliError(`Configuration file already exists.`, [`${pico6.bold("Path")}: ${output}`]); } logBanner(); const result = await getPromptResult(options, configFileType); const isAnchor = await isAnchorIdl(result.idlPath); await installMissingDependencies(`Your configuration requires additional dependencies.`, [ ...isAnchor ? ["@codama/nodes-from-anchor"] : [], ...result.scripts.includes("js") ? ["@codama/renderers-js"] : [], ...result.scripts.includes("rust") ? ["@codama/renderers-rust"] : [] ]); const content = getContentFromPromptResult(result, configFileType); await writeFile(output, content); console.log(); logSuccess(pico6.bold("Configuration file created."), [`${pico6.bold("Path")}: ${output}`]); } function getOutputPath(explicitOutput, options) { if (explicitOutput) { return resolveRelativePath(explicitOutput); } return resolveRelativePath(options.js || options.gill ? "codama.js" : "codama.json"); } async function getPromptResult(options, configFileType) { const defaults = getDefaultPromptResult(); if (options.default) { return defaults; } const hasScript = (script, type = "text") => (_, values) => values.scripts.includes(script) ? type : null; return await prompts2( [ { initial: defaults.idlPath, message: "Where is your IDL located? (Supports Codama and Anchor IDLs).", name: "idlPath", type: "text" }, { choices: [ { selected: true, title: "Generate JavaScript client", value: "js" }, { selected: true, title: "Generate Rust client", value: "rust" } ], instructions: "[space] to toggle / [a] to toggle all / [enter] to submit", message: "Which script preset would you like to use?", name: "scripts", type: "multiselect", onRender() { if (configFileType === "gill") { const value = this.value; const jsChoice = value.find((choice) => choice.value === "js"); jsChoice.description = pico6.yellow("Required with --gill option."); jsChoice.selected = true; } } }, { initial: defaults.jsPath, message: "[js] Where should the JavaScript code be generated?", name: "jsPath", type: hasScript("js") }, { initial: defaults.rustCrate, message: "[rust] Where is the Rust client crate located?", name: "rustCrate", type: hasScript("rust") }, { initial: (prev) => `${prev}/src/generated`, message: "[rust] Where should the Rust code be generated?", name: "rustPath", type: hasScript("rust") } ], PROMPT_OPTIONS ); } function getDefaultPromptResult() { return { idlPath: "program/idl.json", jsPath: "clients/js/src/generated", rustCrate: "clients/rust", rustPath: "clients/rust/src/generated", scripts: ["js", "rust"] }; } function getConfigFileType(output, options) { if (options.gill) return "gill"; else if (options.js) return "js"; return output.endsWith(".js") ? "js" : "json"; } function getContentFromPromptResult(result, configFileType) { switch (configFileType) { case "gill": return getContentForGill(result); case "js": return `export default ` + JSON.stringify(getConfigFromPromptResult(result), null, 4).replace(/"([^"]+)":/g, "$1:").replace(/"([^"]*)"/g, "'$1'"); case "json": default: return JSON.stringify(getConfigFromPromptResult(result), null, 4); } } function getConfigFromPromptResult(result) { const scripts = {}; if (result.scripts.includes("js")) { scripts.js = { from: "@codama/renderers-js", args: [result.jsPath] }; } if (result.scripts.includes("rust")) { scripts.rust = { from: "@codama/renderers-rust", args: [result.rustPath, { crateFolder: result.rustCrate, formatCode: true }] }; } return { idl: result.idlPath, before: [], scripts }; } function getContentForGill(result) { const attributes = [ `idl: "${result.idlPath}"`, `clientJs: "${result.jsPath}"`, ...result.scripts.includes("rust") ? [`clientRust: "${result.rustPath}"`] : [] ]; const attributesString = attributes.map((attr) => ` ${attr}, `).join(""); return `import { createCodamaConfig } from "gill"; export default createCodamaConfig({ ${attributesString}}); `; } async function isAnchorIdl(idlPath) { const resolvedIdlPath = resolveRelativePath(idlPath); if (!await canRead(resolvedIdlPath)) return false; try { const idlContent = await importModuleItem({ identifier: "IDL", from: resolvedIdlPath }); return !isRootNode(idlContent); } catch { return false; } } // src/commands/run.ts import { visit as visit2 } from "@codama/visitors-core"; import pico8 from "picocolors"; // src/config.ts import path2 from "path"; import pico7 from "picocolors"; async function getConfig(options) { const configPath = options.config != null ? path2.resolve(options.config) : await getDefaultConfigPath(); if (!configPath) { logWarning("No configuration file found. Using empty configs. Make sure you provide the `--idl` option."); return [{}, configPath]; } const configFile = await importModuleItem({ identifier: "configuration file", from: configPath }); if (!configFile || typeof configFile !== "object") { throw new CliError(`Invalid configuration file.`, [`${pico7.bold("Path")}: ${configPath}`]); } return [configFile, configPath]; } async function getDefaultConfigPath() { const candidatePaths = ["codama.js", "codama.mjs", "codama.cjs", "codama.json"]; for (const candidatePath of candidatePaths) { const resolvedPath = path2.resolve(process.cwd(), candidatePath); if (await canRead(resolvedPath)) { return resolvedPath; } } return null; } // src/parsedConfig.ts async function getParsedConfigFromCommand(cmd) { return await getParsedConfig(cmd.optsWithGlobals()); } async function getParsedConfig(options) { const [config, configPath] = await getConfig(options); return await parseConfig(config, configPath, options); } async function parseConfig(config, configPath, options) { const idlPath = parseIdlPath(config, configPath, options); const idlContent = await importModuleItem({ identifier: "IDL", from: idlPath }); const rootNode = await getRootNodeFromIdl(idlContent); const scripts = parseScripts(config.scripts ?? {}, configPath); const visitors = (config.before ?? []).map((v, i) => parseVisitorConfig(v, configPath, i, null)); return { configPath, idlContent, idlPath, rootNode, scripts, before: visitors }; } function parseIdlPath(config, configPath, options) { if (options.idl) { return resolveRelativePath(options.idl); } if (config.idl) { return resolveConfigPath(config.idl, configPath); } throw new CliError("No IDL identified. Please provide the `--idl` option or set it in the configuration file."); } function parseScripts(scripts, configPath) { const entryPromises = Object.entries(scripts).map(([name, scriptConfig]) => { const visitors = Array.isArray(scriptConfig) ? scriptConfig : [scriptConfig]; return [name, visitors.map((v, i) => parseVisitorConfig(v, configPath, i, name))]; }); return Object.fromEntries(entryPromises); } function parseVisitorConfig(visitorConfig, configPath, index, script) { const emptyArgs = []; const visitorPath = typeof visitorConfig === "string" ? visitorConfig : visitorConfig.from; const visitorArgs = typeof visitorConfig === "string" ? emptyArgs : visitorConfig.args ?? emptyArgs; const [path3, item] = resolveVisitorPath(visitorPath, configPath); return { args: visitorArgs, index, item, path: path3, script }; } function resolveVisitorPath(visitorPath, configPath) { const [modulePath, itemName] = visitorPath.split("#"); const resolveModulePath = isLocalModulePath(modulePath) ? resolveConfigPath(modulePath, configPath) : modulePath; return [resolveModulePath, itemName]; } // src/commands/run.ts function setRunCommand(program) { program.command("run").argument("[scripts...]", "The scripts to execute").option("-a, --all", "Run all scripts in the configuration file").action(doRun); } async function doRun(explicitScripts, { all }, cmd) { if (all && explicitScripts.length > 0) { logWarning(`CLI arguments "${explicitScripts.join(" ")}" are ignored because the "--all" option is set.`); } const parsedConfig = await getParsedConfigFromCommand(cmd); const scripts = all ? Object.keys(parsedConfig.scripts) : explicitScripts; const plans = await getPlans(parsedConfig, scripts); runPlans(plans, parsedConfig.rootNode); } async function getPlans(parsedConfig, scripts) { const plans = []; if (scripts.length === 0 && parsedConfig.before.length === 0) { throw new CliError("There are no scripts or before visitors to run."); } checkMissingScripts(parsedConfig, scripts); await checkMissingDependencies(parsedConfig, scripts); if (parsedConfig.before.length > 0) { plans.push({ script: null, visitors: await getRootNodeVisitors(parsedConfig.before) }); } for (const script of scripts) { plans.push({ script, visitors: await getRootNodeVisitors(parsedConfig.scripts[script]) }); } return plans; } function runPlans(plans, rootNode) { for (const plan of plans) { const result = runPlan(plan, rootNode); if (!plan.script) { rootNode = result; } } } function runPlan(plan, rootNode) { const visitorLength = plan.visitors.length; const visitorPluralized = visitorLength === 1 ? "visitor" : "visitors"; const identifier = plan.script ? `script "${plan.script}" with ${visitorLength} ${visitorPluralized}` : `${visitorLength} before ${visitorPluralized}`; logInfo(`Running ${identifier}...`); const newRoot = plan.visitors.reduce(visit2, rootNode); logSuccess(`Executed ${identifier}!`); return newRoot; } function checkMissingScripts(parsedConfig, scripts) { const missingScripts = scripts.filter((script) => !parsedConfig.scripts[script]); if (missingScripts.length === 0) return; const scriptPluralized = missingScripts.length === 1 ? "Script" : "Scripts"; const message = parsedConfig.configPath ? `${scriptPluralized} not found in configuration file.` : `${scriptPluralized} not found because no configuration file was found.`; const items = [ `${pico8.bold(scriptPluralized)}: ${missingScripts.join(", ")}`, ...parsedConfig.configPath ? [`${pico8.bold("Path")}: ${parsedConfig.configPath}`] : [] ]; throw new CliError(message, items); } async function checkMissingDependencies(parsedConfig, scripts) { const dependencies = /* @__PURE__ */ new Set([ ...parsedConfig.before.map((v) => v.path), ...scripts.flatMap((script) => parsedConfig.scripts[script]?.map((v) => v.path) ?? []) ]); const externalDependencies = [...dependencies].filter((dep) => !isLocalModulePath(dep)); const scriptsRequirePluralized = scripts.length === 1 ? "script requires" : "scripts require"; const installed = await installMissingDependencies( `Your ${scriptsRequirePluralized} additional dependencies.`, externalDependencies ); if (!installed) { throw new CliError("Cannot proceed without missing dependencies."); } } // src/commands/convert.ts function setConvertCommand(program) { program.command("convert").argument("<idlPath>", "Path to the input IDL file (e.g., Anchor or supported standard)").argument("<outPath>", "Path for the output Codama IDL file").description("Convert a supported IDL file to Codama IDL format").action(doConvert); } async function doConvert(idlPath, outPath) { const idl = await readJson(idlPath); const node = await getRootNodeFromIdl(idl); await writeFile(outPath, JSON.stringify(node, null, 2)); console.log(`Converted IDL written to ${outPath}`); } // src/programOptions.ts function setProgramOptions(program) { program.option("--debug", "include debugging information, such as stack dump").option("-i, --idl <path>", "The path to the IDL to use.").option("-c, --config <path>", "The path to the Codama configuration file. Defaults to `codama.(js|json)`."); } // src/program.ts async function codama(args, opts) { const program = createProgram({ exitOverride: true, suppressOutput: opts?.suppressOutput }); await runProgram(program, args, { from: "user" }); } async function runProgram(program, argv, parseOptions) { try { await program.parseAsync(argv, parseOptions); } catch (err) { const error = err; if (program.opts().debug) { logDebug(`${error.stack}`); } logError(pico9.bold(error.message), error.items ?? []); process.exitCode = 1; } } function createProgram(internalOptions) { const program = createCommand().version("1.4.4").allowExcessArguments(false).configureHelp({ showGlobalOptions: true, sortOptions: true, sortSubcommands: true }); setProgramOptions(program); setInitCommand(program); setRunCommand(program); setConvertCommand(program); if (internalOptions?.exitOverride) { program.exitOverride(); } if (internalOptions?.suppressOutput) { program.configureOutput({ writeErr: () => { }, writeOut: () => { } }); } return program; } export { codama, createProgram, runProgram }; //# sourceMappingURL=index.node.mjs.map