11import type { ModuleFederationRuntimePlugin } from '@module-federation/modern-js-v3' ;
22
3- const getRemotePublicPath = ( entry : string ) => {
3+ const TARGET_REMOTE_ALIAS = 'rscRemote' ;
4+ const SSR_BUNDLES_SEGMENT = '/bundles/' ;
5+
6+ interface FederationRemoteConfig {
7+ alias ?: string ;
8+ name ?: string ;
9+ entry ?: string ;
10+ }
11+
12+ interface FederationInstanceLike {
13+ options ?: {
14+ remotes ?: Record < string , string | FederationRemoteConfig > | unknown [ ] ;
15+ } ;
16+ }
17+
18+ interface FederationModuleInfoLike {
19+ alias ?: string ;
20+ name ?: string ;
21+ metaData ?: {
22+ publicPath ?: string ;
23+ ssrPublicPath ?: string ;
24+ } ;
25+ }
26+
27+ interface FederationGlobalLike {
28+ __INSTANCES__ ?: FederationInstanceLike [ ] ;
29+ moduleInfo ?: Record < string , FederationModuleInfoLike > ;
30+ }
31+
32+ const getRemoteOriginPublicPath = ( entry : string ) => {
433 try {
534 return `${ new URL ( entry ) . origin } /` ;
635 } catch {
736 return undefined ;
837 }
938} ;
1039
40+ const getNormalizedAbsolutePublicPath = ( value : string ) => {
41+ try {
42+ const publicPathUrl = new URL ( value ) ;
43+ publicPathUrl . search = '' ;
44+ publicPathUrl . hash = '' ;
45+ publicPathUrl . pathname = publicPathUrl . pathname . endsWith ( '/' )
46+ ? publicPathUrl . pathname
47+ : `${ publicPathUrl . pathname } /` ;
48+ return publicPathUrl . toString ( ) ;
49+ } catch {
50+ return undefined ;
51+ }
52+ } ;
53+
54+ const getPublicPathFromSsrPublicPath = ( ssrPublicPath : string ) => {
55+ const normalizedSsrPublicPath =
56+ getNormalizedAbsolutePublicPath ( ssrPublicPath ) ;
57+ if ( ! normalizedSsrPublicPath ) {
58+ return undefined ;
59+ }
60+ try {
61+ const ssrPublicPathUrl = new URL ( normalizedSsrPublicPath ) ;
62+ if ( ! ssrPublicPathUrl . pathname . endsWith ( SSR_BUNDLES_SEGMENT ) ) {
63+ return undefined ;
64+ }
65+ ssrPublicPathUrl . pathname = ssrPublicPathUrl . pathname . replace (
66+ / \/ b u n d l e s \/ $ / ,
67+ '/' ,
68+ ) ;
69+ return ssrPublicPathUrl . toString ( ) ;
70+ } catch {
71+ return undefined ;
72+ }
73+ } ;
74+
75+ const getGlobalFederationState = ( ) =>
76+ ( globalThis as typeof globalThis & { __FEDERATION__ ?: FederationGlobalLike } )
77+ . __FEDERATION__ ;
78+
79+ const getRemoteEntryFromRemoteConfig = (
80+ remoteConfig : string | FederationRemoteConfig | undefined ,
81+ ) => {
82+ if ( ! remoteConfig ) {
83+ return undefined ;
84+ }
85+ if ( typeof remoteConfig === 'string' ) {
86+ const atIndex = remoteConfig . indexOf ( '@' ) ;
87+ return atIndex >= 0 ? remoteConfig . slice ( atIndex + 1 ) : remoteConfig ;
88+ }
89+ if ( typeof remoteConfig . entry === 'string' ) {
90+ return remoteConfig . entry ;
91+ }
92+ return undefined ;
93+ } ;
94+
95+ const getRemoteEntryFromFederationInstances = ( remoteAlias : string ) => {
96+ const federationState = getGlobalFederationState ( ) ;
97+ if ( ! federationState ?. __INSTANCES__ ) {
98+ return undefined ;
99+ }
100+ for ( const instance of federationState . __INSTANCES__ ) {
101+ const remotes = instance ?. options ?. remotes ;
102+ if ( ! remotes ) {
103+ continue ;
104+ }
105+ if ( ! Array . isArray ( remotes ) && typeof remotes === 'object' ) {
106+ const remoteConfig = remotes [ remoteAlias ] as
107+ | string
108+ | FederationRemoteConfig
109+ | undefined ;
110+ const remoteEntry = getRemoteEntryFromRemoteConfig ( remoteConfig ) ;
111+ if ( remoteEntry ) {
112+ return remoteEntry ;
113+ }
114+ for ( const remotesEntry of Object . values ( remotes ) ) {
115+ if (
116+ remotesEntry &&
117+ typeof remotesEntry === 'object' &&
118+ 'alias' in remotesEntry &&
119+ remotesEntry . alias === remoteAlias
120+ ) {
121+ return getRemoteEntryFromRemoteConfig (
122+ remotesEntry as FederationRemoteConfig ,
123+ ) ;
124+ }
125+ }
126+ continue ;
127+ }
128+ if ( ! Array . isArray ( remotes ) ) {
129+ continue ;
130+ }
131+ for ( const remoteConfig of remotes ) {
132+ if ( ! remoteConfig || typeof remoteConfig !== 'object' ) {
133+ continue ;
134+ }
135+ const alias = 'alias' in remoteConfig ? remoteConfig . alias : undefined ;
136+ const name = 'name' in remoteConfig ? remoteConfig . name : undefined ;
137+ if ( alias !== remoteAlias && name !== remoteAlias ) {
138+ continue ;
139+ }
140+ const remoteEntry = getRemoteEntryFromRemoteConfig (
141+ remoteConfig as FederationRemoteConfig ,
142+ ) ;
143+ if ( remoteEntry ) {
144+ return remoteEntry ;
145+ }
146+ }
147+ }
148+ return undefined ;
149+ } ;
150+
151+ const getRemoteModuleInfoFromFederationState = ( remoteAlias : string ) => {
152+ const federationState = getGlobalFederationState ( ) ;
153+ if ( ! federationState ?. moduleInfo ) {
154+ return undefined ;
155+ }
156+ const directRemoteModuleInfo = federationState . moduleInfo [ remoteAlias ] ;
157+ if ( directRemoteModuleInfo ) {
158+ return directRemoteModuleInfo ;
159+ }
160+ return Object . values ( federationState . moduleInfo ) . find (
161+ moduleInfo =>
162+ moduleInfo ?. alias === remoteAlias || moduleInfo ?. name === remoteAlias ,
163+ ) ;
164+ } ;
165+
166+ const resolveRemotePublicPaths = ( {
167+ remoteAlias,
168+ remoteEntry,
169+ } : {
170+ remoteAlias : string ;
171+ remoteEntry ?: string ;
172+ } ) => {
173+ const preferredRemoteEntry =
174+ remoteEntry || getRemoteEntryFromFederationInstances ( remoteAlias ) ;
175+ const remotePublicPath = preferredRemoteEntry
176+ ? getRemoteOriginPublicPath ( preferredRemoteEntry )
177+ : undefined ;
178+ if ( remotePublicPath ) {
179+ return {
180+ remotePublicPath,
181+ remoteSsrPublicPath : `${ remotePublicPath } bundles/` ,
182+ } ;
183+ }
184+
185+ const remoteModuleInfo = getRemoteModuleInfoFromFederationState ( remoteAlias ) ;
186+ const remoteModuleInfoPublicPath = remoteModuleInfo ?. metaData ?. publicPath
187+ ? getNormalizedAbsolutePublicPath ( remoteModuleInfo . metaData . publicPath )
188+ : undefined ;
189+ const remoteModuleInfoSsrPublicPath = remoteModuleInfo ?. metaData
190+ ?. ssrPublicPath
191+ ? getNormalizedAbsolutePublicPath ( remoteModuleInfo . metaData . ssrPublicPath )
192+ : undefined ;
193+ const fallbackRemotePublicPath =
194+ remoteModuleInfoPublicPath ||
195+ ( remoteModuleInfoSsrPublicPath
196+ ? getPublicPathFromSsrPublicPath ( remoteModuleInfoSsrPublicPath )
197+ : undefined ) ;
198+ const fallbackRemoteSsrPublicPath =
199+ remoteModuleInfoSsrPublicPath ||
200+ ( fallbackRemotePublicPath
201+ ? `${ fallbackRemotePublicPath } bundles/`
202+ : undefined ) ;
203+ return {
204+ remotePublicPath : fallbackRemotePublicPath ,
205+ remoteSsrPublicPath : fallbackRemoteSsrPublicPath ,
206+ } ;
207+ } ;
208+
11209const forceRemotePublicPath = ( ) : ModuleFederationRuntimePlugin => ( {
12210 name : 'rsc-mf-force-remote-public-path' ,
13211 loadRemoteSnapshot ( args : any ) {
14212 const { remoteInfo, remoteSnapshot } = args ;
15- if ( remoteInfo ?. alias !== 'rscRemote' || ! remoteSnapshot ) {
16- return args ;
17- }
18-
19- const entry = remoteInfo ?. entry ;
20- if ( ! entry || typeof entry !== 'string' ) {
213+ if ( remoteInfo ?. alias !== TARGET_REMOTE_ALIAS || ! remoteSnapshot ) {
21214 return args ;
22215 }
23- const remotePublicPath = getRemotePublicPath ( entry ) ;
216+ const { remotePublicPath, remoteSsrPublicPath } = resolveRemotePublicPaths ( {
217+ remoteAlias : remoteInfo . alias ,
218+ remoteEntry :
219+ typeof remoteInfo ?. entry === 'string' ? remoteInfo . entry : undefined ,
220+ } ) ;
24221 if ( ! remotePublicPath ) {
25222 return args ;
26223 }
@@ -32,7 +229,8 @@ const forceRemotePublicPath = (): ModuleFederationRuntimePlugin => ({
32229 remoteSnapshot . metaData . publicPath = remotePublicPath ;
33230 }
34231 if ( remoteSnapshot . metaData && 'ssrPublicPath' in remoteSnapshot . metaData ) {
35- remoteSnapshot . metaData . ssrPublicPath = `${ remotePublicPath } bundles/` ;
232+ remoteSnapshot . metaData . ssrPublicPath =
233+ remoteSsrPublicPath || `${ remotePublicPath } bundles/` ;
36234 }
37235
38236 return args ;
0 commit comments