@api-buddy/plugin-utils
Version:
Shared utilities for API Buddy plugins
615 lines (605 loc) • 17.3 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/index.ts
var index_exports = {};
__export(index_exports, {
BaseCommand: () => BaseCommand,
PluginCLI: () => PluginCLI,
SimplePluginManager: () => SimplePluginManager,
Spinner: () => Spinner,
confirm: () => confirm,
copyDir: () => copyDir,
copyFile: () => copyFile,
default: () => index_default,
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
});
module.exports = __toCommonJS(index_exports);
// 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
var import_commander2 = require("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
var import_fs = require("fs");
var import_path = __toESM(require("path"), 1);
var import_url = require("url");
var import_util = require("util");
var import_child_process = require("child_process");
var exec = (0, import_util.promisify)(import_child_process.exec);
function getDirname(importMetaUrl) {
return import_path.default.dirname((0, import_url.fileURLToPath)(importMetaUrl));
}
async function exists(filePath) {
try {
await import_fs.promises.access(filePath);
return true;
} catch {
return false;
}
}
async function ensureDir(dirPath) {
try {
await import_fs.promises.mkdir(dirPath, { recursive: true });
} catch (error) {
if (error.code !== "EEXIST") {
throw error;
}
}
}
async function readFile(filePath) {
return import_fs.promises.readFile(filePath, "utf-8");
}
async function writeFile(filePath, content) {
await ensureDir(import_path.default.dirname(filePath));
await import_fs.promises.writeFile(filePath, content, "utf-8");
}
async function copyFile(src, dest) {
await ensureDir(import_path.default.dirname(dest));
await import_fs.promises.copyFile(src, dest);
}
async function copyDir(src, dest) {
await ensureDir(dest);
const entries = await import_fs.promises.readdir(src, { withFileTypes: true });
for (const entry of entries) {
const srcPath = import_path.default.join(src, entry.name);
const destPath = import_path.default.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
var import_path2 = __toESM(require("path"), 1);
async function readPackageJson(dir) {
const packageJsonPath = import_path2.default.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 = import_path2.default.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(import_path2.default.join(cwd, "pnpm-lock.yaml"))) {
return "pnpm";
}
if (await fsUtils.exists(import_path2.default.join(cwd, "yarn.lock"))) {
return "yarn";
}
return "npm";
}
var packageUtils = {
readPackageJson,
updatePackageJson,
installDependencies,
detectPackageManager
};
var package_default = packageUtils;
// src/cli/utils/ui.ts
var import_chalk = __toESM(require("chalk"), 1);
var import_inquirer = __toESM(require("inquirer"), 1);
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 import_inquirer.default.prompt([
{
type: "confirm",
name: "confirmed",
message,
default: defaultValue
}
]);
return confirmed;
}
async function select(message, choices, pageSize = 10) {
const { result } = await import_inquirer.default.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 import_inquirer.default.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: import_chalk.default.green,
error: import_chalk.default.red,
warning: import_chalk.default.yellow,
info: import_chalk.default.blue,
highlight: import_chalk.default.cyan
}
};
var ui_default = ui;
// src/cli/commands/base-command.ts
var import_commander = require("commander");
var import_chalk2 = __toESM(require("chalk"), 1);
var import_inquirer2 = __toESM(require("inquirer"), 1);
var BaseCommand = class {
options;
context;
meta;
program;
constructor(context) {
var _a, _b;
this.context = context;
this.meta = this.getMeta();
this.options = {};
this.program = new import_commander.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(import_chalk2.default.green(`\u2713 ${message}`));
}
log(message) {
this.context.logger.info(message);
}
warn(message) {
this.context.logger.warn(import_chalk2.default.yellow(`\u26A0 ${message}`));
}
async confirm(message, defaultValue = false) {
if (process.env.CI) {
return defaultValue;
}
const { confirmed } = await import_inquirer2.default.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 import_commander2.Command();
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;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BaseCommand,
PluginCLI,
SimplePluginManager,
Spinner,
confirm,
copyDir,
copyFile,
detectPackageManager,
ensureDir,
exists,
fsUtils,
getDirname,
input,
installDependencies,
packageUtils,
readFile,
readPackageJson,
runCommand,
select,
ui,
updatePackageJson,
writeFile
});
//# sourceMappingURL=index.cjs.map