diff --git a/src/lib/loadPlugin.js b/src/lib/loadPlugin.js index 9c3d50a83..14aa18a50 100644 --- a/src/lib/loadPlugin.js +++ b/src/lib/loadPlugin.js @@ -8,6 +8,27 @@ export default async function loadPlugin(pluginId, justInstalled = false) { const baseUrl = await helpers.toInternalUri(Url.join(PLUGIN_DIR, pluginId)); const cacheFile = Url.join(CACHE_STORAGE, pluginId); + // Unmount the old version before loading the new one. + // This MUST be done here by the framework, not by the new plugin code itself, + // because once the new script loads, it calls acode.setPluginUnmount(id, newDestroy) + // which overwrites the old version's destroy callback. At that point the old + // destroy — which holds references to the old sidebar app, commands, event + // listeners, etc. — is lost and can never be called. Letting the framework + // invoke unmountPlugin() first ensures the OLD destroy() runs while it still + // exists, so all old-version resources are properly cleaned up. + try { + acode.unmountPlugin(pluginId); + } catch (e) { + // unmountPlugin() itself is safe when no callback is registered (it no-ops), + // but a plugin's destroy() callback may throw. We catch here so a faulty + // cleanup in the old version does not block reloading the new one. + console.error(`Error while unmounting plugin "${pluginId}":`, e); + } + + // Remove the old