import type {
Thenable,
FulfilledThenable,
RejectedThenable,
ReactDebugInfo,
ReactIOInfo,
ReactAsyncInfo,
} from 'shared/ReactTypes';
import type {ModuleLoading} from 'react-client/src/ReactFlightClientConfig';
import {canUseDOM} from 'shared/ExecutionEnvironment';
export type ServerConsumerModuleMap = null;
export type ServerManifest = null;
export type ServerReferenceId = string;
import {prepareDestinationForModuleImpl} from 'react-client/src/ReactFlightClientConfig';
export opaque type ClientReferenceMetadata = {
$$typeof: symbol,
$$id: string,
$$hblp: mixed,
};
export opaque type ClientReference<T> = {
$$typeof: symbol,
$$id: string,
$$hblp: mixed,
};
export function prepareDestinationForModule(
moduleLoading: ModuleLoading,
nonce: ?string,
metadata: ClientReferenceMetadata,
) {
prepareDestinationForModuleImpl(moduleLoading, metadata.$$hblp, nonce);
}
export function resolveClientReference<T>(
bundlerConfig: ServerConsumerModuleMap,
metadata: ClientReferenceMetadata,
): ClientReference<T> {
return metadata;
}
export function resolveServerReference<T>(
config: ServerManifest,
id: ServerReferenceId,
): ClientReference<T> {
return ({
$$typeof: Symbol.for('react.client.reference'),
$$id: id,
$$hblp: null,
}: any);
}
const asyncModuleCache: Map<string, Thenable<any>> = new Map();
export function preloadModule<T>(
metadata: ClientReference<T>,
): null | Thenable<any> {
if (!canUseDOM) {
return null;
}
const jsr: any = require('JSResource')(metadata.$$id);
const previouslyLoadedModule = jsr.getModuleIfRequireable();
if (previouslyLoadedModule != null) {
return null;
}
if (metadata.$$hblp != null) {
window.Bootloader.handlePayload(metadata.$$hblp);
}
const modulePromise: Thenable<T> = jsr.load();
modulePromise.then(
value => {
const fulfilledThenable: FulfilledThenable<mixed> = (modulePromise: any);
fulfilledThenable.status = 'fulfilled';
fulfilledThenable.value = value;
},
reason => {
const rejectedThenable: RejectedThenable<mixed> = (modulePromise: any);
rejectedThenable.status = 'rejected';
rejectedThenable.reason = reason;
},
);
asyncModuleCache.set(metadata.$$id, modulePromise);
return modulePromise;
}
export function requireModule<T>(metadata: ClientReference<T>): T {
if (!canUseDOM) {
const id = metadata.$$id;
const idx = id.lastIndexOf('#');
if (idx !== -1) {
const moduleName = id.slice(0, idx);
const exportName = id.slice(idx + 1);
const mod = require.call(null, moduleName);
if (exportName === '' || exportName === 'default') {
return mod.__esModule ? mod.default : mod;
}
return mod[exportName];
}
return require.call(null, id);
}
const jsr: any = require('JSResource')(metadata.$$id);
const moduleExports = jsr.getModuleIfRequireable();
if (moduleExports != null) {
return moduleExports;
}
const promise: any = asyncModuleCache.get(metadata.$$id);
if (promise && promise.status === 'fulfilled') {
return promise.value;
} else {
throw promise.reason;
}
}
const moduleIOInfoCache: Map<string, ReactIOInfo> = __DEV__
? new Map()
: (null: any);
export function getModuleDebugInfo<T>(
metadata: ClientReference<T>,
): null | ReactDebugInfo {
if (!__DEV__) {
return null;
}
const filename = metadata.$$id;
let ioInfo = moduleIOInfoCache.get(filename);
if (ioInfo === undefined) {
let href;
try {
href = new URL(filename, document.baseURI).href;
} catch (_) {
href = filename;
}
let start = -1;
let end = -1;
let byteSize = 0;
if (typeof performance.getEntriesByType === 'function') {
const resourceEntries = performance.getEntriesByType('resource');
for (let i = 0; i < resourceEntries.length; i++) {
const resourceEntry = resourceEntries[i];
if (resourceEntry.name === href) {
start = resourceEntry.startTime;
end = start + resourceEntry.duration;
byteSize = (resourceEntry.transferSize: any) || 0;
}
}
}
const value = Promise.resolve(href);
value.status = 'fulfilled';
value.value = href;
const fakeStack = new Error('react-stack-top-frame');
if (fakeStack.stack.startsWith('Error: react-stack-top-frame')) {
fakeStack.stack =
'Error: react-stack-top-frame\n' +
' at Client Component Bundle (' +
href +
':1:1)\n' +
' at Client Component Bundle (' +
href +
':1:1)';
} else {
fakeStack.stack =
'Client Component Bundle@' +
href +
':1:1\n' +
'Client Component Bundle@' +
href +
':1:1';
}
ioInfo = ({
name: 'script',
start: start,
end: end,
value: value,
debugStack: fakeStack,
}: ReactIOInfo);
if (byteSize > 0) {
ioInfo.byteSize = byteSize;
}
moduleIOInfoCache.set(filename, ioInfo);
}
const asyncInfo: ReactAsyncInfo = {
awaited: ioInfo,
};
return [asyncInfo];
}