59
import {
60
extractLocationFromComponentStack,
61
extractLocationFromOwnerStack,
62
+ parseStackTrace,
63
} from 'react-devtools-shared/src/backend/utils/parseStackTrace';
64
import {
65
cleanForBridge,
4747
4748
function getSuspendedByOfSuspenseNode(
4749
suspenseNode: SuspenseNode,
4749
- ): Array<ReactAsyncInfo> {
4750
+ ): Array<SerializedAsyncInfo> {
4751
// Collect all ReactAsyncInfo that was suspending this SuspenseNode but
4752
// isn't also in any parent set.
4752
- const result: Array<ReactAsyncInfo> = [];
4753
+ const result: Array<SerializedAsyncInfo> = [];
4754
if (!suspenseNode.hasUniqueSuspenders) {
4755
return result;
4756
}
4775
ioInfo,
4776
);
4777
if (asyncInfo !== null) {
4777
- result.push(asyncInfo);
4778
+ const index = result.length;
4779
+ result.push(serializeAsyncInfo(asyncInfo, index, firstInstance));
4780
}
4781
}
4782
});
4793
parentInstance,
4794
ioInfo.owner,
4795
);
4794
- const awaitOwnerInstance = findNearestOwnerInstance(
4795
- parentInstance,
4796
- asyncInfo.owner,
4797
- );
4796
+ let awaitStack =
4797
+ asyncInfo.debugStack == null
4798
+ ? null
4799
+ : // While we have a ReactStackTrace on ioInfo.stack, that will point to the location on
4800
+ // the server. We need a location that points to the virtual source on the client which
4801
+ // we can then use to source map to the original location.
4802
+ parseStackTrace(asyncInfo.debugStack, 1);
4803
+ let awaitOwnerInstance: null | FiberInstance | VirtualInstance;
4804
+ if (
4805
+ asyncInfo.owner == null &&
4806
+ (awaitStack === null || awaitStack.length === 0)
4807
+ ) {
4808
+ // We had no owner nor stack for the await. This can happen if you render it as a child
4809
+ // or throw a Promise. Replace it with the parent as the await.
4810
+ awaitStack = null;
4811
+ awaitOwnerInstance =
4812
+ parentInstance.kind === FILTERED_FIBER_INSTANCE ? null : parentInstance;
4813
+ if (
4814
+ parentInstance.kind === FIBER_INSTANCE ||
4815
+ parentInstance.kind === FILTERED_FIBER_INSTANCE
4816
+ ) {
4817
+ const fiber = parentInstance.data;
4818
+ switch (fiber.tag) {
4819
+ case ClassComponent:
4820
+ case FunctionComponent:
4821
+ case IncompleteClassComponent:
4822
+ case IncompleteFunctionComponent:
4823
+ case IndeterminateComponent:
4824
+ case MemoComponent:
4825
+ case SimpleMemoComponent:
4826
+ // If we awaited in the child position of a component, then the best stack would be the
4827
+ // return callsite but we don't have that available so instead we skip. The callsite of
4828
+ // the JSX would be misleading in this case. The same thing happens with throw-a-Promise.
4829
+ break;
4830
+ default:
4831
+ // If we awaited by passing a Promise to a built-in element, then the JSX callsite is a
4832
+ // good stack trace to use for the await.
4833
+ if (
4834
+ fiber._debugOwner != null &&
4835
+ fiber._debugStack != null &&
4836
+ typeof fiber._debugStack !== 'string'
4837
+ ) {
4838
+ awaitStack = parseStackTrace(fiber._debugStack, 1);
4839
+ awaitOwnerInstance = findNearestOwnerInstance(
4840
+ parentInstance,
4841
+ fiber._debugOwner,
4842
+ );
4843
+ }
4844
+ }
4845
+ }
4846
+ } else {
4847
+ awaitOwnerInstance = findNearestOwnerInstance(
4848
+ parentInstance,
4849
+ asyncInfo.owner,
4850
+ );
4851
+ }
4852
+
4853
const value: any = ioInfo.value;
4854
let resolvedValue = undefined;
4855
if (
4878
ioOwnerInstance === null
4879
? null
4880
: instanceToSerializedElement(ioOwnerInstance),
4826
- stack: ioInfo.stack == null ? null : ioInfo.stack,
4881
+ stack:
4882
+ ioInfo.debugStack == null
4883
+ ? null
4884
+ : // While we have a ReactStackTrace on ioInfo.stack, that will point to the location on
4885
+ // the server. We need a location that points to the virtual source on the client which
4886
+ // we can then use to source map to the original location.
4887
+ parseStackTrace(ioInfo.debugStack, 1),
4888
},
4889
env: asyncInfo.env == null ? null : asyncInfo.env,
4890
owner:
4891
awaitOwnerInstance === null
4892
? null
4893
: instanceToSerializedElement(awaitOwnerInstance),
4833
- stack: asyncInfo.stack == null ? null : asyncInfo.stack,
4894
+ stack: awaitStack,
4895
};
4896
}
4897
5197
// In this case, this becomes associated with the Client/Host Component where as normally
5198
// you'd expect these to be associated with the Server Component that awaited the data.
5199
// TODO: Prepend other suspense sources like css, images and use().
5139
- fiberInstance.suspendedBy;
5140
-
5200
+ fiberInstance.suspendedBy === null
5201
+ ? []
5202
+ : fiberInstance.suspendedBy.map((info, index) =>
5203
+ serializeAsyncInfo(info, index, fiberInstance),
5204
+ );
5205
return {
5206
id: fiberInstance.id,
5207
5258
? []
5259
: Array.from(componentLogsEntry.warnings.entries()),
5260
5197
- suspendedBy:
5198
- suspendedBy === null
5199
- ? []
5200
- : suspendedBy.map((info, index) =>
5201
- serializeAsyncInfo(info, index, fiberInstance),
5202
- ),
5261
+ suspendedBy: suspendedBy,
5262
5263
// List of owners
5264
owners,