@@ -8,6 +8,27 @@ export default async function loadPlugin(pluginId, justInstalled = false) {
88 const baseUrl = await helpers . toInternalUri ( Url . join ( PLUGIN_DIR , pluginId ) ) ;
99 const cacheFile = Url . join ( CACHE_STORAGE , pluginId ) ;
1010
11+ // Unmount the old version before loading the new one.
12+ // This MUST be done here by the framework, not by the new plugin code itself,
13+ // because once the new script loads, it calls acode.setPluginUnmount(id, newDestroy)
14+ // which overwrites the old version's destroy callback. At that point the old
15+ // destroy — which holds references to the old sidebar app, commands, event
16+ // listeners, etc. — is lost and can never be called. Letting the framework
17+ // invoke unmountPlugin() first ensures the OLD destroy() runs while it still
18+ // exists, so all old-version resources are properly cleaned up.
19+ try {
20+ acode . unmountPlugin ( pluginId ) ;
21+ } catch ( e ) {
22+ // unmountPlugin() itself is safe when no callback is registered (it no-ops),
23+ // but a plugin's destroy() callback may throw. We catch here so a faulty
24+ // cleanup in the old version does not block reloading the new one.
25+ console . error ( `Error while unmounting plugin "${ pluginId } ":` , e ) ;
26+ }
27+
28+ // Remove the old <script> tag so the browser fetches the new source.
29+ const oldScript = document . getElementById ( `${ pluginId } -mainScript` ) ;
30+ if ( oldScript ) oldScript . remove ( ) ;
31+
1132 const pluginJson = await fsOperation (
1233 Url . join ( PLUGIN_DIR , pluginId , "plugin.json" ) ,
1334 ) . readFile ( "json" ) ;
0 commit comments