@samitouri / QOS-React-2 / commits / e0241b6600

Simplify Webpack References by encoding file path + export name as single id (#26300)

We always look up these references in a map so it doesn't matter what their value is. It could be a hash for example. The loaders now encode a single $$id instead of filepath + name. This changes the react-client-manifest to have a single level. The value inside the map is still split into module id + export name because that's what gets looked up in webpack. The react-ssr-manifest is still two levels because that's a reverse lookup.

Sebastian Markbåge committed Mar 4, 2023 at 19:51 UTC e0241b66005a7232fa2c2529afa1bd5b27bf8925
18 files changed +120 -157
fixtures/flight/public/favicon.ico
Binary files /dev/null and b/fixtures/flight/public/favicon.ico differ
fixtures/flight/server/region.js
+2 -2
@@ -98,8 +98,8 @@ app.post('/', bodyParser.text(), async function (req, res) {
98 const {renderToPipeableStream} = await import(
99 'react-server-dom-webpack/server'
100 );
101 - const serverReference = JSON.parse(req.get('rsc-action'));
102 - const {filepath, name} = serverReference;
101 + const serverReference = req.get('rsc-action');
102 + const [filepath, name] = serverReference.split('#');
103 const action = (await import(filepath))[name];
104 // Validate that this is actually a function we intended to expose and
105 // not the client trying to invoke arbitrary functions. In a real app,
fixtures/flight/src/index.js
+1 -1
@@ -18,7 +18,7 @@ let data = ReactServerDOMReader.createFromFetch(
18 method: 'POST',
19 headers: {
20 Accept: 'text/x-component',
21 - 'rsc-action': JSON.stringify({filepath: id.id, name: id.name}),
21 + 'rsc-action': id,
22 },
23 body: JSON.stringify(args),
24 });
packages/react-client/src/ReactFlightClient.js
+6 -4
@@ -473,7 +473,7 @@ function createModelReject<T>(chunk: SomeChunk<T>): (error: mixed) => void {
473
474 function createServerReferenceProxy<A: Iterable<any>, T>(
475 response: Response,
476 - metaData: any,
476 + metaData: {id: any, bound: Thenable<Array<any>>},
477 ): (...A) => Promise<T> {
478 const callServer = response._callServer;
479 const proxy = function (): Promise<T> {
@@ -482,12 +482,14 @@ function createServerReferenceProxy<A: Iterable<any>, T>(
482 const p = metaData.bound;
483 if (p.status === INITIALIZED) {
484 const bound = p.value;
485 - return callServer(metaData, bound.concat(args));
485 + return callServer(metaData.id, bound.concat(args));
486 }
487 // Since this is a fake Promise whose .then doesn't chain, we have to wrap it.
488 // TODO: Remove the wrapper once that's fixed.
489 - return Promise.resolve(p).then(function (bound) {
490 - return callServer(metaData, bound.concat(args));
489 + return ((Promise.resolve(p): any): Promise<Array<any>>).then(function (
490 + bound,
491 + ) {
492 + return callServer(metaData.id, bound.concat(args));
493 });
494 };
495 return proxy;
packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js
+2 -2
@@ -19,7 +19,7 @@ import isArray from 'shared/isArray';
19
20 export type ClientReference<T> = JSResourceReference<T>;
21 export type ServerReference<T> = T;
22 -export type ServerReferenceMetadata = {};
22 +export type ServerReferenceId = {};
23
24 import type {
25 Destination,
@@ -69,7 +69,7 @@ export function resolveClientReferenceMetadata<T>(
69 export function resolveServerReferenceMetadata<T>(
70 config: BundlerConfig,
71 resource: ServerReference<T>,
72 -): ServerReferenceMetadata {
72 +): {id: ServerReferenceId, bound: Promise<Array<any>>} {
73 throw new Error('Not implemented.');
74 }
75
packages/react-server-dom-webpack/src/ReactFlightClientNodeBundlerConfig.js
-1
@@ -25,7 +25,6 @@ export opaque type ClientReferenceMetadata = {
25 id: string,
26 chunks: Array<string>,
27 name: string,
28 - async: boolean,
28 };
29
30 // eslint-disable-next-line no-unused-vars
packages/react-server-dom-webpack/src/ReactFlightDOMClientBrowser.js
+1 -4
@@ -20,10 +20,7 @@ import {
20 close,
21 } from 'react-client/src/ReactFlightClientStream';
22
23 -type CallServerCallback = <A, T>(
24 - {filepath: string, name: string},
25 - args: A,
26 -) => Promise<T>;
23 +type CallServerCallback = <A, T>(string, args: A) => Promise<T>;
24
25 export type Options = {
26 callServer?: CallServerCallback,
packages/react-server-dom-webpack/src/ReactFlightServerWebpackBundlerConfig.js
+10 -25
@@ -10,32 +10,24 @@
10 import type {ReactModel} from 'react-server/src/ReactFlightServer';
11
12 type WebpackMap = {
13 - [filepath: string]: {
14 - [name: string]: ClientReferenceMetadata,
15 - },
13 + [id: string]: ClientReferenceMetadata,
14 };
15
16 export type BundlerConfig = WebpackMap;
17
18 export type ServerReference<T: Function> = T & {
19 $$typeof: symbol,
22 - $$filepath: string,
23 - $$name: string,
20 + $$id: string,
21 $$bound: Array<ReactModel>,
22 };
23
27 -export type ServerReferenceMetadata = {
28 - id: string,
29 - name: string,
30 - bound: Promise<Array<ReactModel>>,
31 -};
24 +export type ServerReferenceId = string;
25
26 // eslint-disable-next-line no-unused-vars
27 export type ClientReference<T> = {
28 $$typeof: symbol,
36 - filepath: string,
37 - name: string,
38 - async: boolean,
29 + $$id: string,
30 + $$async: boolean,
31 };
32
33 export type ClientReferenceMetadata = {
@@ -53,12 +45,7 @@ const SERVER_REFERENCE_TAG = Symbol.for('react.server.reference');
45 export function getClientReferenceKey(
46 reference: ClientReference<any>,
47 ): ClientReferenceKey {
56 - return (
57 - reference.filepath +
58 - '#' +
59 - reference.name +
60 - (reference.async ? '#async' : '')
61 - );
48 + return reference.$$async ? reference.$$id + '#async' : reference.$$id;
49 }
50
51 export function isClientReference(reference: Object): boolean {
@@ -73,9 +60,8 @@ export function resolveClientReferenceMetadata<T>(
60 config: BundlerConfig,
61 clientReference: ClientReference<T>,
62 ): ClientReferenceMetadata {
76 - const resolvedModuleData =
77 - config[clientReference.filepath][clientReference.name];
78 - if (clientReference.async) {
63 + const resolvedModuleData = config[clientReference.$$id];
64 + if (clientReference.$$async) {
65 return {
66 id: resolvedModuleData.id,
67 chunks: resolvedModuleData.chunks,
@@ -90,10 +76,9 @@ export function resolveClientReferenceMetadata<T>(
76 export function resolveServerReferenceMetadata<T>(
77 config: BundlerConfig,
78 serverReference: ServerReference<T>,
93 -): ServerReferenceMetadata {
79 +): {id: ServerReferenceId, bound: Promise<Array<any>>} {
80 return {
95 - id: serverReference.$$filepath,
96 - name: serverReference.$$name,
81 + id: serverReference.$$id,
82 bound: Promise.resolve(serverReference.$$bound),
83 };
84 }
packages/react-server-dom-webpack/src/ReactFlightWebpackNodeLoader.js
+2 -4
@@ -187,8 +187,7 @@ function transformServerModule(
187 }
188 newSrc += 'Object.defineProperties(' + local + ',{';
189 newSrc += '$$typeof: {value: Symbol.for("react.server.reference")},';
190 - newSrc += '$$filepath: {value: ' + JSON.stringify(url) + '},';
191 - newSrc += '$$name: { value: ' + JSON.stringify(exported) + '},';
190 + newSrc += '$$id: {value: ' + JSON.stringify(url + '#' + exported) + '},';
191 newSrc += '$$bound: { value: [] }';
192 newSrc += '});\n';
193 });
@@ -343,9 +342,8 @@ async function transformClientModule(
342 ');';
343 }
344 newSrc += '},{';
346 - newSrc += 'name: { value: ' + JSON.stringify(name) + '},';
345 newSrc += '$$typeof: {value: CLIENT_REFERENCE},';
348 - newSrc += 'filepath: {value: ' + JSON.stringify(url) + '}';
346 + newSrc += '$$id: {value: ' + JSON.stringify(url + '#' + name) + '}';
347 newSrc += '});\n';
348 }
349 return newSrc;
packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js
+28 -50
@@ -29,8 +29,7 @@ module.exports = function register() {
29 // $FlowFixMe[method-unbinding]
30 const args = Array.prototype.slice.call(arguments, 1);
31 newFn.$$typeof = SERVER_REFERENCE;
32 - newFn.$$filepath = this.$$filepath;
33 - newFn.$$name = this.$$name;
32 + newFn.$$id = this.$$id;
33 newFn.$$bound = this.$$bound.concat(args);
34 }
35 return newFn;
@@ -44,14 +43,14 @@ module.exports = function register() {
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;
47 - case 'filepath':
48 - return target.filepath;
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;
53 - case 'async':
54 - return target.async;
54 // We need to special case this because createElement reads it if we pass this
55 // reference.
56 case 'defaultProps':
@@ -69,20 +68,8 @@ module.exports = function register() {
68 `that itself renders a Client Context Provider.`,
69 );
70 }
72 - let expression;
73 - switch (target.name) {
74 - case '':
75 - // eslint-disable-next-line react-internal/safe-string-coercion
76 - expression = String(name);
77 - break;
78 - case '*':
79 - // eslint-disable-next-line react-internal/safe-string-coercion
80 - expression = String(name);
81 - break;
82 - default:
83 - // eslint-disable-next-line react-internal/safe-string-coercion
84 - expression = String(target.name) + '.' + String(name);
85 - }
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. ' +
@@ -103,15 +90,13 @@ module.exports = function register() {
90 switch (name) {
91 // These names are read by the Flight runtime if you end up using the exports object.
92 case '$$typeof':
106 - // These names are a little too common. We should probably have a way to
107 - // have the Flight runtime extract the inner target instead.
93 return target.$$typeof;
109 - case 'filepath':
110 - return target.filepath;
94 + case '$$id':
95 + return target.$$id;
96 + case '$$async':
97 + return target.$$async;
98 case 'name':
99 return target.name;
113 - case 'async':
114 - return target.async;
100 // We need to special case this because createElement reads it if we pass this
101 // reference.
102 case 'defaultProps':
@@ -125,7 +110,7 @@ module.exports = function register() {
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.
128 - const moduleId = target.filepath;
113 + const moduleId = target.$$id;
114 target.default = Object.defineProperties(
115 (function () {
116 throw new Error(
@@ -136,12 +121,11 @@ module.exports = function register() {
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.
141 - name: {value: ''},
142 - $$typeof: {value: CLIENT_REFERENCE},
143 - filepath: {value: target.filepath},
144 - async: {value: target.async},
127 + $$id: {value: target.$$id + '#'},
128 + $$async: {value: target.$$async},
129 },
130 );
131 return true;
@@ -150,17 +134,15 @@ module.exports = function register() {
134 // Use a cached value
135 return target.then;
136 }
153 - if (!target.async) {
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), {
159 - // Represents the whole Module object instead of a particular import.
160 - name: {value: '*'},
143 $$typeof: {value: CLIENT_REFERENCE},
162 - filepath: {value: target.filepath},
163 - async: {value: true},
144 + $$id: {value: target.$$id},
145 + $$async: {value: true},
146 });
147 const proxy = new Proxy(clientReference, proxyHandlers);
148
@@ -176,10 +158,9 @@ module.exports = function register() {
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 {
179 - name: {value: 'then'},
161 $$typeof: {value: CLIENT_REFERENCE},
181 - filepath: {value: target.filepath},
182 - async: {value: false},
162 + $$id: {value: target.$$id + '#then'},
163 + $$async: {value: false},
164 },
165 ));
166 return then;
@@ -206,8 +187,8 @@ module.exports = function register() {
187 {
188 name: {value: name},
189 $$typeof: {value: CLIENT_REFERENCE},
209 - filepath: {value: target.filepath},
210 - async: {value: target.async},
190 + $$id: {value: target.$$id + '#' + name},
191 + $$async: {value: target.$$async},
192 },
193 );
194 cachedReference = target[name] = new Proxy(
@@ -284,11 +265,10 @@ module.exports = function register() {
265 if (useClient) {
266 const moduleId: string = (url.pathToFileURL(filename).href: any);
267 const clientReference = Object.defineProperties(({}: any), {
287 - // Represents the whole Module object instead of a particular import.
288 - name: {value: '*'},
268 $$typeof: {value: CLIENT_REFERENCE},
290 - filepath: {value: moduleId},
291 - async: {value: false},
269 + // Represents the whole Module object instead of a particular import.
270 + $$id: {value: moduleId},
271 + $$async: {value: false},
272 });
273 // $FlowFixMe[incompatible-call] found when upgrading Flow
274 this.exports = new Proxy(clientReference, proxyHandlers);
@@ -306,10 +286,9 @@ module.exports = function register() {
286 if (typeof exports === 'function') {
287 // The module exports a function directly,
288 Object.defineProperties((exports: any), {
309 - // Represents the whole Module object instead of a particular import.
289 $$typeof: {value: SERVER_REFERENCE},
311 - $$filepath: {value: moduleId},
312 - $$name: {value: '*'},
290 + // Represents the whole Module object instead of a particular import.
291 + $$id: {value: moduleId},
292 $$bound: {value: []},
293 });
294 } else {
@@ -320,8 +299,7 @@ module.exports = function register() {
299 if (typeof value === 'function') {
300 Object.defineProperties((value: any), {
301 $$typeof: {value: SERVER_REFERENCE},
323 - $$filepath: {value: moduleId},
324 - $$name: {value: key},
302 + $$id: {value: moduleId + '#' + key},
303 $$bound: {value: []},
304 });
305 }
packages/react-server-dom-webpack/src/ReactFlightWebpackPlugin.js
+28 -20
@@ -220,9 +220,7 @@ export default class ReactFlightWebpackPlugin {
220 }
221
222 const clientManifest: {
223 - [string]: {
224 - [string]: {chunks: $FlowFixMe, id: string, name: string},
225 - },
223 + [string]: {chunks: $FlowFixMe, id: string, name: string},
224 } = {};
225 const ssrManifest: {
226 [string]: {
@@ -248,25 +246,35 @@ export default class ReactFlightWebpackPlugin {
246 .getExportsInfo(module)
247 .getProvidedExports();
248
251 - const clientExports: {
252 - [string]: {chunks: $FlowFixMe, id: $FlowFixMe, name: string},
253 - } = {};
254 -
255 - const ssrExports: {
256 - [string]: {specifier: string, name: string},
257 - } = {};
258 -
249 const href = pathToFileURL(module.resource).href;
250
251 if (href !== undefined) {
262 - ['', '*']
263 - .concat(
264 - Array.isArray(moduleProvidedExports)
265 - ? moduleProvidedExports
266 - : [],
267 - )
268 - .forEach(function (name) {
269 - clientExports[name] = {
252 + const ssrExports: {
253 + [string]: {specifier: string, name: string},
254 + } = {};
255 +
256 + clientManifest[href] = {
257 + id,
258 + chunks: chunkIds,
259 + name: '*',
260 + };
261 + ssrExports['*'] = {
262 + specifier: href,
263 + name: '*',
264 + };
265 + clientManifest[href + '#'] = {
266 + id,
267 + chunks: chunkIds,
268 + name: '',
269 + };
270 + ssrExports[''] = {
271 + specifier: href,
272 + name: '',
273 + };
274 +
275 + if (Array.isArray(moduleProvidedExports)) {
276 + moduleProvidedExports.forEach(function (name) {
277 + clientManifest[href + '#' + name] = {
278 id,
279 chunks: chunkIds,
280 name: name,
@@ -276,8 +284,8 @@ export default class ReactFlightWebpackPlugin {
284 name: name,
285 };
286 });
287 + }
288
280 - clientManifest[href] = clientExports;
289 ssrManifest[id] = ssrExports;
290 }
291 }
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
+1 -1
@@ -755,7 +755,7 @@ describe('ReactFlightDOMBrowser', () => {
755 });
756
757 function requireServerRef(ref) {
758 - const metaData = webpackServerMap[ref.id][ref.name];
758 + const metaData = webpackServerMap[ref];
759 const mod = __webpack_require__(metaData.id);
760 if (metaData.name === '*') {
761 return mod;
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js
+2 -2
@@ -61,12 +61,12 @@ describe('ReactFlightDOMEdge', () => {
61 const ClientComponentOnTheServer = clientExports(ClientComponent);
62
63 // In the SSR bundle this module won't exist. We simulate this by deleting it.
64 - const clientId = webpackMap[ClientComponentOnTheClient.filepath]['*'].id;
64 + const clientId = webpackMap[ClientComponentOnTheClient.$$id].id;
65 delete webpackModules[clientId];
66
67 // Instead, we have to provide a translation from the client meta data to the SSR
68 // meta data.
69 - const ssrMetadata = webpackMap[ClientComponentOnTheServer.filepath]['*'];
69 + const ssrMetadata = webpackMap[ClientComponentOnTheServer.$$id];
70 const translationMap = {
71 [clientId]: {
72 '*': ssrMetadata,
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMNode-test.js
+2 -2
@@ -67,12 +67,12 @@ describe('ReactFlightDOMNode', () => {
67 const ClientComponentOnTheServer = clientExports(ClientComponent);
68
69 // In the SSR bundle this module won't exist. We simulate this by deleting it.
70 - const clientId = webpackMap[ClientComponentOnTheClient.filepath]['*'].id;
70 + const clientId = webpackMap[ClientComponentOnTheClient.$$id].id;
71 delete webpackModules[clientId];
72
73 // Instead, we have to provide a translation from the client meta data to the SSR
74 // meta data.
75 - const ssrMetadata = webpackMap[ClientComponentOnTheServer.filepath]['*'];
75 + const ssrMetadata = webpackMap[ClientComponentOnTheServer.$$id];
76 const translationMap = {
77 [clientId]: {
78 '*': ssrMetadata,
packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js
+27 -33
@@ -48,16 +48,14 @@ exports.clientModuleError = function clientModuleError(moduleError) {
48 webpackErroredModules[idx] = moduleError;
49 const path = url.pathToFileURL(idx).href;
50 webpackClientMap[path] = {
51 - '': {
52 - id: idx,
53 - chunks: [],
54 - name: '',
55 - },
56 - '*': {
57 - id: idx,
58 - chunks: [],
59 - name: '*',
60 - },
51 + id: idx,
52 + chunks: [],
53 + name: '*',
54 + };
55 + webpackClientMap[path + '#'] = {
56 + id: idx,
57 + chunks: [],
58 + name: '',
59 };
60 const mod = {exports: {}};
61 nodeCompile.call(mod, '"use client"', idx);
@@ -69,22 +67,20 @@ exports.clientExports = function clientExports(moduleExports) {
67 webpackClientModules[idx] = moduleExports;
68 const path = url.pathToFileURL(idx).href;
69 webpackClientMap[path] = {
72 - '': {
73 - id: idx,
74 - chunks: [],
75 - name: '',
76 - },
77 - '*': {
78 - id: idx,
79 - chunks: [],
80 - name: '*',
81 - },
70 + id: idx,
71 + chunks: [],
72 + name: '*',
73 + };
74 + webpackClientMap[path + '#'] = {
75 + id: idx,
76 + chunks: [],
77 + name: '',
78 };
79 if (typeof moduleExports.then === 'function') {
80 moduleExports.then(
81 asyncModuleExports => {
82 for (const name in asyncModuleExports) {
87 - webpackClientMap[path][name] = {
83 + webpackClientMap[path + '#' + name] = {
84 id: idx,
85 chunks: [],
86 name: name,
@@ -95,7 +91,7 @@ exports.clientExports = function clientExports(moduleExports) {
91 );
92 }
93 for (const name in moduleExports) {
98 - webpackClientMap[path][name] = {
94 + webpackClientMap[path + '#' + name] = {
95 id: idx,
96 chunks: [],
97 name: name,
@@ -112,19 +108,17 @@ exports.serverExports = function serverExports(moduleExports) {
108 webpackServerModules[idx] = moduleExports;
109 const path = url.pathToFileURL(idx).href;
110 webpackServerMap[path] = {
115 - '': {
116 - id: idx,
117 - chunks: [],
118 - name: '',
119 - },
120 - '*': {
121 - id: idx,
122 - chunks: [],
123 - name: '*',
124 - },
111 + id: idx,
112 + chunks: [],
113 + name: '*',
114 + };
115 + webpackServerMap[path + '#'] = {
116 + id: idx,
117 + chunks: [],
118 + name: '',
119 };
120 for (const name in moduleExports) {
127 - webpackServerMap[path][name] = {
121 + webpackServerMap[path + '#' + name] = {
122 id: idx,
123 chunks: [],
124 name: name,
packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js
+2 -2
@@ -16,7 +16,7 @@ import JSResourceReferenceImpl from 'JSResourceReferenceImpl';
16
17 export type ClientReference<T> = JSResourceReference<T>;
18 export type ServerReference<T> = T;
19 -export type ServerReferenceMetadata = {};
19 +export type ServerReferenceId = {};
20
21 import type {
22 Destination,
@@ -66,7 +66,7 @@ export function resolveClientReferenceMetadata<T>(
66 export function resolveServerReferenceMetadata<T>(
67 config: BundlerConfig,
68 resource: ServerReference<T>,
69 -): ServerReferenceMetadata {
69 +): {id: ServerReferenceId, bound: Promise<Array<any>>} {
70 throw new Error('Not implemented.');
71 }
72
packages/react-server/src/ReactFlightServer.js
+5 -3
@@ -15,7 +15,7 @@ import type {
15 ClientReference,
16 ClientReferenceKey,
17 ServerReference,
18 - ServerReferenceMetadata,
18 + ServerReferenceId,
19 } from './ReactFlightServerConfig';
20 import type {ContextSnapshot} from './ReactFlightNewContext';
21 import type {ThenableState} from './ReactFlightThenable';
@@ -590,8 +590,10 @@ function serializeServerReference(
590 if (existingId !== undefined) {
591 return serializeServerReferenceID(existingId);
592 }
593 - const serverReferenceMetadata: ServerReferenceMetadata =
594 - resolveServerReferenceMetadata(request.bundlerConfig, serverReference);
593 + const serverReferenceMetadata: {
594 + id: ServerReferenceId,
595 + bound: Promise<Array<any>>,
596 + } = resolveServerReferenceMetadata(request.bundlerConfig, serverReference);
597 request.pendingChunks++;
598 const metadataId = request.nextChunkId++;
599 // We assume that this object doesn't suspend.
packages/react-server/src/ReactFlightServerBundlerConfigCustom.js
+1 -1
@@ -13,7 +13,7 @@ export opaque type BundlerConfig = mixed;
13 export opaque type ClientReference<T> = mixed; // eslint-disable-line no-unused-vars
14 export opaque type ServerReference<T> = mixed; // eslint-disable-line no-unused-vars
15 export opaque type ClientReferenceMetadata: any = mixed;
16 -export opaque type ServerReferenceMetadata: any = mixed;
16 +export opaque type ServerReferenceId: any = mixed;
17 export opaque type ClientReferenceKey: any = mixed;
18 export const isClientReference = $$$hostConfig.isClientReference;
19 export const isServerReference = $$$hostConfig.isServerReference;