main
js 131 lines 3.42 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 $$hblp: mixed,
24 };
25
26 const CLIENT_REFERENCE_TAG = Symbol.for('react.client.reference');
27 const SERVER_REFERENCE_TAG = Symbol.for('react.server.reference');
28
29 export function isClientReference(reference: Object): boolean {
30 return reference.$$typeof === CLIENT_REFERENCE_TAG;
31 }
32
33 export function isServerReference(reference: Object): boolean {
34 return reference.$$typeof === SERVER_REFERENCE_TAG;
35 }
36
37 export function registerClientReference<T>(
38 proxyImplementation: any,
39 id: string,
40 hblp: mixed,
41 ): ClientReference<T> {
42 return Object.defineProperties(proxyImplementation, {
43 $$typeof: {value: CLIENT_REFERENCE_TAG},
44 $$id: {value: id},
45 $$hblp: {value: hblp},
46 });
47 }
48
49 // $FlowFixMe[method-unbinding]
50 const FunctionBind = Function.prototype.bind;
51 // $FlowFixMe[method-unbinding]
52 const ArraySlice = Array.prototype.slice;
53 function bind(this: ServerReference<any>): any {
54 // $FlowFixMe[incompatible-type]
55 const newFn = FunctionBind.apply(this, arguments);
56 if (this.$$typeof === SERVER_REFERENCE_TAG) {
57 if (__DEV__) {
58 const thisBind = arguments[0];
59 if (thisBind != null) {
60 console.error(
61 'Cannot bind "this" of a Server Action. Pass null or undefined as the first argument to .bind().',
62 );
63 }
64 }
65 const args = ArraySlice.call(arguments, 1);
66 const $$typeof = {value: SERVER_REFERENCE_TAG};
67 const $$id = {value: this.$$id};
68 const $$bound = {value: this.$$bound ? this.$$bound.concat(args) : args};
69 return Object.defineProperties(
70 newFn as any,
71 (__DEV__
72 ? {
73 $$typeof,
74 $$id,
75 $$bound,
76 $$location: {
77 value: this.$$location,
78 configurable: true,
79 },
80 bind: {value: bind, configurable: true},
81 }
82 : {
83 $$typeof,
84 $$id,
85 $$bound,
86 bind: {value: bind, configurable: true},
87 }) as PropertyDescriptorMap,
88 );
89 }
90 return newFn;
91 }
92
93 const serverReferenceToString = {
94 value: () => 'function () { [omitted code] }',
95 configurable: true,
96 writable: true,
97 };
98
99 export function registerServerReference<T: Function>(
100 reference: T,
101 id: string,
102 ): ServerReference<T> {
103 const $$typeof = {value: SERVER_REFERENCE_TAG};
104 const $$id = {
105 value: id,
106 configurable: true,
107 };
108 const $$bound = {value: null, configurable: true};
109 return Object.defineProperties(
110 reference as any,
111 (__DEV__
112 ? {
113 $$typeof,
114 $$id,
115 $$bound,
116 $$location: {
117 value: Error('react-stack-top-frame'),
118 configurable: true,
119 },
120 bind: {value: bind, configurable: true},
121 toString: serverReferenceToString,
122 }
123 : {
124 $$typeof,
125 $$id,
126 $$bound,
127 bind: {value: bind, configurable: true},
128 toString: serverReferenceToString,
129 }) as PropertyDescriptorMap,
130 );
131 }