import type {
Wakeable,
Thenable,
FulfilledThenable,
RejectedThenable,
ReactDebugInfo,
ReactIOInfo,
} from 'shared/ReactTypes';
import {enableAsyncDebugInfo} from 'shared/ReactFeatureFlags';
import {REACT_LAZY_TYPE} from 'shared/ReactSymbols';
import noop from 'shared/noop';
const Uninitialized = -1;
const Pending = 0;
const Resolved = 1;
const Rejected = 2;
type UninitializedPayload<T> = {
_status: -1,
_result: () => Thenable<{default: T, ...}>,
_ioInfo?: ReactIOInfo,
};
type PendingPayload = {
_status: 0,
_result: Wakeable,
_ioInfo?: ReactIOInfo,
};
type ResolvedPayload<T> = {
_status: 1,
_result: {default: T, ...},
_ioInfo?: ReactIOInfo,
};
type RejectedPayload = {
_status: 2,
_result: mixed,
_ioInfo?: ReactIOInfo,
};
type Payload<T> =
| UninitializedPayload<T>
| PendingPayload
| ResolvedPayload<T>
| RejectedPayload;
export type LazyComponent<T, P> = {
$$typeof: symbol | number,
_payload: P,
_init: (payload: P) => T,
_debugInfo?: null | ReactDebugInfo,
_store?: {validated: 0 | 1 | 2, ...},
};
function lazyInitializer<T>(payload: Payload<T>): T {
if (payload._status === Uninitialized) {
let resolveDebugValue: (void | T) => void = (null: any);
let rejectDebugValue: mixed => void = (null: any);
if (__DEV__ && enableAsyncDebugInfo) {
const ioInfo = payload._ioInfo;
if (ioInfo != null) {
ioInfo.start = ioInfo.end = performance.now();
ioInfo.value = new Promise((resolve, reject) => {
resolveDebugValue = resolve;
rejectDebugValue = reject;
});
}
}
const ctor = payload._result;
const thenable = ctor();
thenable.then(
moduleObject => {
if (
(payload: Payload<T>)._status === Pending ||
payload._status === Uninitialized
) {
const resolved: ResolvedPayload<T> = (payload: any);
resolved._status = Resolved;
resolved._result = moduleObject;
if (__DEV__ && enableAsyncDebugInfo) {
const ioInfo = payload._ioInfo;
if (ioInfo != null) {
ioInfo.end = performance.now();
const debugValue =
moduleObject == null ? undefined : moduleObject.default;
resolveDebugValue(debugValue);
ioInfo.value.status = 'fulfilled';
ioInfo.value.value = debugValue;
}
}
if (thenable.status === undefined) {
const fulfilledThenable: FulfilledThenable<{default: T, ...}> =
(thenable: any);
fulfilledThenable.status = 'fulfilled';
fulfilledThenable.value = moduleObject;
}
}
},
error => {
if (
(payload: Payload<T>)._status === Pending ||
payload._status === Uninitialized
) {
const rejected: RejectedPayload = (payload: any);
rejected._status = Rejected;
rejected._result = error;
if (__DEV__ && enableAsyncDebugInfo) {
const ioInfo = payload._ioInfo;
if (ioInfo != null) {
ioInfo.end = performance.now();
ioInfo.value.then(noop, noop);
rejectDebugValue(error);
ioInfo.value.status = 'rejected';
ioInfo.value.reason = error;
}
}
if (thenable.status === undefined) {
const rejectedThenable: RejectedThenable<{default: T, ...}> =
(thenable: any);
rejectedThenable.status = 'rejected';
rejectedThenable.reason = error;
}
}
},
);
if (__DEV__ && enableAsyncDebugInfo) {
const ioInfo = payload._ioInfo;
if (ioInfo != null) {
const displayName = thenable.displayName;
if (typeof displayName === 'string') {
ioInfo.name = displayName;
}
}
}
if (payload._status === Uninitialized) {
const pending: PendingPayload = (payload: any);
pending._status = Pending;
pending._result = thenable;
}
}
if (payload._status === Resolved) {
const moduleObject = payload._result;
if (__DEV__) {
if (moduleObject === undefined) {
console.error(
'lazy: Expected the result of a dynamic imp' +
'ort() call. ' +
'Instead received: %s\n\nYour code should look like: \n ' +
'const MyComponent = lazy(() => imp' +
"ort('./MyComponent'))\n\n" +
'Did you accidentally put curly braces around the import?',
moduleObject,
);
}
}
if (__DEV__) {
if (!('default' in moduleObject)) {
console.error(
'lazy: Expected the result of a dynamic imp' +
'ort() call. ' +
'Instead received: %s\n\nYour code should look like: \n ' +
'const MyComponent = lazy(() => imp' +
"ort('./MyComponent'))",
moduleObject,
);
}
}
return moduleObject.default;
} else {
throw payload._result;
}
}
export function lazy<T>(
ctor: () => Thenable<{default: T, ...}>,
): LazyComponent<T, Payload<T>> {
const payload: Payload<T> = {
_status: Uninitialized,
_result: ctor,
};
const lazyType: LazyComponent<T, Payload<T>> = {
$$typeof: REACT_LAZY_TYPE,
_payload: payload,
_init: lazyInitializer,
};
if (__DEV__ && enableAsyncDebugInfo) {
const owner = null;
const ioInfo: ReactIOInfo = {
name: 'lazy',
start: -1,
end: -1,
value: null,
owner: owner,
debugStack: new Error('react-stack-top-frame'),
debugTask: console.createTask ? console.createTask('lazy()') : null,
};
payload._ioInfo = ioInfo;
lazyType._debugInfo = [{awaited: ioInfo}];
}
return lazyType;
}