14
const Module = require('module');
15
16
module.exports = function register() {
17
- const CLIENT_REFERENCE = Symbol.for('react.client.reference');
18
- const SERVER_REFERENCE = Symbol.for('react.server.reference');
19
- const PROMISE_PROTOTYPE = Promise.prototype;
20
-
21
- // Patch bind on the server to ensure that this creates another
22
- // bound server reference with the additional arguments.
23
- const originalBind = Function.prototype.bind;
24
- /*eslint-disable no-extend-native */
25
- Function.prototype.bind = (function bind(this: any, self: any) {
26
- // $FlowFixMe[unsupported-syntax]
27
- const newFn = originalBind.apply(this, arguments);
28
- if (this.$$typeof === SERVER_REFERENCE) {
29
- // $FlowFixMe[method-unbinding]
30
- const args = Array.prototype.slice.call(arguments, 1);
31
- newFn.$$typeof = SERVER_REFERENCE;
32
- newFn.$$id = this.$$id;
33
- newFn.$$bound = this.$$bound ? this.$$bound.concat(args) : args;
34
- }
35
- return newFn;
36
- }: any);
37
-
38
- const deepProxyHandlers = {
39
- get: function (target: Function, name: string, receiver: Proxy<Function>) {
40
- switch (name) {
41
- // These names are read by the Flight runtime if you end up using the exports object.
42
- case '$$typeof':
43
- // These names are a little too common. We should probably have a way to
44
- // have the Flight runtime extract the inner target instead.
45
- return target.$$typeof;
46
- case '$$id':
47
- return target.$$id;
48
- case '$$async':
49
- return target.$$async;
50
- case 'name':
51
- return target.name;
52
- case 'displayName':
53
- return undefined;
54
- // We need to special case this because createElement reads it if we pass this
55
- // reference.
56
- case 'defaultProps':
57
- return undefined;
58
- // Avoid this attempting to be serialized.
59
- case 'toJSON':
60
- return undefined;
61
- case Symbol.toPrimitive:
62
- // $FlowFixMe[prop-missing]
63
- return Object.prototype[Symbol.toPrimitive];
64
- case 'Provider':
65
- throw new Error(
66
- `Cannot render a Client Context Provider on the Server. ` +
67
- `Instead, you can export a Client Component wrapper ` +
68
- `that itself renders a Client Context Provider.`,
69
- );
70
- }
71
- // eslint-disable-next-line react-internal/safe-string-coercion
72
- const expression = String(target.name) + '.' + String(name);
73
- throw new Error(
74
- `Cannot access ${expression} on the server. ` +
75
- 'You cannot dot into a client module from a server component. ' +
76
- 'You can only pass the imported name through.',
77
- );
78
- },
79
- set: function () {
80
- throw new Error('Cannot assign to a client module from a server module.');
81
- },
82
- };
83
-
84
- const proxyHandlers = {
85
- get: function (
86
- target: Function,
87
- name: string,
88
- receiver: Proxy<Function>,
89
- ): $FlowFixMe {
90
- switch (name) {
91
- // These names are read by the Flight runtime if you end up using the exports object.
92
- case '$$typeof':
93
- return target.$$typeof;
94
- case '$$id':
95
- return target.$$id;
96
- case '$$async':
97
- return target.$$async;
98
- case 'name':
99
- return target.name;
100
- // We need to special case this because createElement reads it if we pass this
101
- // reference.
102
- case 'defaultProps':
103
- return undefined;
104
- // Avoid this attempting to be serialized.
105
- case 'toJSON':
106
- return undefined;
107
- case Symbol.toPrimitive:
108
- // $FlowFixMe[prop-missing]
109
- return Object.prototype[Symbol.toPrimitive];
110
- case '__esModule':
111
- // Something is conditionally checking which export to use. We'll pretend to be
112
- // an ESM compat module but then we'll check again on the client.
113
- const moduleId = target.$$id;
114
- target.default = Object.defineProperties(
115
- (function () {
116
- throw new Error(
117
- `Attempted to call the default export of ${moduleId} from the server ` +
118
- `but it's on the client. It's not possible to invoke a client function from ` +
119
- `the server, it can only be rendered as a Component or passed to props of a ` +
120
- `Client Component.`,
121
- );
122
- }: any),
123
- {
124
- $$typeof: {value: CLIENT_REFERENCE},
125
- // This a placeholder value that tells the client to conditionally use the
126
- // whole object or just the default export.
127
- $$id: {value: target.$$id + '#'},
128
- $$async: {value: target.$$async},
129
- },
130
- );
131
- return true;
132
- case 'then':
133
- if (target.then) {
134
- // Use a cached value
135
- return target.then;
136
- }
137
- if (!target.$$async) {
138
- // If this module is expected to return a Promise (such as an AsyncModule) then
139
- // we should resolve that with a client reference that unwraps the Promise on
140
- // the client.
141
-
142
- const clientReference = Object.defineProperties(({}: any), {
143
- $$typeof: {value: CLIENT_REFERENCE},
144
- $$id: {value: target.$$id},
145
- $$async: {value: true},
146
- });
147
- const proxy = new Proxy(clientReference, proxyHandlers);
148
-
149
- // Treat this as a resolved Promise for React's use()
150
- target.status = 'fulfilled';
151
- target.value = proxy;
152
-
153
- const then = (target.then = Object.defineProperties(
154
- (function then(resolve, reject: any) {
155
- // Expose to React.
156
- return Promise.resolve(resolve(proxy));
157
- }: any),
158
- // If this is not used as a Promise but is treated as a reference to a `.then`
159
- // export then we should treat it as a reference to that name.
160
- {
161
- $$typeof: {value: CLIENT_REFERENCE},
162
- $$id: {value: target.$$id + '#then'},
163
- $$async: {value: false},
164
- },
165
- ));
166
- return then;
167
- } else {
168
- // Since typeof .then === 'function' is a feature test we'd continue recursing
169
- // indefinitely if we return a function. Instead, we return an object reference
170
- // if we check further.
171
- return undefined;
172
- }
173
- }
174
- let cachedReference = target[name];
175
- if (!cachedReference) {
176
- const reference = Object.defineProperties(
177
- (function () {
178
- throw new Error(
179
- // eslint-disable-next-line react-internal/safe-string-coercion
180
- `Attempted to call ${String(name)}() from the server but ${String(
181
- name,
182
- )} is on the client. ` +
183
- `It's not possible to invoke a client function from the server, it can ` +
184
- `only be rendered as a Component or passed to props of a Client Component.`,
185
- );
186
- }: any),
187
- {
188
- name: {value: name},
189
- $$typeof: {value: CLIENT_REFERENCE},
190
- $$id: {value: target.$$id + '#' + name},
191
- $$async: {value: target.$$async},
192
- },
193
- );
194
- cachedReference = target[name] = new Proxy(
195
- reference,
196
- deepProxyHandlers,
197
- );
198
- }
199
- return cachedReference;
200
- },
201
- getPrototypeOf(target: Function): Object {
202
- // Pretend to be a Promise in case anyone asks.
203
- return PROMISE_PROTOTYPE;
204
- },
205
- set: function (): empty {
206
- throw new Error('Cannot assign to a client module from a server module.');
207
- },
208
- };
17
+ const Server: any = require('react-server-dom-webpack/server');
18
+ const registerServerReference = Server.registerServerReference;
19
+ const createClientModuleProxy = Server.createClientModuleProxy;
20
21
// $FlowFixMe[prop-missing] found when upgrading Flow
22
const originalCompile = Module.prototype._compile;
75
76
if (useClient) {
77
const moduleId: string = (url.pathToFileURL(filename).href: any);
267
- const clientReference = Object.defineProperties(({}: any), {
268
- $$typeof: {value: CLIENT_REFERENCE},
269
- // Represents the whole Module object instead of a particular import.
270
- $$id: {value: moduleId},
271
- $$async: {value: false},
272
- });
273
- this.exports = new Proxy(clientReference, proxyHandlers);
78
+ this.exports = createClientModuleProxy(moduleId);
79
}
80
81
if (useServer) {
89
// reference. If there are any functions in the export.
90
if (typeof exports === 'function') {
91
// The module exports a function directly,
287
- Object.defineProperties((exports: any), {
288
- $$typeof: {value: SERVER_REFERENCE},
92
+ registerServerReference(
93
+ (exports: any),
94
+ moduleId,
95
// Represents the whole Module object instead of a particular import.
290
- $$id: {value: moduleId},
291
- $$bound: {value: null},
292
- });
96
+ null,
97
+ );
98
} else {
99
const keys = Object.keys(exports);
100
for (let i = 0; i < keys.length; i++) {
101
const key = keys[i];
102
const value = exports[keys[i]];
103
if (typeof value === 'function') {
299
- Object.defineProperties((value: any), {
300
- $$typeof: {value: SERVER_REFERENCE},
301
- $$id: {value: moduleId + '#' + key},
302
- $$bound: {value: null},
303
- });
104
+ registerServerReference((value: any), moduleId, key);
105
}
106
}
107
}