jan/electron/handlers/plugin.ts
2023-11-14 12:08:40 +07:00

119 lines
3.9 KiB
TypeScript

import { app, ipcMain } from "electron";
import { readdirSync, rmdir, writeFileSync } from "fs";
import { ModuleManager } from "../managers/module";
import { join, extname } from "path";
import { PluginManager } from "../managers/plugin";
import { WindowManager } from "../managers/window";
import { manifest, tarball } from "pacote";
export function handlePluginIPCs() {
/**
* Invokes a function from a plugin module in main node process.
* @param _event - The IPC event object.
* @param modulePath - The path to the plugin module.
* @param method - The name of the function to invoke.
* @param args - The arguments to pass to the function.
* @returns The result of the invoked function.
*/
ipcMain.handle(
"invokePluginFunc",
async (_event, modulePath, method, ...args) => {
const module = require(
/* webpackIgnore: true */ join(
app.getPath("userData"),
"plugins",
modulePath
)
);
ModuleManager.instance.setModule(modulePath, module);
if (typeof module[method] === "function") {
return module[method](...args);
} else {
console.log(module[method]);
console.error(`Function "${method}" does not exist in the module.`);
}
}
);
/**
* Returns the paths of the base plugins.
* @param _event - The IPC event object.
* @returns An array of paths to the base plugins.
*/
ipcMain.handle("basePlugins", async (_event) => {
const basePluginPath = join(
__dirname,
"../",
app.isPackaged
? "../../app.asar.unpacked/core/pre-install"
: "../core/pre-install"
);
return readdirSync(basePluginPath)
.filter((file) => extname(file) === ".tgz")
.map((file) => join(basePluginPath, file));
});
/**
* Returns the path to the user's plugin directory.
* @param _event - The IPC event object.
* @returns The path to the user's plugin directory.
*/
ipcMain.handle("pluginPath", async (_event) => {
return join(app.getPath("userData"), "plugins");
});
/**
* Deletes the `plugins` directory in the user data path and disposes of required modules.
* If the app is packaged, the function relaunches the app and exits.
* Otherwise, the function deletes the cached modules and sets up the plugins and reloads the main window.
* @param _event - The IPC event object.
* @param url - The URL to reload.
*/
ipcMain.handle("reloadPlugins", async (_event, url) => {
const userDataPath = app.getPath("userData");
const fullPath = join(userDataPath, "plugins");
rmdir(fullPath, { recursive: true }, function (err) {
if (err) console.log(err);
ModuleManager.instance.clearImportedModules();
// just relaunch if packaged, should launch manually in development mode
if (app.isPackaged) {
app.relaunch();
app.exit();
} else {
for (const modulePath in ModuleManager.instance.requiredModules) {
delete require.cache[
require.resolve(
join(app.getPath("userData"), "plugins", modulePath)
)
];
}
PluginManager.instance.setupPlugins();
WindowManager.instance.currentWindow?.reload();
}
});
});
/**
* Installs a remote plugin by downloading its tarball and writing it to a tgz file.
* @param _event - The IPC event object.
* @param pluginName - The name of the remote plugin to install.
* @returns A Promise that resolves to the path of the installed plugin file.
*/
ipcMain.handle("installRemotePlugin", async (_event, pluginName) => {
const destination = join(
app.getPath("userData"),
pluginName.replace(/^@.*\//, "") + ".tgz"
);
return manifest(pluginName)
.then(async (manifest: any) => {
await tarball(manifest._resolved).then((data: Buffer) => {
writeFileSync(destination, data);
});
})
.then(() => destination);
});
}