Skip to main content

设计插件

底层原理 动态的 import 文件


async function loadJs(path) {
console.log(path);
let { plugin } = await import(path);
console.log(plugin);
console.log(plugin(ctx).uploader);
}

async function requirePlugin(name) {
let home = await homeDir();
const pluginDir = join(home, ".atoolsplugin/node_modules/");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const plugin = require(pluginDir + name)(this.ctx);
console.log(plugin);
}

设计基类 包含插件属性

this.configPath = configPath  //配置文件
this._pluginLoader = new PluginLoader(this) //加载插件
this.pluginHandler = new PluginHandler(this) //管理插件
this.lifecycle = new Lifecycle(this) //生命周期

//帮助方法
this.helper = {
transformer: new LifecyclePlugins('transformer'),
uploader: new LifecyclePlugins('uploader'),
beforeTransformPlugins: new LifecyclePlugins('beforeTransformPlugins'),
beforeUploadPlugins: new LifecyclePlugins('beforeUploadPlugins'),
afterUploadPlugins: new LifecyclePlugins('afterUploadPlugins')
}


//核心函数
async upload(input) {
if (this.configPath === '') {
return []
}

//核心代码 通过 调用 生命周期函数 在其内部 调用插件中的方法
// upload from path
const { output } = await this.lifecycle.start(input)
return output

}

加载插件

private readonly ctx: IPicGo   //传入基类信息
private list: string[] = [] //全部插件
private readonly pluginMap: Map<string, IPicGoPluginInterface> = new Map() //可通过名称获取插件

// 没有 就导入
async getPlugin(name) {
if (this.pluginMap.has(name)) {
return this.pluginMap.get(name)
}
let re= await exists('.atoolsplugin/'+name, { dir: BaseDirectory.Home });
if (re){
let path = join(this.ctx.baseDir,'.atoolsplugin/',name,'index.js')
let { plugin } = await import(path);
this.pluginMap.set(name, plugin)
return plugin
}

}

registerPlugin(name, plugin) {

this.fullList.add(name)
try {
// register local plugin
if (!plugin) {
if (this.ctx.getConfig(`picgoPlugins.${name}`) === true || (this.ctx.getConfig(`picgoPlugins.${name}`) === undefined)) {
this.list.push(name)
setCurrentPluginName(name)
// 核心语句 获取到插件后 调用 插件内的注册方法
//将当前上下文传入后 即可 在插件内部 设置上下文的信息了
this.getPlugin(name).register(this.ctx)
const plugin = `picgoPlugins[${name}]`
this.ctx.saveConfig(
{
[plugin]: true
}
)
}
} else {
// register provided plugin
// && won't write config to files
this.list.push(name)
setCurrentPluginName(name)
const pluginInterface = plugin(this.ctx)
this.pluginMap.set(name, pluginInterface)
// 核心语句 获取到插件后 调用 插件内的注册方法
//将当前上下文传入后 即可 在插件内部 设置上下文的信息了
pluginInterface.register(this.ctx)
}
} catch (e) {
this.pluginMap.delete(name)
this.list = this.list.filter((item) => item !== name)
this.fullList.delete(name)
}
}

插件设计

所有插件都是函数 (传入上下文信息)  
插件返回的结构

return {
uploader: 'alist-tool', //插件名
// transformer: 'web-uploader',
config, //配置信息
register //注册插件到上下文中

}



plugin = (ctx) => {


const register = () => {
//核心方法 调用下文定义的 全部插件的 注册方法
ctx.helper.uploader.register('alist-tool', {
handle, // 核心内容 注册 插件的 执行逻辑
name: 'alist up',
config: config
})
ctx.helper.beforeUploadPlugins.register('alist-tool', beforeUploadPlugins)
}
const handle = async function (ctx) {
let userConfig = ctx.getConfig('picBed.alist-tool')
if (!userConfig) {
throw new Error('Can\'t find alist ar uploader config')
}
const url = userConfig.url
const alistPath = userConfig.alistPath

if (!configToken) {
throw new Error('Can\'t find configToken')
}
let error = 0


let imgList = ctx.output //核心输出 传递给上下文的信息
for (let i in imgList) {
let image = imgList[i].buffer
if (!image && imgList[i].base64Image) {
image = Buffer.from(imgList[i].base64Image, 'base64')
}
let fileName = imgList[i].fileName
let extension = fileName.split('.').pop(); // 获取扩展名

try {
var body = await ctx.request(postConfig)
let bodyJson = JSON.parse(body)
ctx.log.info(`body: ${bodyJson.message}`)

} catch (err) {
}

delete imgList[i].base64Image
delete imgList[i].buffer

imgList[i]['imgUrl'] = url + "/d" + alistPath + imgName

}
if (error != 0) {
imgList = []
ctx.emit('notification', {
title: '上传失败',
body: '上传失败'
})
return
}
// showMsg(ctx, '上传成功', imgList[0]['imgUrl'])
}

const beforeUploadPlugins = {
async handle(ctx) {
}
}


//核心配置
const config = ctx => {
let userConfig = ctx.getConfig('picBed.alist-tool')
if (!userConfig) {
userConfig = {}
}
return [
{
name: 'url',
type: 'input',
default: userConfig.url,
required: true,
message: 'API地址',
alias: 'API地址'
}

]
}
return {
uploader: 'alist-tool',
// transformer: 'web-uploader',
config,
register

}
}


export {
plugin
}

全部插件

static currentPlugin: string | null
private readonly list: Map<string, IPlugin>
private readonly pluginIdMap: Map<string, string[]>
private readonly name: string

register(id, plugin) {
if (!id) throw new TypeError('id is required!')
if (typeof plugin.handle !== 'function') throw new TypeError('plugin.handle must be a function!')
if (this.list.has(id)) throw new TypeError(`${this.name} duplicate id: ${id}!`)
this.list.set(id, plugin) //添加的数组中
if (LifecyclePlugins.currentPlugin) {
if (this.pluginIdMap.has(LifecyclePlugins.currentPlugin)) {
this.pluginIdMap.get(LifecyclePlugins.currentPlugin)?.push(id)
} else {
this.pluginIdMap.set(LifecyclePlugins.currentPlugin, [id])
}
}
}

设计生命周期

async start(input) {
const ctx = createContext(this.ctx)
try {
ctx.input = input
ctx.output = []

// lifecycle main
await this.beforeTransform(ctx)
await this.doTransform(ctx)
await this.beforeUpload(ctx)
await this.doUpload(ctx)
await this.afterUpload(ctx)
return ctx
} catch (e) {
ctx.log.error(e)
return ctx
}
}

async beforeUpload(ctx) {
await this.handlePlugins(ctx.helper.lifeCyclePlugins, ctx)
return ctx
}

async handlePlugins(lifeCyclePlugins, ctx) {
const plugins = lifeCyclePlugins.getList()
const pluginNames = lifeCyclePlugins.getIdList()
const lifeCycleName = lifeCyclePlugins.getName()
await Promise.all(plugins.map(async (plugin, index) => {
try {
await plugin.handle(ctx)
} catch (e) {
throw e
}
}))
return ctx
}

async doUpload(ctx) {
let type = ctx.getConfig('picBed.uploader') || ctx.getConfig('picBed.current') || 'smms'
let uploader = ctx.helper.uploader.get(type)
let currentTransformer = type
await uploader?.handle(ctx)
for (const outputImg of ctx.output) {
outputImg.type = type
}
return ctx
}