58
ReactComponentInfo,
59
ReactAsyncInfo,
60
} from 'shared/ReactTypes';
61
+import type {ReactElement} from 'shared/ReactElementType';
62
import type {LazyComponent} from 'react/src/ReactLazy';
63
import type {TemporaryReference} from './ReactFlightServerTemporaryReferences';
64
154
// We don't currently use this id for anything but we emit it so that we can later
155
// refer to previous logs in debug info to associate them with a component.
156
const id = request.nextChunkId++;
156
- emitConsoleChunk(request, id, methodName, stack, arguments);
157
+ const owner: null | ReactComponentInfo = ReactCurrentOwner.current;
158
+ emitConsoleChunk(request, id, methodName, owner, stack, arguments);
159
}
160
// $FlowFixMe[prop-missing]
161
return originalMethod.apply(this, arguments);
305
ReactCurrentCache,
306
} = ReactServerSharedInternals;
307
const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
308
+const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
309
310
function throwTaintViolation(message: string) {
311
// eslint-disable-next-line react-internal/prod-error-codes
597
key: null | string,
598
Component: (p: Props, arg: void) => any,
599
props: Props,
600
+ owner: null | ReactComponentInfo,
601
): ReactJSONValue {
602
// Reset the task's thenable state before continuing, so that if a later
603
// component suspends we can reuse the same task object. If the same
605
const prevThenableState = task.thenableState;
606
task.thenableState = null;
607
608
+ let componentDebugInfo: null | ReactComponentInfo = null;
609
if (__DEV__) {
610
if (debugID === null) {
611
// We don't have a chunk to assign debug info. We need to outline this
614
} else if (prevThenableState !== null) {
615
// This is a replay and we've already emitted the debug info of this component
616
// in the first pass. We skip emitting a duplicate line.
617
+ // As a hack we stashed the previous component debug info on this object in DEV.
618
+ componentDebugInfo = (prevThenableState: any)._componentDebugInfo;
619
} else {
620
// This is a new component in the same task so we can emit more debug info.
621
const componentName =
622
(Component: any).displayName || Component.name || '';
623
request.pendingChunks++;
617
- emitDebugChunk(request, debugID, {
624
+
625
+ const componentDebugID = debugID;
626
+ componentDebugInfo = {
627
name: componentName,
628
env: request.environmentName,
620
- });
629
+ owner: owner,
630
+ };
631
+ // We outline this model eagerly so that we can refer to by reference as an owner.
632
+ // If we had a smarter way to dedupe we might not have to do this if there ends up
633
+ // being no references to this as an owner.
634
+ outlineModel(request, componentDebugInfo);
635
+ emitDebugChunk(request, componentDebugID, componentDebugInfo);
636
}
637
}
638
624
- prepareToUseHooksForComponent(prevThenableState);
639
+ prepareToUseHooksForComponent(prevThenableState, componentDebugInfo);
640
// The secondArg is always undefined in Server Components since refs error early.
641
const secondArg = undefined;
627
- let result = Component(props, secondArg);
642
+ let result;
643
+ if (__DEV__) {
644
+ ReactCurrentOwner.current = componentDebugInfo;
645
+ try {
646
+ result = Component(props, secondArg);
647
+ } finally {
648
+ ReactCurrentOwner.current = null;
649
+ }
650
+ } else {
651
+ result = Component(props, secondArg);
652
+ }
653
if (
654
typeof result === 'object' &&
655
result !== null &&
748
type: any,
749
key: null | string,
750
props: any,
751
+ owner: null | ReactComponentInfo, // DEV-only
752
): ReactJSONValue {
753
if (!enableServerComponentKeys) {
728
- return [REACT_ELEMENT_TYPE, type, key, props];
754
+ return __DEV__
755
+ ? [REACT_ELEMENT_TYPE, type, key, props, owner]
756
+ : [REACT_ELEMENT_TYPE, type, key, props];
757
}
758
// We prepend the terminal client element that actually gets serialized with
759
// the keys of any Server Components which are not serialized.
763
} else if (keyPath !== null) {
764
key = keyPath + ',' + key;
765
}
738
- const element = [REACT_ELEMENT_TYPE, type, key, props];
766
+ const element = __DEV__
767
+ ? [REACT_ELEMENT_TYPE, type, key, props, owner]
768
+ : [REACT_ELEMENT_TYPE, type, key, props];
769
if (task.implicitSlot && key !== null) {
770
// The root Server Component had no key so it was in an implicit slot.
771
// If we had a key lower, it would end up in that slot with an explicit key.
811
key: null | string,
812
ref: mixed,
813
props: any,
814
+ owner: null | ReactComponentInfo, // DEV only
815
): ReactJSONValue {
816
if (ref !== null && ref !== undefined) {
817
// When the ref moves to the regular props object this will implicitly
832
if (typeof type === 'function') {
833
if (isClientReference(type) || isTemporaryReference(type)) {
834
// This is a reference to a Client Component.
804
- return renderClientElement(task, type, key, props);
835
+ return renderClientElement(task, type, key, props, owner);
836
}
837
// This is a Server Component.
807
- return renderFunctionComponent(request, task, key, type, props);
838
+ return renderFunctionComponent(request, task, key, type, props, owner);
839
} else if (typeof type === 'string') {
840
// This is a host element. E.g. HTML.
810
- return renderClientElement(task, type, key, props);
841
+ return renderClientElement(task, type, key, props, owner);
842
} else if (typeof type === 'symbol') {
843
if (type === REACT_FRAGMENT_TYPE && key === null) {
844
// For key-less fragments, we add a small optimization to avoid serializing
859
}
860
// This might be a built-in React component. We'll let the client decide.
861
// Any built-in works as long as its props are serializable.
831
- return renderClientElement(task, type, key, props);
862
+ return renderClientElement(task, type, key, props, owner);
863
} else if (type != null && typeof type === 'object') {
864
if (isClientReference(type)) {
865
// This is a reference to a Client Component.
835
- return renderClientElement(task, type, key, props);
866
+ return renderClientElement(task, type, key, props, owner);
867
}
868
switch (type.$$typeof) {
869
case REACT_LAZY_TYPE: {
870
const payload = type._payload;
871
const init = type._init;
872
const wrappedType = init(payload);
842
- return renderElement(request, task, wrappedType, key, ref, props);
873
+ return renderElement(
874
+ request,
875
+ task,
876
+ wrappedType,
877
+ key,
878
+ ref,
879
+ props,
880
+ owner,
881
+ );
882
}
883
case REACT_FORWARD_REF_TYPE: {
845
- return renderFunctionComponent(request, task, key, type.render, props);
884
+ return renderFunctionComponent(
885
+ request,
886
+ task,
887
+ key,
888
+ type.render,
889
+ props,
890
+ owner,
891
+ );
892
}
893
case REACT_MEMO_TYPE: {
848
- return renderElement(request, task, type.type, key, ref, props);
894
+ return renderElement(request, task, type.type, key, ref, props, owner);
895
}
896
}
897
}
1402
writtenObjects.set((value: any).props, NEVER_OUTLINED);
1403
}
1404
1359
- const element: React$Element<any> = (value: any);
1405
+ const element: ReactElement = (value: any);
1406
1407
if (__DEV__) {
1408
const debugInfo: ?ReactDebugInfo = (value: any)._debugInfo;
1440
element.key,
1441
ref,
1442
props,
1443
+ __DEV__ ? element._owner : null,
1444
);
1445
}
1446
case REACT_LAZY_TYPE: {
1951
);
1952
}
1953
1954
+ // We use the console encoding so that we can dedupe objects but don't necessarily
1955
+ // use the full serialization that requires a task.
1956
+ const counter = {objectCount: 0};
1957
+ function replacer(
1958
+ this:
1959
+ | {+[key: string | number]: ReactClientValue}
1960
+ | $ReadOnlyArray<ReactClientValue>,
1961
+ parentPropertyName: string,
1962
+ value: ReactClientValue,
1963
+ ): ReactJSONValue {
1964
+ return renderConsoleValue(
1965
+ request,
1966
+ counter,
1967
+ this,
1968
+ parentPropertyName,
1969
+ value,
1970
+ );
1971
+ }
1972
+
1973
// $FlowFixMe[incompatible-type] stringify can return null
1908
- const json: string = stringify(debugInfo);
1974
+ const json: string = stringify(debugInfo, replacer);
1975
const row = serializeRowHeader('D', id) + json + '\n';
1976
const processedChunk = stringToChunk(row);
1977
request.completedRegularChunks.push(processedChunk);
2273
request: Request,
2274
id: number,
2275
methodName: string,
2276
+ owner: null | ReactComponentInfo,
2277
stackTrace: string,
2278
args: Array<any>,
2279
): void {
2308
2309
// TODO: Don't double badge if this log came from another Flight Client.
2310
const env = request.environmentName;
2244
- const payload = [methodName, stackTrace, env];
2311
+ const payload = [methodName, stackTrace, owner, env];
2312
// $FlowFixMe[method-unbinding]
2313
payload.push.apply(payload, args);
2314
// $FlowFixMe[incompatible-type] stringify can return null