Files
beekeeper-studio/apps/studio/src-commercial/backend/plugin-system/modules/BundledPluginModule.ts
Mohammad Azmi fde987f77c refactor
2026-02-17 17:44:14 +07:00

104 lines
3.1 KiB
TypeScript

import path from "path";
import fs from "fs";
import rawLog from "@bksLogger";
import platformInfo from "@/common/platform_info";
import globals from "@/common/globals";
import { Module, type ModuleOptions } from "@/services/plugin/Module";
const log = rawLog.scope("BundledPluginModule");
/**
* A plugin system module that copies bundled plugins from node_modules (dev)
* or extraResources (prod) to the user's plugins directory on first launch.
*
* @example
*
* ```ts
* const manager = new PluginManager();
* manager.registerModule(BundledPluginModule);
* await manager.initialize();
* ```
**/
export class BundledPluginModule extends Module {
constructor(options: ModuleOptions) {
super(options);
this.hook("before-initialize", this.installBundledPlugins);
}
private async installBundledPlugins() {
for (const plugin of globals.plugins.ensureInstalled) {
try {
await this.ensureInstall(plugin);
} catch (e) {
log.error(`Error installing plugin ${plugin}`, e);
}
}
}
/**
* Install a plugin from a given path if it is not already installed.
*
* @param pkg Package name (e.g., "@beekeeperstudio/bks-ai-shell")
*/
private async ensureInstall(pkg: string) {
log.info(`Resolving ${pkg}`);
const pluginPath = BundledPluginModule.resolve(pkg);
const pluginsDirectory = this.manager.fileManager.options.pluginsDirectory;
if (!fs.existsSync(pluginsDirectory)) {
fs.mkdirSync(pluginsDirectory, { recursive: true });
}
const manifestPath = path.join(pluginPath, "manifest.json");
if (!fs.existsSync(manifestPath)) {
throw new Error(`Plugin not found at ${pluginPath}`);
}
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
const pluginId = manifest.id;
// Have installed before?
if (this.manager.pluginSettings[pluginId]) {
log.info(
`Plugin "${pluginId}" is previously installed, skipping.`
);
return;
}
const dst = path.join(pluginsDirectory, pluginId);
if (fs.existsSync(dst)) {
// This must be set, otherwise the plugin will be copied again
await this.manager.setPluginAutoUpdateEnabled(pluginId, true);
log.info(
`Plugin "${pluginId}" installation directory already exists on disk.`
);
return;
}
log.info(`Installing plugin ${pluginId}`);
fs.cpSync(pluginPath, dst, { recursive: true });
// This must be set, otherwise the plugin will be copied again
await this.manager.setPluginAutoUpdateEnabled(pluginId, true);
}
/**
* Resolve a bundled plugin path.
*
* @param pkg Package name (e.g., "@beekeeperstudio/bks-ai-shell")
* @returns The resolved path to the plugin directory
*/
static resolve(pkg: string): string {
if (platformInfo.env.production) {
// Production: use extraResources location
return path.join(platformInfo.resourcesPath, "bundled_plugins", pkg);
}
// Development: resolve from node_modules
const manifestPath = require.resolve(`${pkg}/manifest.json`);
return path.dirname(manifestPath);
}
}