main
js 130 lines 3.45 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
11
12 export type ServerReference<T: Function> = T & {
13 $$typeof: symbol,
14 $$id: string,
15 $$bound: null | Array<ReactClientValue>,
16 $$location?: Error,
17 };
18
19 // eslint-disable-next-line no-unused-vars
20 export type ClientReference<T> = {
21 $$typeof: symbol,
22 $$id: string,
23 };
24
25 const CLIENT_REFERENCE_TAG = Symbol.for('react.client.reference');
26 const SERVER_REFERENCE_TAG = Symbol.for('react.server.reference');
27
28 export function isClientReference(reference: Object): boolean {
29 return reference.$$typeof === CLIENT_REFERENCE_TAG;
30 }
31
32 export function isServerReference(reference: Object): boolean {
33 return reference.$$typeof === SERVER_REFERENCE_TAG;
34 }
35
36 export function registerClientReference<T>(
37 proxyImplementation: any,
38 id: string,
39 exportName: string,
40 ): ClientReference<T> {
41 return Object.defineProperties(proxyImplementation, {
42 $$typeof: {value: CLIENT_REFERENCE_TAG},
43 $$id: {value: id + '#' + exportName},
44 });
45 }
46
47 // $FlowFixMe[method-unbinding]
48 const FunctionBind = Function.prototype.bind;
49 // $FlowFixMe[method-unbinding]
50 const ArraySlice = Array.prototype.slice;
51 function bind(this: ServerReference<any>): any {
52 // $FlowFixMe[incompatible-type]
53 const newFn = FunctionBind.apply(this, arguments);
54 if (this.$$typeof === SERVER_REFERENCE_TAG) {
55 if (__DEV__) {
56 const thisBind = arguments[0];
57 if (thisBind != null) {
58 console.error(
59 'Cannot bind "this" of a Server Action. Pass null or undefined as the first argument to .bind().',
60 );
61 }
62 }
63 const args = ArraySlice.call(arguments, 1);
64 const $$typeof = {value: SERVER_REFERENCE_TAG};
65 const $$id = {value: this.$$id};
66 const $$bound = {value: this.$$bound ? this.$$bound.concat(args) : args};
67 return Object.defineProperties(
68 newFn as any,
69 (__DEV__
70 ? {
71 $$typeof,
72 $$id,
73 $$bound,
74 $$location: {
75 value: this.$$location,
76 configurable: true,
77 },
78 bind: {value: bind, configurable: true},
79 }
80 : {
81 $$typeof,
82 $$id,
83 $$bound,
84 bind: {value: bind, configurable: true},
85 }) as PropertyDescriptorMap,
86 );
87 }
88 return newFn;
89 }
90
91 const serverReferenceToString = {
92 value: () => 'function () { [omitted code] }',
93 configurable: true,
94 writable: true,
95 };
96
97 export function registerServerReference<T: Function>(
98 reference: T,
99 id: string,
100 exportName: string,
101 ): ServerReference<T> {
102 const $$typeof = {value: SERVER_REFERENCE_TAG};
103 const $$id = {
104 value: id + '#' + exportName,
105 configurable: true,
106 };
107 const $$bound = {value: null, configurable: true};
108 return Object.defineProperties(
109 reference as any,
110 (__DEV__
111 ? {
112 $$typeof,
113 $$id,
114 $$bound,
115 $$location: {
116 value: Error('react-stack-top-frame'),
117 configurable: true,
118 },
119 bind: {value: bind, configurable: true},
120 toString: serverReferenceToString,
121 }
122 : {
123 $$typeof,
124 $$id,
125 $$bound,
126 bind: {value: bind, configurable: true},
127 toString: serverReferenceToString,
128 }) as PropertyDescriptorMap,
129 );
130 }