Skip to content

Change warning() to automatically inject the stack, and add warningWithoutStack() as opt-out#13161

Merged
gaearon merged 13 commits intofacebook:masterfrom
gaearon:more-warning-stuff
Jul 16, 2018
Merged

Change warning() to automatically inject the stack, and add warningWithoutStack() as opt-out#13161
gaearon merged 13 commits intofacebook:masterfrom
gaearon:more-warning-stuff

Conversation

@gaearon
Copy link
Copy Markdown
Collaborator

@gaearon gaearon commented Jul 6, 2018

Summary

This flips warning() to always include a component stack by default if it exists. It also adds a counterpart warningWithoutStack() for cases where you don't want to have a stack.

I opted to have two separate methods because warning arguments already have a meaning (they get interpolated). I decided against something like warning.withoutStack() because this proved to be a bit annoying in the past (e.g. emptyFunction.*), and would be a tiny bit more difficult to adopt our transforms to. I don't feel strongly about this though.

This PR shouldn't contain any functional changes. Only warnings without stacks were converted to use the new function. Warnings that already have stacks just lost their last argument because it's provided automatically now.

Why

  • I want to make it easy for newly added warnings to always have stacks.
  • This removes a bunch of ReactDebugCurrentFrame direct calls, imports, and wiring.
  • This paves the way towards doing more in warning(), e.g. determining whether we're in strict mode. Which is something we'll want to expose in every warning so that we can get rid of internal blacklist.

How to Review

  • Calls to warning() that had stacks now omit the last formatting argument.
  • Calls to warning() that didn't have stacks now use warningWithoutStack().
  • There are a few infra changes that make me more confident in this, e.g. I added validation of the warning argument number again the count of %s interpolations in tests.

One notable change is that instead of formatting before calling console.error(), we now pass through the message and the arguments, e.g. console.error('%s is a bad component', 'Foo'). This works in all major browsers (including IE), and Node.js. Theoretically this might be a breaking change for somebody who was relying on filtering console.error calls by exact messages. But we already change warning messages between releases anyway, and this change should make it easier to filter messages because the original formatting string is now exposed before the interpolation.

Work Log

  • Introduce warningWithStack() that automatically includes a stack from the currently injected implementation.
  • Start converting callsites to use warningWithStack() and remove threading of getStack through utilities shared between SSR and client.
  • Figure out places like assertValidProps and how they need the frame. Why don't test fails in prod there?
  • Consider what to do for isomorphic callsites. They currently wouldn't work with warningWithStack() because it would create a circular dependency between React and warningWithStack.
  • Maybe flip them. Make warning() have a stack, and warningWithoutStack() its explicit counterpart.
  • Fail the build if we use warning with stack in a package that doesn't have React dep
  • Change our transforms to handle the new warning method too.
  • Fix Flow
Follow-up work
  • Consider snapshotting warnings?
  • Sometimes we deduplicate based on the stack. Should warningWithStack() just do it by default? Other ideas?
  • Convert all renderer callsites that include the stack to warningWithStack().
  • Remove direct usage of getCurrentFiberStackAddendum. Always go through the central isomorphic module.
  • Consider doing the same for invariants.
  • Enforce that number of formatting arguments is expected in tests.
  • Consider attaching stack to invariants too? Make debug frame non-DEV-only?
  • Include strictness into the warnings. Integrate StrictMode warnings with this new infra.
  • Ensure internal callsites that depend on %s interpolation having already happened don't break

@gaearon
Copy link
Copy Markdown
Collaborator Author

gaearon commented Jul 6, 2018

@sebmarkbage Not ready for review but feel free to flag if I'm going in some completely different direction than you imagined

@pull-bot
Copy link
Copy Markdown

pull-bot commented Jul 6, 2018

ReactDOM: size: -0.0%, gzip: -0.1%

Details of bundled changes.

Comparing: 854c953...e0eab4c

react

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react.development.js +1.2% +1.1% 56.74 KB 57.39 KB 15.86 KB 16.03 KB UMD_DEV
react.development.js +1.3% +1.3% 50.93 KB 51.58 KB 14.03 KB 14.21 KB NODE_DEV
React-dev.js +2.6% +3.5% 48.59 KB 49.86 KB 13.31 KB 13.78 KB FB_WWW_DEV

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.development.js +0.1% +0.2% 640.04 KB 640.55 KB 149.99 KB 150.24 KB UMD_DEV
react-dom.production.min.js -0.0% -0.1% 95.53 KB 95.49 KB 30.91 KB 30.88 KB UMD_PROD
react-dom.development.js +0.1% +0.2% 636.17 KB 636.69 KB 148.81 KB 149.05 KB NODE_DEV
react-dom.production.min.js -0.0% -0.0% 95.51 KB 95.47 KB 30.41 KB 30.4 KB NODE_PROD
react-dom-test-utils.development.js -0.2% +0.5% 44.97 KB 44.89 KB 12.25 KB 12.31 KB UMD_DEV
react-dom-test-utils.development.js -0.2% +0.5% 44.69 KB 44.61 KB 12.19 KB 12.25 KB NODE_DEV
react-dom-unstable-native-dependencies.development.js -0.1% -0.2% 59.99 KB 59.96 KB 15.88 KB 15.85 KB UMD_DEV
react-dom-unstable-native-dependencies.development.js -0.1% -0.2% 59.66 KB 59.63 KB 15.75 KB 15.72 KB NODE_DEV
react-dom-server.browser.development.js 0.0% +0.6% 102.29 KB 102.3 KB 27.06 KB 27.23 KB UMD_DEV
react-dom-server.browser.development.js 0.0% +0.6% 98.42 KB 98.43 KB 26.13 KB 26.3 KB NODE_DEV
react-dom-server.node.development.js 0.0% +0.6% 100.34 KB 100.35 KB 26.67 KB 26.83 KB NODE_DEV
ReactDOM-dev.js +0.2% +0.3% 642.51 KB 643.65 KB 147.13 KB 147.55 KB FB_WWW_DEV
ReactDOM-prod.js -0.1% -0.1% 276.77 KB 276.63 KB 51.75 KB 51.72 KB FB_WWW_PROD
ReactTestUtils-dev.js +3.3% +4.2% 41.88 KB 43.28 KB 11.35 KB 11.83 KB FB_WWW_DEV
ReactDOMUnstableNativeDependencies-dev.js +2.6% +3.3% 57.33 KB 58.81 KB 14.69 KB 15.17 KB FB_WWW_DEV
ReactDOMUnstableNativeDependencies-prod.js -0.1% -0.1% 26.21 KB 26.19 KB 5.32 KB 5.32 KB FB_WWW_PROD
ReactDOMServer-dev.js +0.4% +1.8% 99.56 KB 99.97 KB 25.67 KB 26.14 KB FB_WWW_DEV
ReactDOMServer-prod.js 0.0% 0.0% 32.87 KB 32.87 KB 7.94 KB 7.94 KB FB_WWW_PROD
react-dom.profiling.min.js -0.0% -0.1% 96.6 KB 96.56 KB 30.82 KB 30.79 KB NODE_PROFILING
ReactDOM-profiling.js -0.0% -0.1% 279.77 KB 279.63 KB 52.41 KB 52.38 KB FB_WWW_PROFILING

react-art

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-art.development.js +0.3% +0.4% 424.45 KB 425.75 KB 95.71 KB 96.06 KB UMD_DEV
react-art.development.js +0.4% +0.4% 357 KB 358.31 KB 78.64 KB 78.99 KB NODE_DEV
ReactART-dev.js +0.6% +0.7% 346.19 KB 348.21 KB 73.43 KB 73.92 KB FB_WWW_DEV
ReactART-prod.js 0.0% 0.0% 147.7 KB 147.7 KB 24.99 KB 24.99 KB FB_WWW_PROD

react-test-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-test-renderer.development.js +0.4% +0.4% 361.32 KB 362.63 KB 79.16 KB 79.51 KB UMD_DEV
react-test-renderer.development.js +0.4% +0.4% 357.44 KB 358.76 KB 78.2 KB 78.54 KB NODE_DEV
react-test-renderer-shallow.development.js -0.5% -0.4% 23.34 KB 23.23 KB 6.31 KB 6.29 KB UMD_DEV
react-test-renderer-shallow.development.js -0.6% -0.7% 18.53 KB 18.42 KB 5.14 KB 5.11 KB NODE_DEV
ReactTestRenderer-dev.js +0.6% +0.6% 361.49 KB 363.63 KB 77.06 KB 77.55 KB FB_WWW_DEV
ReactShallowRenderer-dev.js +8.2% +11.5% 16.84 KB 18.23 KB 4.41 KB 4.92 KB FB_WWW_DEV

react-reconciler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-reconciler.development.js +0.4% +0.5% 345.38 KB 346.71 KB 74.55 KB 74.9 KB NODE_DEV
react-reconciler-persistent.development.js +0.4% +0.5% 344 KB 345.32 KB 73.99 KB 74.34 KB NODE_DEV
react-reconciler-reflection.development.js -0.7% -0.8% 14.33 KB 14.24 KB 4.51 KB 4.47 KB NODE_DEV

react-native-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactNativeRenderer-dev.js +0.3% +0.3% 479.79 KB 481.32 KB 106.14 KB 106.45 KB RN_FB_DEV
ReactNativeRenderer-dev.js +0.3% +0.3% 479.53 KB 481.05 KB 106.08 KB 106.39 KB RN_OSS_DEV
ReactFabric-dev.js +0.3% +0.3% 470.02 KB 471.53 KB 103.71 KB 104.01 KB RN_FB_DEV
ReactFabric-dev.js +0.3% +0.3% 470.05 KB 471.57 KB 103.72 KB 104.03 KB RN_OSS_DEV

simple-cache-provider

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
simple-cache-provider.development.js -0.8% -1.2% 8.86 KB 8.79 KB 2.86 KB 2.83 KB NODE_DEV
SimpleCacheProvider-dev.js +18.2% +21.2% 7.86 KB 9.29 KB 2.38 KB 2.89 KB FB_WWW_DEV
SimpleCacheProvider-prod.js -0.5% -0.9% 3.65 KB 3.63 KB 1.11 KB 1.1 KB FB_WWW_PROD

react-scheduler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-scheduler.development.js -0.6% -0.8% 16.74 KB 16.65 KB 5.15 KB 5.11 KB UMD_DEV
react-scheduler.development.js -0.6% -0.8% 16.55 KB 16.46 KB 5.11 KB 5.06 KB NODE_DEV
ReactScheduler-dev.js +9.1% +10.6% 15.36 KB 16.76 KB 4.63 KB 5.12 KB FB_WWW_DEV
ReactScheduler-prod.js -0.2% -0.6% 7.59 KB 7.58 KB 1.91 KB 1.89 KB FB_WWW_PROD

Generated by 🚫 dangerJS

@gaearon gaearon force-pushed the more-warning-stuff branch 6 times, most recently from 67500ad to 71cafa8 Compare July 13, 2018 01:54
@gaearon gaearon force-pushed the more-warning-stuff branch from a1903f4 to c597279 Compare July 16, 2018 20:00
I changed my mind and want to keep this PR without functional changes. So we won't "fix" any warnings that are already missing stacks. We'll do it in follow-ups instead.
@gaearon gaearon force-pushed the more-warning-stuff branch from c597279 to 349fb1e Compare July 16, 2018 20:19
@gaearon gaearon changed the title [WIP] Automatically inject stack into some warnings Change warning() to automatically inject the stack, and add warningWithoutStack() as opt-out Jul 16, 2018
@gaearon gaearon requested review from acdlite, bvaughn, flarnie and sophiebits and removed request for sebmarkbage July 16, 2018 20:56
@gaearon gaearon requested a review from sebmarkbage July 16, 2018 21:00
Copy link
Copy Markdown
Contributor

@bvaughn bvaughn left a comment

Choose a reason for hiding this comment

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

Nice cleanup

// Update
ReactNoop.render(<Context.Provider value={2} />);
ReactNoop.flush();
}).toWarnDev(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

tiny nit: Might be nicer to move ReactNoop.render outside of the expect call.

console.error('Hi %s');
}).toWarnDev('Hi', {withoutStack: true});
}).toThrow('Received 0 arguments for a message with 1 placeholders');
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice 👍

args,
expectedArgCount: argIndex,
};
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Clever!

Could use an inline comment. Wasn't clear (to me at least) on first read what it was doing.

Comment thread scripts/rollup/forks.js
'warning() instead of warningWithoutStack() in a package that does not ' +
'depend on React.'
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice!

require.resolve(targetModule)
typeof targetModule === 'string'
? require.resolve(targetModule)
: targetModule
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hm? 🤔

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Since I added ability to return an error from a fork config, I need to ensure I don’t run require.resolve on that error 🙂

On the other hand I can’t throw it early either because I don’t know if that module will get used or not.

@gaearon gaearon merged commit f9358c5 into facebook:master Jul 16, 2018
gaearon added a commit that referenced this pull request Jul 16, 2018
This is a leftover from #13161 that I forgot to include.
It ensures we don't accidentally write code in the old way and end up passing the stack twice.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants