mirror of
https://github.com/rubickCenter/rubick
synced 2025-12-18 00:34:19 +08:00
✨ 主界面开发&插件运行容器开发&菜单开发
This commit is contained in:
175
src/core/plugin-handler/index.ts
Normal file
175
src/core/plugin-handler/index.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
import {
|
||||
AdapterHandlerOptions,
|
||||
AdapterInfo,
|
||||
} from "@/core/plugin-handler/types";
|
||||
import fs from "fs-extra";
|
||||
import search, { Result } from "libnpmsearch";
|
||||
import path from "path";
|
||||
import got from "got";
|
||||
|
||||
import spwan from "cross-spawn";
|
||||
|
||||
/**
|
||||
* 系统插件管理器
|
||||
* @class AdapterHandler
|
||||
*/
|
||||
class AdapterHandler {
|
||||
// 插件安装地址
|
||||
public baseDir: string;
|
||||
// 插件源地址
|
||||
readonly registry: string;
|
||||
|
||||
/**
|
||||
* Creates an instance of AdapterHandler.
|
||||
* @param {AdapterHandlerOptions} options
|
||||
* @memberof AdapterHandler
|
||||
*/
|
||||
constructor(options: AdapterHandlerOptions) {
|
||||
// 初始化插件存放
|
||||
if (!fs.existsSync(options.baseDir)) {
|
||||
fs.mkdirsSync(options.baseDir);
|
||||
fs.writeFileSync(
|
||||
`${options.baseDir}/package.json`,
|
||||
'{"dependencies":{}}'
|
||||
);
|
||||
}
|
||||
this.baseDir = options.baseDir;
|
||||
this.registry = options.registry ?? "https://registry.npm.taobao.org";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件信息
|
||||
* @param {string} adapter 插件名称
|
||||
* @param {string} adapterPath 插件指定路径
|
||||
* @memberof PluginHandler
|
||||
*/
|
||||
async getAdapterInfo(
|
||||
adapter: string,
|
||||
adapterPath: string
|
||||
): Promise<AdapterInfo> {
|
||||
let adapterInfo: AdapterInfo;
|
||||
const infoPath =
|
||||
adapterPath ||
|
||||
path.resolve(this.baseDir, "node_modules", adapter, "plugin.json");
|
||||
// 从本地获取
|
||||
if (await fs.pathExists(infoPath)) {
|
||||
adapterInfo = JSON.parse(
|
||||
fs.readFileSync(infoPath, "utf-8")
|
||||
) as AdapterInfo;
|
||||
} else {
|
||||
// 本地没有从远程获取
|
||||
const resp = await got.get(
|
||||
`https://cdn.jsdelivr.net/npm/${adapter}/plugin.json`
|
||||
);
|
||||
// Todo 校验合法性
|
||||
adapterInfo = JSON.parse(resp.body) as AdapterInfo;
|
||||
}
|
||||
return adapterInfo;
|
||||
}
|
||||
|
||||
// 安装并启动插件
|
||||
async install(adapters: Array<string>, opts?: any) {
|
||||
const installCmd = opts.isDev ? "link" : "install";
|
||||
// 安装
|
||||
await this.execCommand(installCmd, adapters);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 npm 搜索插件
|
||||
* 传入 streamFunc 可以流式处理
|
||||
* @param {string} adapter 插件名称
|
||||
* @param {(data: Result) => void} [streamFunc] 流式处理钩子
|
||||
* @memberof AdapterHandler
|
||||
*/
|
||||
async search(adapter: string, streamFunc?: (data: Result) => void) {
|
||||
return await new Promise<Result[]>((resolve, reject) => {
|
||||
const result: Result[] = [];
|
||||
const stream = search.stream(adapter);
|
||||
stream.on("data", (data: Result) => {
|
||||
result.push(data);
|
||||
if (streamFunc !== undefined) streamFunc(data);
|
||||
});
|
||||
stream.on("end", () => {
|
||||
resolve(result);
|
||||
});
|
||||
stream.on("error", (e: any) => {
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定插件
|
||||
* @param {...string[]} adapters 插件名称
|
||||
* @memberof AdapterHandler
|
||||
*/
|
||||
async update(...adapters: string[]) {
|
||||
await this.execCommand("update", adapters);
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载指定插件
|
||||
* @param {...string[]} adapters 插件名称
|
||||
* @memberof AdapterHandler
|
||||
*/
|
||||
async uninstall(adapters: string[]) {
|
||||
// 卸载插件
|
||||
await this.execCommand("uninstall", adapters);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出所有已安装插件
|
||||
* @memberof AdapterHandler
|
||||
*/
|
||||
async list() {
|
||||
const installInfo = JSON.parse(
|
||||
await fs.readFile(`${this.baseDir}/package.json`, "utf-8")
|
||||
);
|
||||
const adapters: string[] = [];
|
||||
for (const adapter in installInfo.dependencies) {
|
||||
adapters.push(adapter);
|
||||
}
|
||||
return adapters;
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行包管理器
|
||||
* @memberof AdapterHandler
|
||||
*/
|
||||
private async execCommand(cmd: string, modules: string[]): Promise<string> {
|
||||
return new Promise((resolve: any, reject: any) => {
|
||||
let args: string[] = [cmd]
|
||||
.concat(modules)
|
||||
.concat("--color=always")
|
||||
.concat("--save");
|
||||
if (cmd !== "uninstall")
|
||||
args = args.concat(`--registry=${this.registry}`);
|
||||
const npm = spwan("npm", args, {
|
||||
cwd: this.baseDir,
|
||||
});
|
||||
|
||||
let output = "";
|
||||
npm.stdout
|
||||
.on("data", (data: string) => {
|
||||
output += data; // 获取输出日志
|
||||
})
|
||||
.pipe(process.stdout);
|
||||
|
||||
npm.stderr
|
||||
.on("data", (data: string) => {
|
||||
output += data; // 获取报错日志
|
||||
})
|
||||
.pipe(process.stderr);
|
||||
|
||||
npm.on("close", (code: number) => {
|
||||
if (!code) {
|
||||
resolve({ code: 0, data: output }); // 如果没有报错就输出正常日志
|
||||
} else {
|
||||
reject({ code: code, data: output }); // 如果报错就输出报错日志
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default AdapterHandler;
|
||||
Reference in New Issue
Block a user