@api-buddy/plugin-utils
Version:
Shared utilities for API Buddy plugins
563 lines (554 loc) • 14.5 kB
JavaScript
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