<!--
Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.
Before submitting a pull request, please make sure the following is
done:
1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
10. If you haven't already, complete the CLA.
Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->
## Summary
Changes `before before` to `before` in error messages.
<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->
I want to improve error logs
## How did you test this change?
<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
If you leave this empty, your PR will very likely be closed.
-->
* Facebook -> Meta in copyright
rg --files | xargs sed -i 's#Copyright (c) Facebook, Inc. and its affiliates.#Copyright (c) Meta Platforms, Inc. and affiliates.#g'
* Manual tweaks
* Revise ESLint rules for string coercion
Currently, react uses `'' + value` to coerce mixed values to strings.
This code will throw for Temporal objects or symbols.
To make string-coercion safer and to improve user-facing error messages,
This commit adds a new ESLint rule called `safe-string-coercion`.
This rule has two modes: a production mode and a non-production mode.
* If the `isProductionUserAppCode` option is true, then `'' + value`
coercions are allowed (because they're faster, although they may
throw) and `String(value)` coercions are disallowed. Exception:
when building error messages or running DEV-only code in prod
files, `String()` should be used because it won't throw.
* If the `isProductionUserAppCode` option is false, then `'' + value`
coercions are disallowed (because they may throw, and in non-prod
code it's not worth the risk) and `String(value)` are allowed.
Production mode is used for all files which will be bundled with
developers' userland apps. Non-prod mode is used for all other React
code: tests, DEV blocks, devtools extension, etc.
In production mode, in addiiton to flagging `String(value)` calls,
the rule will also flag `'' + value` or `value + ''` coercions that may
throw. The rule is smart enough to silence itself in the following
"will never throw" cases:
* When the coercion is wrapped in a `typeof` test that restricts to safe
(non-symbol, non-object) types. Example:
if (typeof value === 'string' || typeof value === 'number') {
thisWontReport('' + value);
}
* When what's being coerced is a unary function result, because unary
functions never return an object or a symbol.
* When the coerced value is a commonly-used numeric identifier:
`i`, `idx`, or `lineNumber`.
* When the statement immeidately before the coercion is a DEV-only
call to a function from shared/CheckStringCoercion.js. This call is a
no-op in production, but in DEV it will show a console error
explaining the problem, then will throw right after a long explanatory
code comment so that debugger users will have an idea what's going on.
The check function call must be in the following format:
if (__DEV__) {
checkXxxxxStringCoercion(value);
};
Manually disabling the rule is usually not necessary because almost all
prod use of the `'' + value` pattern falls into one of the categories
above. But in the rare cases where the rule isn't smart enough to detect
safe usage (e.g. when a coercion is inside a nested ternary operator),
manually disabling the rule will be needed.
The rule should also be manually disabled in prod error handling code
where `String(value)` should be used for coercions, because it'd be
bad to throw while building an error message or stack trace!
The prod and non-prod modes have differentiated error messages to
explain how to do a proper coercion in that mode.
If a production check call is needed but is missing or incorrect
(e.g. not in a DEV block or not immediately before the coercion), then
a context-sensitive error message will be reported so that developers
can figure out what's wrong and how to fix the problem.
Because string coercions are now handled by the `safe-string-coercion`
rule, the `no-primitive-constructor` rule no longer flags `String()`
usage. It still flags `new String(value)` because that usage is almost
always a bug.
* Add DEV-only string coercion check functions
This commit adds DEV-only functions to check whether coercing
values to strings using the `'' + value` pattern will throw. If it will
throw, these functions will:
1. Display a console error with a friendly error message describing
the problem and the developer can fix it.
2. Perform the coercion, which will throw. Right before the line where
the throwing happens, there's a long code comment that will help
debugger users (or others looking at the exception call stack) figure
out what happened and how to fix the problem.
One of these check functions should be called before all string coercion
of user-provided values, except when the the coercion is guaranteed not
to throw, e.g.
* if inside a typeof check like `if (typeof value === 'string')`
* if coercing the result of a unary function like `+value` or `value++`
* if coercing a variable named in a whitelist of numeric identifiers:
`i`, `idx`, or `lineNumber`.
The new `safe-string-coercion` internal ESLint rule enforces that
these check functions are called when they are required.
Only use these check functions in production code that will be bundled
with user apps. For non-prod code (and for production error-handling
code), use `String(value)` instead which may be a little slower but will
never throw.
* Add failing tests for string coercion
Added failing tests to verify:
* That input, select, and textarea elements with value and defaultValue
set to Temporal-like objects which will throw when coerced to string
using the `'' + value` pattern.
* That text elements will throw for Temporal-like objects
* That dangerouslySetInnerHTML will *not* throw for Temporal-like
objects because this value is not cast to a string before passing to
the DOM.
* That keys that are Temporal-like objects will throw
All tests above validate the friendly error messages thrown.
* Use `String(value)` for coercion in non-prod files
This commit switches non-production code from `'' + value` (which
throws for Temporal objects and symbols) to instead use `String(value)`
which won't throw for these or other future plus-phobic types.
"Non-produciton code" includes anything not bundled into user apps:
* Tests and test utilities. Note that I didn't change legacy React
test fixtures because I assumed it was good for those files to
act just like old React, including coercion behavior.
* Build scripts
* Dev tools package - In addition to switching to `String`, I also
removed special-case code for coercing symbols which is now
unnecessary.
* Add DEV-only string coercion checks to prod files
This commit adds DEV-only function calls to to check if string coercion
using `'' + value` will throw, which it will if the value is a Temporal
object or a symbol because those types can't be added with `+`.
If it will throw, then in DEV these checks will show a console error
to help the user undertsand what went wrong and how to fix the
problem. After emitting the console error, the check functions will
retry the coercion which will throw with a call stack that's easy (or
at least easier!) to troubleshoot because the exception happens right
after a long comment explaining the issue. So whether the user is in
a debugger, looking at the browser console, or viewing the in-browser
DEV call stack, it should be easy to understand and fix the problem.
In most cases, the safe-string-coercion ESLint rule is smart enough to
detect when a coercion is safe. But in rare cases (e.g. when a coercion
is inside a ternary) this rule will have to be manually disabled.
This commit also switches error-handling code to use `String(value)`
for coercion, because it's bad to crash when you're trying to build
an error message or a call stack! Because `String()` is usually
disallowed by the `safe-string-coercion` ESLint rule in production
code, the rule must be disabled when `String()` is used.
* Migrated several additional tests to use new .toWarnDev() matcher
* Migrated ReactDOMComponent-test to use .toWarnDev() matcher
Note this test previous had some hacky logic to verify errors were reported against unique line numbers. Since the new matcher doesn't suppor this, I replaced this check with an equivalent (I think) comparison of unique DOM elements (eg div -> span)
* Updated several additional tests to use the new .toWarnDev() matcher
* Updated many more tests to use .toWarnDev()
* Move Jest setup files to /dev/ subdirectory
* Clone Jest /dev/ files into /prod/
* Move shared code into scripts/jest
* Move Jest config into the scripts folder
* Fix the equivalence test
It fails because the config is now passed to Jest explicitly.
But the test doesn't know about the config.
To fix this, we just run it via `yarn test` (which includes the config).
We already depend on Yarn for development anyway.
* Add yarn test-prod to run Jest with production environment
* Actually flip the production tests to run in prod environment
This produces a bunch of errors:
Test Suites: 64 failed, 58 passed, 122 total
Tests: 740 failed, 26 skipped, 1809 passed, 2575 total
Snapshots: 16 failed, 4 passed, 20 total
* Ignore expectDev() calls in production
Down from 740 to 175 failed.
Test Suites: 44 failed, 78 passed, 122 total
Tests: 175 failed, 26 skipped, 2374 passed, 2575 total
Snapshots: 16 failed, 4 passed, 20 total
* Decode errors so tests can assert on their messages
Down from 175 to 129.
Test Suites: 33 failed, 89 passed, 122 total
Tests: 129 failed, 1029 skipped, 1417 passed, 2575 total
Snapshots: 16 failed, 4 passed, 20 total
* Remove ReactDOMProduction-test
There is no need for it now. The only test that was special is moved into ReactDOM-test.
* Remove production switches from ReactErrorUtils
The tests now run in production in a separate pass.
* Add and use spyOnDev() for warnings
This ensures that by default we expect no warnings in production bundles.
If the warning *is* expected, use the regular spyOn() method.
This currently breaks all expectDev() assertions without __DEV__ blocks so we go back to:
Test Suites: 56 failed, 65 passed, 121 total
Tests: 379 failed, 1029 skipped, 1148 passed, 2556 total
Snapshots: 16 failed, 4 passed, 20 total
* Replace expectDev() with expect() in __DEV__ blocks
We started using spyOnDev() for console warnings to ensure we don't *expect* them to occur in production. As a consequence, expectDev() assertions on console.error.calls fail because console.error.calls doesn't exist. This is actually good because it would help catch accidental warnings in production.
To solve this, we are getting rid of expectDev() altogether, and instead introduce explicit expectation branches. We'd need them anyway for testing intentional behavior differences.
This commit replaces all expectDev() calls with expect() calls in __DEV__ blocks. It also removes a few unnecessary expect() checks that no warnings were produced (by also removing the corresponding spyOnDev() calls).
Some DEV-only assertions used plain expect(). Those were also moved into __DEV__ blocks.
ReactFiberErrorLogger was special because it console.error()'s in production too. So in that case I intentionally used spyOn() instead of spyOnDev(), and added extra assertions.
This gets us down to:
Test Suites: 21 failed, 100 passed, 121 total
Tests: 72 failed, 26 skipped, 2458 passed, 2556 total
Snapshots: 16 failed, 4 passed, 20 total
* Enable User Timing API for production testing
We could've disabled it, but seems like a good idea to test since we use it at FB.
* Test for explicit Object.freeze() differences between PROD and DEV
This is one of the few places where DEV and PROD behavior differs for performance reasons.
Now we explicitly test both branches.
* Run Jest via "yarn test" on CI
* Remove unused variable
* Assert different error messages
* Fix error handling tests
This logic is really complicated because of the global ReactFiberErrorLogger mock.
I understand it now, so I added TODOs for later.
It can be much simpler if we change the rest of the tests that assert uncaught errors to also assert they are logged as warnings.
Which mirrors what happens in practice anyway.
* Fix more assertions
* Change tests to document the DEV/PROD difference for state invariant
It is very likely unintentional but I don't want to change behavior in this PR.
Filed a follow up as https://github.com/facebook/react/issues/11618.
* Remove unnecessary split between DEV/PROD ref tests
* Fix more test message assertions
* Make validateDOMNesting tests DEV-only
* Fix error message assertions
* Document existing DEV/PROD message difference (possible bug)
* Change mocking assertions to be DEV-only
* Fix the error code test
* Fix more error message assertions
* Fix the last failing test due to known issue
* Run production tests on CI
* Unify configuration
* Fix coverage script
* Remove expectDev from eslintrc
* Run everything in band
We used to before, too. I just forgot to add the arguments after deleting the script.
* Move files and tests to more meaningful places
* Fix the build
Now that we import reconciler via react-reconciler, I needed to make a few tweaks.
* Update sizes
* Move @preventMunge directive to FB header
* Revert unintentional change
* Fix Flow coverage
I forgot to @flow-ify those files. This uncovered some issues.
* Prettier, I love you but you're bringing me down
Prettier, I love you but you're bringing me down
Like a rat in a cage
Pulling minimum wage
Prettier, I love you but you're bringing me down
Prettier, you're safer and you're wasting my time
Our records all show you were filthy but fine
But they shuttered your stores
When you opened the doors
To the cops who were bored once they'd run out of crime
Prettier, you're perfect, oh, please don't change a thing
Your mild billionaire mayor's now convinced he's a king
So the boring collect
I mean all disrespect
In the neighborhood bars I'd once dreamt I would drink
Prettier, I love you but you're freaking me out
There's a ton of the twist but we're fresh out of shout
Like a death in the hall
That you hear through your wall
Prettier, I love you but you're freaking me out
Prettier, I love you but you're bringing me down
Prettier, I love you but you're bringing me down
Like a death of the heart
Jesus, where do I start?
But you're still the one pool where I'd happily drown
And oh! Take me off your mailing list
For kids who think it still exists
Yes, for those who think it still exists
Maybe I'm wrong and maybe you're right
Maybe I'm wrong and maybe you're right
Maybe you're right, maybe I'm wrong
And just maybe you're right
And oh! Maybe mother told you true
And there'll always be somebody there for you
And you'll never be alone
But maybe she's wrong and maybe I'm right
And just maybe she's wrong
Maybe she's wrong and maybe I'm right
And if so, here's this song!