The old version of prettier we were using didn't support the Flow syntax
to access properties in a type using `SomeType['prop']`. This updates
`prettier` and `rollup-plugin-prettier` to the latest versions.
I added the prettier config `arrowParens: "avoid"` to reduce the diff
size as the default has changed in Prettier 2.0. The largest amount of
changes comes from function expressions now having a space. This doesn't
have an option to preserve the old behavior, so we have to update this.
We've heard from multiple contributors that the Reconciler forking
mechanism was confusing and/or annoying to deal with. Since it's
currently unused and there's no immediate plans to start using it again,
this removes the forking.
Fully removing the fork is split into 2 steps to preserve file history:
**This PR**
- remove `enableNewReconciler` feature flag.
- remove `unstable_isNewReconciler` export
- remove eslint rules for cross fork imports
- remove `*.new.js` files and update imports
- merge non-suffixed files into `*.old` files where both exist
(sometimes types were defined there)
**#25775**
- rename `*.old` files
* 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
* Update safe-string-coercion to handle additions of string literals
Adding strings shouldn't trigger a lint violation of this rule, since
adding strings are always safe.
* Hoist error codes import to module scope
When this code was written, the error codes map (`codes.json`) was
created on-the-fly, so we had to lazily require from inside the visitor.
Because `codes.json` is now checked into source, we can import it a
single time in module scope.
* Minify error constructors in production
We use a script to minify our error messages in production. Each message
is assigned an error code, defined in `scripts/error-codes/codes.json`.
Then our build script replaces the messages with a link to our
error decoder page, e.g. https://reactjs.org/docs/error-decoder.html/?invariant=92
This enables us to write helpful error messages without increasing the
bundle size.
Right now, the script only works for `invariant` calls. It does not work
if you throw an Error object. This is an old Facebookism that we don't
really need, other than the fact that our error minification script
relies on it.
So, I've updated the script to minify error constructors, too:
Input:
Error(`A ${adj} message that contains ${noun}`);
Output:
Error(formatProdErrorMessage(ERR_CODE, adj, noun));
It only works for constructors that are literally named Error, though we
could add support for other names, too.
As a next step, I will add a lint rule to enforce that errors written
this way must have a corresponding error code.
* Minify "no fallback UI specified" error in prod
This error message wasn't being minified because it doesn't use
invariant. The reason it didn't use invariant is because this particular
error is created without begin thrown — it doesn't need to be thrown
because it's located inside the error handling part of the runtime.
Now that the error minification script supports Error constructors, we
can minify it by assigning it a production error code in
`scripts/error-codes/codes.json`.
To support the use of Error constructors more generally, I will add a
lint rule that enforces each message has a corresponding error code.
* Lint rule to detect unminified errors
Adds a lint rule that detects when an Error constructor is used without
a corresponding production error code.
We already have this for `invariant`, but not for regular errors, i.e.
`throw new Error(msg)`. There's also nothing that enforces the use of
`invariant` besides convention.
There are some packages where we don't care to minify errors. These are
packages that run in environments where bundle size is not a concern,
like react-pg. I added an override in the ESLint config to ignore these.
* Temporarily add invariant codemod script
I'm adding this codemod to the repo temporarily, but I'll revert it
in the same PR. That way we don't have to check it in but it's still
accessible (via the PR) if we need it later.
* [Automated] Codemod invariant -> Error
This commit contains only automated changes:
npx jscodeshift -t scripts/codemod-invariant.js packages --ignore-pattern="node_modules/**/*"
yarn linc --fix
yarn prettier
I will do any manual touch ups in separate commits so they're easier
to review.
* Remove temporary codemod script
This reverts the codemod script and ESLint config I added temporarily
in order to perform the invariant codemod.
* Manual touch ups
A few manual changes I made after the codemod ran.
* Enable error code transform per package
Currently we're not consistent about which packages should have their
errors minified in production and which ones should.
This adds a field to the bundle configuration to control whether to
apply the transform. We should decide what the criteria is going
forward. I think it's probably a good idea to minify any package that
gets sent over the network. So yes to modules that run in the browser,
and no to modules that run on the server and during development only.
* 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.
* Lint rule to forbid access of cross-fork fields
We use a shared Fiber type for both reconciler forks (old and new). It
is a superset of all the fields used by both forks. However, there are
some fields that should only be used in the new fork, and others that
should only be used in the old fork.
Ideally we would enforce this with separate Flow types for each fork.
The problem is that the Fiber type is accessed by some packages outside
the reconciler (like React DOM), and get passed into the reconciler as
arguments. So there's no way to fork the Fiber type without also forking
the packages where they are used. FiberRoot has the same issue.
Instead, I've added a lint rule that forbids cross-fork access of
fork-specific fields. Fields that end in `_old` or `_new` are forbidden
from being used inside the new or old fork respectively. Or you can
specific custom fields using the ESLint plugin options.
I used this plugin to find and remove references to the effect list
in d2e914a.
* Mark effect list fields as old
And `subtreeTag` as new.
I didn't mark `lastEffect` because that name is also used by the
Hook type. Not super important; could rename to `lastEffect_old` but
idk if it's worth the effort.
* Add autofix to cross-fork lint rule
* replace-fork: Replaces old fork contents with new
For each file in the new fork, copies the contents into the
corresponding file of the old fork, replacing what was already there.
In contrast to merge-fork, which performs a three-way merge.
* Replace old fork contents with new fork
First I ran `yarn replace-fork`.
Then I ran `yarn lint` with autofix enabled. There's currently no way to
do that from the command line (we should fix that), so I had to edit the
lint script file.
* Manual fix-ups
Removes dead branches, removes prefixes from internal fields. Stuff
like that.
* Fix DevTools tests
DevTools tests only run against the old fork, which is why I didn't
catch these earlier.
There is one test that is still failing. I'm fairly certain it's related
to the layout of the Suspense fiber: we no longer conditionally wrap the
primary children. They are always wrapped in an extra fiber.
Since this has been running in www for weeks without major issues, I'll
defer fixing the remaining test to a follow up.
Modules that belong to one fork should not import modules that belong to
the other fork.
Helps make sure you correctly update imports when syncing changes across
implementations.
Also could help protect against code size regressions that might happen
if one of the forks accidentally depends on two copies of the same
module.
* Replace all warning/lowPriWarning with console calls
* Replace console.warn/error with a custom wrapper at build time
* Fail the build for console.error/warn() where we can't read the stack
* prep for codemod
* prep warnings
* rename lint rules
* codemod for ifs
* shim www functions
* Handle more cases in the transform
* Thanks De Morgan
* Run the codemod
* Delete the transform
* Fix up confusing conditions manually
* Fix up www shims to match expected API
* Also check for low-pri warning in the lint rule
* Replace Babel plugin with an ESLint plugin
* Fix ESLint rule violations
* Move shared conditions higher
* Test formatting nits
* Tweak ESLint rule
* Bugfix: inside else branch, 'if' tests are not satisfactory
* Use a stricter check for exactly if (__DEV__)
This makes it easier to see what's going on and matches dominant style in the codebase.
* Fix remaining files after stricter check
* Don't bother including `unstable_` in error
The method names don't get stripped out of the production bundles
because they are passed as arguments to the error decoder.
Let's just always use the unprefixed APIs in the messages.
* Set up experimental builds
The experimental builds are packaged exactly like builds in the stable
release channel: same file structure, entry points, and npm package
names. The goal is to match what will eventually be released in stable
as closely as possible, but with additional features turned on.
Versioning and Releasing
------------------------
The experimental builds will be published to the same registry and
package names as the stable ones. However, they will be versioned using
a separate scheme. Instead of semver versions, experimental releases
will receive arbitrary version strings based on their content hashes.
The motivation is to thwart attempts to use a version range to match
against future experimental releases. The only way to install or depend
on an experimental release is to refer to the specific version number.
Building
--------
I did not use the existing feature flag infra to configure the
experimental builds. The reason is because feature flags are designed
to configure a single package. They're not designed to generate multiple
forks of the same package; for each set of feature flags, you must
create a separate package configuration.
Instead, I've added a new build dimension called the **release
channel**. By default, builds use the **stable** channel. There's
also an **experimental** release channel. We have the option to add more
in the future.
There are now two dimensions per artifact: build type (production,
development, or profiling), and release channel (stable or
experimental). These are separate dimensions because they are
combinatorial: there are stable and experimental production builds,
stable and experimental developmenet builds, and so on.
You can add something to an experimental build by gating on
`__EXPERIMENTAL__`, similar to how we use `__DEV__`. Anything inside
these branches will be excluded from the stable builds.
This gives us a low effort way to add experimental behavior in any
package without setting up feature flags or configuring a new package.
* Lint rule for unminified errors
Add a lint rule that fails if an invariant message is not part of the
error code map.
The goal is to be more disciplined about adding and modifiying
production error codes. Error codes should be consistent across releases
even if their wording changes, for continuity in logs.
Currently, error codes are added to the error code map via an automated
script that runs right before release. The problem with this approach is
that if someone modifies an error message in the source, but neglects to
modify the corresponding message in the error code map, then the message
will be assigned a new error code, instead of reusing the existing one.
Because the error extraction script only runs before a release, people
rarely modify the error code map in practice. By moving the extraction
step to the PR stage, it forces the author to consider whether the
message should be assigned a new error code. It also allows the reviewer
to review the changes.
The trade off is that it requires more effort and context to land new
error messages, or to modify existing ones, particular for new
contributors who are not familiar with our processes.
Since we already expect users to lint their code, I would argue the
additional burden is marginal. Even if they forget to run the lint
command locally, they will get quick feedback from the CI lint job,
which typically finishes within 2-3 minutes.
* Add unreleased error messages to map
* use different eslint config for es6 and es5
* remove confusing eslint/baseConfig.js & add more eslint setting for es5, es6
* more clear way to run eslint on es5 & es6 file
* seperate ESNext, ES6, ES6 path, and use different lint config
* rename eslint config file & update eslint rules
* Undo yarn.lock changes
* Rename a file
* Remove unnecessary exceptions
* Refactor a little bit
* Refactor and tweak the logic
* Minor issues
* Extract Jest config into a separate file
* Refactor Jest scripts directory structure
Introduces a more consistent naming scheme.
* Add yarn test-bundles and yarn test-prod-bundles
Only files ending with -test.public.js are opted in (so far we don't have any).
* Fix error decoding for production bundles
GCC seems to remove `new` from `new Error()` which broke our proxy.
* Build production version of react-noop-renderer
This lets us test more bundles.
* Switch to blacklist (exclude .private.js tests)
* Rename tests that are currently broken against bundles to *-test.internal.js
Some of these are using private APIs. Some have other issues.
* Add bundle tests to CI
* Split private and public ReactJSXElementValidator tests
* Remove internal deps from ReactServerRendering-test and make it public
* Only run tests directly in __tests__
This lets us share code between test files by placing them in __tests__/utils.
* Remove ExecutionEnvironment dependency from DOMServerIntegrationTest
It's not necessary since Stack.
* Split up ReactDOMServerIntegration into test suite and utilities
This enables us to further split it down. Good both for parallelization and extracting public parts.
* Split Fragment tests from other DOMServerIntegration tests
This enables them to opt other DOMServerIntegration tests into bundle testing.
* Split ReactDOMServerIntegration into different test files
It was way too slow to run all these in sequence.
* Don't reset the cache twice in DOMServerIntegration tests
We used to do this to simulate testing separate bundles.
But now we actually *do* test bundles. So there is no need for this, as it makes tests slower.
* Rename test-bundles* commands to test-build*
Also add test-prod-build as alias for test-build-prod because I keep messing them up.
* Use regenerator polyfill for react-noop
This fixes other issues and finally lets us run ReactNoop tests against a prod bundle.
* Run most Incremental tests against bundles
Now that GCC generator issue is fixed, we can do this.
I split ErrorLogging test separately because it does mocking. Other error handling tests don't need it.
* Update sizes
* Fix ReactMount test
* Enable ReactDOMComponent test
* Fix a warning issue uncovered by flat bundle testing
With flat bundles, we couldn't produce a good warning for <div onclick={}> on SSR
because it doesn't use the event system. However the issue was not visible in normal
Jest runs because the event plugins have been injected by the time the test ran.
To solve this, I am explicitly passing whether event system is available as an argument
to the hook. This makes the behavior consistent between source and bundle tests. Then
I change the tests to document the actual logic and _attempt_ to show a nice message
(e.g. we know for sure `onclick` is a bad event but we don't know the right name for it
on the server so we just say a generic message about camelCase naming convention).
* Inline getTestDocument into test cases
* Remove mention of mock file we do not use
* Remove unused configuration entries
* Move eslint-rules package into the scripts/ folder