Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fiber] Use Owner/JSX Stack When Appending Stacks to Console #29206

Merged
merged 7 commits into from
May 25, 2024

Conversation

sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented May 21, 2024

This one should be fully behind the enableOwnerStacks flag.

Instead of printing the parent Component stack all the way to the root, this now prints the owner stack of every JSX callsite. It also includes intermediate callsites between the Component and the JSX call so it has potentially more frames. Mainly it provides the line number of the JSX callsite. In terms of the number of components is a subset of the parent component stack so it's less information in that regard. This is usually better since it's more focused on components that might affect the output but if it's contextual based on rendering it's still good to have parent stack. Therefore, I still use the parent stack when printing DOM nesting warnings but I plan on switching that format to a diff view format instead (Next.js already reformats the parent stack like this).

Follow ups

  • Server Components show up in the owner stack for client logs but logs done by Server Components don't yet get their owner stack printed as they're replayed. They're also not yet printed in the server logs of the RSC server.

  • Server Component stack frames are formatted as the server and added to the end but this might be a different format than the browser. E.g. if server is running V8 and browser is running JSC or vice versa. Ideally we can reformat them in terms of the client formatting.

  • This doesn't yet update Fizz or DevTools. Those will be follow ups. Fizz still prints parent stacks in the server side logs. The stacks added to user space console.error calls by DevTools still get the parent stacks instead.

  • It also doesn't yet expose these to user space so there's no way to get them inside onCaughtError for example or inside a custom console.error override.

  • In another follow up I'll use console.createTask instead and completely remove these stacks if it's available.

Copy link

vercel bot commented May 21, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
react-compiler-playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 23, 2024 4:57pm

@react-sizebot
Copy link

react-sizebot commented May 21, 2024

Comparing: 84239da...65d16bb

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.66 kB 6.66 kB = 1.82 kB 1.82 kB
oss-stable/react-dom/cjs/react-dom-client.production.js +0.02% 495.90 kB 495.99 kB = 88.79 kB 88.79 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.67 kB 6.67 kB = 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js +0.02% 500.70 kB 500.79 kB = 89.47 kB 89.48 kB
facebook-www/ReactDOM-prod.classic.js +0.02% 593.46 kB 593.55 kB = 104.41 kB 104.41 kB
facebook-www/ReactDOM-prod.modern.js +0.02% 569.84 kB 569.93 kB = 100.80 kB 100.80 kB
test_utils/ReactAllWarnings.js Deleted 64.27 kB 0.00 kB Deleted 16.06 kB 0.00 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-experimental/react/cjs/react-jsx-runtime.react-server.development.js +1.23% 27.54 kB 27.88 kB +0.31% 8.93 kB 8.96 kB
oss-experimental/react/cjs/react-jsx-dev-runtime.react-server.development.js +1.23% 27.55 kB 27.88 kB +0.31% 8.93 kB 8.96 kB
oss-experimental/react-art/cjs/react-art.development.js +1.00% 829.47 kB 837.78 kB +1.12% 179.77 kB 181.80 kB
react-native/implementations/ReactFabric-dev.js +0.96% 864.90 kB 873.21 kB +1.10% 187.62 kB 189.68 kB
react-native/implementations/ReactNativeRenderer-dev.js +0.95% 878.09 kB 886.40 kB +1.09% 191.57 kB 193.66 kB
oss-experimental/react-reconciler/cjs/react-reconciler.development.js +0.89% 931.48 kB 939.80 kB +1.00% 200.23 kB 202.24 kB
oss-experimental/react/cjs/react-jsx-dev-runtime.development.js +0.79% 25.93 kB 26.13 kB +0.25% 8.45 kB 8.47 kB
oss-experimental/react-dom/cjs/react-dom-client.development.js +0.78% 1,302.09 kB 1,312.26 kB +0.81% 289.28 kB 291.62 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.development.js +0.77% 1,320.21 kB 1,330.38 kB +0.78% 293.72 kB 296.00 kB
oss-experimental/react-dom/cjs/react-dom-profiling.development.js +0.77% 1,320.77 kB 1,330.94 kB +0.78% 292.68 kB 294.97 kB
oss-stable-semver/react/cjs/react-jsx-dev-runtime.development.js +0.44% 41.31 kB 41.49 kB +0.15% 12.53 kB 12.55 kB
oss-stable/react/cjs/react-jsx-dev-runtime.development.js +0.44% 41.31 kB 41.49 kB +0.15% 12.53 kB 12.55 kB
oss-stable-semver/react/cjs/react-jsx-runtime.react-server.development.js +0.43% 42.96 kB 43.14 kB +0.21% 13.00 kB 13.02 kB
oss-stable/react/cjs/react-jsx-runtime.react-server.development.js +0.43% 42.96 kB 43.14 kB +0.21% 13.00 kB 13.02 kB
oss-stable-semver/react/cjs/react-jsx-dev-runtime.react-server.development.js +0.43% 42.96 kB 43.15 kB +0.21% 13.00 kB 13.03 kB
oss-stable/react/cjs/react-jsx-dev-runtime.react-server.development.js +0.43% 42.96 kB 43.15 kB +0.21% 13.00 kB 13.03 kB
facebook-react-native/react/cjs/JSXDEVRuntime-dev.js +0.43% 42.11 kB 42.29 kB +0.19% 12.77 kB 12.79 kB
facebook-www/JSXDEVRuntime-dev.classic.js +0.35% 51.59 kB 51.77 kB +0.14% 14.56 kB 14.58 kB
facebook-www/JSXDEVRuntime-dev.modern.js +0.35% 51.62 kB 51.80 kB +0.14% 14.56 kB 14.58 kB
facebook-react-native/react/cjs/React-dev.js +0.28% 104.38 kB 104.67 kB +0.15% 28.05 kB 28.09 kB
facebook-www/React-dev.modern.js +0.25% 115.74 kB 116.03 kB +0.19% 30.06 kB 30.11 kB
facebook-www/React-dev.classic.js +0.25% 116.23 kB 116.52 kB +0.18% 30.16 kB 30.21 kB
test_utils/ReactAllWarnings.js Deleted 64.27 kB 0.00 kB Deleted 16.06 kB 0.00 kB

Generated by 🚫 dangerJS against 65d16bb

@@ -1386,19 +1389,40 @@ describe('ReactFlight', () => {
ReactNoopFlightClient.read(transport);
});

it('should warn in DEV a child is missing keys', () => {
it('should warn in DEV a child is missing keys on server component', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

1,
],
],
"errors": [],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wat

// @gate !disableStringRefs
it('throws an error', async () => {
// @gate !disableStringRefs && !__DEV__
it('throws an error in prod', async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How was this passing before?

Currently this is just keeps the original stack/task and doesn't update
with new elements, which is the same as _debugOwner but it could be changed
to update.
Skip the React internals for the stack outside user space.
This ensures that the name is used by real stack traces.
They provide more context than the owner stacks since it's about parent
nesting.

We should really just use a diff view here that can highlight the affected
parent/child nodes in the diff view.
…nDev

To do this we need to break out the jsxDEV implementation into a helper.
Comment on lines +94 to +96
if (!enableOwnerStacks || !__DEV__) {
return '';
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we going to fallback to getStackByFiberInDevAndProd in PROD?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we could really error here because this should not be called in prod. Hence InDev suffix.

That's because we never add component stacks to prod automatically:

  • We only include our own console.error override in DEV.
  • We only include our component stacks for default onCaughtError/onUncaughtError in DEV.
  • When we eventually expose this to the errorInfo object it would be on a DEV-only property.

The React DevTools could in principle be different. Basically older React versions will look the same as prod versions of new React.

@sebmarkbage sebmarkbage merged commit d6cfa0f into facebook:main May 25, 2024
40 checks passed
github-actions bot pushed a commit that referenced this pull request May 25, 2024
This one should be fully behind the `enableOwnerStacks` flag.

Instead of printing the parent Component stack all the way to the root,
this now prints the owner stack of every JSX callsite. It also includes
intermediate callsites between the Component and the JSX call so it has
potentially more frames. Mainly it provides the line number of the JSX
callsite. In terms of the number of components is a subset of the parent
component stack so it's less information in that regard. This is usually
better since it's more focused on components that might affect the
output but if it's contextual based on rendering it's still good to have
parent stack. Therefore, I still use the parent stack when printing DOM
nesting warnings but I plan on switching that format to a diff view
format instead (Next.js already reformats the parent stack like this).

__Follow ups__

- Server Components show up in the owner stack for client logs but logs
done by Server Components don't yet get their owner stack printed as
they're replayed. They're also not yet printed in the server logs of the
RSC server.

- Server Component stack frames are formatted as the server and added to
the end but this might be a different format than the browser. E.g. if
server is running V8 and browser is running JSC or vice versa. Ideally
we can reformat them in terms of the client formatting.

- This doesn't yet update Fizz or DevTools. Those will be follow ups.
Fizz still prints parent stacks in the server side logs. The stacks
added to user space `console.error` calls by DevTools still get the
parent stacks instead.

- It also doesn't yet expose these to user space so there's no way to
get them inside `onCaughtError` for example or inside a custom
`console.error` override.

- In another follow up I'll use `console.createTask` instead and
completely remove these stacks if it's available.

DiffTrain build for commit d6cfa0f.
github-actions bot pushed a commit that referenced this pull request May 25, 2024
This one should be fully behind the `enableOwnerStacks` flag.

Instead of printing the parent Component stack all the way to the root,
this now prints the owner stack of every JSX callsite. It also includes
intermediate callsites between the Component and the JSX call so it has
potentially more frames. Mainly it provides the line number of the JSX
callsite. In terms of the number of components is a subset of the parent
component stack so it's less information in that regard. This is usually
better since it's more focused on components that might affect the
output but if it's contextual based on rendering it's still good to have
parent stack. Therefore, I still use the parent stack when printing DOM
nesting warnings but I plan on switching that format to a diff view
format instead (Next.js already reformats the parent stack like this).

__Follow ups__

- Server Components show up in the owner stack for client logs but logs
done by Server Components don't yet get their owner stack printed as
they're replayed. They're also not yet printed in the server logs of the
RSC server.

- Server Component stack frames are formatted as the server and added to
the end but this might be a different format than the browser. E.g. if
server is running V8 and browser is running JSC or vice versa. Ideally
we can reformat them in terms of the client formatting.

- This doesn't yet update Fizz or DevTools. Those will be follow ups.
Fizz still prints parent stacks in the server side logs. The stacks
added to user space `console.error` calls by DevTools still get the
parent stacks instead.

- It also doesn't yet expose these to user space so there's no way to
get them inside `onCaughtError` for example or inside a custom
`console.error` override.

- In another follow up I'll use `console.createTask` instead and
completely remove these stacks if it's available.

DiffTrain build for [d6cfa0f](d6cfa0f)
sebmarkbage added a commit that referenced this pull request May 26, 2024
Stacked on #29206 and #29221.

This disables appending owner stacks to console when
`console.createTask` is available in the environment. Instead we rely on
native "async" stacks that end up looking like this with source maps and
ignore list enabled.

<img width="673" alt="Screenshot 2024-05-22 at 4 00 27 PM"
src="https://github.com/facebook/react/assets/63648/5313ed53-b298-4386-8f76-8eb85bdfbbc7">

Unfortunately Chrome requires a string name for each async stack and,
worse, a suffix of `(async)` is automatically added which is very
confusing since it seems like it might be an async component or
something which it is not.

In this case it's not so bad because it's nice to refer to the host
component which otherwise doesn't have a stack frame since it's
internal. However, if there were more owners here there would also be a
`<Counter> (async)` which ends up being kind of duplicative.

If the Chrome DevTools is not open from the start of the app, then
`console.createTask` is disabled and so you lose the stack for those
errors (or those parents if the devtools is opened later). Unlike our
appended ones that are always added. That's unfortunate and likely to be
a bit of a DX issue but it's also nice that it saves on perf in DEV mode
for those cases. Framework dialogs can still surface the stack since we
also track it in user space in parallel.

This currently doesn't track Server Components yet. We need a more
clever hack for that part in a follow up.

I think I probably need to also add something to React DevTools to
disable its stacks for this case too. Since it looks for stacks in the
console.error and adds a stack otherwise. Since we don't add them
anymore from the runtime, the DevTools adds them instead.
github-actions bot pushed a commit that referenced this pull request May 26, 2024
Stacked on #29206 and #29221.

This disables appending owner stacks to console when
`console.createTask` is available in the environment. Instead we rely on
native "async" stacks that end up looking like this with source maps and
ignore list enabled.

<img width="673" alt="Screenshot 2024-05-22 at 4 00 27 PM"
src="https://github.com/facebook/react/assets/63648/5313ed53-b298-4386-8f76-8eb85bdfbbc7">

Unfortunately Chrome requires a string name for each async stack and,
worse, a suffix of `(async)` is automatically added which is very
confusing since it seems like it might be an async component or
something which it is not.

In this case it's not so bad because it's nice to refer to the host
component which otherwise doesn't have a stack frame since it's
internal. However, if there were more owners here there would also be a
`<Counter> (async)` which ends up being kind of duplicative.

If the Chrome DevTools is not open from the start of the app, then
`console.createTask` is disabled and so you lose the stack for those
errors (or those parents if the devtools is opened later). Unlike our
appended ones that are always added. That's unfortunate and likely to be
a bit of a DX issue but it's also nice that it saves on perf in DEV mode
for those cases. Framework dialogs can still surface the stack since we
also track it in user space in parallel.

This currently doesn't track Server Components yet. We need a more
clever hack for that part in a follow up.

I think I probably need to also add something to React DevTools to
disable its stacks for this case too. Since it looks for stacks in the
console.error and adds a stack otherwise. Since we don't add them
anymore from the runtime, the DevTools adds them instead.

DiffTrain build for commit ea6e059.
github-actions bot pushed a commit that referenced this pull request May 26, 2024
Stacked on #29206 and #29221.

This disables appending owner stacks to console when
`console.createTask` is available in the environment. Instead we rely on
native "async" stacks that end up looking like this with source maps and
ignore list enabled.

<img width="673" alt="Screenshot 2024-05-22 at 4 00 27 PM"
src="https://github.com/facebook/react/assets/63648/5313ed53-b298-4386-8f76-8eb85bdfbbc7">

Unfortunately Chrome requires a string name for each async stack and,
worse, a suffix of `(async)` is automatically added which is very
confusing since it seems like it might be an async component or
something which it is not.

In this case it's not so bad because it's nice to refer to the host
component which otherwise doesn't have a stack frame since it's
internal. However, if there were more owners here there would also be a
`<Counter> (async)` which ends up being kind of duplicative.

If the Chrome DevTools is not open from the start of the app, then
`console.createTask` is disabled and so you lose the stack for those
errors (or those parents if the devtools is opened later). Unlike our
appended ones that are always added. That's unfortunate and likely to be
a bit of a DX issue but it's also nice that it saves on perf in DEV mode
for those cases. Framework dialogs can still surface the stack since we
also track it in user space in parallel.

This currently doesn't track Server Components yet. We need a more
clever hack for that part in a follow up.

I think I probably need to also add something to React DevTools to
disable its stacks for this case too. Since it looks for stacks in the
console.error and adds a stack otherwise. Since we don't add them
anymore from the runtime, the DevTools adds them instead.

DiffTrain build for [ea6e059](ea6e059)
sebmarkbage added a commit that referenced this pull request May 31, 2024
When defining a displayName on forwardRef/memo we forward that name to
the inner function.

We used to use displayName for this but in #29206 I switched this to use
`"name"`. That's because V8 doesn't use displayName, it only uses the
overridden name in stack traces. This is the only thing covered by our
tests for component stacks.

However, I realized that Safari only uses displayName and not the name.
So this sets both.
github-actions bot pushed a commit that referenced this pull request May 31, 2024
When defining a displayName on forwardRef/memo we forward that name to
the inner function.

We used to use displayName for this but in #29206 I switched this to use
`"name"`. That's because V8 doesn't use displayName, it only uses the
overridden name in stack traces. This is the only thing covered by our
tests for component stacks.

However, I realized that Safari only uses displayName and not the name.
So this sets both.

DiffTrain build for commit 63d673c.
github-actions bot pushed a commit that referenced this pull request May 31, 2024
When defining a displayName on forwardRef/memo we forward that name to
the inner function.

We used to use displayName for this but in #29206 I switched this to use
`"name"`. That's because V8 doesn't use displayName, it only uses the
overridden name in stack traces. This is the only thing covered by our
tests for component stacks.

However, I realized that Safari only uses displayName and not the name.
So this sets both.

DiffTrain build for [63d673c](63d673c)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants