inula/packages/inula-cli/src/plugin/Plugin.ts

168 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
import resolve from 'resolve';
import chalk from 'chalk';
import PluginAPI, { IOpts } from './PluginAPI.js';
import { IHook, ICommand } from '../types/types.js';
import Hub from '../core/Hub';
import { loadModule } from '../utils/loadModule.js';
import { Logger } from '../utils/logger.js';
interface pluginManagerOpts {
cwd: string;
hub: Hub;
logger: Logger;
}
interface PluginObj {
path: string;
api: PluginAPI;
}
export interface IPlugin {
id: string;
key: string;
path: string;
apply: Function;
}
export default class Plugin {
cwd: string;
builtInPlugins: string[] = [];
userPlugins: string[] = [];
commands: {
[name: string]: ICommand | string;
} = {};
hooksByPluginPath: {
[id: string]: IHook[];
} = {};
hooks: {
[key: string]: IHook[];
} = {};
store: {
[key: string]: any;
} = {};
hub: Hub;
logger: Logger;
registerFunction: Function[] = [];
// 解决调用this[props]时ts提示属性未知
[key: string]: any;
constructor(opts: pluginManagerOpts) {
this.cwd = opts.cwd || process.cwd();
this.hub = opts.hub;
this.logger = opts.logger;
}
getPluginPaths(builtInPlugins: string[], userPlugins: string[] | undefined): string[] {
const paths: string[] = [];
paths.push(...builtInPlugins);
if (userPlugins) {
paths.push(...userPlugins);
}
// 获取所有插件文件的绝对路径
const absPaths: string[] = paths.map(path => {
return resolve.sync(path, {
basedir: this.cwd,
extensions: ['.js', '.ts'],
});
});
return absPaths;
}
setStore(name: string, initialValue: any) {
const store = this.store;
if (this.store[name]) {
return;
}
store[name] = initialValue;
}
async register(builtInPlugins: string[], userPlugins: string[] | undefined) {
const paths = this.getPluginPaths(builtInPlugins, userPlugins);
this.hub.pluginPaths = paths;
const objs: PluginObj[] = paths.map(path => {
const api: PluginAPI = this.createPluginAPI({
path,
manager: this,
logger: this.logger,
});
return {
path,
api,
};
});
for (const obj of objs) {
const module: Function | undefined = await loadModule(obj.path);
if (module) {
try {
module(obj.api);
} catch (err: unknown) {
if (err instanceof Error) {
this.logger.error(chalk.red(err.message));
if (err.stack) {
this.logger.error(err.stack);
}
}
}
}
}
}
// todo 给API换个名字
createPluginAPI(opts: IOpts): PluginAPI {
const pluginAPI = new PluginAPI(opts);
// 为PluginAPI添加代理
// 除了PluginAPI自有的方法之外为开发者提供更丰富的api
return new Proxy(pluginAPI, {
get: (target: PluginAPI, prop: string) => {
if (['userConfig', 'devBuildConfig', 'buildConfig', 'compileMode', 'packageJson', 'cwd'].includes(prop)) {
return typeof this.hub[prop] === 'function'
? this.hub[prop].bind(this.hub)
: this.hub[prop];
}
if (['setStore', 'logger', 'commands'].includes(prop)) {
return typeof this[prop] === 'function'
? this[prop].bind(this)
: this[prop];
}
return target[prop];
},
});
}
initHook() {
Object.keys(this.hooksByPluginPath).forEach(path => {
const hooks = this.hooksByPluginPath[path];
hooks.forEach(hook => {
const { name } = hook;
hook.pluginId = path;
if (!this.hooks[name]) {
this.hooks[name] = [];
}
this.hooks[name].push(hook);
});
});
}
}