7
* @flow
8
*/
9
10
+import {enableOwnerStacks} from 'shared/ReactFeatureFlags';
11
import type {Fiber} from './ReactInternalTypes';
12
+import type {ReactComponentInfo} from 'shared/ReactTypes';
13
14
import {
15
HostComponent,
22
ForwardRef,
23
SimpleMemoComponent,
24
ClassComponent,
25
+ HostText,
26
} from './ReactWorkTags';
27
import {
28
describeBuiltInComponentFrame,
30
describeClassComponentFrame,
31
describeDebugInfoFrame,
32
} from 'shared/ReactComponentStackFrame';
33
+import {formatOwnerStack} from './ReactFiberOwnerStack';
34
35
function describeFiber(fiber: Fiber): string {
36
switch (fiber.tag) {
82
return '\nError generating stack: ' + x.message + '\n' + x.stack;
83
}
84
}
85
+
86
+function describeFunctionComponentFrameWithoutLineNumber(fn: Function): string {
87
+ // We use this because we don't actually want to describe the line of the component
88
+ // but just the component name.
89
+ const name = fn ? fn.displayName || fn.name : '';
90
+ return name ? describeBuiltInComponentFrame(name) : '';
91
+}
92
+
93
+export function getOwnerStackByFiberInDev(workInProgress: Fiber): string {
94
+ if (!enableOwnerStacks || !__DEV__) {
95
+ return '';
96
+ }
97
+ try {
98
+ let info = '';
99
+
100
+ if (workInProgress.tag === HostText) {
101
+ // Text nodes never have an owner/stack because they're not created through JSX.
102
+ // We use the parent since text nodes are always created through a host parent.
103
+ workInProgress = (workInProgress.return: any);
104
+ }
105
+
106
+ // The owner stack of the current fiber will be where it was created, i.e. inside its owner.
107
+ // There's no actual name of the currently executing component. Instead, that is available
108
+ // on the regular stack that's currently executing. However, for built-ins there is no such
109
+ // named stack frame and it would be ignored as being internal anyway. Therefore we add
110
+ // add one extra frame just to describe the "current" built-in component by name.
111
+ // Similarly, if there is no owner at all, then there's no stack frame so we add the name
112
+ // of the root component to the stack to know which component is currently executing.
113
+ switch (workInProgress.tag) {
114
+ case HostHoistable:
115
+ case HostSingleton:
116
+ case HostComponent:
117
+ info += describeBuiltInComponentFrame(workInProgress.type);
118
+ break;
119
+ case SuspenseComponent:
120
+ info += describeBuiltInComponentFrame('Suspense');
121
+ break;
122
+ case SuspenseListComponent:
123
+ info += describeBuiltInComponentFrame('SuspenseList');
124
+ break;
125
+ case FunctionComponent:
126
+ case SimpleMemoComponent:
127
+ case ClassComponent:
128
+ if (!workInProgress._debugOwner) {
129
+ info += describeFunctionComponentFrameWithoutLineNumber(
130
+ workInProgress.type,
131
+ );
132
+ }
133
+ break;
134
+ case ForwardRef:
135
+ if (!workInProgress._debugOwner) {
136
+ info += describeFunctionComponentFrameWithoutLineNumber(
137
+ workInProgress.type.render,
138
+ );
139
+ }
140
+ break;
141
+ }
142
+
143
+ let owner: void | null | Fiber | ReactComponentInfo = workInProgress;
144
+
145
+ while (owner) {
146
+ if (typeof owner.tag === 'number') {
147
+ const fiber: Fiber = (owner: any);
148
+ owner = fiber._debugOwner;
149
+ let debugStack = fiber._debugStack;
150
+ // If we don't actually print the stack if there is no owner of this JSX element.
151
+ // In a real app it's typically not useful since the root app is always controlled
152
+ // by the framework. These also tend to have noisy stacks because they're not rooted
153
+ // in a React render but in some imperative bootstrapping code. It could be useful
154
+ // if the element was created in module scope. E.g. hoisted. We could add a a single
155
+ // stack frame for context for example but it doesn't say much if that's a wrapper.
156
+ if (owner && debugStack) {
157
+ if (typeof debugStack !== 'string') {
158
+ // Stash the formatted stack so that we can avoid redoing the filtering.
159
+ fiber._debugStack = debugStack = formatOwnerStack(debugStack);
160
+ }
161
+ if (debugStack !== '') {
162
+ info += '\n' + debugStack;
163
+ }
164
+ }
165
+ } else if (typeof owner.stack === 'string') {
166
+ // Server Component
167
+ // The Server Component stack can come from a different VM that formats it different.
168
+ // Likely V8. Since Chrome based browsers support createTask which is going to use
169
+ // another code path anyway. I.e. this is likely NOT a V8 based browser.
170
+ // This will cause some of the stack to have different formatting.
171
+ // TODO: Normalize server component stacks to the client formatting.
172
+ if (owner.stack !== '') {
173
+ info += '\n' + owner.stack;
174
+ }
175
+ const componentInfo: ReactComponentInfo = (owner: any);
176
+ owner = componentInfo.owner;
177
+ } else {
178
+ break;
179
+ }
180
+ }
181
+ return info;
182
+ } catch (x) {
183
+ return '\nError generating stack: ' + x.message + '\n' + x.stack;
184
+ }
185
+}