This is similar to #28771 but for isomorphic. We need a make over for
these dispatchers anyway so this is the first step. Also helps flush out
some internals usage that will break anyway.
It flattens the inner mutable objects onto the ReactSharedInternals.
Moving this to `internal-test-utils` so I can add helpers in the next PR
for:
- assertLogDev
- assertWarnDev
- assertErrorDev
Which will be exported from `internal-test-utils`. This isn't strictly
necessary, but it makes the factoring nicer, so internal-test-until
doesn't need to depend on `scripts/jest`.
Stacked on top of #28498 for test fixes.
### Don't Rethrow
When we started React it was 1:1 setState calls a series of renders and
if they error, it errors where the setState was called. Simple. However,
then batching came and the error actually got thrown somewhere else.
With concurrent mode, it's not even possible to get setState itself to
throw anymore.
In fact, all APIs that can rethrow out of React are executed either at
the root of the scheduler or inside a DOM event handler.
If you throw inside a React.startTransition callback that's sync, then
that will bubble out of the startTransition but if you throw inside an
async callback or a useTransition we now need to handle it at the hook
site. So in 19 we need to make all React.startTransition swallow the
error (and report them to reportError).
The only one remaining that can throw is flushSync but it doesn't really
make sense for it to throw at the callsite neither because batching.
Just because something rendered in this flush doesn't mean it was
rendered due to what was just scheduled and doesn't mean that it should
abort any of the remaining code afterwards. setState is fire and forget.
It's send an instruction elsewhere, it's not part of the current
imperative code.
Error boundaries never rethrow. Since you should really always have
error boundaries, most of the time, it wouldn't rethrow anyway.
Rethrowing also actually currently drops errors on the floor since we
can only rethrow the first error, so to avoid that we'd need to call
reportError anyway. This happens in RN events.
The other issue with rethrowing is that it logs an extra console.error.
Since we're not sure that user code will actually log it anywhere we
still log it too just like we do with errors inside error boundaries
which leads all of these to log twice.
The goal of this PR is to never rethrow out of React instead, errors
outside of error boundaries get logged to reportError. Event system
errors too.
### Breaking Changes
The main thing this affects is testing where you want to inspect the
errors thrown. To make it easier to port, if you're inside `act` we
track the error into act in an aggregate error and then rethrow it at
the root of `act`. Unlike before though, if you flush synchronously
inside of act it'll still continue until the end of act before
rethrowing.
I expect most user code breakages would be to migrate from `flushSync`
to `act` if you assert on throwing.
However, in the React repo we also have `internalAct` and the
`waitForThrow` helpers. Since these have to use public production
implementations we track these using the global onerror or process
uncaughtException. Unlike regular act, includes both event handler
errors and onRecoverableError by default too. Not just render/commit
errors. So I had to account for that in our tests.
We restore logging an extra log for uncaught errors after the main log
with the component stack in it. We use `console.warn`. This is not yet
ignorable if you preventDefault to the main error event. To avoid
confusion if you don't end up logging the error to console I just added
`An error occurred`.
### Polyfill
All browsers we support really supports `reportError` but not all test
and server environments do, so I implemented a polyfill for browser and
node in `shared/reportGlobalError`. I don't love that this is included
in all builds and gets duplicated into isomorphic even though it's not
actually needed in production. Maybe in the future we can require a
polyfill for this.
### Follow Ups
In a follow up, I'll make caught vs uncaught error handling be
configurable too.
---------
Co-authored-by: Ricky Hanlon <rickhanlonii@gmail.com>
Stacked on #28458.
This doesn't actually really change the messages yet, it's just a
refactor.
Hydration warnings can be presented either as HTML or React JSX format.
If presented as HTML it makes more sense to make that a DOM specific
concept, however, I think it's actually better to present it in terms of
React JSX.
Most of the time the errors aren't going to be something messing with
them at the HTML/HTTP layer. It's because the JS code does something
different. Most of the time you're working in just React. People don't
necessarily even know what the HTML form of it looks like. So this takes
the approach that the warnings are presented in React JSX in their rich
object form.
Therefore, I'm moving the approach to yield diff data to the reconciler
but it's the reconciler that's actually printing all the warnings.
## Summary
Based on
- https://github.com/facebook/react/pull/27903
This PR
- Silence warning in React tests
- Turn on flag
We want to finish cleaning up internal RTR usage, but let's prioritize
the deprecation process. We do this by silencing the internal warning
for now.
## How did you test this change?
`yarn build`
`yarn test ReactHooksInspectionIntegration -b`
## Overview
The error messages that say:
> ReactDOM.hydrate is no longer supported in React 18
Don't make sense in the React 19 release. Instead, they should say:
> ReactDOM.hydrate was removed in React 19.
For legacy mode, they should say:
> ReactDOM.hydrate has not been supported since React 18.
If a global error event is dispatched during a test, Jest reports that
test as a failure.
Our `@gate` pragma feature should account for this — if the gate
condition is false, and the global error event is dispatched, then the
test should be reported as a success.
The solution is to install an error event handler right before invoking
the test function. Because we install our own handler, Jest will not
report the test as a failure if a global error event is dispatched; it's
conceptually as if we wrapped the whole test event in a try-catch.
We broke the ability to "break on uncaught exceptions" by adding a
try/catch higher up in the scheduling. We're giving up on fixing that so
we can remove the replay trick inside an event handler.
The issue with that approach is that we end up double logging a lot of
errors in DEV since they get reported to the page.
It's also a lot of complexity around this feature.
When developing in an RSC environment, you should be able to work in a
single environment as if it was a unified environment. With thrown
errors we already serialize them and then rethrow them on the client.
Since by default we log them via onError both in Flight and Fizz, you
can get the same log in the RSC runtime, the SSR runtime and on the
client.
With console logs made in SSR renders, you typically replay the same
code during hydration on the client. So for example warnings already
show up both in the SSR logs and on the client (although not guaranteed
to be the same). You could just spend your time in the client and you'd
be fine.
Previously, RSC logs would not be replayed because they don't hydrate.
So it's easy to miss warnings for example.
With this approach, we replay RSC logs both during SSR so they end up in
the SSR logs and on the client. That way you can just stay in the
browser window during normal development cycles. You shouldn't have to
care if your component is a server or client component when working on
logical things or iterating on a product.
With this change, you probably should mostly ignore the Flight log
stream and just look at the client or maybe the SSR one. Unless you're
digging into something specific. In particular if you just naively run
both Flight and Fizz in the same terminal you get duplicates. I like to
run out fixtures `yarn dev:region` and `yarn dev:global` in two separate
terminals.
Console logs may contain complex objects which can be inspected. Ideally
a DevTools inspector could reach into the RSC server and remotely
inspect objects using the remote inspection protocol. That way complex
objects can be loaded on demand as you expand into them. However, that
is a complex environment to set up and the server might not even be
alive anymore by the time you inspect the objects. Therefore, I do a
best effort to serialize the objects using the RSC protocol but limit
the depth that can be rendered.
This feature is only own in dev mode since it can be expensive.
In a follow up, I'll give the logs a special styling treatment to
clearly differentiate them from logs coming from the client. As well as
deal with stacks.
Depends on:
- #28317
- #28320
---
Changes the behavior of the JSX runtime to pass through `ref` as a
normal prop, rather than plucking it from the props object and storing
on the element.
This is a breaking change since it changes the type of the receiving
component. However, most code is unaffected since it's unlikely that a
component would have attempted to access a `ref` prop, since it was not
possible to get a reference to one.
`forwardRef` _will_ still pluck `ref` from the props object, though,
since it's extremely common for users to spread the props object onto
the inner component and pass `ref` as a differently named prop. This is
for maximum compatibility with existing code — the real impact of this
change is that `forwardRef` is no longer required.
Currently, refs are resolved during child reconciliation and stored on
the fiber. As a result of this change, we can move ref resolution to
happen only much later, and only for components that actually use them.
Then we can remove the `ref` field from the Fiber type. I have not yet
done that in this step, though.
Instead of createElement.
We should have done this when we initially released jsx-runtime but
better late than never. The general principle is that our tests should
be written using the most up-to-date idioms that we recommend for users,
except when explicitly testing an edge case or legacy behavior, like for
backwards compatibility.
Most of the diff is related to tweaking test output and isn't very
interesting.
I did have to workaround an issue related to component stacks. The
component stack logic depends on shared state that lives in the React
module. The problem is that most of our tests reset the React module
state and re-require a fresh instance of React, React DOM, etc. However,
the JSX runtime is not re-required because it's injected by the compiler
as a static import. This means its copy of the shared state is no longer
the same as the one used by React, causing any warning logged by the JSX
runtime to not include a component stack. (This same issue also breaks
string refs, but since we're removing those soon I'm not so concerned
about that.) The solution I went with for now is to mock the JSX runtime
with a proxy that re-requires the module on every function invocation. I
don't love this but it will have to do for now. What we should really do
is migrate our tests away from manually resetting the module state and
use import syntax instead.
Partially reverting what has been removed in
https://github.com/facebook/react/pull/28186.
We need `'scheduler/tracing'` mock for React >= 16.8.
The error:
```
Invariant Violation: It is not supported to run the profiling version of a renderer (for example, `react-dom/profiling`) without also replacing the `scheduler/tracing` module with `scheduler/tracing-profiling`. Your bundler might have a setting for aliasing both modules. Learn more at http://fb.me/react-profiling
```
Validated by running regression tests for the whole version matrix:
```
./scripts/circleci/download_devtools_regression_build.js 16.0 --replaceBuild && node ./scripts/jest/jest-cli.js --build --project devtools --release-channel=experimental --reactVersion 16.0 --ci && ./scripts/circleci/download_devtools_regression_build.js 16.5 --replaceBuild && node ./scripts/jest/jest-cli.js --build --project devtools --release-channel=experimental --reactVersion 16.5 --ci && ./scripts/circleci/download_devtools_regression_build.js 16.8 --replaceBuild && node ./scripts/jest/jest-cli.js --build --project devtools --release-channel=experimental --reactVersion 16.8 --ci && ./scripts/circleci/download_devtools_regression_build.js 17.0 --replaceBuild && node ./scripts/jest/jest-cli.js --build --project devtools --release-channel=experimental --reactVersion 17.0 --ci && ./scripts/circleci/download_devtools_regression_build.js 18.0 --replaceBuild && node ./scripts/jest/jest-cli.js --build --project devtools --release-channel=experimental --reactVersion 18.0 --ci
```
- Moving `act` implementation to a single getter-function, which is
based on React version we are testing RDT against.
- Removing unused mocks for `act`, which were designed for legacy
versions of React, validated with running tests against React 16 build.
## Overview
Adds support for `ReactDOMClient` for `ServerIntegration*` tests.
Also converts tests that pass without any other changes. Will follow up
with other PRs for more complex cases.
There's no need to separate strict mode from strict effects mode any
more.
I didn't clean up the `StrictEffectMode` fiber flag, because it's used
to prevent strict effects in legacy mode. I could replace those checks
with `LegacyMode` checks, but when we remove legacy mode, we can remove
that flag and condense them into one StrictMode flag away.
These were made dynamic again in
https://github.com/facebook/react/pull/27919, and already have the
dynamic flags set. It's a bummer to push this around, we should come up
with a better way.
The internal file ReactSharedSubset is what the `react` module resolves
to when imported from a Server Component environment. We gave it this
name because, originally, the idea was that Server Components can access
a subset of the APIs available on the client.
However, since then, we've also added APIs that can _only_ by accessed
on the server and not the client. In other words, it's no longer a
subset, it's a slightly different overlapping set.
So this commit renames ReactSharedSubet to ReactServer and updates all
the references. This does not affect the public API, only our internal
implementation.
I think these have been dead for a while now. If the purpose is
documentation, we should see if we need to improve `yarn test --help` or
something instead.
`Activity` is the current candidate name. This PR starts the rename work
by renaming the exported unstable component name.
NOTE: downstream consumers need to rename the import when updating to
this commit.
I do this by simply renaming the secret export name in the "subset"
bundle and this renamed version is what the FlightServer uses.
This requires us to be more diligent about always using the correct
instance of "react" in our tests so there's a bunch of clean up for
that.
Currently when we SSR a Flight response we do not emit any resources for
module imports. This means that when the client hydrates it won't have
already loaded the necessary scripts to satisfy the Imports defined in
the Flight payload which will lead to a delay in hydration completing.
This change updates `react-server-dom-webpack` and
`react-server-dom-esm` to emit async script tags in the head when we
encounter a modules in the flight response.
To support this we need some additional server configuration. We need to
know the path prefix for chunk loading and whether the chunks will load
with CORS or not (and if so with what configuration).
Since we're not using haste at all, we can just remove the config to
disable haste instead of enabling, just to inject an implementation that
blocks any haste modules from being recognized.
Test Plan:
Creating a module and required it to get the expected error that the
module doesn't exist.
Search for more generic fork files if an exact match does not exist. If
`forks/MyFile.dom.js` exists but `forks/MyFile.dom-node.js` does not
then use it when trying to resolve forks for the `"dom-node"` renderer
in flow, tests, and build
consolidate certain fork files that were identical and make semantic
sense to be generalized
add `dom-browser-esm` bundle and use it for
`react-server-dom-esm/client.browser` build
Modern runtimes support native async/await, as does the version of Node
we use for our tests. To match how most of our users run React, this
disables the transpilation of async/await in our test suite.
This PR contains a regression test and two separate fixes: a targeted
fix, and a more general one that's designed as a last-resort guard
against these types of bugs (both bugs in app code and bugs in React).
I confirmed that each of these fixes separately are sufficient to fix
the regression test I added.
We can't realistically detect all infinite update loop scenarios because
they could be async; even a single microtask can foil our attempts to
detect a cycle. But this improves our strategy for detecting the most
common kind.
See commit messages for more details.
## Summary
Running `yarn test --project devtools --build` currently skips all
non-gated (without `@reactVersion` directives) devtools tests. This is
not expected behaviour, these changes are fixing it.
There were multiple related PRs to it:
- https://github.com/facebook/react/pull/26742
- https://github.com/facebook/react/pull/25712
- https://github.com/facebook/react/pull/24555
With these changes, the resulting behaviour will be:
- If `REACT_VERSION` env variable is specified:
- jest will not include all non-gated test cases in the test run
- jest will run only a specific test case, when specified
`REACT_VERSION` value satisfies the range defined by `@reactVersion`
directives for this test case
- If `REACT_VERSION` env variable is not specified, jest will run all
non-gated tests:
- jest will include all non-gated test cases in the test run
- jest will run all non-gated test cases, the only skipped test cases
will be those, which specified the range that does not include the next
stable version of react, which will be imported from `ReactVersions.js`
## How did you test this change?
Running `profilingCache` test suite without specifying `reactVersion`
now skips gated (>= 17 & < 18) test
<img width="1447" alt="Screenshot 2023-06-15 at 11 18 22"
src="https://github.com/facebook/react/assets/28902667/cad58994-2cb3-44b3-9eb2-1699c01a1eb3">
Running `profilingCache` test suite with specifying `reactVersion` to
`17` now runs this test case and skips others correctly
<img width="1447" alt="Screenshot 2023-06-15 at 11 20 11"
src="https://github.com/facebook/react/assets/28902667/d308960a-c172-4422-ba6f-9c0dbcd6f7d5">
Running `yarn test --project devtools ...` without specifying
`reactVersion` now runs all non-gated test cases
<img width="398" alt="Screenshot 2023-06-15 at 12 25 12"
src="https://github.com/facebook/react/assets/28902667/2b329634-0efd-4c4c-b460-889696bbc9e1">
Running `yarn test --project devtools ...` with specifying
`reactVersion` (to `17` in this example) now includes only gated tests
<img width="414" alt="Screenshot 2023-06-15 at 12 26 31"
src="https://github.com/facebook/react/assets/28902667/a702c27e-4c35-4b12-834c-e5bb06728997">
## Overview
Does a few things:
- Renames `enableSyncDefaultUpdates` to
`forceConcurrentByDefaultForTesting`
- Changes the way it's used so it's dead-code eliminated separate from
`allowConcurrentByDefault`
- Deletes a bunch of the gated code
The gates that are deleted are unnecessary now. We were keeping them
when we originally thought we would come back to being concurrent by
default. But we've shifted and now sync-by default is the desired
behavior long term, so there's no need to keep all these forked tests
around.
I'll follow up to delete more of the forked behavior if possible.
Ideally we wouldn't need this flag even if we're still using
`allowConcurrentByDefault`.
With df12d7eac4 I accidentally made it so
that tests aren't run with the 2 variant modes for most
SchedulerFeatureFlags anymore. This fixes it with the same approach as
ee4233bdbc.
Test Plan:
Run and notice the boolean flags follow the variant:
```
yarn test-www --variant=true
yarn test-www --variant=false
```
The idea is that the default `yarn test` command should be the one that
includes the most bleeding edge features, because during development you
probably want as many features enabled as possible.
That used to be `www-modern` but nowadays it's `experimental` because
we've landed a bunch of async actions stuff in experimental but it isn't
yet being tested at Meta.
So this switches the default to `experimental`.
This test started failing recently in older versions of React because
the Scheduler priority inside a microtask is Normal instead of
Immediate. This is expected because microtasks are not Scheduler tasks;
it's an implementation detail.
I gated the test to only run in v17 because it's a regression test for
legacy Suspense behavior, and the implementation details of the snapshot
changed in v18.
Test plan
---------
Using latest:
```
yarn test --build --project devtools --release-channel=experimental profilingcache
```
Using v17 (typically runs in a timed CI workflow):
```
/scripts/circleci/download_devtools_regression_build.js 17.0 --replaceBuild
yarn test --build --project devtools --release-channel=experimental --reactVersion 17.0 profilingcache
```
We currently use rollup to make an adhoc bundle from the file system
when we're testing an import of an external file.
This doesn't follow all the interception rules that we use in jest and
in our actual builds.
This switches to just using jest require() to load these. This means
that they effectively have to load into the global document so this only
works with global document tests which is all we have now anyway.
- substr is Annex B
- substring silently flips its arguments if they're in the "wrong order", which is confusing
- slice is better than sliced bread (no pun intended) and also it works the same way on Arrays so there's less to remember
---
> I'd be down to just lint and enforce a single form just for the potential compression savings by using a repeated string.
_Originally posted by @sebmarkbage in https://github.com/facebook/react/pull/26663#discussion_r1170455401_
part of https://github.com/facebook/react/pull/26571
merging separately to improve tracking of files renames in git
Rename HostConfig files to FiberConfig to clarify they are configs for
Fiber and not Fizz/Flight. This better conforms to the naming used in
Flight and now Fizz of `ReactFlightServerConfig` and `ReactFizzConfig`
`act` uses the `didScheduleLegacyUpdate` field to simulate the behavior
of batching in React <17 and below. It's a quirk leftover from a
previous implementation, not intentionally designed.
This sets `didScheduleLegacyUpdate` every time a legacy root receives an
update as opposed to only when the `executionContext` is empty. There's
no real reason to do it this way over some other way except that it's
how it used to work before #26512 and we should try our best to maintain
the existing behavior, quirks and all, since existing tests may have
come to accidentally rely on it.
This should fix some (though not all) of the internal Meta tests that
started failing after #26512 landed.
Will add a regression test before merging.
Adding `.internal` to a test file prevents it from being tested in build
mode. The best practice is to instead gate the test based on whether the
feature is enabled.
Ideally we'd use the `@gate` pragma in these tests, but the `itRenders`
test helpers don't support that.
The www builds include disableLegacyContext as a dynamic flag, so we
should be running the tests in that mode, too. Previously we were
overriding the flag during the test run. This strategy usually doesn't
work because the flags get compiled out in the final build, but we
happen to not test www in build mode, only source.
To get of this hacky override, I added a test gate to every test that
uses legacy context. When we eventually remove legacy context from the
codebase, this should make it slightly easier to find which tests are
affected. And removes one more hack from our hack-ridden test config.
Given that sometimes www has features enabled that aren't on in other
builds, we might want to consider testing its build artifacts in CI,
rather than just source. That would have forced this cleanup to happen
sooner. Currently we only test the public builds in CI.
(This only affects our own internal repo; it's not a public API.)
I think most of us agree this is a less confusing name. It's possible
someone will confuse it with `console.log`. If that becomes a problem we
can warn in dev or something.
This converts some of our test suite to use the `waitFor` test pattern,
instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of
these changes are automated with jscodeshift, with some slight manual
cleanup in certain cases.
See #26285 for full context.
Over the years, we've gradually aligned on a set of best practices for
for testing concurrent React features in this repo. The default in most
cases is to use `act`, the same as you would do when testing a real
React app. However, because we're testing React itself, as opposed to an
app that uses React, our internal tests sometimes need to make
assertions on intermediate states that `act` intentionally disallows.
For those cases, we built a custom set of Jest assertion matchers that
provide greater control over the concurrent work queue. It works by
mocking the Scheduler package. (When we eventually migrate to using
native postTask, it would probably work by stubbing that instead.)
A problem with these helpers that we recently discovered is, because
they are synchronous function calls, they aren't sufficient if the work
you need to flush is scheduled in a microtask — we don't control the
microtask queue, and can't mock it.
`act` addresses this problem by encouraging you to await the result of
the `act` call. (It's not currently required to await, but in future
versions of React it likely will be.) It will then continue flushing
work until both the microtask queue and the Scheduler queue is
exhausted.
We can follow a similar strategy for our custom test helpers, by
replacing the current set of synchronous helpers with a corresponding
set of async ones:
- `expect(Scheduler).toFlushAndYield(log)` -> `await waitForAll(log)`
- `expect(Scheduler).toFlushAndYieldThrough(log)` -> `await
waitFor(log)`
- `expect(Scheduler).toFlushUntilNextPaint(log)` -> `await
waitForPaint(log)`
These APIs are inspired by the existing best practice for writing e2e
React tests. Rather than mock all task queues, in an e2e test you set up
a timer loop and wait for the UI to match an expecte condition. Although
we are mocking _some_ of the task queues in our tests, the general
principle still holds: it makes it less likely that our tests will
diverge from real world behavior in an actual browser.
In this commit, I've implemented the new testing helpers and converted
one of the Suspense tests to use them. In subsequent steps, I'll codemod
the rest of our test suite.
It's confusing to new contributors, and me, that you're supposed to use
`yarn build-combined` for almost everything but not fixtures.
We should use only one build command for everything.
Updated fixtures to use the folder convention of build-combined.
This is the first of a series of PRs, that let you pass functions, by
reference, to the client and back. E.g. through Server Context. It's
like client references but they're opaque on the client and resolved on
the server.
To do this, for security, you must opt-in to exposing these functions to
the client using the `"use server"` directive. The `"use client"`
directive lets you enter the client from the server. The `"use server"`
directive lets you enter the server from the client.
This works by tagging those functions as Server References. We could
potentially expand this to other non-serializable or stateful objects
too like classes.
This only implements server->server CJS imports and server->server ESM
imports. We really should add a loader to the webpack plug-in for
client->server imports too. I'll leave closures as an exercise for
integrators.
You can't "call" a client reference on the server, however, you can
"call" a server reference on the client. This invokes a callback on the
Flight client options called `callServer`. This lets a router implement
calling back to the server. Effectively creating an RPC. This is using
JSON for serializing those arguments but more utils coming from
client->server serialization.
## Summary
- yarn.lock diff +-6249, **small pr**
- use jest-environment-jsdom by default
- uncaught error from jsdom is an error object instead of strings
- abortSignal.reason is read-only in jsdom and node,
https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/reason
## How did you test this change?
ci green
---------
Co-authored-by: Sebastian Silbermann <silbermann.sebastian@gmail.com>
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.
This renames Module References to Client References, since they are in
the server->client direction.
I also changed the Proxies exposed from the `node-register` loader to
provide better error messages. Ideally, some of this should be
replicated in the ESM loader too but neither are the source of truth.
We'll replicate this in the static form in the Next.js loaders. cc
@huozhi @shuding
- All references are now functions so that when you call them on the
server, we can yield a better error message.
- References that are themselves already referring to an export name are
now proxies that error when you dot into them.
- `use(...)` can now be used on a client reference to unwrap it server
side and then pass a reference to the awaited value.
## Summary
Should unblock https://github.com/facebook/react/pull/25970
If the callback for `toWarnDev` was `async` and threw, we didn't
ultimately reject the await Promise from the matcher. This resulted in
tests failing even though the failure was expected due to a test gate.
## How did you test this change?
- [x] tested in https://github.com/facebook/react/pull/25970 with `yarn
test --r=stable --env=development
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js --watch`
- [x] `yarn test`
- [x] CI
Flow introduced a new syntax to annotated the context type of a
function, this tries to update the rest and add 1 example usage.
- 2b1fb91a55 already added the changes
required for eslint.
- Jest transform is updated to use the recommended `hermes-parser` which
can parse current and Flow syntax and will be updated in the future.
- Rollup uses a new plugin to strip the flow types. This isn't ideal as
the npm module is deprecated in favor of using `hermes-parser`, but I
couldn't figure out how to integrate that with Rollup.
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
<!--
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 debug-test --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
Jest caching wasn't working correctly for
`transform-react-version-pragma`. One condition for including
`transform-react-version-pragma` is that `process.env.REACT_VERSION` is
set, but it wasn't included in the cache key computation. Thus local
test runs would only run without `transform-react-version-pragma`, if
jest runs weren't using the `-reactVersion` flag and then added it.
Inlined the `scripts/jest/devtools/preprocessor.js` file, because it
makes it more obvious that `process.env.REACT_VERSION` is used in
`scripts/jest/preprocessor.js`
<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->
## 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.
-->
Repro step:
- Clear jest cache
- node ./scripts/jest/jest-cli.js --build --project devtools
--release-channel=experimental --reactVersion 18.0
- node ./scripts/jest/jest-cli.js --build --project devtools
--release-channel=experimental
Before:
Jest cached the first run with `REACT_VERSION` set, so in the second run
`transform-react-version-pragma` is still there and runs only the
regression tests on old react versions.
After:
- The second run runs all tests and ignore `// @reactVersion` as
expected.
To derisk the rollout of `use`, and simplify the implementation, this
reverts the yield-to-microtasks behavior for promises that are thrown
directly (as opposed to being unwrapped by `use`).
We may add this back later. However, the plan is to deprecate throwing a
promise directly and migrate all existing Suspense code to `use`, so the
extra code probably isn't worth it.
With the `react-dom/server-rendering-stub` you can import `react-dom` in
RSC so that you can call `preload` and `preinit` but if you don't alias
it, then requiring it breaks because we React.Component which doesn't
exist in the react subset.
* 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
Offscreen is only enabled in the www and experimental channels. Instead
of listing these on every Offscreen test, I added a test gate alias
called `enableOffscreen`. Makes it easier to grep for these, and edit or
remove the channels later.
We need the regression config moduleNameMapper to come before the current moduleNameMapper so when it tries to map "/^react-dom\/([^/]+)$/ it doesn't get confused. The reason is because order in which the mappings are defined matters. Patterns are checked one by one until one fits, and the most specific rule should be listed first.
Some older React versions have different module import names and are missing certain features. This PR mocks modules that don't exist and maps modules in older versions to the ones that are required in tests. Specifically:
* In React v16.5, scheduler is named schedule
* In pre concurrent React, there is no act
* Prior to React v18.0, react-dom/client doesn't exist
* In DevTools, we expect to use scheduler/tracing-profiling instead of scheduler/tracing
`string.replaceAll` doesn't exist in our CircleCI Docker environment. We also don't need it in this case because `semver.satisfies` allows for whitespace when specifying a range. This PR removes the unnecessary call.
I added a `--sourceMaps` option to our test command that enables inline
source maps. I've kept it disabled by default, since it makes the tests
run slower. But it's super useful when attaching to a debugger.
Add `--reactVersion` argument. This argument is only used in DevTools. When this is specified, run only the tests that have the `// @reactVersion` pragma that satisfies the semver version range. Otherwise, run tests as normal
In DevTools tests, if the REACT_VERSION specified, we know this is a regression test (testing older React Versions). Because a lot of tests test the DevTools front end and we don't want to run them in the regression test scenario, we decided to only run tests that have the // @reactVersion pragma defined.
Because if there are no tests specified, jest will fail, we also opt to use jest.skip to skip all the tests that we don't want to run for a specific React version istead.
This PR makes this change.
This PR:
Adds a transform-react-version-pragma that transforms // @reactVersion SEMVER_VERSION into _test_react_version(...) and _test_react_version_focus(...) that lets us only run a test if it satisfies the right react version.
Adds _test_react_version and _test_react_version_focus to the devtools setupEnv file
Add a devtools preprocessor file for devtools specific plugins
* Move createRoot/hydrateRoot to /client
We want these APIs ideally to be imported separately from things you
might use in arbitrary components (like flushSync). Those other methods
are "isomorphic" to how the ReactDOM tree is rendered. Similar to hooks.
E.g. importing flushSync into a component that only uses it on the client
should ideally not also pull in the entry client implementation on the
server.
This also creates a nicer parity with /server where the roots are in a
separate entry point.
Unfortunately, I can't quite do this yet because we have some legacy APIs
that we plan on removing (like findDOMNode) and we also haven't implemented
flushSync using a flag like startTransition does yet.
Another problem is that we currently encourage these APIs to be aliased by
/profiling (or unstable_testing). In the future you don't have to alias
them because you can just change your roots to just import those APIs and
they'll still work with the isomorphic forms. Although we might also just
use export conditions for them.
For that all to work, I went with a different strategy for now where the
real API is in / but it comes with a warning if you use it. If you instead
import /client it disables the warning in a wrapper. That means that if you
alias / then import /client that will inturn import the alias and it'll
just work.
In a future breaking changes (likely when we switch to ESM) we can just
remove createRoot/hydrateRoot from / and move away from the aliasing
strategy.
* Update tests to import from react-dom/client
* Fix fixtures
* Update warnings
* Add test for the warning
* Update devtools
* Change order of react-dom, react-dom/client alias
I think the order matters here. The first one takes precedence.
* Require react-dom through client so it can be aliased
Co-authored-by: Andrew Clark <git@andrewclark.io>
Refactor DevTools to record Timeline data (in memory) while profiling. Updated the Profiler UI to import/export Timeline data along with legacy profiler data.
Relates to issue #22529
Numbers in JavaScript can have precision issues due to how they are encoded. This shows up in snapshot tests sometimes with values like 0.0009999999999999992, which makes the tests hard to read and visually diff.
This PR adds a new snapshot serializers which clamps numbers at 3 decimal points (e.g. the above number 0.0009999999999999992 is serialized as 0.001). This new serializer does not impact non-numeric values, integers, and special numbers like NaN and Infinity.
Until now, DEV and PROFILING builds of React recorded Timeline profiling data using the User Timing API. This commit changes things so that React records this data by calling methods on the DevTools hook. (For now, DevTools still records that data using the User Timing API, to match previous behavior.)
This commit is large but most of it is just moving things around:
* New methods have been added to the DevTools hook (in "backend/profilingHooks") for recording the Timeline performance events.
* Reconciler's "ReactFiberDevToolsHook" has been updated to call these new methods (when they're present).
* User Timing method calls in "SchedulingProfiler" have been moved to DevTools "backend/profilingHooks" (to match previous behavior, for now).
* The old reconciler tests, "SchedulingProfiler-test" and "SchedulingProfilerLabels-test", have been moved into DevTools "TimelineProfiler-test" to ensure behavior didn't change unexpectedly.
* Two new methods have been added to the injected renderer interface: injectProfilingHooks() and getLaneLabelMap().
Relates to #22529.
* Move useSyncExternalStore shim to a nested entrypoint
Also renames `useSyncExternalStoreExtra` to
`useSyncExternalStoreWithSelector`.
- 'use-sync-external-store/shim' -> A shim for `useSyncExternalStore`
that works in React 16 and 17 (any release that supports hooks). The
module will first check if the built-in React API exists, before
falling back to the shim.
- 'use-sync-external-store/with-selector' -> An extended version of
`useSyncExternalStore` that also supports `selector` and `isEqual`
options. It does _not_ shim `use-sync-external-store`; it composes the
built-in React API. **Use this if you only support 18+.**
- 'use-sync-external-store/shim/with-selector' -> Same API, but it
composes `use-sync-external-store/shim` instead. **Use this for
compatibility with 16 and 17.**
- 'use-sync-external-store' -> Re-exports React's built-in API. Not
meant to be used. It will warn and direct users to either the shim or
the built-in API.
* Upgrade useSyncExternalStore to alpha channel
* Output FIXME during build for unminified errors
The invariant Babel transform used to output a FIXME comment if it
could not find a matching error code. This could happen if there were
a configuration mistake that caused an unminified message to
slip through.
Linting the compiled bundles is the most reliable way to do it because
there's not a one-to-one mapping between source modules and bundles. For
example, the same source module may appear in multiple bundles, some
which are minified and others which aren't.
This updates the transform to output the same messages for Error calls.
The source lint rule is still useful for catching mistakes during
development, to prompt you to update the error codes map before pushing
the PR to CI.
* Don't run error transform in development
We used to run the error transform in both production and development,
because in development it was used to convert `invariant` calls into
throw statements.
Now that don't use `invariant` anymore, we only have to run the
transform for production builds.
* Add ! to FIXME comment so Closure doesn't strip it
Don't love this solution because Closure could change this heuristic,
or we could switch to a differnt compiler that doesn't support it. But
it works.
Could add a bundle that contains an unminified error solely for the
purpose of testing it, but that seems like overkill.
* Alternate extract-errors that scrapes artifacts
The build script outputs a special FIXME comment when it fails to minify
an error message. CI will detect these comments and fail the workflow.
The comments also include the expected error message. So I added an
alternate extract-errors that scrapes unminified messages from the
build artifacts and updates `codes.json`.
This is nice because it works on partial builds. And you can also run it
after the fact, instead of needing build all over again.
* Disable error minification in more bundles
Not worth it because the number of errors does not outweight the size
of the formatProdErrorMessage runtime.
* Run extract-errors script in CI
The lint_build job already checks for unminified errors, but the output
isn't super helpful.
Instead I've added a new job that runs the extract-errors script and
fails the build if `codes.json` changes. It also outputs the expected
diff so you can easily see which messages were missing from the map.
* Replace old extract-errors script with new one
Deletes the old extract-errors in favor of extract-errors2
This is an initial, partial implementation of a cleanup mechanism for the experimental Cache API. The idea is that consumers of the Cache API can register to be informed when a given Cache instance is no longer needed so that they can perform associated cleanup tasks to free resources stored in the cache. A canonical example would be cancelling pending network requests.
An overview of the high-level changes:
* Changes the `Cache` type from a Map of cache instances to be an object with the original Map of instances, a reference count (to count roughly "active references" to the cache instances - more below), and an AbortController.
* Adds a new public API, `unstable_getCacheSignal(): AbortSignal`, which is callable during render. It returns an AbortSignal tied to the lifetime of the cache - developers can listen for the 'abort' event on the signal, which React now triggers when a given cache instance is no longer referenced.
* Note that `AbortSignal` is a web standard that is supported by other platform APIs; for example a signal can be passed to `fetch()` to trigger cancellation of an HTTP request.
* Implements the above - triggering the 'abort' event - by handling passive mount/unmount for HostRoot and CacheComponent fiber nodes.
Cases handled:
* Aborted transitions: we clean up a new cache created for an aborted transition
* Suspense: we retain a fresh cache instance until a suspended tree resolves
For follow-ups:
* When a subsequent cache refresh is issued before a previous refresh completes, the refreshes are queued. Fresh cache instances for previous refreshes in the queue should be cleared, retaining only the most recent cache. I plan to address this in a follow-up PR.
* If a refresh is cancelled, the fresh cache should be cleaned up.
Update all our local scripts to use `build` instead of `build2`.
There are still downstream scripts that depend on `build2`, though, so
we can't remove it yet.
* [useSyncExternalStore] Remove extra hook object
Because we already track `getSnapshot` and `value` on the store
instance, we don't need to also track them as effect dependencies. And
because the effect doesn't require any clean-up, we don't need to track
a `destroy` function.
So, we don't need to store any additional state for this effect. We can
call `pushEffect` directly, and only during renders where something
has changed.
This saves some memory, but my main motivation is because I plan to use
this same logic to schedule a pre-commit consistency check. (See the
inline comments for more details.)
* Split shouldTimeSlice into two separate functions
Lanes that are blocking (SyncLane, and DefaultLane inside a blocking-
by-default root) are always blocking for a given root. Whereas expired
lanes can expire while the render phase is already in progress.
I want to check if a lane is blocking without checking whether it
expired, so I split `shouldTimeSlice` into two separate functions.
I'll use this in the next step.
* Check for store mutations before commit
When a store is read for the first time, or when `subscribe` or
`getSnapshot` changes, during a concurrent render, we have to check
at the end of the render phase whether the store was mutated by
an concurrent event.
In the userspace shim, we perform this check in a layout effect, and
patch up any inconsistencies by scheduling another render + commit.
However, even though we patch them up in the next render, the parent
layout effects that fire in the original render will still observe an
inconsistent tree.
In the native implementation, we can instead check for inconsistencies
right after the root is completed, before entering the commit phase. If
we do detect a mutaiton, we can discard the tree and re-render before
firing any effects. The re-render is synchronous to block further
concurrent mutations (which is also what we do to recover from tearing
bugs that result in an error). After the synchronous re-render, we can
assume the tree the tree is consistent and continue with the normal
algorithm for finishing a completed root (i.e. either suspend
or commit).
The result is that layout effects will always observe a consistent tree.