@@ -92,7 +92,11 @@ export function registerServerReference<T: Function>(
9292const PROMISE_PROTOTYPE = Promise.prototype;
9393
9494const deepProxyHandlers = {
95- get : function ( target : Function , name : string , receiver : Proxy < Function > ) {
95+ get : function (
96+ target : Function ,
97+ name : string | symbol ,
98+ receiver : Proxy < Function > ,
99+ ) {
96100 switch ( name ) {
97101 // These names are read by the Flight runtime if you end up using the exports object.
98102 case '$$typeof' :
@@ -117,6 +121,9 @@ const deepProxyHandlers = {
117121 case Symbol . toPrimitive :
118122 // $FlowFixMe[prop-missing]
119123 return Object . prototype [ Symbol . toPrimitive ] ;
124+ case Symbol . toStringTag :
125+ // $FlowFixMe[prop-missing]
126+ return Object . prototype [ Symbol . toStringTag ] ;
120127 case 'Provider' :
121128 throw new Error (
122129 `Cannot render a Client Context Provider on the Server. ` +
@@ -137,105 +144,134 @@ const deepProxyHandlers = {
137144 } ,
138145} ;
139146
140- const proxyHandlers = {
141- get : function (
142- target : Function ,
143- name : string ,
144- receiver : Proxy < Function > ,
145- ) : $FlowFixMe {
146- switch ( name ) {
147- // These names are read by the Flight runtime if you end up using the exports object.
148- case '$$typeof' :
149- return target . $$typeof ;
150- case '$$id' :
151- return target . $$id ;
152- case '$$async' :
153- return target . $$async ;
154- case 'name' :
155- return target . name ;
156- // We need to special case this because createElement reads it if we pass this
157- // reference.
158- case 'defaultProps' :
159- return undefined ;
160- // Avoid this attempting to be serialized.
161- case 'toJSON' :
162- return undefined ;
163- case Symbol . toPrimitive :
164- // $FlowFixMe[prop-missing]
165- return Object . prototype [ Symbol . toPrimitive ] ;
166- case '__esModule' :
167- // Something is conditionally checking which export to use. We'll pretend to be
168- // an ESM compat module but then we'll check again on the client.
169- const moduleId = target . $$id ;
170- target . default = registerClientReferenceImpl (
171- ( function ( ) {
172- throw new Error (
173- `Attempted to call the default export of ${ moduleId } from the server ` +
174- `but it's on the client. It's not possible to invoke a client function from ` +
175- `the server, it can only be rendered as a Component or passed to props of a ` +
176- `Client Component.` ,
177- ) ;
178- } : any ) ,
179- target . $$id + '#' ,
180- target . $$async ,
181- ) ;
182- return true ;
183- case 'then' :
184- if ( target . then ) {
185- // Use a cached value
186- return target . then ;
187- }
188- if ( ! target . $$async ) {
189- // If this module is expected to return a Promise (such as an AsyncModule) then
190- // we should resolve that with a client reference that unwraps the Promise on
191- // the client.
192-
193- const clientReference : ClientReference < any > =
194- registerClientReferenceImpl(({ } : any), target.$$id, true);
195- const proxy = new Proxy(clientReference, proxyHandlers);
196-
197- // Treat this as a resolved Promise for React's use()
198- target.status = 'fulfilled';
199- target.value = proxy;
200-
201- const then = (target.then = registerClientReferenceImpl(
202- (function then(resolve, reject: any) {
203- // Expose to React.
204- return Promise . resolve ( resolve ( proxy ) ) ;
205- } : any),
206- // If this is not used as a Promise but is treated as a reference to a `.then`
207- // export then we should treat it as a reference to that name.
208- target.$$id + '#then',
209- false,
210- ));
211- return then;
212- } else {
213- // Since typeof .then === 'function' is a feature test we'd continue recursing
214- // indefinitely if we return a function. Instead, we return an object reference
215- // if we check further.
216- return undefined ;
217- }
218- }
219- let cachedReference = target [ name ] ;
220- if ( ! cachedReference ) {
221- const reference : ClientReference < any > = registerClientReferenceImpl (
147+ function getReference ( target : Function , name : string | symbol ) : $FlowFixMe {
148+ switch ( name ) {
149+ // These names are read by the Flight runtime if you end up using the exports object.
150+ case '$$typeof' :
151+ return target . $$typeof ;
152+ case '$$id' :
153+ return target . $$id ;
154+ case '$$async' :
155+ return target . $$async ;
156+ case 'name' :
157+ return target . name ;
158+ // We need to special case this because createElement reads it if we pass this
159+ // reference.
160+ case 'defaultProps' :
161+ return undefined ;
162+ // Avoid this attempting to be serialized.
163+ case 'toJSON' :
164+ return undefined ;
165+ case Symbol . toPrimitive :
166+ // $FlowFixMe[prop-missing]
167+ return Object . prototype [ Symbol . toPrimitive ] ;
168+ case Symbol . toStringTag :
169+ // $FlowFixMe[prop-missing]
170+ return Object . prototype [ Symbol . toStringTag ] ;
171+ case '__esModule' :
172+ // Something is conditionally checking which export to use. We'll pretend to be
173+ // an ESM compat module but then we'll check again on the client.
174+ const moduleId = target . $$id ;
175+ target . default = registerClientReferenceImpl (
222176 ( function ( ) {
223177 throw new Error (
224- // eslint-disable-next-line react-internal/safe-string-coercion
225- `Attempted to call ${ String ( name ) } () from the server but ${ String (
226- name ,
227- ) } is on the client. ` +
228- `It's not possible to invoke a client function from the server, it can ` +
229- `only be rendered as a Component or passed to props of a Client Component.` ,
178+ `Attempted to call the default export of ${ moduleId } from the server ` +
179+ `but it's on the client. It's not possible to invoke a client function from ` +
180+ `the server, it can only be rendered as a Component or passed to props of a ` +
181+ `Client Component.` ,
230182 ) ;
231183 } : any ) ,
232- target . $$id + '#' + name ,
184+ target . $$id + '#' ,
233185 target . $$async ,
234186 ) ;
235- Object . defineProperty ( ( reference : any ) , 'name' , { value : name } ) ;
236- cachedReference = target [ name ] = new Proxy ( reference , deepProxyHandlers ) ;
187+ return true ;
188+ case 'then ':
189+ if ( target . then ) {
190+ // Use a cached value
191+ return target . then ;
192+ }
193+ if (!target.$$async) {
194+ // If this module is expected to return a Promise (such as an AsyncModule) then
195+ // we should resolve that with a client reference that unwraps the Promise on
196+ // the client.
197+
198+ const clientReference : ClientReference < any > =
199+ registerClientReferenceImpl ( ( { } : any ) , target . $$id , true ) ;
200+ const proxy = new Proxy ( clientReference , proxyHandlers ) ;
201+
202+ // Treat this as a resolved Promise for React's use()
203+ target . status = 'fulfilled' ;
204+ target . value = proxy ;
205+
206+ const then = ( target . then = registerClientReferenceImpl (
207+ ( function then ( resolve , reject : any ) {
208+ // Expose to React.
209+ return Promise . resolve ( resolve ( proxy ) ) ;
210+ } : any ) ,
211+ // If this is not used as a Promise but is treated as a reference to a `.then`
212+ // export then we should treat it as a reference to that name.
213+ target . $$id + '#then' ,
214+ false ,
215+ ) ) ;
216+ return then ;
217+ } else {
218+ // Since typeof .then === 'function' is a feature test we'd continue recursing
219+ // indefinitely if we return a function. Instead, we return an object reference
220+ // if we check further.
221+ return undefined ;
222+ }
223+ }
224+ if ( typeof name === 'symbol ') {
225+ throw new Error (
226+ 'Cannot read Symbol exports. Only named exports are supported on a client module ' +
227+ 'imported on the server.' ,
228+ ) ;
229+ }
230+ let cachedReference = target[name];
231+ if (!cachedReference) {
232+ const reference : ClientReference < any > = registerClientReferenceImpl (
233+ ( function ( ) {
234+ throw new Error (
235+ // eslint-disable-next-line react-internal/safe-string-coercion
236+ `Attempted to call ${ String ( name ) } () from the server but ${ String (
237+ name ,
238+ ) } is on the client. ` +
239+ `It's not possible to invoke a client function from the server, it can ` +
240+ `only be rendered as a Component or passed to props of a Client Component.` ,
241+ ) ;
242+ } : any ) ,
243+ target . $$id + '#' + name ,
244+ target . $$async ,
245+ ) ;
246+ Object . defineProperty ( ( reference : any ) , 'name' , { value : name } ) ;
247+ cachedReference = target [ name ] = new Proxy ( reference , deepProxyHandlers ) ;
248+ }
249+ return cachedReference;
250+ }
251+
252+ const proxyHandlers = {
253+ get : function (
254+ target : Function ,
255+ name : string | symbol ,
256+ receiver : Proxy < Function > ,
257+ ) : $FlowFixMe {
258+ return getReference ( target , name ) ;
259+ } ,
260+ getOwnPropertyDescriptor : function (
261+ target : Function ,
262+ name : string | symbol ,
263+ ) : $FlowFixMe {
264+ let descriptor = Object . getOwnPropertyDescriptor ( target , name ) ;
265+ if ( ! descriptor ) {
266+ descriptor = {
267+ value : getReference ( target , name ) ,
268+ writable : false ,
269+ configurable : false ,
270+ enumerable : false ,
271+ } ;
272+ Object . defineProperty ( target , name , descriptor ) ;
237273 }
238- return cachedReference ;
274+ return descriptor ;
239275 } ,
240276 getPrototypeOf ( target : Function ) : Object {
241277 // Pretend to be a Promise in case anyone asks.
0 commit comments