@@ -22,6 +22,7 @@ const untrustedDomains: string[] = [];
2222export class PluginManager implements IPluginManager {
2323 readonly plugins = new Map < string , Plugin > ( ) ;
2424 readonly manifests = new Map < string , PluginManifest > ( ) ;
25+ readonly shouldRevokes = new Map < string , string [ ] > ( ) ;
2526
2627 constructor ( readonly app : IApplication ) { }
2728
@@ -52,7 +53,7 @@ export class PluginManager implements IPluginManager {
5253 if ( ! url . endsWith ( "/" ) ) url += "/" ;
5354 const manifest = await this . readManifestFromUrl ( `${ url } manifest.json` ) ;
5455 if ( manifest ) {
55- await this . loadPluginFromUrl ( manifest . name , url , manifest . main , manifest . importMap ) ;
56+ await this . loadPluginFromUrl ( manifest . name , url , manifest . main , manifest . importmap ) ;
5657 }
5758 }
5859 }
@@ -121,26 +122,29 @@ export class PluginManager implements IPluginManager {
121122 alert ( `${ manifest . main } not found in plugin archive` ) ;
122123 return ;
123124 }
124- const code = await codeFile . async ( "text" ) ;
125- const handlePluginIcon = async ( plugin : Plugin ) => {
126- await this . transformZipCommandIcon ( zip , plugin ) ;
127- } ;
128125
129- const importMap = await this . getImportMapFromZip ( zip , manifest ) ;
130- if ( importMap ) {
131- this . injectImportMap ( JSON . stringify ( importMap ) ) ;
132- }
133- await this . loadMainCode ( manifest . name , code , handlePluginIcon ) ;
134- await this . loadCssFromZip ( zip , manifest ) ;
126+ const importmap = await this . getImportmapFromZip ( zip , manifest ) ;
127+ if ( importmap ) {
128+ this . injectImportmap ( JSON . stringify ( importmap ) ) ;
135129
136- if ( importMap ) {
137- Object . values ( importMap . imports ) . forEach ( ( url ) => {
138- URL . revokeObjectURL ( url ) ;
139- } ) ;
130+ this . shouldRevokes . set ( manifest . name , Object . values ( importmap . imports ) ) ;
140131 }
132+
133+ const code = await codeFile . async ( "text" ) ;
134+ const blob = new Blob ( [ code ] , { type : "application/javascript" } ) ;
135+ const blobUrl = URL . createObjectURL ( blob ) ;
136+ await Promise . try ( async ( ) => {
137+ const handlePluginIcon = async ( plugin : Plugin ) => {
138+ await this . transformZipCommandIcon ( zip , plugin ) ;
139+ } ;
140+ await this . loadMainCode ( manifest . name , blobUrl , handlePluginIcon ) ;
141+ await this . loadCssFromZip ( zip , manifest ) ;
142+ } ) . finally ( ( ) => {
143+ URL . revokeObjectURL ( blobUrl ) ;
144+ } ) ;
141145 }
142146
143- private async loadPluginFromUrl ( name : string , baseUrl : string , codePath : string , importMapPath ?: string ) {
147+ private async loadPluginFromUrl ( name : string , baseUrl : string , codePath : string , importmapPath ?: string ) {
144148 if ( codePath . startsWith ( "/" ) ) codePath = codePath . substring ( 1 ) ;
145149
146150 const fullUrl = baseUrl + codePath ;
@@ -149,52 +153,72 @@ export class PluginManager implements IPluginManager {
149153 return undefined ;
150154 }
151155
152- const code = await response . text ( ) ;
153156 const handlePluginIcon = async ( plugin : Plugin ) => {
154157 await this . transformUrlCommandIcon ( baseUrl , plugin ) ;
155158 } ;
156159
157- if ( importMapPath ) {
158- await this . loadImportMapFromUrl ( baseUrl , importMapPath ) ;
160+ if ( importmapPath ) {
161+ await this . loadImportmapFromUrl ( baseUrl , importmapPath ) ;
159162 }
160163
161- await this . loadMainCode ( name , code , handlePluginIcon ) ;
164+ await this . loadMainCode ( name , fullUrl , handlePluginIcon ) ;
162165 await this . loadCssFromUrl ( baseUrl , name ) ;
163166 }
164167
165- private async loadImportMapFromUrl ( baseUrl : string , importMapPath : string ) {
166- if ( importMapPath . startsWith ( "/" ) ) importMapPath = importMapPath . substring ( 1 ) ;
167- const response = await fetch ( baseUrl + importMapPath ) ;
168+ private async loadImportmapFromUrl ( baseUrl : string , importmapPath : string ) {
169+ if ( importmapPath . startsWith ( "/" ) ) importmapPath = importmapPath . substring ( 1 ) ;
170+ const importmapUrl = baseUrl + importmapPath ;
171+ const response = await fetch ( importmapUrl ) ;
168172 if ( ! response . ok ) {
169173 return undefined ;
170174 }
171175
172- const json = await response . text ( ) ;
173- this . injectImportMap ( json ) ;
176+ const importmapObj = await response . json ( ) ;
177+ const importmapBaseUrl = new URL ( importmapPath , baseUrl ) . href ;
178+ if ( importmapObj . imports ) {
179+ for ( const key in importmapObj . imports ) {
180+ const value = importmapObj . imports [ key ] ;
181+ if ( ! value . startsWith ( "http://" ) && ! value . startsWith ( "https://" ) ) {
182+ const absoluteUrl = new URL ( value , importmapBaseUrl ) . href ;
183+ importmapObj . imports [ key ] = absoluteUrl ;
184+ }
185+ }
186+ }
187+
188+ if ( importmapObj . scopes ) {
189+ for ( const scope in importmapObj . scopes ) {
190+ const scopeBaseUrl = new URL ( scope , importmapBaseUrl ) . href ;
191+ for ( const key in importmapObj . scopes [ scope ] ) {
192+ const value = importmapObj . scopes [ scope ] [ key ] ;
193+ if ( ! value . startsWith ( "http://" ) && ! value . startsWith ( "https://" ) ) {
194+ const absoluteUrl = new URL ( value , scopeBaseUrl ) . href ;
195+ importmapObj . scopes [ scope ] [ key ] = absoluteUrl ;
196+ }
197+ }
198+ }
199+ }
200+
201+ this . injectImportmap ( JSON . stringify ( importmapObj ) ) ;
174202 }
175203
176204 private async loadMainCode (
177205 name : string ,
178- code : string ,
206+ url : string ,
179207 handlePluginIcon : ( plugin : Plugin ) => Promise < void > ,
180208 ) {
181- const blob = new Blob ( [ code ] , { type : "application/javascript" } ) ;
182- const blobUrl = URL . createObjectURL ( blob ) ;
183209 await Promise . try ( async ( ) => {
184- const module = await import ( /*webpackIgnore: true*/ blobUrl ) ;
210+ const module = await import ( /*webpackIgnore: true*/ url ) ;
185211 const plugin : Plugin = module . default ;
186212 await handlePluginIcon ( plugin ) ;
187213 this . registerPlugin ( plugin ) ;
188214 this . plugins . set ( name , plugin ) ;
189215
190216 Logger . info ( `Plugin ${ name } loaded successfully` ) ;
191- } )
192- . catch ( ( err ) => {
193- alert ( `Failed to load plugin ${ name } : ${ err } ` ) ;
194- } )
195- . finally ( ( ) => {
196- URL . revokeObjectURL ( blobUrl ) ;
197- } ) ;
217+ } ) . catch ( ( err ) => {
218+ console . log ( err ) ;
219+
220+ alert ( `Failed to load plugin ${ name } : ${ err } ` ) ;
221+ } ) ;
198222 }
199223
200224 private async transformZipCommandIcon ( zip : JSZip , plugin : Plugin ) {
@@ -233,6 +257,10 @@ export class PluginManager implements IPluginManager {
233257 await this . unregisterPlugin ( pluginName , plugin ) ;
234258 this . plugins . delete ( pluginName ) ;
235259
260+ this . shouldRevokes . get ( pluginName ) ?. forEach ( ( value ) => {
261+ URL . revokeObjectURL ( value ) ;
262+ } ) ;
263+
236264 Logger . info ( `Plugin ${ pluginName } unloaded successfully` ) ;
237265 }
238266
@@ -409,20 +437,20 @@ export class PluginManager implements IPluginManager {
409437 }
410438 }
411439
412- private async getImportMapFromZip (
440+ private async getImportmapFromZip (
413441 zip : JSZip ,
414442 manifest : PluginManifest ,
415443 ) : Promise < { imports : Record < string , string > } | undefined > {
416- if ( ! manifest . importMap ) return undefined ;
444+ if ( ! manifest . importmap ) return undefined ;
417445
418- const codeFile = zip . file ( manifest . importMap ) ;
446+ const codeFile = zip . file ( manifest . importmap ) ;
419447 if ( ! codeFile ) {
420448 alert ( `${ manifest . main } not found in plugin archive` ) ;
421449 return undefined ;
422450 }
423451
424- const importMap = await codeFile . async ( "text" ) ;
425- const json = JSON . parse ( importMap ) ;
452+ const importmap = await codeFile . async ( "text" ) ;
453+ const json = JSON . parse ( importmap ) ;
426454 for ( const key in json . imports ) {
427455 const importFile = zip . file ( json . imports [ key ] ) ;
428456 if ( ! importFile ) {
@@ -438,10 +466,10 @@ export class PluginManager implements IPluginManager {
438466 return json ;
439467 }
440468
441- private injectImportMap ( importMapJson : string ) {
469+ private injectImportmap ( importmapJson : string ) {
442470 const script = document . createElement ( "script" ) ;
443471 script . type = "importmap" ;
444- script . textContent = importMapJson ;
472+ script . textContent = importmapJson ;
445473 document . head . appendChild ( script ) ;
446474 }
447475
0 commit comments