mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-09-11 14:46:37 +03:00
More React reconciler fixes/debug output
Summary: Release notes: none More improvements the React reconciler so it gives better output and scan/tracks for more trees. Closes https://github.com/facebook/prepack/pull/1539 Differential Revision: D7186071 Pulled By: trueadm fbshipit-source-id: 8cf3c08e23a6d3b386917e58371b4f021a537d7e
This commit is contained in:
parent
e1c23e98c8
commit
81e47461e0
File diff suppressed because it is too large
Load Diff
@ -75,7 +75,9 @@ function printReactEvaluationGraph(evaluatedRootNode, depth) {
|
||||
printReactEvaluationGraph(child, depth);
|
||||
}
|
||||
} else {
|
||||
let line = `- ${evaluatedRootNode.name} (${evaluatedRootNode.status.toLowerCase()})`;
|
||||
let status = evaluatedRootNode.status.toLowerCase();
|
||||
let message = evaluatedRootNode.message !== "" ? `: ${evaluatedRootNode.message}` : "";
|
||||
let line = `- ${evaluatedRootNode.name} (${status}${message})`;
|
||||
console.log(line.padStart(line.length + depth));
|
||||
printReactEvaluationGraph(evaluatedRootNode.children, depth + 2);
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import {
|
||||
createReactEvaluatedNode,
|
||||
getComponentName,
|
||||
sanitizeReactElementForFirstRenderOnly,
|
||||
getValueFromRenderCall,
|
||||
} from "./utils";
|
||||
import { Get } from "../methods/index.js";
|
||||
import invariant from "../invariant.js";
|
||||
@ -54,7 +55,7 @@ import {
|
||||
createClassInstanceForFirstRenderOnly,
|
||||
} from "./components.js";
|
||||
import { ExpectedBailOut, SimpleClassBailOut, NewComponentTreeBranch } from "./errors.js";
|
||||
import { Completion } from "../completions.js";
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Logger } from "../utils/logger.js";
|
||||
import type { ClassComponentMetadata, ReactComponentTreeConfig } from "../types.js";
|
||||
|
||||
@ -130,33 +131,36 @@ export class Reconciler {
|
||||
this.alreadyEvaluatedRootNodes.set(componentType, evaluatedRootNode);
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error instanceof Completion) {
|
||||
// this.logger.logCompletion(error);
|
||||
evaluatedRootNode.status = "UNSUPPORTED_COMPLETION";
|
||||
return this.realm.intrinsics.undefined;
|
||||
} else {
|
||||
// if we get an error and we're not dealing with the root
|
||||
// rather than throw a FatalError, we log the error as a warning
|
||||
// and continue with the other tree roots
|
||||
// TODO: maybe control what levels gets treated as warning/error?
|
||||
if (!isRoot) {
|
||||
// if we get an error and we're not dealing with the root
|
||||
// rather than throw a FatalError, we log the error as a warning
|
||||
// and continue with the other tree roots
|
||||
// TODO: maybe control what levels gets treated as warning/error?
|
||||
if (!isRoot) {
|
||||
if (error instanceof AbruptCompletion) {
|
||||
this.logger.logWarning(
|
||||
componentType,
|
||||
`__optimizeReactComponentTree() React component tree (branch) failed due runtime runtime exception thrown`
|
||||
);
|
||||
evaluatedRootNode.status = "ABRUPT_COMPLETION";
|
||||
} else {
|
||||
this.logger.logWarning(
|
||||
componentType,
|
||||
`__optimizeReactComponentTree() React component tree (branch) failed due to - ${error.message}`
|
||||
);
|
||||
evaluatedRootNode.message = "evaluation failed on new component tree branch";
|
||||
evaluatedRootNode.status = "BAIL-OUT";
|
||||
return this.realm.intrinsics.undefined;
|
||||
}
|
||||
if (error instanceof ExpectedBailOut) {
|
||||
let diagnostic = new CompilerDiagnostic(
|
||||
`__optimizeReactComponentTree() React component tree (root) failed due to - ${error.message}`,
|
||||
this.realm.currentLocation,
|
||||
"PP0020",
|
||||
"FatalError"
|
||||
);
|
||||
this.realm.handleError(diagnostic);
|
||||
if (this.realm.handleError(diagnostic) === "Fail") throw new FatalError();
|
||||
}
|
||||
return this.realm.intrinsics.undefined;
|
||||
}
|
||||
if (error instanceof ExpectedBailOut) {
|
||||
let diagnostic = new CompilerDiagnostic(
|
||||
`__optimizeReactComponentTree() React component tree (root) failed due to - ${error.message}`,
|
||||
this.realm.currentLocation,
|
||||
"PP0020",
|
||||
"FatalError"
|
||||
);
|
||||
this.realm.handleError(diagnostic);
|
||||
if (this.realm.handleError(diagnostic) === "Fail") throw new FatalError();
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
@ -228,12 +232,9 @@ export class Reconciler {
|
||||
let instance = createClassInstance(this.realm, componentType, props, context, classMetadata);
|
||||
// get the "render" method off the instance
|
||||
let renderMethod = Get(this.realm, instance, "render");
|
||||
invariant(
|
||||
renderMethod instanceof ECMAScriptSourceFunctionValue && renderMethod.$Call,
|
||||
"Expected render method to be a FunctionValue with $Call method"
|
||||
);
|
||||
invariant(renderMethod instanceof ECMAScriptSourceFunctionValue);
|
||||
// the render method doesn't have any arguments, so we just assign the context of "this" to be the instance
|
||||
return renderMethod.$Call(instance, []);
|
||||
return getValueFromRenderCall(this.realm, renderMethod, instance, []);
|
||||
}
|
||||
|
||||
_renderSimpleClassComponent(
|
||||
@ -247,12 +248,9 @@ export class Reconciler {
|
||||
let instance = createSimpleClassInstance(this.realm, componentType, props, context);
|
||||
// get the "render" method off the instance
|
||||
let renderMethod = Get(this.realm, instance, "render");
|
||||
invariant(
|
||||
renderMethod instanceof ECMAScriptSourceFunctionValue && renderMethod.$Call,
|
||||
"Expected render method to be a FunctionValue with $Call method"
|
||||
);
|
||||
invariant(renderMethod instanceof ECMAScriptSourceFunctionValue);
|
||||
// the render method doesn't have any arguments, so we just assign the context of "this" to be the instance
|
||||
return renderMethod.$Call(instance, []);
|
||||
return getValueFromRenderCall(this.realm, renderMethod, instance, []);
|
||||
}
|
||||
|
||||
_renderFunctionalComponent(
|
||||
@ -260,8 +258,7 @@ export class Reconciler {
|
||||
props: ObjectValue | AbstractValue | AbstractObjectValue,
|
||||
context: ObjectValue | AbstractObjectValue
|
||||
) {
|
||||
invariant(componentType.$Call, "Expected componentType to be a FunctionValue with $Call method");
|
||||
return componentType.$Call(this.realm.intrinsics.undefined, [props, context]);
|
||||
return getValueFromRenderCall(this.realm, componentType, this.realm.intrinsics.undefined, [props, context]);
|
||||
}
|
||||
|
||||
_getClassComponentMetadata(
|
||||
@ -307,6 +304,8 @@ export class Reconciler {
|
||||
// that we need to also evaluate. given we can't find those components
|
||||
return renderResult;
|
||||
}
|
||||
} else {
|
||||
this._findReactComponentTrees(props, evaluatedNode);
|
||||
}
|
||||
}
|
||||
// this is the worst case, we were unable to find the render prop function
|
||||
@ -392,12 +391,9 @@ export class Reconciler {
|
||||
if (componentWillMount instanceof ECMAScriptSourceFunctionValue && componentWillMount.$Call) {
|
||||
componentWillMount.$Call(instance, []);
|
||||
}
|
||||
invariant(
|
||||
renderMethod instanceof ECMAScriptSourceFunctionValue && renderMethod.$Call,
|
||||
"Expected render method to be a FunctionValue with $Call method"
|
||||
);
|
||||
invariant(renderMethod instanceof ECMAScriptSourceFunctionValue);
|
||||
// the render method doesn't have any arguments, so we just assign the context of "this" to be the instance
|
||||
return renderMethod.$Call(instance, []);
|
||||
return getValueFromRenderCall(this.realm, renderMethod, instance, []);
|
||||
}
|
||||
|
||||
_renderComponent(
|
||||
@ -485,6 +481,33 @@ export class Reconciler {
|
||||
return "NORMAL";
|
||||
}
|
||||
|
||||
_resolveAbstractValue(
|
||||
componentType: Value,
|
||||
value: AbstractValue,
|
||||
context: ObjectValue | AbstractObjectValue,
|
||||
branchStatus: BranchStatusEnum,
|
||||
branchState: BranchState | null,
|
||||
evaluatedNode: ReactEvaluatedNode
|
||||
) {
|
||||
let length = value.args.length;
|
||||
if (length > 0) {
|
||||
let newBranchState = new BranchState();
|
||||
// TODO investigate what other kinds than "conditional" might be safe to deeply resolve
|
||||
for (let i = 0; i < length; i++) {
|
||||
value.args[i] = this._resolveDeeply(
|
||||
componentType,
|
||||
value.args[i],
|
||||
context,
|
||||
"NEW_BRANCH",
|
||||
newBranchState,
|
||||
evaluatedNode
|
||||
);
|
||||
}
|
||||
newBranchState.applyBranchedLogic(this.realm, this.reactSerializerState);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
_resolveDeeply(
|
||||
componentType: Value,
|
||||
value: Value,
|
||||
@ -503,23 +526,7 @@ export class Reconciler {
|
||||
// terminal values
|
||||
return value;
|
||||
} else if (value instanceof AbstractValue) {
|
||||
let length = value.args.length;
|
||||
if (length > 0) {
|
||||
let newBranchState = new BranchState();
|
||||
// TODO investigate what other kinds than "conditional" might be safe to deeply resolve
|
||||
for (let i = 0; i < length; i++) {
|
||||
value.args[i] = this._resolveDeeply(
|
||||
componentType,
|
||||
value.args[i],
|
||||
context,
|
||||
"NEW_BRANCH",
|
||||
newBranchState,
|
||||
evaluatedNode
|
||||
);
|
||||
}
|
||||
newBranchState.applyBranchedLogic(this.realm, this.reactSerializerState);
|
||||
}
|
||||
return value;
|
||||
return this._resolveAbstractValue(componentType, value, context, branchStatus, branchState, evaluatedNode);
|
||||
}
|
||||
// TODO investigate what about other iterables type objects
|
||||
if (value instanceof ArrayValue) {
|
||||
@ -570,9 +577,11 @@ export class Reconciler {
|
||||
if (!(refValue instanceof NullValue)) {
|
||||
let evaluatedChildNode = createReactEvaluatedNode("BAIL-OUT", getComponentName(this.realm, typeValue));
|
||||
evaluatedNode.children.push(evaluatedChildNode);
|
||||
evaluatedChildNode.status = "BAIL-OUT";
|
||||
let bailOutMessage = `refs are not supported on <Components />`;
|
||||
evaluatedChildNode.message = bailOutMessage;
|
||||
this._queueNewComponentTree(typeValue, evaluatedChildNode);
|
||||
this._assignBailOutMessage(reactElement, `Bail-out: refs are not supported on <Components />`);
|
||||
this._findReactComponentTrees(propsValue, evaluatedNode);
|
||||
this._assignBailOutMessage(reactElement, bailOutMessage);
|
||||
return reactElement;
|
||||
}
|
||||
if (
|
||||
@ -584,7 +593,7 @@ export class Reconciler {
|
||||
) {
|
||||
this._assignBailOutMessage(
|
||||
reactElement,
|
||||
`Bail-out: props on <Component /> was not not an ObjectValue or an AbstractValue`
|
||||
`props on <Component /> was not not an ObjectValue or an AbstractValue`
|
||||
);
|
||||
return reactElement;
|
||||
}
|
||||
@ -594,11 +603,18 @@ export class Reconciler {
|
||||
renderStrategy === "NORMAL" &&
|
||||
!(typeValue instanceof ECMAScriptSourceFunctionValue || valueIsKnownReactAbstraction(this.realm, typeValue))
|
||||
) {
|
||||
this._assignBailOutMessage(
|
||||
reactElement,
|
||||
`Bail-out: type on <Component /> was not a ECMAScriptSourceFunctionValue`
|
||||
);
|
||||
return reactElement;
|
||||
this._findReactComponentTrees(propsValue, evaluatedNode);
|
||||
if (typeValue instanceof AbstractValue) {
|
||||
this._findReactComponentTrees(typeValue, evaluatedNode);
|
||||
return reactElement;
|
||||
} else {
|
||||
let evaluatedChildNode = createReactEvaluatedNode("BAIL-OUT", getComponentName(this.realm, typeValue));
|
||||
evaluatedNode.children.push(evaluatedChildNode);
|
||||
let bailOutMessage = `type on <Component /> was not a ECMAScriptSourceFunctionValue`;
|
||||
evaluatedChildNode.message = bailOutMessage;
|
||||
this._assignBailOutMessage(reactElement, bailOutMessage);
|
||||
return reactElement;
|
||||
}
|
||||
} else if (renderStrategy === "FRAGMENT") {
|
||||
return resolveChildren();
|
||||
}
|
||||
@ -639,7 +655,12 @@ export class Reconciler {
|
||||
}
|
||||
|
||||
if (result instanceof UndefinedValue) {
|
||||
this._assignBailOutMessage(reactElement, `Bail-out: undefined was returned from render`);
|
||||
let evaluatedChildNode = createReactEvaluatedNode("BAIL-OUT", getComponentName(this.realm, typeValue));
|
||||
evaluatedNode.children.push(evaluatedChildNode);
|
||||
let bailOutMessage = `undefined was returned from render`;
|
||||
evaluatedChildNode.message = bailOutMessage;
|
||||
this._assignBailOutMessage(reactElement, bailOutMessage);
|
||||
this._findReactComponentTrees(propsValue, evaluatedNode);
|
||||
if (branchStatus === "NEW_BRANCH" && branchState) {
|
||||
return branchState.captureBranchedValue(typeValue, reactElement);
|
||||
}
|
||||
@ -654,15 +675,29 @@ export class Reconciler {
|
||||
if (error instanceof NewComponentTreeBranch) {
|
||||
// NO-OP (we don't queue a newComponentTree as this was already done)
|
||||
} else {
|
||||
let evaluatedChildNode = createReactEvaluatedNode("BAIL-OUT", getComponentName(this.realm, typeValue));
|
||||
evaluatedNode.children.push(evaluatedChildNode);
|
||||
this._queueNewComponentTree(typeValue, evaluatedChildNode);
|
||||
if (error instanceof ExpectedBailOut) {
|
||||
this._assignBailOutMessage(reactElement, "Bail-out: " + error.message);
|
||||
} else if (error instanceof FatalError) {
|
||||
this._assignBailOutMessage(reactElement, "Evaluation bail-out");
|
||||
// handle abrupt completions
|
||||
if (error instanceof AbruptCompletion) {
|
||||
let evaluatedChildNode = createReactEvaluatedNode(
|
||||
"ABRUPT_COMPLETION",
|
||||
getComponentName(this.realm, typeValue)
|
||||
);
|
||||
evaluatedNode.children.push(evaluatedChildNode);
|
||||
} else {
|
||||
throw error;
|
||||
let evaluatedChildNode = createReactEvaluatedNode("BAIL-OUT", getComponentName(this.realm, typeValue));
|
||||
evaluatedNode.children.push(evaluatedChildNode);
|
||||
this._queueNewComponentTree(typeValue, evaluatedChildNode);
|
||||
this._findReactComponentTrees(propsValue, evaluatedNode);
|
||||
if (error instanceof ExpectedBailOut) {
|
||||
evaluatedChildNode.message = error.message;
|
||||
this._assignBailOutMessage(reactElement, error.message);
|
||||
} else if (error instanceof FatalError) {
|
||||
let message = "evaluation failed";
|
||||
evaluatedChildNode.message = message;
|
||||
this._assignBailOutMessage(reactElement, message);
|
||||
} else {
|
||||
evaluatedChildNode.message = `unknown error`;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
// a child component bailed out during component folding, so return the function value and continue
|
||||
@ -679,6 +714,7 @@ export class Reconciler {
|
||||
_assignBailOutMessage(reactElement: ObjectValue, message: string): void {
|
||||
// $BailOutReason is a field on ObjectValue that allows us to specify a message
|
||||
// that gets serialized as a comment node during the ReactElement serialization stage
|
||||
message = `Bail-out: ${message}`;
|
||||
if (reactElement.$BailOutReason !== undefined) {
|
||||
// merge bail out messages if one already exists
|
||||
reactElement.$BailOutReason += `, ${message}`;
|
||||
@ -718,4 +754,22 @@ export class Reconciler {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_findReactComponentTrees(value: Value, evaluatedNode: ReactEvaluatedNode): void {
|
||||
if (value instanceof AbstractValue) {
|
||||
for (let arg of value.args) {
|
||||
this._findReactComponentTrees(arg, evaluatedNode);
|
||||
}
|
||||
} else if (value instanceof ObjectValue) {
|
||||
for (let [propName, binding] of value.properties) {
|
||||
if (binding && binding.descriptor && binding.enumerable) {
|
||||
this._findReactComponentTrees(getProperty(this.realm, value, propName), evaluatedNode);
|
||||
}
|
||||
}
|
||||
} else if (value instanceof ECMAScriptSourceFunctionValue || valueIsKnownReactAbstraction(this.realm, value)) {
|
||||
let evaluatedChildNode = createReactEvaluatedNode("NEW_TREE", getComponentName(this.realm, value));
|
||||
evaluatedNode.children.push(evaluatedChildNode);
|
||||
this._queueNewComponentTree(value, evaluatedChildNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
import { Realm, type Effects } from "../realm.js";
|
||||
import { FunctionEnvironmentRecord, Reference } from "../environment.js";
|
||||
import { Completion } from "../completions.js";
|
||||
import { Completion, PossiblyNormalCompletion, AbruptCompletion } from "../completions.js";
|
||||
import type { BabelNode, BabelNodeJSXIdentifier } from "babel-types";
|
||||
import {
|
||||
AbstractObjectValue,
|
||||
@ -674,13 +674,22 @@ export function isRenderPropFunctionSelfContained(
|
||||
}
|
||||
|
||||
export function createReactEvaluatedNode(
|
||||
status: "ROOT" | "NEW_TREE" | "INLINED" | "BAIL-OUT" | "UNKNOWN_TYPE" | "RENDER_PROPS" | "UNSUPPORTED_COMPLETION",
|
||||
status:
|
||||
| "ROOT"
|
||||
| "NEW_TREE"
|
||||
| "INLINED"
|
||||
| "BAIL-OUT"
|
||||
| "UNKNOWN_TYPE"
|
||||
| "RENDER_PROPS"
|
||||
| "UNSUPPORTED_COMPLETION"
|
||||
| "ABRUPT_COMPLETION",
|
||||
name: string
|
||||
): ReactEvaluatedNode {
|
||||
return {
|
||||
children: [],
|
||||
message: "",
|
||||
name,
|
||||
status,
|
||||
children: [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -744,6 +753,37 @@ export function convertConfigObjectToReactComponentTreeConfig(
|
||||
};
|
||||
}
|
||||
|
||||
export function getValueFromRenderCall(
|
||||
realm: Realm,
|
||||
renderFunction: ECMAScriptSourceFunctionValue,
|
||||
instance: ObjectValue | AbstractObjectValue | UndefinedValue,
|
||||
args: Array<Value>
|
||||
): Value {
|
||||
invariant(renderFunction.$Call, "Expected render function to be a FunctionValue with $Call method");
|
||||
let funcCall = renderFunction.$Call;
|
||||
let effects;
|
||||
try {
|
||||
effects = realm.evaluateForEffects(() => funcCall(instance, args));
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
let completion = effects[0];
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
}
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in joinedEffects, but are tracked separately inside completion.
|
||||
realm.applyEffects(effects);
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
invariant(completion instanceof Value);
|
||||
return completion;
|
||||
}
|
||||
|
||||
export function sanitizeReactElementForFirstRenderOnly(realm: Realm, reactElement: ObjectValue): ObjectValue {
|
||||
let typeValue = Get(realm, reactElement, "type");
|
||||
|
||||
|
@ -28,7 +28,12 @@ import {
|
||||
import { Get } from "../methods/index.js";
|
||||
import { ModuleTracer } from "../utils/modules.js";
|
||||
import buildTemplate from "babel-template";
|
||||
import { ReactStatistics, type ReactSerializerState, type AdditionalFunctionEffects } from "./types";
|
||||
import {
|
||||
ReactStatistics,
|
||||
type ReactSerializerState,
|
||||
type AdditionalFunctionEffects,
|
||||
type ReactEvaluatedNode,
|
||||
} from "./types";
|
||||
import { Reconciler, type ComponentTreeState } from "../react/reconcilation.js";
|
||||
import {
|
||||
valueIsClassComponent,
|
||||
@ -155,7 +160,8 @@ export class Functions {
|
||||
_generateWriteEffectsForReactComponentTree(
|
||||
componentType: ECMAScriptSourceFunctionValue,
|
||||
effects: Effects,
|
||||
componentTreeState: ComponentTreeState
|
||||
componentTreeState: ComponentTreeState,
|
||||
evaluatedNode: ReactEvaluatedNode
|
||||
): void {
|
||||
let additionalFunctionEffects = this._createAdditionalEffects(effects);
|
||||
let value = effects[0];
|
||||
@ -168,6 +174,7 @@ export class Functions {
|
||||
if (value instanceof Completion) {
|
||||
// TODO we don't support this yet, but will do very soon
|
||||
// to unblock work, we'll just return at this point right now
|
||||
evaluatedNode.status = "UNSUPPORTED_COMPLETION";
|
||||
return;
|
||||
}
|
||||
invariant(value instanceof Value);
|
||||
@ -222,7 +229,7 @@ export class Functions {
|
||||
}
|
||||
let effects = reconciler.render(componentType, null, null, true, evaluatedRootNode);
|
||||
let componentTreeState = reconciler.componentTreeState;
|
||||
this._generateWriteEffectsForReactComponentTree(componentType, effects, componentTreeState);
|
||||
this._generateWriteEffectsForReactComponentTree(componentType, effects, componentTreeState, evaluatedRootNode);
|
||||
|
||||
// for now we just use abstract props/context, in the future we'll create a new branch with a new component
|
||||
// that used the props/context. It will extend the original component and only have a render method
|
||||
@ -240,7 +247,12 @@ export class Functions {
|
||||
reconciler.clearComponentTreeState();
|
||||
let branchEffects = reconciler.render(branchComponentType, null, null, false, evaluatedNode);
|
||||
let branchComponentTreeState = reconciler.componentTreeState;
|
||||
this._generateWriteEffectsForReactComponentTree(branchComponentType, branchEffects, branchComponentTreeState);
|
||||
this._generateWriteEffectsForReactComponentTree(
|
||||
branchComponentType,
|
||||
branchEffects,
|
||||
branchComponentTreeState,
|
||||
evaluatedNode
|
||||
);
|
||||
});
|
||||
}
|
||||
if (this.realm.react.output === "bytecode") {
|
||||
|
@ -152,9 +152,18 @@ export class TimingStatistics {
|
||||
}
|
||||
|
||||
export type ReactEvaluatedNode = {
|
||||
name: string,
|
||||
status: "ROOT" | "NEW_TREE" | "INLINED" | "BAIL-OUT" | "UNKNOWN_TYPE" | "RENDER_PROPS" | "UNSUPPORTED_COMPLETION",
|
||||
children: Array<ReactEvaluatedNode>,
|
||||
message: string,
|
||||
name: string,
|
||||
status:
|
||||
| "ROOT"
|
||||
| "NEW_TREE"
|
||||
| "INLINED"
|
||||
| "BAIL-OUT"
|
||||
| "UNKNOWN_TYPE"
|
||||
| "RENDER_PROPS"
|
||||
| "UNSUPPORTED_COMPLETION"
|
||||
| "ABRUPT_COMPLETION",
|
||||
};
|
||||
|
||||
export class ReactStatistics {
|
||||
|
Loading…
Reference in New Issue
Block a user