Skip to content

Commit

Permalink
✨ Feature: add local plugin install/uninstall/update support & imporv…
Browse files Browse the repository at this point in the history
…e plugin name handler
  • Loading branch information
Molunerfinn committed Jan 24, 2021
1 parent 5a6d638 commit f8ec464
Show file tree
Hide file tree
Showing 10 changed files with 326 additions and 47 deletions.
5 changes: 3 additions & 2 deletions src/lib/Logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import {
ILogArgvTypeWithError,
IConfig,
Undefinable,
ILogColor
ILogColor,
ILogger
} from '../types'

class Logger {
class Logger implements ILogger {
private readonly level = {
[ILogType.success]: 'green',
[ILogType.info]: 'blue',
Expand Down
159 changes: 124 additions & 35 deletions src/lib/PluginHandler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import PicGo from '../core/PicGo'
import spawn from 'cross-spawn'
import { IResult, IProcessEnv, Undefinable } from '../types'
import { IBuildInEvent } from 'src/utils/enum'
import { IResult, IProcessEnv, Undefinable, IPluginProcessResult } from '../types'
import { IBuildInEvent } from '../utils/enum'
import { getProcessPluginName, getNormalPluginName } from '../utils/common'

class PluginHandler {
// Thanks to feflow -> https://github.com/feflow/feflow/blob/master/lib/internal/install/plugin.js
Expand All @@ -11,41 +12,89 @@ class PluginHandler {
}

async install (plugins: string[], proxy: string = '', env?: IProcessEnv): Promise<void> {
plugins = plugins.map((item: string) => 'picgo-plugin-' + item)
const result = await this.execCommand('install', plugins, this.ctx.baseDir, proxy, env)
if (!result.code) {
plugins.forEach((plugin: string) => {
this.ctx.pluginLoader.registerPlugin(plugin)
const installedPlugins: string[] = []
const processPlugins = plugins
.map((item: string) => handlePluginNameProcess(this.ctx, item))
.filter((item) => {
// detect if has already installed
// or will cause error
if (this.ctx.pluginLoader.hasPlugin(item.pkgName)) {
installedPlugins.push(item.pkgName)
this.ctx.log.success(`PicGo has already installed ${item.pkgName}`)
return false
}
// if something wrong, filter it out
if (!item.success) {
return false
}
return true
})
this.ctx.log.success('插件安装成功')
this.ctx.emit('installSuccess', {
title: '插件安装成功',
body: plugins
})
} else {
const err = `插件安装失败,失败码为${result.code},错误日志为${result.data}`
const fullNameList = processPlugins.map(item => item.fullName)
const pkgNameList = processPlugins.map(item => item.pkgName)
if (fullNameList.length > 0) {
// install plugins must use fullNameList:
// 1. install remote pacage
// 2. install local pacage
const result = await this.execCommand('install', fullNameList, this.ctx.baseDir, proxy, env)
if (!result.code) {
pkgNameList.forEach((pluginName: string) => {
this.ctx.pluginLoader.registerPlugin(pluginName)
})
this.ctx.log.success('插件安装成功')
this.ctx.emit('installSuccess', {
title: '插件安装成功',
body: [...pkgNameList, ...installedPlugins]
})
} else {
const err = `插件安装失败,失败码为${result.code},错误日志为${result.data}`
this.ctx.log.error(err)
this.ctx.emit('installFailed', {
title: '插件安装失败',
body: err
})
}
} else if (installedPlugins.length === 0) {
const err = '插件安装失败,请输入合法插件名或合法安装路径'
this.ctx.log.error(err)
this.ctx.emit('installFailed', {
title: '插件安装失败',
body: err
})
} else {
this.ctx.log.success('插件安装成功')
this.ctx.emit('installSuccess', {
title: '插件安装成功',
body: [...pkgNameList, ...installedPlugins]
})
}
}

async uninstall (plugins: string[]): Promise<void> {
plugins = plugins.map((item: string) => 'picgo-plugin-' + item)
const result = await this.execCommand('uninstall', plugins, this.ctx.baseDir)
if (!result.code) {
plugins.forEach((plugin: string) => {
this.ctx.pluginLoader.unregisterPlugin(plugin)
})
this.ctx.log.success('插件卸载成功')
this.ctx.emit('uninstallSuccess', {
title: '插件卸载成功',
body: plugins
})
const processPlugins = plugins.map((item: string) => handlePluginNameProcess(this.ctx, item)).filter(item => item.success)
const pkgNameList = processPlugins.map(item => item.pkgName)
if (pkgNameList.length > 0) {
// uninstall plugins must use pkgNameList:
// npm uninstall will use the package.json's name
const result = await this.execCommand('uninstall', pkgNameList, this.ctx.baseDir)
if (!result.code) {
pkgNameList.forEach((pluginName: string) => {
this.ctx.pluginLoader.unregisterPlugin(pluginName)
})
this.ctx.log.success('插件卸载成功')
this.ctx.emit('uninstallSuccess', {
title: '插件卸载成功',
body: pkgNameList
})
} else {
const err = `插件卸载失败,失败码为${result.code},错误日志为${result.data}`
this.ctx.log.error(err)
this.ctx.emit('uninstallFailed', {
title: '插件卸载失败',
body: err
})
}
} else {
const err = `插件卸载失败,失败码为${result.code},错误日志为${result.data}`
const err = '插件卸载失败,请输入合法插件名'
this.ctx.log.error(err)
this.ctx.emit('uninstallFailed', {
title: '插件卸载失败',
Expand All @@ -55,16 +104,28 @@ class PluginHandler {
}

async update (plugins: string[], proxy: string = '', env?: IProcessEnv): Promise<void> {
plugins = plugins.map((item: string) => 'picgo-plugin-' + item)
const result = await this.execCommand('update', plugins, this.ctx.baseDir, proxy, env)
if (!result.code) {
this.ctx.log.success('插件更新成功')
this.ctx.emit('updateSuccess', {
title: '插件更新成功',
body: plugins
})
const processPlugins = plugins.map((item: string) => handlePluginNameProcess(this.ctx, item)).filter(item => item.success)
const pkgNameList = processPlugins.map(item => item.pkgName)
if (pkgNameList.length > 0) {
// update plugins must use pkgNameList:
// npm update will use the package.json's name
const result = await this.execCommand('update', pkgNameList, this.ctx.baseDir, proxy, env)
if (!result.code) {
this.ctx.log.success('插件更新成功')
this.ctx.emit('updateSuccess', {
title: '插件更新成功',
body: pkgNameList
})
} else {
const err = `插件更新失败,失败码为${result.code},错误日志为 \n ${result.data}`
this.ctx.log.error(err)
this.ctx.emit('updateFailed', {
title: '插件更新失败',
body: err
})
}
} else {
const err = `插件更新失败,失败码为${result.code},错误日志为 \n ${result.data}`
const err = '插件更新失败,请输入合法插件名'
this.ctx.log.error(err)
this.ctx.emit('updateFailed', {
title: '插件更新失败',
Expand Down Expand Up @@ -116,4 +177,32 @@ class PluginHandler {
}
}

/**
* transform the input plugin name or path string to valid result
* @param ctx
* @param nameOrPath
*/
const handlePluginNameProcess = (ctx: PicGo, nameOrPath: string): IPluginProcessResult => {
const res = {
success: false,
fullName: '',
pkgName: ''
}
const result = getProcessPluginName(nameOrPath, ctx.log)
if (!result) {
return res
}
// first get result then do this process
// or some error will log twice
const pkgName = getNormalPluginName(result, ctx.log)
if (!pkgName) {
return res
}
return {
success: true,
fullName: result,
pkgName
}
}

export default PluginHandler
4 changes: 4 additions & 0 deletions src/lib/PluginLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ class PluginLoader {
return this.list
}

hasPlugin (name: string): boolean {
return this.fullList.has(name)
}

/**
* Get the full list of plugins, whether it is enabled or not
*/
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/uploader/smms.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import PicGo from '../../core/PicGo'
import { IPluginConfig, ISmmsConfig } from '../../types'
import { Options } from 'request-promise-native'
import { IBuildInEvent } from 'src/utils/enum'
import { IBuildInEvent } from '../../utils/enum'

const postOptions = (fileName: string, image: Buffer, apiToken: string): Options => {
return {
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/uploader/tcyun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import crypto from 'crypto'
import mime from 'mime-types'
import { IPluginConfig, ITcyunConfig } from '../../types'
import { Options } from 'request-promise-native'
import { IBuildInEvent } from 'src/utils/enum'
import { IBuildInEvent } from '../../utils/enum'

// generate COS signature string

Expand Down
2 changes: 1 addition & 1 deletion src/plugins/uploader/upyun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { IPluginConfig, IUpyunConfig } from '../../types'
import crypto from 'crypto'
import MD5 from 'md5'
import { Options } from 'request-promise-native'
import { IBuildInEvent } from 'src/utils/enum'
import { IBuildInEvent } from '../../utils/enum'

// generate COS signature string
const generateSignature = (options: IUpyunConfig, fileName: string): string => {
Expand Down
24 changes: 22 additions & 2 deletions src/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import PicGo from '../core/PicGo'
import LifecyclePlugins from '../lib/LifecyclePlugins'
import Logger from '../lib/Logger'
import Commander from '../lib/Commander'
import PluginHandler from '../lib/PluginHandler'
import PluginLoader from '../lib/PluginLoader'
Expand All @@ -9,7 +8,7 @@ import Request from '../lib/Request'
interface IPicGo extends NodeJS.EventEmitter {
configPath: string
baseDir: string
log: Logger
log: ILogger
cmd: Commander
output: IImgInfo[]
input: any[]
Expand Down Expand Up @@ -198,6 +197,20 @@ interface IPlugin {
[propName: string]: any
}

type IPluginNameType = 'simple' | 'scope' | 'normal' | 'unknown'

interface IPluginProcessResult {
success: boolean
/**
* the package.json's name filed
*/
pkgName: string
/**
* the plugin name or the fs absolute path
*/
fullName: string
}

/**
* for picgo npm plugins
*/
Expand Down Expand Up @@ -282,3 +295,10 @@ type ILogArgvTypeWithError = ILogArgvType | Error

type Nullable<T> = T | null
type Undefinable<T> = T | undefined

interface ILogger {
success: (...msg: ILogArgvType[]) => void
info: (...msg: ILogArgvType[]) => void
error: (...msg: ILogArgvType[]) => void
warn: (...msg: ILogArgvType[]) => void
}
Loading

0 comments on commit f8ec464

Please sign in to comment.