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