mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
Stacked on https://github.com/facebook/react/pull/30400 and https://github.com/facebook/react/pull/30369 Previously we were using fake evals to recreate a stack for console replaying and thrown errors. However, for owner stacks we just used the raw string that came from the server. This means that the format of the owner stack could include different formats. Like Spidermonkey format for the client components and V8 for the server components. This means that this stack can't be parsed natively by the browser like when printing them as error like in https://github.com/facebook/react/pull/30289. Additionally, since there's no source file registered with that name and no source mapping url, it can't be source mapped. Before: <img width="1329" alt="before-firefox" src="https://github.com/user-attachments/assets/cbe03f9c-96ac-48fb-b58f-f3a224a774f4"> Instead, we need to create a fake stack like we do for the other things. That way when it's printed as an Error it gets source mapped. It also means that the format is consistently in the native format of the current browser. After: <img width="753" alt="after-firefox" src="https://github.com/user-attachments/assets/b436f1f5-ca37-4203-b29f-df9828c9fad3"> So this is nice because you can just take the result from `captureOwnerStack()` and append it to an `Error` stack and print it natively. E.g. this is what React DevTools will do. If you want to parse and present it yourself though it's a bit awkward though. The `captureOwnerStack()` API now includes a bunch of `rsc://React/` URLs. These don't really have any direct connection to the source map. Only the browser knows this connection from the eval. You basically have to strip the prefix and then manually pass the remainder to your own `findSourceMapURL`. Another awkward part is that since Safari doesn't support eval sourceURL exposed into `error.stack` - it means that `captureOwnerStack()` get an empty location for server components since the fake eval doesn't work there. That's not a big deal since these stacks are already broken even for client modules for many because the `eval-source-map` strategy in Webpack doesn't work in Safari for this same reason. A lot of this refactoring is just clarifying that there's three kind of ReactComponentInfo fields: - `stack` - The raw stack as described on the original server. - `debugStack` - The Error object containing the stack as represented in the current client as fake evals. - `debugTask` - The same thing as `debugStack` but described in terms of a native `console.createTask`.
198 lines
5.0 KiB
JavaScript
198 lines
5.0 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
export type ReactNode =
|
|
| React$Element<any>
|
|
| ReactPortal
|
|
| ReactText
|
|
| ReactFragment
|
|
| ReactProvider<any>
|
|
| ReactConsumer<any>;
|
|
|
|
export type ReactEmpty = null | void | boolean;
|
|
|
|
export type ReactFragment = ReactEmpty | Iterable<React$Node>;
|
|
|
|
export type ReactNodeList = ReactEmpty | React$Node;
|
|
|
|
export type ReactText = string | number;
|
|
|
|
export type ReactProvider<T> = {
|
|
$$typeof: symbol | number,
|
|
type: ReactContext<T>,
|
|
key: null | string,
|
|
ref: null,
|
|
props: {
|
|
value: T,
|
|
children?: ReactNodeList,
|
|
},
|
|
};
|
|
|
|
export type ReactConsumerType<T> = {
|
|
$$typeof: symbol | number,
|
|
_context: ReactContext<T>,
|
|
};
|
|
|
|
export type ReactConsumer<T> = {
|
|
$$typeof: symbol | number,
|
|
type: ReactConsumerType<T>,
|
|
key: null | string,
|
|
ref: null,
|
|
props: {
|
|
children: (value: T) => ReactNodeList,
|
|
},
|
|
};
|
|
|
|
export type ReactContext<T> = {
|
|
$$typeof: symbol | number,
|
|
Consumer: ReactConsumerType<T>,
|
|
Provider: ReactContext<T>,
|
|
_currentValue: T,
|
|
_currentValue2: T,
|
|
_threadCount: number,
|
|
// DEV only
|
|
_currentRenderer?: Object | null,
|
|
_currentRenderer2?: Object | null,
|
|
// This value may be added by application code
|
|
// to improve DEV tooling display names
|
|
displayName?: string,
|
|
};
|
|
|
|
export type ReactPortal = {
|
|
$$typeof: symbol | number,
|
|
key: null | string,
|
|
containerInfo: any,
|
|
children: ReactNodeList,
|
|
// TODO: figure out the API for cross-renderer implementation.
|
|
implementation: any,
|
|
};
|
|
|
|
export type RefObject = {
|
|
current: any,
|
|
};
|
|
|
|
export type ReactScope = {
|
|
$$typeof: symbol | number,
|
|
};
|
|
|
|
export type ReactScopeQuery = (
|
|
type: string,
|
|
props: {[string]: mixed},
|
|
instance: mixed,
|
|
) => boolean;
|
|
|
|
export type ReactScopeInstance = {
|
|
DO_NOT_USE_queryAllNodes(ReactScopeQuery): null | Array<Object>,
|
|
DO_NOT_USE_queryFirstNode(ReactScopeQuery): null | Object,
|
|
containsNode(Object): boolean,
|
|
getChildContextValues: <T>(context: ReactContext<T>) => Array<T>,
|
|
};
|
|
|
|
// The subset of a Thenable required by things thrown by Suspense.
|
|
// This doesn't require a value to be passed to either handler.
|
|
export interface Wakeable {
|
|
then(onFulfill: () => mixed, onReject: () => mixed): void | Wakeable;
|
|
}
|
|
|
|
// The subset of a Promise that React APIs rely on. This resolves a value.
|
|
// This doesn't require a return value neither from the handler nor the
|
|
// then function.
|
|
interface ThenableImpl<T> {
|
|
then(
|
|
onFulfill: (value: T) => mixed,
|
|
onReject: (error: mixed) => mixed,
|
|
): void | Wakeable;
|
|
}
|
|
interface UntrackedThenable<T> extends ThenableImpl<T> {
|
|
status?: void;
|
|
_debugInfo?: null | ReactDebugInfo;
|
|
}
|
|
|
|
export interface PendingThenable<T> extends ThenableImpl<T> {
|
|
status: 'pending';
|
|
_debugInfo?: null | ReactDebugInfo;
|
|
}
|
|
|
|
export interface FulfilledThenable<T> extends ThenableImpl<T> {
|
|
status: 'fulfilled';
|
|
value: T;
|
|
_debugInfo?: null | ReactDebugInfo;
|
|
}
|
|
|
|
export interface RejectedThenable<T> extends ThenableImpl<T> {
|
|
status: 'rejected';
|
|
reason: mixed;
|
|
_debugInfo?: null | ReactDebugInfo;
|
|
}
|
|
|
|
export type Thenable<T> =
|
|
| UntrackedThenable<T>
|
|
| PendingThenable<T>
|
|
| FulfilledThenable<T>
|
|
| RejectedThenable<T>;
|
|
|
|
export type OffscreenMode =
|
|
| 'hidden'
|
|
| 'unstable-defer-without-hiding'
|
|
| 'visible'
|
|
| 'manual';
|
|
|
|
export type StartTransitionOptions = {
|
|
name?: string,
|
|
};
|
|
|
|
export type Usable<T> = Thenable<T> | ReactContext<T>;
|
|
|
|
export type ReactCustomFormAction = {
|
|
name?: string,
|
|
action?: string,
|
|
encType?: string,
|
|
method?: string,
|
|
target?: string,
|
|
data?: null | FormData,
|
|
};
|
|
|
|
// This is an opaque type returned by decodeFormState on the server, but it's
|
|
// defined in this shared file because the same type is used by React on
|
|
// the client.
|
|
export type ReactFormState<S, ReferenceId> = [
|
|
S /* actual state value */,
|
|
string /* key path */,
|
|
ReferenceId /* Server Reference ID */,
|
|
number /* number of bound arguments */,
|
|
];
|
|
|
|
export type Awaited<T> = T extends null | void
|
|
? T // special case for `null | undefined` when not in `--strictNullChecks` mode
|
|
: T extends Object // `await` only unwraps object types with a callable then. Non-object types are not unwrapped.
|
|
? T extends {then(onfulfilled: infer F): any} // thenable, extracts the first argument to `then()`
|
|
? F extends (value: infer V) => any // if the argument to `then` is callable, extracts the argument
|
|
? Awaited<V> // recursively unwrap the value
|
|
: empty // the argument to `then` was not callable.
|
|
: T // argument was not an object
|
|
: T; // non-thenable
|
|
|
|
export type ReactComponentInfo = {
|
|
+name?: string,
|
|
+env?: string,
|
|
+owner?: null | ReactComponentInfo,
|
|
+stack?: null | string,
|
|
// Stashed Data for the Specific Execution Environment. Not part of the transport protocol
|
|
+debugStack?: null | Error,
|
|
+debugTask?: null | ConsoleTask,
|
|
};
|
|
|
|
export type ReactAsyncInfo = {
|
|
+started?: number,
|
|
+completed?: number,
|
|
+stack?: string,
|
|
};
|
|
|
|
export type ReactDebugInfo = Array<ReactComponentInfo | ReactAsyncInfo>;
|