@codama/cli
Version:
A CLI for setting up and managing Codama IDLs
801 lines (776 loc) • 30.8 kB
JavaScript
;
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