@@ -25,6 +25,10 @@ function handleImportError(error: Error, module: string) {
2525 } ) ;
2626}
2727
28+ function errorMessage ( error : unknown ) : string {
29+ return error instanceof Error ? error . message : String ( error ) ;
30+ }
31+
2832export namespace ImportResolver {
2933 export interface IOptions {
3034 loadKnownModule : ( name : string ) => Promise < IModule | null > ;
@@ -83,6 +87,10 @@ interface ILocalCssSnapshotEntry {
8387 previousCss : string | null ;
8488}
8589
90+ interface IFederatedExtensionContainer {
91+ get : ( key : string ) => Promise < ( ( ) => IModule ) | IModule > ;
92+ }
93+
8694export class ImportResolver {
8795 private static _localCssStyles = new Map < string , HTMLStyleElement > ( ) ;
8896 private static _localCssSnapshotStacks = new Map <
@@ -183,31 +191,18 @@ export class ImportResolver {
183191 */
184192 async resolve ( module : string ) : Promise < Token < any > | IModule | IModuleMember > {
185193 try {
186- const tokenAndDefaultHandler = {
187- get : (
188- target : IModule ,
189- prop : string | number | symbol ,
190- receiver : any
191- ) => {
192- if ( typeof prop !== 'string' ) {
193- return Reflect . get ( target , prop , receiver ) ;
194- }
195- const tokenName = `${ module } :${ prop } ` ;
196- if ( this . _options . tokenMap . has ( tokenName ) ) {
197- return this . _options . tokenMap . get ( tokenName ) ;
198- }
199- // synthetic default import (without proxy)
200- if ( prop === 'default' && ! ( prop in target ) ) {
201- return target ;
202- }
203- return Reflect . get ( target , prop , receiver ) ;
204- }
205- } ;
206-
207194 const knownModule = await this . _resolveKnownModule ( module ) ;
208195 if ( knownModule !== null ) {
209- return new Proxy ( knownModule , tokenAndDefaultHandler ) ;
196+ return this . _createTokenAwareModule ( module , knownModule ) ;
197+ }
198+
199+ const federatedModule = await this . _resolveFederatedExtensionModule (
200+ module
201+ ) ;
202+ if ( federatedModule !== null ) {
203+ return this . _createTokenAwareModule ( module , federatedModule ) ;
210204 }
205+
211206 const localFile = await this . _resolveLocalFile ( module ) ;
212207 if ( localFile !== null ) {
213208 return localFile ;
@@ -233,6 +228,90 @@ export class ImportResolver {
233228 }
234229 }
235230
231+ private _createTokenAwareModule (
232+ module : string ,
233+ targetModule : IModule
234+ ) : IModule {
235+ return new Proxy ( targetModule , {
236+ get : ( target : IModule , prop : string | number | symbol , receiver : any ) => {
237+ if ( typeof prop !== 'string' ) {
238+ return Reflect . get ( target , prop , receiver ) ;
239+ }
240+ const tokenName = `${ module } :${ prop } ` ;
241+ if ( this . _options . tokenMap . has ( tokenName ) ) {
242+ return this . _options . tokenMap . get ( tokenName ) ;
243+ }
244+ // synthetic default import (without proxy)
245+ if ( prop === 'default' && ! ( prop in target ) ) {
246+ return target ;
247+ }
248+ return Reflect . get ( target , prop , receiver ) ;
249+ }
250+ } ) ;
251+ }
252+
253+ private async _resolveFederatedExtensionModule (
254+ module : string
255+ ) : Promise < IModule | null > {
256+ if ( module . startsWith ( '.' ) ) {
257+ return null ;
258+ }
259+ if ( typeof window === 'undefined' ) {
260+ return null ;
261+ }
262+
263+ const runtime = window as Window & {
264+ _JUPYTERLAB ?: Record < string , IFederatedExtensionContainer > ;
265+ } ;
266+ const container = runtime . _JUPYTERLAB ?. [ module ] ;
267+ if ( ! container ) {
268+ return null ;
269+ }
270+ if ( typeof container . get !== 'function' ) {
271+ throw new Error (
272+ `Federated extension container ${ module } does not expose get().`
273+ ) ;
274+ }
275+
276+ let exposed : ( ( ) => IModule ) | IModule ;
277+ try {
278+ exposed = await container . get ( './extension' ) ;
279+ } catch ( error ) {
280+ throw new Error (
281+ `Failed to resolve federated extension module ${ module } from ./extension: ${ errorMessage (
282+ error
283+ ) } `
284+ ) ;
285+ }
286+
287+ const factory =
288+ typeof exposed === 'function'
289+ ? ( exposed as ( ) => IModule )
290+ : ( ) => exposed as IModule ;
291+
292+ let resolved : unknown ;
293+ try {
294+ resolved = factory ( ) ;
295+ } catch ( error ) {
296+ throw new Error (
297+ `Failed to evaluate federated extension module ${ module } from ./extension: ${ errorMessage (
298+ error
299+ ) } `
300+ ) ;
301+ }
302+
303+ if (
304+ ! resolved ||
305+ ( typeof resolved !== 'object' && typeof resolved !== 'function' )
306+ ) {
307+ throw new Error (
308+ `Federated extension module ${ module } did not return a module object from ./extension.`
309+ ) ;
310+ }
311+
312+ return resolved as IModule ;
313+ }
314+
236315 private async _getCDNConsent (
237316 module : string ,
238317 cdnUrl : string
0 commit comments