UNPKG

@api-buddy/plugin-utils

Version:

Shared utilities for API Buddy plugins

563 lines (554 loc) 14.5 kB
var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/plugin-manager.js var SimplePluginManager = class { constructor(options = {}) { this.plugins = /* @__PURE__ */ new Map(); this.defaultLogger = { info: console.log, warn: console.warn, error: console.error, debug: console.debug }; this.options = { pluginsDir: Array.isArray(options.pluginsDir) ? options.pluginsDir : options.pluginsDir ? [options.pluginsDir] : [], nodeModulesDirs: options.nodeModulesDirs || [] }; this.context = { cwd: options.cwd || process.cwd(), logger: { ...this.defaultLogger, ...options.logger || {} }, config: {}, data: /* @__PURE__ */ new Map(), utils: { registerHook: () => { }, unregisterHook: () => { }, callHook: async () => void 0 } }; } /** * Register a plugin */ async registerPlugin(plugin) { if (this.plugins.has(plugin.name)) { throw new Error(`Plugin with name '${plugin.name}' is already registered`); } try { if (typeof plugin.initialize === "function") { await plugin.initialize(this.context); } this.plugins.set(plugin.name, plugin); this.context.logger.info(`Registered plugin: ${plugin.name}@${plugin.version}`); } catch (error) { this.context.logger.error(`Failed to initialize plugin ${plugin.name}:`, error); throw error; } } /** * Get a registered plugin by name */ getPlugin(name) { return this.plugins.get(name); } /** * Get all registered plugins */ getPlugins() { return Array.from(this.plugins.values()); } /** * Check if a plugin is registered */ hasPlugin(name) { return this.plugins.has(name); } /** * Unregister a plugin */ async unregisterPlugin(name) { const plugin = this.plugins.get(name); if (!plugin) return false; try { if (typeof plugin.cleanup === "function") { await plugin.cleanup(this.context); } this.plugins.delete(name); this.context.logger.info(`Unregistered plugin: ${name}`); return true; } catch (error) { this.context.logger.error(`Failed to cleanup plugin ${name}:`, error); return false; } } /** * Load plugins from the specified directories */ async loadPlugins() { this.context.logger.info("Loading plugins..."); } }; // src/cli/index.ts import { Command as Command2 } from "commander"; // src/cli/utils/index.ts var utils_exports = {}; __export(utils_exports, { Spinner: () => Spinner, confirm: () => confirm, copyDir: () => copyDir, copyFile: () => copyFile, detectPackageManager: () => detectPackageManager, ensureDir: () => ensureDir, exists: () => exists, fsUtils: () => fs_default, getDirname: () => getDirname, input: () => input, installDependencies: () => installDependencies, packageUtils: () => package_default, readFile: () => readFile, readPackageJson: () => readPackageJson, runCommand: () => runCommand, select: () => select, ui: () => ui_default, updatePackageJson: () => updatePackageJson, writeFile: () => writeFile }); // src/cli/utils/fs.ts import { promises as fs } from "fs"; import path from "path"; import { fileURLToPath } from "url"; import { promisify } from "util"; import { exec as execCb } from "child_process"; var exec = promisify(execCb); function getDirname(importMetaUrl) { return path.dirname(fileURLToPath(importMetaUrl)); } async function exists(filePath) { try { await fs.access(filePath); return true; } catch { return false; } } async function ensureDir(dirPath) { try { await fs.mkdir(dirPath, { recursive: true }); } catch (error) { if (error.code !== "EEXIST") { throw error; } } } async function readFile(filePath) { return fs.readFile(filePath, "utf-8"); } async function writeFile(filePath, content) { await ensureDir(path.dirname(filePath)); await fs.writeFile(filePath, content, "utf-8"); } async function copyFile(src, dest) { await ensureDir(path.dirname(dest)); await fs.copyFile(src, dest); } async function copyDir(src, dest) { await ensureDir(dest); const entries = await fs.readdir(src, { withFileTypes: true }); for (const entry of entries) { const srcPath = path.join(src, entry.name); const destPath = path.join(dest, entry.name); if (entry.isDirectory()) { await copyDir(srcPath, destPath); } else { await copyFile(srcPath, destPath); } } } async function runCommand(command, options = {}) { return exec(command, { cwd: options.cwd || process.cwd(), ...options.stdio === "inherit" ? { stdio: "inherit" } : {} }); } var fsUtils = { getDirname, exists, ensureDir, readFile, writeFile, copyFile, copyDir, runCommand }; var fs_default = fsUtils; // src/cli/utils/package.ts import path2 from "path"; async function readPackageJson(dir) { const packageJsonPath = path2.join(dir, "package.json"); const content = await fsUtils.readFile(packageJsonPath); return JSON.parse(content); } async function updatePackageJson(dir, updater) { const pkg = await readPackageJson(dir); const updatedPkg = await updater(pkg); const packageJsonPath = path2.join(dir, "package.json"); await fsUtils.writeFile( packageJsonPath, JSON.stringify(updatedPkg, null, 2) + "\n" ); } async function installDependencies(dir, deps, { isDev = false, cwd = process.cwd() } = {}) { if (deps.length === 0) return; const pkgManager = await detectPackageManager(cwd); const args = [ pkgManager === "yarn" ? "add" : "install", ...pkgManager === "npm" ? ["install"] : [], ...isDev ? [pkgManager === "npm" ? "--save-dev" : "-D"] : [], ...deps ].filter(Boolean); await fsUtils.runCommand(`${pkgManager} ${args.join(" ")}`, { cwd }); } async function detectPackageManager(cwd) { if (await fsUtils.exists(path2.join(cwd, "pnpm-lock.yaml"))) { return "pnpm"; } if (await fsUtils.exists(path2.join(cwd, "yarn.lock"))) { return "yarn"; } return "npm"; } var packageUtils = { readPackageJson, updatePackageJson, installDependencies, detectPackageManager }; var package_default = packageUtils; // src/cli/utils/ui.ts import chalk from "chalk"; import inquirer from "inquirer"; var ora; async function getOra() { if (!ora) { ora = (await import("ora")).default; } return ora; } var Spinner = class { spinner = null; isActive = false; async start({ text, color = "blue", spinner = "dots" }) { const oraInstance = await getOra(); if (this.isActive) { this.stop(); } this.spinner = oraInstance({ text, spinner, color }).start(); this.isActive = true; } updateText(text) { if (this.spinner && this.isActive) { this.spinner.text = text; } } succeed(text) { if (this.spinner && this.isActive) { this.spinner.succeed(text); this.isActive = false; } } fail(text) { if (this.spinner && this.isActive) { this.spinner.fail(text); this.isActive = false; } } warn(text) { if (this.spinner && this.isActive) { this.spinner.warn(text); this.isActive = false; } } stop() { if (this.spinner && this.isActive) { this.spinner.stop(); this.isActive = false; } } }; async function confirm(message, defaultValue = false) { const { confirmed } = await inquirer.prompt([ { type: "confirm", name: "confirmed", message, default: defaultValue } ]); return confirmed; } async function select(message, choices, pageSize = 10) { const { result } = await inquirer.prompt([ { type: "list", name: "result", message, choices: choices.map((choice) => ({ name: choice.name, value: choice.value, short: choice.name })), pageSize } ]); return result; } async function input(message, defaultValue, validate) { const { result } = await inquirer.prompt([ { type: "input", name: "result", message, default: defaultValue, validate: validate ? (input2) => { const result2 = validate(input2); if (typeof result2 === "string") { return result2; } return result2 || "Invalid input"; } : void 0 } ]); return result; } var ui = { Spinner, confirm, select, input, colors: { success: chalk.green, error: chalk.red, warning: chalk.yellow, info: chalk.blue, highlight: chalk.cyan } }; var ui_default = ui; // src/cli/commands/base-command.ts import { Command } from "commander"; import chalk2 from "chalk"; import inquirer2 from "inquirer"; var BaseCommand = class { options; context; meta; program; constructor(context) { var _a, _b; this.context = context; this.meta = this.getMeta(); this.options = {}; this.program = new Command(); this.program.name(this.meta.name).description(this.meta.description || ""); this.program.option("-v, --verbose", "Enable verbose logging", false).option("--dry-run", "Perform a dry run without making changes", false); (_a = this.meta.arguments) == null ? void 0 : _a.forEach((arg) => { this.program.argument( arg.required ? `<${arg.name}>` : `[${arg.name}]`, arg.description || "" ); }); (_b = this.meta.options) == null ? void 0 : _b.forEach((opt) => { const flags = opt.defaultValue !== void 0 ? `${opt.flags} [${typeof opt.defaultValue}]` : opt.flags; this.program.option( flags, opt.description || "" ); if (opt.defaultValue !== void 0) { const optionName = this.getOptionName(opt.flags); if (optionName) { this.options[optionName] = opt.defaultValue; } } }); } async execute(args = process.argv) { this.program.parse(args); const options = this.program.opts(); this.setOptions(options); if (options.verbose && this.context.logger) { this.context.logger.level = "debug"; } try { await this.run(this.options); } catch (error) { const err = error; this.context.logger.error(`Error: ${err.message}`); if (options.verbose && err.stack) { this.context.logger.debug(err.stack); } process.exit(1); } } /** * Extracts the option name from command line flags * @param flags - The command line flags (e.g., '-n, --name') * @returns The option name or null if not found */ getOptionName(flags) { const match = flags.match(/--([a-zA-Z0-9-]+)/); return match ? match[1] : null; } setOptions(options) { this.options = { ...this.options, ...options }; } setOptionsFromEnv(prefix = "API_BUDDY_") { const env = process.env; const envOptions = {}; Object.entries(env).forEach(([key, value]) => { if (key.startsWith(prefix) && value !== void 0) { const optionKey = key.substring(prefix.length).toLowerCase().replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()); try { envOptions[optionKey] = value ? JSON.parse(value) : null; } catch { envOptions[optionKey] = value; } } }); this.setOptions(envOptions); } success(message) { this.context.logger.info(chalk2.green(`\u2713 ${message}`)); } log(message) { this.context.logger.info(message); } warn(message) { this.context.logger.warn(chalk2.yellow(`\u26A0 ${message}`)); } async confirm(message, defaultValue = false) { if (process.env.CI) { return defaultValue; } const { confirmed } = await inquirer2.prompt([{ type: "confirm", name: "confirmed", message, default: defaultValue }]); return confirmed; } }; // src/cli/index.ts var PluginCLI = class { program; commandClasses = /* @__PURE__ */ new Map(); context; constructor(options) { var _a; this.program = new Command2(); this.program.name(options.name).version(options.version).description(options.description || ""); this.program.option("--verbose", "Enable verbose logging", false).option("--dry-run", "Perform a dry run without making changes", false); this.context = { cwd: options.cwd || process.cwd(), logger: { info: console.log, warn: console.warn, error: console.error, debug: ((_a = options.logger) == null ? void 0 : _a.debug) || (() => { }) }, config: {}, data: /* @__PURE__ */ new Map(), utils: { registerHook: () => { }, unregisterHook: () => { }, callHook: async () => void 0 } }; } /** * Register a command */ registerCommand(commandClass) { var _a, _b; const command = new commandClass(this.context); const meta = command.getMetadata(); const cmd = this.program.command(meta.name).description(meta.description || ""); (_a = meta.arguments) == null ? void 0 : _a.forEach((arg) => { cmd.argument( arg.required ? `<${arg.name}>` : `[${arg.name}]`, arg.description || "" ); }); (_b = meta.options) == null ? void 0 : _b.forEach((opt) => { cmd.option( opt.flags, opt.description || "", opt.defaultValue ); }); cmd.action(async (...args) => { try { const commandInstance = new commandClass(this.context); await commandInstance.run(process.argv); } catch (error) { console.error("Error executing command:", error); process.exit(1); } }); return this; } /** * Parse and execute the CLI */ async run(argv = process.argv) { await this.program.parseAsync(argv); } }; var cli_default = { PluginCLI, ...utils_exports }; // src/index.ts var defaultExport = { SimplePluginManager, ...cli_default || {} }; var index_default = defaultExport; export { BaseCommand, PluginCLI, SimplePluginManager, Spinner, confirm, copyDir, copyFile, index_default as default, detectPackageManager, ensureDir, exists, fs_default as fsUtils, getDirname, input, installDependencies, package_default as packageUtils, readFile, readPackageJson, runCommand, select, ui_default as ui, updatePackageJson, writeFile }; //# sourceMappingURL=index.js.map