UNPKG

@codama/cli

Version:

A CLI for setting up and managing Codama IDLs

801 lines (776 loc) 30.8 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/cli/index.ts var cli_exports = {}; __export(cli_exports, { run: () => run }); module.exports = __toCommonJS(cli_exports); // src/program.ts var import_commander = require("commander"); var import_picocolors9 = __toESM(require("picocolors")); // src/commands/init.ts var import_picocolors6 = __toESM(require("picocolors")); var import_prompts3 = __toESM(require("prompts")); // src/utils/childCommands.ts var import_child_process = require("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 = (0, import_child_process.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 var import_node_constants = require("constants"); var import_node_fs = __toESM(require("fs")); var import_node_path = __toESM(require("path")); function resolveRelativePath(childPath, relativeDirectory = null) { return import_node_path.default.resolve(relativeDirectory ?? process.cwd(), childPath); } function resolveConfigPath(childPath, configPath) { const configDir = configPath ? import_node_path.default.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 import_node_fs.default.promises.readFile(filePath, "utf8"); } async function writeFile(filePath, content) { const directory = import_node_path.default.dirname(filePath); if (!await canWrite(directory)) { await import_node_fs.default.promises.mkdir(directory, { recursive: true }); } await import_node_fs.default.promises.writeFile(filePath, content); } async function canRead(p) { try { await import_node_fs.default.promises.access(p, import_node_constants.R_OK); return true; } catch { return false; } } async function canWrite(p) { try { await import_node_fs.default.promises.access(p, import_node_constants.W_OK); return true; } catch { return false; } } // src/utils/import.ts var import_node_module = require("module"); var import_picocolors = __toESM(require("picocolors")); async function importModuleItem(options) { const module2 = await importModule(options); const moduleItem = pickModuleItem(module2, options.item); if (moduleItem === void 0) { const items = getErrorItems(options); throw new CliError(`Failed to load ${options.identifier ?? "module"}.`, items); } return moduleItem; } function pickModuleItem(module2, item = "default") { if (item === "default") { return module2.default?.default ?? module2.default ?? module2; } return module2[item] ?? module2.default?.[item] ?? module2.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 = (0, import_node_module.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 = [`${import_picocolors.default.bold("Module")}: ${from}`]; if (item) { items.push(`${import_picocolors.default.bold("Item")}: ${item}`); } const hasCause = !!cause && typeof cause === "object" && "message" in cause && typeof cause.message === "string"; if (hasCause) { items.push(`${import_picocolors.default.bold("Caused by")}: ${cause.message}`); } return items; } // src/utils/logs.ts var import_picocolors2 = __toESM(require("picocolors")); function getLogLevelInfo(logLevel) { const identity = (text) => text; const infos = { success: ["\u2714", import_picocolors2.default.green, import_picocolors2.default.green], info: ["\u2192", import_picocolors2.default.blueBright, identity], warning: ["\u25B2", import_picocolors2.default.yellow, import_picocolors2.default.yellow], error: ["\u2716", import_picocolors2.default.red, import_picocolors2.default.red], debug: ["\u2731", import_picocolors2.default.magenta, import_picocolors2.default.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(getBanner()); } function getBanner() { const textBanner = "Welcome to Codama!"; const gradientBanner = import_picocolors2.default.bold(`\x1B[38;2;231;171;97m${textBanner}\x1B[0m`); return process.stdout.isTTY && process.stdout.getColorDepth() > 8 ? gradientBanner : textBanner; } // src/utils/packageInstall.ts var import_picocolors4 = __toESM(require("picocolors")); var import_prompts = __toESM(require("prompts")); // src/utils/packageJson.ts var import_picocolors3 = __toESM(require("picocolors")); var packageJson; async function getPackageJson() { if (!packageJson) { const packageJsonPath = resolveRelativePath("package.json"); if (!await canRead(packageJsonPath)) { throw new CliError("Cannot read package.json.", [`${import_picocolors3.default.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 = import_picocolors4.default.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 (0, import_prompts.default)( { 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 var import_visitors_core = require("@codama/visitors-core"); var import_picocolors5 = __toESM(require("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 (0, import_visitors_core.rootNodeVisitor)((root) => { const result = (0, import_visitors_core.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.`, [ `${import_picocolors5.default.bold("Visitor")}: ${identifier}`, `${import_picocolors5.default.bold("Path")}: ${path3}`, ...item ? [`${import_picocolors5.default.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(program2) { program2.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.`, [`${import_picocolors6.default.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(import_picocolors6.default.bold("Configuration file created."), [`${import_picocolors6.default.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 (0, import_prompts3.default)( [ { 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 = import_picocolors6.default.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 var import_visitors_core2 = require("@codama/visitors-core"); var import_picocolors8 = __toESM(require("picocolors")); // src/config.ts var import_node_path2 = __toESM(require("path")); var import_picocolors7 = __toESM(require("picocolors")); async function getConfig(options) { const configPath = options.config != null ? import_node_path2.default.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.`, [`${import_picocolors7.default.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 = import_node_path2.default.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(program2) { program2.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(import_visitors_core2.visit, 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 = [ `${import_picocolors8.default.bold(scriptPluralized)}: ${missingScripts.join(", ")}`, ...parsedConfig.configPath ? [`${import_picocolors8.default.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/programOptions.ts function setProgramOptions(program2) { program2.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 runProgram(program2, argv, parseOptions) { try { await program2.parseAsync(argv, parseOptions); } catch (err) { const error = err; if (program2.opts().debug) { logDebug(`${error.stack}`); } logError(import_picocolors9.default.bold(error.message), error.items ?? []); process.exitCode = 1; } } function createProgram(internalOptions) { const program2 = (0, import_commander.createCommand)().version("1.3.1").allowExcessArguments(false).configureHelp({ showGlobalOptions: true, sortOptions: true, sortSubcommands: true }); setProgramOptions(program2); setInitCommand(program2); setRunCommand(program2); if (internalOptions?.exitOverride) { program2.exitOverride(); } if (internalOptions?.suppressOutput) { program2.configureOutput({ writeErr: () => { }, writeOut: () => { } }); } return program2; } // src/cli/index.ts var program = createProgram(); async function run(argv) { await runProgram(program, argv); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { run }); //# sourceMappingURL=cli.cjs.map