fix: plugin not unloaded when reloading
Massimo Melina committed
Dec 15, 2022 at 14:15 UTC
64d2b6135990f63de94bf8848e261501dfa8e81e
2 files changed
+27
-23
src/api.plugins.ts
+3
-2
@@ -29,8 +29,9 @@ const apis: ApiHandlers = {
29
})
30
31
function serialize(p: Readonly<Plugin> | AvailablePlugin) {
32
- return _.defaults('getData' in p ? Object.assign(_.pick(p, ['id','started']), p.getData()) : p,
33
- { started: null, badApi: null }) // nulls should be used to be sure to overwrite previous values,
32
+ const o = 'getData' in p ? Object.assign(_.pick(p, ['id','started']), p.getData())
33
+ : { ...p } // _.defaults mutates object, and we don't want that
34
+ return _.defaults(o, { started: null, badApi: null }) // nulls should be used to be sure to overwrite previous values,
35
}
36
},
37
src/plugins.ts
+24
-21
@@ -3,7 +3,7 @@
3
import glob from 'fast-glob'
4
import { watchLoad } from './watchLoad'
5
import _ from 'lodash'
6
-import pathLib, { join } from 'path'
6
+import pathLib from 'path'
7
import { API_VERSION, APP_PATH, COMPATIBLE_API_VERSION, PLUGINS_PUB_URI } from './const'
8
import * as Const from './const'
9
import Koa from 'koa'
@@ -57,7 +57,8 @@ export function setPluginConfig(id: string, changes: Dict) {
57
}
58
59
export function getPluginInfo(id: string) {
60
- return plugins[id]?.getData() ?? availablePlugins[id]
60
+ const running = plugins[id]?.getData()
61
+ return running && Object.assign(running, {id}) || availablePlugins[id]
62
}
63
64
export function mapPlugins<T>(cb:(plugin:Readonly<Plugin>, pluginName:string)=> T) {
@@ -116,16 +117,8 @@ export class Plugin {
117
118
constructor(readonly id:string, private readonly data:any, private unwatch:()=>void){
119
if (!data) throw 'invalid data'
119
- // if a previous instance is present, we are going to overwrite it, but first call its unload callback
120
- const old = plugins[id]
121
- try { old?.data?.unload?.() } // we don't want all the effects of the Plugin.unload
122
- catch(e){
123
- console.debug('error unloading plugin', id, String(e))
124
- }
125
- // track this
126
- const wasStopped = availablePlugins[id]
127
- if (wasStopped)
128
- delete availablePlugins[id]
120
+ if (plugins[id])
121
+ throw "unload first: " + id
122
plugins[id] = this
123
124
this.data = data = { ...data } // clone to make object modifiable. Objects coming from import are not.
@@ -139,7 +132,6 @@ export class Plugin {
132
console.warn('invalid', k)
133
}
134
}
142
- events.emit(old || wasStopped ? 'pluginStarted' : 'pluginInstalled', this)
135
}
136
get middleware(): undefined | PluginMiddleware {
137
return this.data?.middleware
@@ -158,14 +150,17 @@ export class Plugin {
150
return { ...this.data }
151
}
152
161
- async unload() {
153
+ async unload(reloading=false) {
154
const { id } = this
163
- console.log('unloading plugin', id)
164
- try { await this.data?.unload?.() }
155
+ try {
156
+ await this.data?.unload?.()
157
+ console.log('unloaded plugin', id)
158
+ }
159
catch(e) {
166
- console.debug('error unloading plugin', id, String(e))
160
+ console.log('error unloading plugin', id, String(e))
161
}
162
delete plugins[id]
163
+ if (reloading) return
164
this.unwatch()
165
if (availablePlugins[id])
166
events.emit('pluginStopped', availablePlugins[id])
@@ -227,8 +222,8 @@ export async function rescan() {
222
const module = pathLib.resolve(f)
223
const { unwatch } = watchLoad(f, async () => {
224
try {
230
- const reloading = plugins[id]
231
- console.log(reloading ? "reloading plugin" : "loading plugin", id)
225
+ const alreadyRunning = plugins[id]
226
+ console.log(alreadyRunning ? "reloading plugin" : "loading plugin", id)
227
const { init, ...data } = await import(module)
228
delete data.default
229
deleteModule(require.resolve(module)) // avoid caching at next import
@@ -236,6 +231,7 @@ export async function rescan() {
231
if (data.badApi)
232
console.log("plugin", id, data.badApi)
233
234
+ await alreadyRunning?.unload(true)
235
const res = await init?.call(null, {
236
srcDir: __dirname,
237
const: Const,
@@ -261,9 +257,16 @@ export async function rescan() {
257
getHfsConfig: getConfig,
258
})
259
Object.assign(data, res)
264
- new Plugin(id, data, unwatch)
265
- if (reloading)
260
+ const plugin = new Plugin(id, data, unwatch)
261
+ if (alreadyRunning)
262
events.emit('pluginUpdated', getPluginInfo(id))
263
+ else {
264
+ const wasInstalled = availablePlugins[id]
265
+ if (wasInstalled)
266
+ delete availablePlugins[id]
267
+ events.emit(wasInstalled ? 'pluginStarted' : 'pluginInstalled', plugin)
268
+ }
269
+
270
} catch (e) {
271
console.log("plugin error:", e)
272
}