* 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.
This commit adds code to all React bundles to explicitly register the beginning and ending of the module. This is done by creating Error objects (which capture the file name, line number, and column number) and passing them explicitly to a DevTools hook (when present).
Next, as the Scheduling Profiler logs metadata to the User Timing API, it prints these module ranges along with other metadata (like Lane values and profiler version number).
Lastly, the Scheduling Profiler UI compares stack frames to these ranges when drawing the flame graph and dims or de-emphasizes frames that fall within an internal module.
The net effect of this is that user code (and 3rd party code) stands out clearly in the flame graph while React internal modules are dimmed.
Internal module ranges are completely optional. Older profiling samples, or ones recorded without the React DevTools extension installed, will simply not dim the internal frames.
* Extract `act` environment check into function
`act` checks the environment to determine whether to fire a warning.
We're changing how this check works in React 18. As a first step, this
refactors the logic into a single function. No behavior changes yet.
* Use IS_REACT_ACT_ENVIRONMENT to disable warnings
If `IS_REACT_ACT_ENVIRONMENT` is set to `false`, we will suppress
any `act` warnings. Otherwise, the behavior of `act` is the same as in
React 17: if `jest` is defined, it warns.
In concurrent mode, the plan is to remove the `jest` check and only warn
if `IS_REACT_ACT_ENVIRONMENT` is true. I have not implemented that
part yet.
* Rename pipeToNodeWritable to renderToNodePipe
* Add startWriting API to Flight
We don't really need it in this case because there's way less reason to
delay the stream in Flight.
* Pass the destination to startWriting instead of renderToNode
* Rename startWriting to pipe
This mirrors the ReadableStream API in Node
* Error codes
* Rename to renderToPipeableStream
This mimics the renderToReadableStream API for the browser.
* 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.
* Move flushSync warning to React DOM
When you call in `flushSync` from an effect, React fires a warning. I've
moved the implementation of this warning out of the reconciler and into
React DOM.
`flushSync` is a renderer API, not an isomorphic API, because it has
behavior that was designed specifically for the constraints of React
DOM. The equivalent API in a different renderer may not be the same.
For example, React Native has a different threading model than the
browser, so it might not make sense to expose a `flushSync` API to the
JavaScript thread.
* Make root.unmount() synchronous
When you unmount a root, the internal state that React stores on the
DOM node is immediately cleared. So, we should also synchronously
delete the React tree. You should be able to create a new root using
the same container.
* 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.
The publish-preleases command prints the URL of the publish workflow
so that you can visit the page and follow along.
But it can take a few seconds before the workflow ID is available, after
you create the pipeline. So the script polls the workflow endpoint
until it's available.
The current polling limit is too low so I increased it.
I also updated the error message to provide more info.
This removes all the remaining references to the `build2` directory
except for the CI job that stores the artifacts. We'll keep the
`build2` artifact until downstream scripts are migrated to `build`.
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.
Now that all the CI jobs have been migrated to the new build script,
we can start renaming the `build2` directory to `build`.
Since there are lots of scripts that reference `build2`, including
downstream scripts that live outside this repo, I'm going to keep
the `build2` directory around as a copy of `build`.
Then once all the references are updated, I will delete the copy.
* Convert useSES shim tests to use React DOM
Idea is that eventually we'll run these tests against an actual build of
React DOM 17 to test backwards compatibility.
* Implement getServerSnapshot in userspace shim
If the DOM is not present, we assume that we are running in a server
environment and return the result of `getServerSnapshot`.
This heuristic doesn't work in React Native, so we'll need to provide
a separate native build (using the `.native` extension). I've left this
for a follow-up.
We can't call `getServerSnapshot` on the client, because in versions of
React before 18, there's no built-in mechanism to detect whether we're
hydrating. To avoid a server mismatch warning, users must account for
this themselves and return the correct value inside `getSnapshot`.
Note that none of this is relevant to the built-in API that is being
added in 18. This only affects the userspace shim that is provided
for backwards compatibility with versions 16 and 17.
Adds a third argument called `getServerSnapshot`.
On the server, React calls this one instead of the normal `getSnapshot`.
We also call it during hydration.
So it represents the snapshot that is used to generate the initial,
server-rendered HTML. The purpose is to avoid server-client mismatches.
What we render during hydration needs to match up exactly with what we
render on the server.
The pattern is for the server to send down a serialized copy of the
store that was used to generate the initial HTML. On the client, React
will call either `getSnapshot` or `getServerSnapshot` on the client as
appropriate, depending on whether it's currently hydrating.
The argument is optional for fully client rendered use cases. If the
user does attempt to omit `getServerSnapshot`, and the hook is called
on the server, React will abort that subtree on the server and
revert to client rendering, up to the nearest Suspense boundary.
For the userspace shim, we will need to use a heuristic (canUseDOM)
to determine whether we are in a server environment. I'll do that in
a follow up.
The replace-fork script depends on ESLint to fix the reconciler imports
— `.old` -> `.new` or vice versa. If ESLint crashes, it can leave the
imports in an incorrect state.
As a convenience, @bvaughn updated the script to automatically run
`git checkout -- .` if the ESLint command fails. An unintended
consequence of the strategy is that if the working directory is not
clean, then any uncommitted changes will be lost.
We need a better strategy for this that prevents the accidental loss of
work. One option is to exit early if the working directory is not clean
before you run the script, though that affects the usability of
the script.
An ideal solution would reset the working directory back to whatever
state it was in before the script ran, perhaps by stashing all the
changes and restoring them if the script aborts.
Until we think of something better, I've commmented out the branch.
This flag was broken due to a buggy race case in the ncp() command. The fix is amittedly a hack but improves on the existing behavior (of leaving the workspace in a broken state).
* [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.
This sets up an initial shim implementation of useSyncExternalStore,
via the use-sync-external-store package. It's designed to mimic the
behavior of the built-in API, but is backwards compatible to any version
of React that supports hooks.
I have not yet implemented the built-in API, but once it exists, the
use-sync-external-store package will always prefer that one. Library
authors can depend on the shim and trust that their users get the
correct implementation.
See https://github.com/reactwg/react-18/discussions/86 for background
on the API.
The tests I've added here are designed to run against both the shim and
built-in implementation, using our variant test flag feature. Tests that
only apply to concurrent roots will live in a separate suite.
React currently suppress console logs in StrictMode during double rendering. However, this causes a lot of confusion. This PR moves the console suppression logic from React into React Devtools. Now by default, we no longer suppress console logs. Instead, we gray out the logs in console during double render. We also add a setting in React Devtools to allow developers to hide console logs during double render if they choose.
I copied the set up we use for React.
In the www-variant test job, the Scheduler `__VARIANT__` flags will be
`true`. When writing a test, we can read the value of the flag with the
`gate` pragma and method.
Note: Since these packages are currently released in lockstep, maybe we
should remove SchedulerFeatureFlags and use ReactFeatureFlags for both.
## Summary
Adds support for statically extracting names for hook calls from source code, and extending source maps with that information so that DevTools does not have to directly parse source code at runtime, which will speed up the Named Hooks feature and allow it to be enabled by default.
Specifically, this PR includes the following parts:
- [x] Adding logic to statically extract relevant hook names from the parsed source code (i.e. the babel ast). Note that this logic differs slightly from the existing logic in that the existing logic also uses runtime information from DevTools (such as whether given hooks are a custom hook) to extract names for hooks, whereas this code is meant to run entirely at build time, so it does not rely on that information.
- [x] Generating an encoded "hook map", which encodes the information about a hooks *original* source location, and it's corresponding name. This "hook map" will be used to generate extended source maps, included tentatively under an extra `x_react_hook_map` field. The map itself is formatted and encoded in a very similar way as how the `names` and `mappings` fields of a standard source map are encoded ( = Base64 VLQ delta coding representing offsets into a string array), and how the "function map" in Metro is encoded, as suggested in #21782. Note that this initial version uses a very basic format, and we are not implementing our own custom encoding, but reusing the `encode` function from `sourcemap-codec`.
- [x] Updating the logic in `parseHookNames` to check if the source maps have been extended with the hook map information, and if so use that information to extract the hook names without loading the original source code. In this PR we are manually generating extended source maps in our tests in order to test that this functionality works as expected, even though we are not actually generating the extended source maps in production.
The second stage of this work, which will likely need to occur outside this repo, is to update bundlers such as Metro to use these new primitives to actually generate source maps that DevTools can use.
### Follow-ups
- Enable named hooks by default when extended source maps are present
- Support looking up hook names when column numbers are not present in source map.
- Measure performance improvement of using extended source maps (manual testing suggests ~4 to 5x faster)
- Update relevant bundlers to generate extended source maps.
## Test Plan
- yarn flow
- Tests still pass
- yarn test
- yarn test-build-devtools
- Named hooks still work on manual test of browser extension on a few different apps (code sandbox, create-react-app, facebook).
- For new functionality:
- New tests for statically extracting hook names.
- New tests for using extended source maps to look up hook names at runtime.
* Re-add old Fabric Offscreen impl behind flag
There's a chance that #21960 will affect layout in a way that we don't
expect, so I'm adding back the old implementation so we can toggle the
feature with a flag.
The flag should read from the ReactNativeFeatureFlags shim so that we
can change it at runtime. I'll do that separately.
* Import dynamic RN flags from external module
Internal feature flags that we wish to control with a GK can now be
imported from an external module, which I've called
"ReactNativeInternalFeatureFlags".
We'll need to add this module to the downstream repo.
We can't yet use this in our tests, because we don't have a test
configuration that runs against the React Native feature flags fork. We
should set up that up the same way we did for www.
I noticed that `enableSuspenseLayoutEffectSemantics` is not fully
implemented in persistent mode. I believe this was an oversight
because we don't have a CI job that runs tests in persistent mode and
with experimental flags enabled.
This adds additional test configurations to the CI job so we don't miss
stuff like this again. It doesn't fix the failing tests — I'll address
that separately.
Change format of @next and @experimental release versions from <number>-<sha> to <number>-<sha>-<date> to make them more human readable. This format still preserves the ability for us to easily map a version number to the changes it contains, while also being able to more easily know at a glance how recent a release is.
* Move internal version of act to shared module
No reason to have three different copies of this anymore.
I've left the the renderer-specific `act` entry points because legacy
mode tests need to also be wrapped in `batchedUpdates`. Next, I'll update
the tests to use `batchedUpdates` manually when needed.
* Migrates tests to use internal module directly
Instead of the `unstable_concurrentAct` exports. Now we can drop those
from the public builds.
I put it in the jest-react package since that's where we put our other
testing utilities (like `toFlushAndYield`). Not so much so it can be
consumed publicly (nobody uses that package except us), but so it works
with our build tests.
* Remove unused internal fields
These were used by the old act implementation. No longer needed.
Currently, in a React 18 root, `act` only works if you mock the
Scheduler package. This was because we didn't want to add additional
checks at runtime.
But now that the `act` testing API is dev-only, we can simplify its
implementation.
Now when an update is wrapped with `act`, React will bypass Scheduler
entirely and push its tasks onto a special internal queue. Then, when
the outermost `act` scope exists, we'll flush that queue.
I also removed the "wrong act" warning, because the plan is to move
`act` to an isomorphic entry point, simlar to `startTransition`. That's
not directly related to this PR, but I didn't want to bother
re-implementing that warning only to immediately remove it.
I'll add the isomorphic API in a follow up.
Note that the internal version of `act` that we use in our own tests
still depends on mocking the Scheduler package, because it needs to work
in production. I'm planning to move that implementation to a shared
(internal) module, too.
Upgrades the deprecation warning to a runtime error.
I did it this way instead of removing the export so the type is the same
in both builds. It will get dead code eliminated regardless.
This adds a new top level API for hydrating a root. It takes the initial
children as part of its constructor. These are unlike other render calls
in that they have to represent what the server sent and they can't be
batched with other updates.
I also changed the options to move the hydrationOptions to the top level
since now these options are all hydration options.
I kept the createRoot one just temporarily to make it easier to codemod
internally but I'm doing a follow up to delete.
As part of this I un-dried a couple of paths. ReactDOMLegacy was intended
to be built on top of the new API but it didn't actually use those root
APIs because there are special paths. It also doesn't actually use most of
the commmon paths since all the options are ignored. It also made it hard
to add only warnings for legacy only or new only code paths.
I also forked the create/hydrate paths because they're subtly different
since now the options are different. The containers are also different
because I now error for comment nodes during hydration which just doesn't
work at all but eventually we'll error for all createRoot calls.
After some iteration it might make sense to break out some common paths but
for now it's easier to iterate on the duplicates.
* Use the server src files as entry points for the builds/tests
We need one top level entry point to target two builds so we can't have
the top level one be the entry point for the builds.
* Same thing but with the modern entry point
* Clean up partial renderer entry points
I made a mistake by leaving server.browser.stable in which is the partial
renderer for the browser build of stable. That should use the legacy fizz
one.
Since the only usage of the partial renderer now is at FB and we don't use
it with Node, I removed the Node build of partial renderer too.
* Remove GC test
No code is running this path anymore. Ideally this should be ported to
a Fizz form.
* Wire up DOM legacy build
* Hack to filter extra comments for testing purposes
* Use string concat in renderToString
I think this might be faster. We could probably use a combination of this
technique in the stream too to lower the overhead.
* Error if we can't complete the root synchronously
Maybe this should always error but in the async forms we can just delay
the stream until it resolves so it does have some useful semantics.
In the synchronous form it's never useful though. I'm mostly adding the
error because we're testing this behavior for renderToString specifically.
* Gate memory leak tests of internals
These tests don't translate as is to the new implementation and have been
ported to the Fizz tests separately.
* Enable Fizz legacy mode in stable
* Add wrapper around the ServerFormatConfig for legacy mode
This ensures that we can inject custom overrides without negatively
affecting the new implementation.
This adds another field for static mark up for example.
* Wrap pushTextInstance to avoid emitting comments for text in static markup
* Don't emit static mark up for completed suspense boundaries
Completed and client rendered boundaries are only marked for the client
to take over.
Pending boundaries are still supported in case you stream non-hydratable
mark up.
* Wire up generateStaticMarkup to static API entry points
* Mark as renderer for stable
This shouldn't affect the FB one ideally but it's done with the same build
so let's hope this works.
* Use existing test warning filter for server tests
We have a warning filter for our internal tests to ignore warnings
that are too noisy or that we haven't removed from our test suite yet:
shouldIgnoreConsoleError.
Many of our server rendering tests don't use this filter, though,
because it has its own special of asserting warnings.
So I added the warning filter to the server tests, too.
* Deprecate ReactDOM.render and ReactDOM.hydrate
These are no longer supported in React 18. They are replaced by the
`createRoot` API.
The warning includes a link to documentation of the new API. Currently
it redirects to the corresponding working group post. Here's the PR to
set up the redirect: https://github.com/reactjs/reactjs.org/pull/3730
Many of our tests still use ReactDOM.render. We will need to gradually
migrate them over to createRoot.
In the meantime, I added the warnings to our internal warning filter.
* Call into Fabric to get current event priority
Fix flow errors
* Prettier
* Better handle null and undefined cases
* Remove optional chaining and use ?? operator
* prettier-all
* Use conditional ternary operator
* prettier
This does not mean that a release of 18.0 is imminent, only that the
main branch includes breaking changes.
Also updates the versioning scheme of the `@next` channel to include
the upcoming semver number, as well as the word "alpha" to indicate the
stability of the release.
- Before: 0.0.0-e0d9b28999
- After: 18.0.0-alpha-e0d9b28999
Since we track these versions in source, we can build `@latest`
releases in CI and store them as artifacts.
Then when it's time to release, and the build has been verified, we use
`prepare-release-from-ci` (the same script we use for `@next` and
`@experimental`) to fetch the already built and versioned packages.
Now that we track package versions in source, `@latest` builds should
be fully reproducible for a given commit. We can prepare the packages in
CI and store them as artifacts, the same way we do for `@next` and
`@experimental`.
Eventually this can replace the interactive script that we currently
use to swap out the version numbers.
The other nice thing about this approach is that we can run tests in CI
to verify that the packages are releasable, instead of waiting until
right before publish.
I named the output directory `oss-stable-semver`, to distinguish from
the `@next` prereleases that are located at `oss-stable`. I don't love
this naming. I'd prefer to use the name of the corresponding npm dist
tag. I'll do that in a follow-up, though, since the `oss-stable` name is
referenced in a handful of places.
Current naming (after this PR):
- `oss-experimental` → `@experimental`
- `oss-stable` → `@next`
- `oss-stable-semver` → `@latest`
Proposed naming (not yet implemented, requires more work):
- `oss-experimental` → `@experimental`
- `oss-next` → `@next`
- `oss-latest` → `@latest`
The versioning scheme for `@next` releases does not include semver
information. Like `@experimental`, the versions are based only on the
hash, i.e. `0.0.0-<commit_sha>`. The reason we do this is to prevent
the use of a tilde (~) or caret (^) to match a range of
prerelease versions.
For `@experimental`, I think this rationale still makes sense — those
releases are very unstable, with frequent breaking changes. But `@next`
is not as volatile. It represents the next stable release. So, I think
we can afford to include an actual verison number at the beginning of
the string instead of `0.0.0`.
We can also add a label that indicates readiness of the upcoming
release, like "alpha", "beta", "rc", etc.
To prepare for this the new versioning scheme, I updated the build
script. However, **this PR does not enable the new versioning scheme
yet**. I left a TODO above the line that we'll change once we're ready.
We need to specify the expected next version numbers for each package,
somewhere. These aren't encoded anywhere today — we don't specify
version numbers until right before publishing to `@latest`, using an
interactive script: `prepare-release-from-npm`.
Instead, what we can do is track these version numbers in a module. I
added `ReactVersions.js` that acts as the single source of truth for
every package's version. The build script uses this module to build the
`@next` packages.
In the future, I want to start building the `@latest` packages the same
way we do `@next` and `@experimental`. (What we do now is download a
`@next` release from npm and swap out its version numbers.) Then we
could run automated tests in CI to confirm the packages are releasable,
instead of waiting to verify that right before publish.
* Resolve the entry point for tests the same way builds do
This way the source tests, test the same entry point configuration.
* Gate test selectors on www
These are currently only exposed in www builds
* Gate createEventHandle / useFocus on www
These are enabled in both www variants but not OSS experimental.
* Temporarily disable www-modern entry point
Use the main one that has all the exports until we fix more tests.
* Remove enableCache override that's no longer correct
* Open gates for www
These used to not be covered because they used Cache which wasn't exposed.
* Convert ES6/TypeScript CoffeeScript Tests to createRoot + act
* Change expectation for WWW+VARIANT because the deferRenderPhaseUpdateToNextBatch flag breaks this behavior
* Define global __WWW__ = true flag during www tests
We already do that for __PERSISTENT__.
* Use @gate www in ReactSuspenseCallback
This allows it to not be internal anymore. We test it against the www build.
* Clean up Scheduler forks
* Un-shadow variables
* Use timer globals directly, add a test for overrides
* Remove more window references
* Don't crash for undefined globals + tests
* Update lint config globals
* Fix test by using async act
* Add test fixture
* Delete test fixture
Uses the layout of the build artifact directory to infer the format
of a given file, and which lint rules to apply.
This has the effect of decoupling the lint build job from the existing
Rollup script, so that if we ever add additional post-processing, or
if we replace Rollup, it will still work.
But the immediate motivation is to replace the separate "stable" and
"experimental" lint-build jobs with a single combined job.
* Add NewContext module
This implements a reverse linked list tree containing the previous
contexts.
* Implement recursive algorithm
This algorithm pops the contexts back to a shared ancestor on the way down
the stack and then pushes new contexts in reverse order up the stack.
* Move isPrimaryRenderer to ServerFormatConfig
This is primarily intended to be used to support renderToString with a
separate build than the main one. This allows them to be nested.
* Wire up more element type matchers
* Wire up Context Provider type
* Wire up Context Consumer
* Test
* Implement reader in class
* Update error codez
My personal workflow is to develop against the www-modern release
channel, with the variant flags enabled, because it encompasses the
largest set of features. Then I rely on CI to run the tests against
all the other configurations.
So in practice, I almost always run
```
yarn test -r=www-modern --variant TEST_FILE
```
instead of
```
yarn test TEST_FILE
```
So, I've updated the `yarn test` command to use those options
by default.
* Implement DOM format config structure
* Styles
* Input warnings
* Textarea special cases
* Select special cases
* Option special cases
We read the currently selected value from the FormatContext.
* Warning for non-lower case HTML
We don't change to lower case at runtime anymore but keep the warning.
* Pre tags innerHTML needs to be prefixed
This is because if you do the equivalent on the client using innerHTML,
this is the effect you'd get.
* Extract errors
* Encode tables as a special insertion mode
The table modes are special in that its children can't be created outside
a table context so we need the segment container to be wrapped in a table.
* Move formatContext from Task to Segment
It works the same otherwise. It's just that this context needs to outlive
the task so that I can use it when writing the segment.
* Use template tag for placeholders and inserted dummy nodes with IDs
These can be used in any parent. At least outside IE11. Not sure yet what
happens in IE11 to these.
Not sure if these are bad for perf since they're special nodes.
* Add special wrappers around inserted segments depending on their insertion mode
* Allow the root namespace to be configured
This allows us to insert the correct wrappers when streaming into an
existing non-HTML tree.
* Add comment
The event priority constants exports by the reconciler package are
meant to be used by the reconciler (host config) itself. So it doesn't
make sense to export them from a module that requires them.
To break the cycle, we can move them to a separate module and import
that. This looks like a "deep import" of an internal module, which we
try to avoid, but conceptually these are part of the public interface
of the reconciler module. So, no different than importing from the main
`react-reconciler`.
We do need to be careful about not mixing these types of imports with
implementation details. Those are the ones to really avoid.
An unintended benefit of the reconciler fork infra is that it makes
deep imports harder. Any module that we treat as "public", like this
one, needs to account for the `enableNewReconciler` flag and forward
to the correct implementation.
Some legacy environments can not encode non-strings. Those would specify
both as strings. They'll throw for binary data.
Some environments have to encode strings (like web streams). Those would
encode both as uint8array.
Some environments (like Node) can do either. It can be beneficial to leave
things as strings in case the native stream can do something smart with it.
* Copy some infra structure patterns from Flight
* Basic data structures
* Move structural nodes and instruction commands to host config
* Move instruction command to host config
In the DOM this is implemented as script tags. The first time it's emitted
it includes the function. Future calls invoke the same function.
The side of the complete boundary function in particular is unfortunately
large.
* Implement Fizz Noop host configs
This is implemented not as a serialized protocol but by-passing the
serialization when possible and instead it's like a live tree being
built.
* Implement React Native host config
This is not wired up. I just need something for the flow types since
Flight and Fizz are both handled by the isServerSupported flag.
Might as well add something though.
The principle of this format is the same structure as for HTML but a
simpler binary format.
Each entry is a tag followed by some data and terminated by null.
* Check in error codes
* Comment
* Move context comparison to consumer
In the lazy context implementation, not all context changes are
propagated from the provider, so we can't rely on the propagation alone
to mark the consumer as dirty. The consumer needs to compare to the
previous value, like we do for state and context.
I added a `memoizedValue` field to the context dependency type. Then in
the consumer, we iterate over the current dependencies to see if
something changed. We only do this iteration after props and state has
already bailed out, so it's a relatively uncommon path, except at the
root of a changed subtree. Alternatively, we could move these
comparisons into `readContext`, but that's a much hotter path, so I
think this is an appropriate trade off.
* [Experiment] Lazily propagate context changes
When a context provider changes, we scan the tree for matching consumers
and mark them as dirty so that we know they have pending work. This
prevents us from bailing out if, say, an intermediate wrapper is
memoized.
Currently, we propagate these changes eagerly, at the provider.
However, in many cases, we would have ended up visiting the consumer
nodes anyway, as part of the normal render traversal, because there's no
memoized node in between that bails out.
We can save CPU cycles by propagating changes only when we hit a
memoized component — so, instead of propagating eagerly at the provider,
we propagate lazily if or when something bails out.
Most of our bailout logic is centralized in
`bailoutOnAlreadyFinishedWork`, so this ended up being not that
difficult to implement correctly.
There are some exceptions: Suspense and Offscreen. Those are special
because they sometimes defer the rendering of their children to a
completely separate render cycle. In those cases, we must take extra
care to propagate *all* the context changes, not just the first one.
I'm pleasantly surprised at how little I needed to change in this
initial implementation. I was worried I'd have to use the reconciler
fork, but I ended up being able to wrap all my changes in a regular
feature flag. So, we could run an experiment in parallel to our other
ones.
I do consider this a risky rollout overall because of the potential for
subtle semantic deviations. However, the model is simple enough that I
don't expect us to have trouble fixing regressions if or when they arise
during internal dogfooding.
---
This is largely based on [RFC#118](https://github.com/reactjs/rfcs/pull/118),
by @gnoff. I did deviate in some of the implementation details, though.
The main one is how I chose to track context changes. Instead of storing
a dirty flag on the stack, I added a `memoizedValue` field to the
context dependency object. Then, to check if something has changed, the
consumer compares the new context value to the old (memoized) one.
This is necessary because of Suspense and Offscreen — those components
defer work from one render into a later one. When the subtree continues
rendering, the stack from the previous render is no longer available.
But the memoized values on the dependencies list are. This requires a
bit more work when a consumer bails out, but nothing considerable, and
there are ways we could optimize it even further. Conceptually, this
model is really appealing, since it matches how our other features
"reactively" detect changes — `useMemo`, `useEffect`,
`getDerivedStateFromProps`, the built-in cache, and so on.
I also intentionally dropped support for
`unstable_calculateChangedBits`. We're planning to remove this API
anyway before the next major release, in favor of context selectors.
It's an unstable feature that we never advertised; I don't think it's
seen much adoption.
Co-Authored-By: Josh Story <jcs.gnoff@gmail.com>
* Propagate all contexts in single pass
Instead of propagating the tree once per changed context, we can check
all the contexts in a single propagation. This inverts the two loops so
that the faster loop (O(numberOfContexts)) is inside the more expensive
loop (O(numberOfFibers * avgContextDepsPerFiber)).
This adds a bit of overhead to the case where only a single context
changes because you have to unwrap the context from the array. I'm also
unsure if this will hurt cache locality.
Co-Authored-By: Josh Story <jcs.gnoff@gmail.com>
* Stop propagating at nearest dependency match
Because we now propagate all context providers in a single traversal, we
can defer context propagation to a subtree without losing information
about which context providers we're deferring — it's all of them.
Theoretically, this is a big optimization because it means we'll never
propagate to any tree that has work scheduled on it, nor will we ever
propagate the same tree twice.
There's an awkward case related to bailing out of the siblings of a
context consumer. Because those siblings don't bail out until after
they've already entered the begin phase, we have to do extra work to
make sure they don't unecessarily propagate context again. We could
avoid this by adding an earlier bailout for sibling nodes, something
we've discussed in the past. We should consider this during the next
refactor of the fiber tree structure.
Co-Authored-By: Josh Story <jcs.gnoff@gmail.com>
* Mark trees that need propagation in readContext
Instead of storing matched context consumers in a Set, we can mark
when a consumer receives an update inside `readContext`.
I hesistated to put anything in this function because it's such a hot
path, but so are bail outs. Fortunately, we only need to set this flag
once, the first time a context is read. So I think it's a reasonable
trade off.
In exchange, propagation is faster because we no longer need to
accumulate a Set of matched consumers, and fiber bailouts are faster
because we don't need to consult that Set. And the code is simpler.
Co-authored-by: Josh Story <jcs.gnoff@gmail.com>
With this change, if a node is a Fabric node, we route the setJSResponder call to FabricUIManager. Native counterpart is already landed. Tested internally as D26241364.
* Move direct port access into a function
* Fork based on presence of setImmediate
* Copy SchedulerDOM-test into another file
* Change the new test to use shimmed setImmediate
* Clarify comment
* Fix test to work with existing feature detection
* Add flags
* Disable OSS flag and skip tests
* Use VARIANT to reenable tests
* lol
When running the publish workflow, either via the command line or
via the daily cron job, we should use a constant SHA instead of
whatever happens to be at the head of the main branch at the time the
workflow is run.
The difference is subtle: currently, the SHA is read at runtime,
each time the workflow is run. With this change, the SHA is read right
before the workflow is created and passed in as a constant parameter.
In practical terms, this means if a workflow is re-run via the CircleCI
web UI, it will always re-run using the same commit SHA as the original
workflow, instead of fetching the latest SHA from GitHub, which may
have changed.
Also avoids a race condition where the head SHA changes in between the
Next publish job and the Experimental publish job.
* Add `supportsMicrotasks` to the host config
Only certain renderers support scheduling a microtask, so we need a
renderer specific flag that we can toggle. That way it's off for some
renderers and on for others.
I copied the approach we use for the other optional parts of the host
config, like persistent mode and test selectors.
Why isn't the feature flag sufficient?
The feature flag modules, confusingly, are not renderer-specific, at
least when running the our tests against the source files. They are
meant to correspond to a release channel, not a renderer, but we got
confused at some point and haven't cleaned it up.
For example, when we run `yarn test`, Jest loads the flags from the
default `ReactFeatureFlags.js` module, even when we import the React
Native renderer — but in the actual builds, we load a different feature
flag module, `ReactFeatureFlags.native-oss.js.` There's no way in our
current Jest load a different host config for each renderer, because
they all just import the same module. We should solve this by creating
separate Jest project for each renderer, so that the flags loaded when
running against source are the same ones that we use in the
compiled bundles.
The feature flag (`enableDiscreteMicrotasks`) still exists — it's used
to set the React DOM host config's `supportsMicrotasks` flag to `true`.
(Same for React Noop) The important part is that turning on the feature
flag does *not* affect the other renderers, like React Native.
The host config will likely outlive the feature flag, too, since the
feature flag only exists so we can gradually roll it out and measure the
impact in production; once we do, we'll remove it. Whereas the host
config flag may continue to be used to disable the discrete microtask
behavior for RN, because RN will likely use a native (non-JavaScript)
API to schedule its tasks.
* Add `supportsMicrotask` to react-reconciler README
* Parallelize Flow in CI
We added more host configs recently, and we run all the checks in
sequence, so sometimes Flow ends up being the slowest CI job.
This splits the job across multiple processes.
* Fix environment variable typo
Co-authored-by: Ricky <rickhanlonii@gmail.com>
Co-authored-by: Ricky <rickhanlonii@gmail.com>
PR #20728 added a command to initiate a prerelease using CI, but it left
the publish job unimplemented. This fills in the publish job.
Uses an npm automation token for authorization, which bypasses the need
for a one-time password. The token is configured via CircleCI's
environment variable panel.
Currently, it will always publish the head of the main branch. If the
head has already been published, it will exit gracefully.
It does not yet support publishing arbitrary commits, though we could
easily add that. I don't know how important that use case is, because
for PR branches, you can use CodeSandbox CI instead. Or as a last
resort, run the publish script locally.
Always publishing from main is nice because it further incentivizes us
to keep main in a releasable state. It also takes the guesswork out of
publishing a prerelease that's in a broken state: as long as we don't
merge broken PRs, we're fine.
```
yarn publish-prereleases
```
Script to trigger a CircleCI workflow that publishes preleases.
**The CI workflow doesn't actually publish yet; it just runs and logs
its inputs.**
GitHub's status API is super flaky. Sometimes it reports a job as
"pending" even after it completes in CircleCI. If it's still pending
when we time out, return the build ID anyway. TODO: The location of the
retry loop is a bit weird. We should probably combine this function with
the one that downloads the artifacts, and wrap the retry loop around the
whole thing.
* build-combined: Fix bundle sizes path
* Output COMMIT_SHA in build directory
Alternative to parsing an arbitrary package's version number, or
its `build-info.json`.
* Remove CircleCI environment variable requirement
I think this only reason we needed this was to support passing any
job id to `--build`, instead of requiring the `process_artifacts` job.
And to do that you needed to use the workflows API endpoint, which
requires an API token.
But now that the `--commit` argument exists and automatically finds the
correct job id, we can just use that.
* Add CI job that gets base artifacts
Uses download-experimental script and places the base artifacts into
a top-level folder.
* Migrate sizebot to combined workflow
Replaces the two separate sizebot jobs (one for each channel, stable and
experimental) with a single combined job that outputs size information
for all bundles in a single GitHub comment.
I didn't attempt to simplify the output at all, but we should. I think
what I would do is remove our custom Rollup sizes plugin, and read the
sizes from the filesystem instead. We would lose some information about
the build configuration used to generate each artifact, but that can be
inferred from the filepath. For example, the filepath
`fb-www/ReactDOM-dev.classic.js` already tells us everything we need to
know about the artifact. Leaving this for a follow up.
* Move GitHub status check inside retry loop
The download script will poll the CircleCI endpoint until the build job
is complete; it should also poll the GitHub status endpoint if the
build job hasn't been spawned yet.
* Only run get_base_build on main branch
Also update instructions to match recent script changes.
Also add reproducible commit SHA to post download instructions to support publishing the Firefox DevTools extension.
PR #20717 accidentally broke the `--commit` parameter because the
script errors if you provide both a `--build` and a `--commit`.
I solved by removing the validation error. When there's a conflict, it
will choose the --`build`.
(Although maybe we should `--build` entirely and always uses `--commit`.
I think `--commit` is a sufficient replacement.)
* Retry loop should not start over from beginning
When the otp times out, we should not retry the packages that were
already successfully published. We should pick up where we left off.
* Don't crash if build-info.json doesn't exist
The "print follow up instructions" step crashes if build-info.json is
not found. The new build workflow doesn't include those yet (might not
need to) and since the instructions that depend on it only affect
semver (latest) releases, I've moved the code to that branch. Will
follow up with a proper fix, either by adding back a build-info.json
file or removing that dependency and reading the commit some other way.
* Concurrent Mode test for uMS render mutation
Same test as the one added in #20665, but for Concurrent Mode.
* Use double render to detect render phase mutation
PR #20665 added a mechanism to detect when a `useMutableSource` source
is mutated during the render phase. It relies on the fact that we double
invoke components that error during development using
`invokeGuardedCallback`. If the version in the double render doesn't
match the first, that indicates there must have been a mutation during
render.
At first I thought it worked by detecting inside the *other* double
render, the one we do for Strict Mode. It turns out that while it does
warn then, the warning is suppressed, because we suppress all console
methods that occur during the Strict Mode double render. So it's really
the `invokeGuardedCallback` one that makes it work.
Anyway, let's set that aside that issue for a second. I realized during
review that errors that occur during the Strict Mode double render
reveal a useful property: A pure component will never throw during the
double render, because if it were pure, it would have also thrown during
the first render... in which case it wouldn't have double rendered! This
is true of all such errors, not just the one thrown by
`useMutableSource`.
Given this, we can simplify the `useMutableSource` mutation detection
mechanism. Instead of tracking and comparing the source's version, we
can instead check if we're inside a double render when the error is
thrown.
To get around the console suppression issue, I changed the warning to an
error. It errors regardless, in both dev and prod, so it doesn't have
semantic implications.
However, because of the paradox I described above, we arguably
_shouldn't_ throw an error in development, since we know that error
won't happen in production, because prod doesn't double render. (It's
still a tearing bug, but that doesn't mean the component will actually
throw.) I considered that, but that requires a larger conversation about
how to handle errors that we know are only possible in development. I
think we should probably be suppressing *all* errors (with a warning)
that occur during a double render.
If build job is still pending, the script will continously poll until
it reaches the retry limit.
I've set the limit at 10 minutes, since our CI pipeline almost always
finishes before that.
Alternative to `--build`. Uses same logic as sizebot and www
sync script.
Immediate motivation is I want sizebot to use the
`download-experimental-build` command in CI. Will do that next.
Changed previous error message from:
> Cannot read from mutable source during the current render without tearing. This is a bug in React. Please file an issue.
To:
> Cannot read from mutable source during the current render without tearing. This may be a bug in React. Please file an issue.
Also added a DEV only warning about the unsafe side effect:
> A mutable source was mutated while the %s component was rendering. This is not supported. Move any mutations into event handlers or effects.
I think this is the best we can do without adding production overhead that we'd probably prefer to avoid.
* RN: Implement `sendAccessibilityEvent` on HostComponent
Implement `sendAccessibilityEvent` on HostComponent for Fabric and non-Fabric RN.
Currently the Fabric version is a noop and non-Fabric uses
AccessibilityInfo directly. The Fabric version will be updated once
native Fabric Android/iOS support this method in the native UIManager.
* Move methods out of HostComponent
* Properly type dispatchCommand and sendAccessibilityEvent handle arg
* Implement Fabric side of sendAccessibilityEvent
* Add tests: 1. Fabric->Fabric, 2. Paper->Fabric, 3. Fabric->Paper, 4. Paper->Paper
* Fix typo: ReactFaricEventTouch -> ReactFabricEventTouch
* fix flow types
* prettier
DevTools was built with a fork of an early idea for how Suspense cache might work. This idea is incompatible with newer APIs like `useTransition` which unfortunately prevented me from making certain UX improvements. This PR swaps out the primary usage of this cache (there are a few) in favor of the newer `unstable_getCacheForType` and `unstable_useCacheRefresh` APIs. We can go back and update the others in follow up PRs.
### Messaging changes
I've refactored the way the frontend loads component props/state/etc to hopefully make it better match the Suspense+cache model. Doing this gave up some of the small optimizations I'd added but hopefully the actual performance impact of that is minor and the overall ergonomic improvements of working with the cache API make this worth it.
The backend no longer remembers inspected paths. Instead, the frontend sends them every time and the backend sends a response with those paths. I've also added a new "force" parameter that the frontend can use to tell the backend to send a response even if the component hasn't rendered since the last time it asked. (This is used to get data for newly inspected paths.)
_Initial inspection..._
```
front | | back
| -- "inspect" (id:1, paths:[], force:true) ---------> |
| <------------------------ "inspected" (full-data) -- |
```
_1 second passes with no updates..._
```
| -- "inspect" (id:1, paths:[], force:false) --------> |
| <------------------------ "inspected" (no-change) -- |
```
_User clicks to expand a path, aka hydrate..._
```
| -- "inspect" (id:1, paths:['foo'], force:true) ----> |
| <------------------------ "inspected" (full-data) -- |
```
_1 second passes during which there is an update..._
```
| -- "inspect" (id:1, paths:['foo'], force:false) ---> |
| <----------------- "inspectedElement" (full-data) -- |
```
### Clear errors/warnings transition
Previously this meant there would be a delay after clicking the "clear" button. The UX after this change is much improved.
### Hydrating paths transition
I also added a transition to hydration (expanding "dehyrated" paths).
### Better error boundaries
I also added a lower-level error boundary in case the new suspense operation ever failed. It provides a better "retry" mechanism (select a new element) so DevTools doesn't become entirely useful. Here I'm intentionally causing an error every time I select an element.
### Improved snapshot tests
I also migrated several of the existing snapshot tests to use inline snapshots and added a new serializer for dehydrated props. Inline snapshots are easier to verify and maintain and the new serializer means dehydrated props will be formatted in a way that makes sense rather than being empty (in external snapshots) or super verbose (default inline snapshot format).
* Migrate prepare-release-from-ci to new workflow
I added a `--releaseChannel (-r)` argument to script. You must choose
either "stable" or "experimental", because every build job now includes
both channels.
The prepare-release-from-npm script is unchanged since those releases
are downloaded from npm, nt CI.
(As a side note, I think we should start preparing semver releases using
the prepare-release-from-ci script, too, and get rid of
prepare-release-from-npm. I think that was a neat idea originally but
because we already run `npm pack` before storing the artifacts in CI,
there's really not much additional safety; the only safeguard it adds is
the requirement that a "next" release must have already been published.)
* Move validation to parse-params module
Currently, if publishing a package fails, the script crashes, and the
user must start it again from the beginning. Usually this happens
because the one-time password has timed out.
With this change, the user will be prompted for a fresh otp, and the
script will resume publishing.
Fixes issue in the new build workflow where the experimental packages do
not include "experimental" in the version string. This was because the
previous approach relied on the RELEASE_CHANNEL environment variable,
which we are no longer setting in the outer CI job, since we use the
same job to build both channels. To solve, I moved the version
post-processing into the build script itself.
Only affects the new build workflow. Old workflow is unchanged.
Longer term, I would like to remove version numbers from the source
entirely, including the package.jsons. We should use a placeholder
instead; that's mostly how it already works, since the release script
swaps out the versions before we publish to stable.
The goal is to simplify our CI pipeline so that all configurations
are built and tested in a single workflow.
As a first step, this adds a new build script entry point that builds
both the experimental and stable release channels into a single
artifacts directory.
The script works by wrapping the existing build script (which only
builds a single release channel at a time), then post-processing the
results to match the desired filesystem layout. A future version of the
build script would output the files directly without post-processing.
Because many parts of our infra depend on the existing layout of the
build artifacts directory, I have left the old workflows untouched.
We can incremental migrate to the new layout, then delete the old
workflows after we've finished.
* Don't allocate the inner cache unnecessarily
We only need it when we're asking for text. I anticipate I'll want to avoid allocating it in other methods too when it's not strictly necessary.
* Add fs.access
* Add fs.lstat
* Add fs.stat
* Add fs.readdir
* Add fs.readlink
* Add fs.realpath
* Rename functions to disambiguate two caches
* [Flight] Add rudimentary FS binding
* Throw for unsupported
* Don't mess with hidden class
* Use absolute path as the key
* Warn on relative and non-normalized paths
* Move files
* Update paths
* Rename import variables
* Rename /server to /writer
This is mainly because "React Server Server" is weird so we need another
dimension.
* Use "react-server" convention to enforce that writer is only loaded in a server
* Basic scan of the file system to find Client modules
This does a rudimentary merge of the plugins. It still uses the global
scan and writes to file system.
Now the plugin accepts a search path or a list of referenced client files.
In prod, the best practice is to provide a list of files that are actually
referenced rather than including everything possibly reachable. Probably
in dev too since it's faster.
This is using the same convention as the upstream ContextModule - which
powers the require.context helpers.
* Add neo-async to dependencies
* Remove react/unstable_cache
We're probably going to make it available via the dispatcher. Let's remove this for now.
* Add readContext() to the dispatcher
On the server, it will be per-request.
On the client, there will be some way to shadow it.
For now, I provide it on the server, and throw on the client.
* Use readContext() from react-fetch
This makes it work on the server (but not on the client until we implement it there.)
Updated the test to use Server Components. Now it passes.
* Fixture: Add fetch from a Server Component
* readCache -> getCacheForType<T>
* Add React.unstable_getCacheForType
* Add a feature flag
* Fix Flow
* Add react-suspense-test-utils and port tests
* Remove extra Map lookup
* Unroll async/await because build system
* Add some error coverage and retry
* Add unstable_getCacheForType to Flight entry
* Rename "name"->"filepath" field on Webpack module references
This field name will get confused with the imported name or the module id.
* Switch back to transformSource instead of getSource
getSource would be more efficient in the cases where we don't need to read
the original file but we'll need to most of the time.
Even then, we can't return a JS file if we're trying to support non-JS
loader because it'll end up being transformed.
Similarly, we'll need to parse the file and we can't parse it before it's
transformed. So we need to chain with other loaders that know how.
* Add acorn dependency
This should be the version used by Webpack since we have a dependency on
Webpack anyway.
* Parse exported names of ESM modules
We need to statically resolve the names that a client component will
export so that we can export a module reference for each of the names.
For export * from, this gets tricky because we need to also load the
source of the next file to parse that. We don't know exactly how the
client is built so we guess it's somewhat default.
* Handle imported names one level deep in CommonJS using a Proxy
We use a proxy to see what property the server access and that will tell
us which property we'll want to import on the client.
* Add export name to module reference and Webpack map
To support named exports each name needs to be encoded as a separate
reference. It's possible with module splitting that different exports end
up in different chunks.
It's also possible that the export is renamed as part of minification.
So the map also includes a map from the original to the bundled name.
* Special case plain CJS requires and conditional imports using __esModule
This models if the server tries to import .default or a plain require.
We should replicate the same thing on the client when we load that
module reference.
* Dedupe acorn-related deps
Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
This convention ensures that you can declare that you intend for a file
to only be used on the server (even if it technically might resolve
on the client).
This configures the Flight fixture to use the "react-server" environment.
This allows the package.json exports field to specify a different resolution
in this environment.
I use this in the "react" package to resolve to a new bundle that excludes
the Hooks that aren't relevant in this environment like useState and useEffect.
This allows us to error early if these names are imported. If we actually
published ESM, it would we a static error. Now it's a runtime error.
You can test this by importing useState in Container.js which is used
by the client and server.
* Add Node ESM loader build
This adds a loader build as a first-class export. This will grow in
complexity so it deserves its own module.
* Add Node CommonJS regiter build
This adds a build as a first-class export for legacy CommonJS registration
in Node.js. This will grow in complexity so it deserves its own module.
* Simplify fixture a bit to easier show usage with or without esm
* Bump es version
We leave async function in here which are newer than ES2015.
* Fix typo
This typo was fixed in the new fork but not the old.
* Reset new fork to old fork
Something in the new fork is causing a topline metrics regression. We're
not sure what it is, so we're going to split it into steps and bisect.
As a first step, this resets the new fork back to the contents of the
old fork. We will land this to confirm that the fork infra itself is
not causing a regression.
* Fix tests: Add `dfsEffectsRefactor` flag
Some of the tests that gated on the effects refactor used the `new`
flag. In order to bisect, we'll need to decompose the new fork changes
into multiple steps.
So I added a hardcoded test flag called `dfsEffectsRefactor` and set it
to false. Will turn back on when we switch back to traversing the
finished tree using DFS and `subtreeTag`.
When enabled, replaces new fork with old fork.
I've done this several times by manually editing the script file, so
seems useful enough to add an option.
* Pass extra CLI args through to ESLint
These now work:
```
yarn run lint --fix
yarn run linc --fix
```
* Autofix imports when running replace-fork
We have a custom ESLint rule that can autofix cross-fork imports.
Usually, after running the `replace-fork` script, you need to run
`yarn lint --fix` to fix the imports.
This combines the two steps into one.
This allows exporting ESM modules for the Webpack plugin. This is necessary
for making a resolver plugin. We could probably make the whole plugin
use ESM instead of CJS ES2015.
* Encode Symbols as special rows that can be referenced by models
If a symbol was extracted from Symbol.for(...) then we can reliably
recreate the same symbol on the client.
S123:"react.suspense"
M456:{mySymbol: '$123'}
This doesn't suffer from the XSS problem because you have to write actual
code to create one of these symbols. That problem is only a problem because
values pass through common other usages of JSON which are not secure.
Since React encodes its built-ins as symbols, we can now use them as long
as its props are serializable. Like Suspense.
* Refactor resolution to avoid memo hack
Going through createElement isn't quite equivalent for ref and key in props.
* Reuse symbol ids that have already been written earlier in the stream
* Simplify Relay protocol integration
* Encode Relay rows as tuples instead of objects
This is slightly more compact and more ressembles more closely the encoding
we use for the raw stream protocol.
This adds a new dimension similar to dom-relay. It's different from
"native" which would be Flight for RN without Relay.
This has some copy-pasta that's the same between the two Relay builds but
the key difference will be Metro and we're not quite sure what other
differences there will be yet.
* Remove Blocks
* Remove Flight Server Runtime
There's no need for this now that the JSResource is part of the bundler
protocol. Might need something for Webpack plugin specifically later.
* Devtools
* Refactor Flight to require a module reference to be brand checked
This exposes a host environment (bundler) specific hook to check if an
object is a module reference. This will be used so that they can be passed
directly into Flight without needing additional wrapper objects.
* Emit module references as a special type of value
We already have JSON and errors as special types of "rows". This encodes
module references as a special type of row value. This was always the
intention because it allows those values to be emitted first in the stream
so that as a large models stream down, we can start preloading as early
as possible.
We preload the module when they resolve but we lazily require them as they
are referenced.
* Emit module references where ever they occur
This emits module references where ever they occur. In blocks or even
directly in elements.
* Don't special case the root row
I originally did this so that a simple stream is also just plain JSON.
However, since we might want to emit things like modules before the root
module in the stream, this gets unnecessarily complicated. We could add
this back as a special case if it's the first byte written but meh.
* Update the protocol
* Add test for using a module reference as a client component
* Relax element type check
Since Flight now accepts a module reference as returned by any bundler
system, depending on the renderer running. We need to drastically relax
the check to include all of them. We can add more as we discover them.
* Move flow annotation
Seems like our compiler is not happy with stripping this.
* Some bookkeeping bug
* Can't use the private field to check
* Deprecate old test script commands
* Update PR template test script
* Add test-stable and test-www-classic
* Update circle test names
* Rename test-www-classic to test-classic
* Missed some job renames
* Missed some more job renames
Also resolve an uncaught error in extension build (#18843).
Co-authored-by: Brian Vaughn <brian.david.vaughn@gmail.com>
Co-authored-by: Brian Vaughn <bvaughn@fb.com>
* Improve error message by expanding the object in question
* Don't warn for key/ref getters
* Error if refs are passed in server components or to client components
If there are any suspended fallbacks at the end of the `act` scope,
force them to display by running the pending timers (i.e. `setTimeout`).
The public implementation of `act` achieves the same behavior with an
extra check in the work loop (`shouldForceFlushFallbacks`). Since our
internal `act` needs to work in both development and production, without
additional runtime checks, we instead rely on Jest's mock timers.
This doesn't not affect refresh transitions, which are meant to delay
indefinitely, because in that case we exit the work loop without
posting a timer.
* Add babel parser which supports ChainExpression
* Add and fix tests for new babel eslint parser
* extract function to mark node
* refactor for compatibility with eslint v7.7.0+
* Update eslint to v7.7.0
Update hook test since eslint now supports nullish coalescing
* Remove distinction between long, short transitions
We're removing the `timeoutMs` option, so there's no longer any
distinction between "short" and "long" transitions. They're all treated
the same.
This commit doesn't remove `timeoutMs` yet, only combines the internal
priority levels.
* Disable `timeoutMs` argument
tl;dr
-----
- We're removing the `timeoutMs` argument from `useTransition`.
- Transitions will either immediately switch to a skeleton/placeholder
view (when loading new content) or wait indefinitely until the data
resolves (when refreshing stale content).
- This commit disables the `timeoutMS` so that the API has the desired
semantics. It doesn't yet update the types or migrate all the test
callers. I'll do those steps in follow-up PRs.
Motivation
----------
Currently, transitions initiated by `startTransition` / `useTransition`
accept a `timeoutMs` option. You can use this to control the maximum
amount of time that a transition is allowed to delay before we give up
and show a placeholder.
What we've discovered is that, in practice, every transition falls into
one of two categories: a **load** or a **refresh**:
- **Loading a new screen**: show the next screen as soon as possible,
even if the data hasn't finished loading. Use a skeleton/placeholder
UI to show progress.
- **Refreshing a screen that's already visible**: keep showing the
current screen indefinitely, for as long as it takes to load the fresh
data, even if the current data is stale. Use a pending state (and
maybe a busy indicator) to show progress.
In other words, transitions should either *delay indefinitely* (for a
refresh) or they should show a placeholder *instantly* (for a load).
There's not much use for transitions that are delayed for a
small-but-noticeable amount of time.
So, the plan is to remove the `timeoutMs` option. Instead, we'll assign
an effective timeout of `0` for loads, and `Infinity` for refreshes.
The mechanism for distinguishing a load from a refresh already exists in
the current model. If a component suspends, and the nearest Suspense
boundary hasn't already mounted, we treat that as a load, because
there's nothing on the screen. However, if the nearest boundary is
mounted, we treat that as a refresh, since it's already showing content.
If you need to fix a transition to be treated as a load instead of a
refresh, or vice versa, the solution will involve rearranging the
location of your Suspense boundaries. It may also involve adding a key.
We're still working on proper documentation for these patterns. In the
meantime, please reach out to us if you run into problems that you're
unsure how to fix.
We will remove `timeoutMs` from `useDeferredValue`, too, and apply the
same load versus refresh semantics to the update that spawns the
deferred value.
Note that there are other types of delays that are not related to
transitions; for example, we will still throttle the appearance of
nested placeholders (we refer to this as the placeholder "train model"),
and we may still apply a Just Noticeable Difference heuristic (JND) in
some cases. These aren't going anywhere. (Well, the JND heuristic might
but for different reasons than those discussed above.)
* 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.
* Failing test for #19608
* Attach Listeners Eagerly to Roots and Portal Containers
* Forbid createEventHandle with custom events
We can't support this without adding more complexity. It's not clear that this is even desirable, as none of our existing use cases need custom events. This API primarily exists as a deprecation strategy for Flare, so I don't think it is important to expand its support beyond what Flare replacement code currently needs. We can later revisit it with a better understanding of the eager/lazy tradeoff but for now let's remove the inconsistency.
* Reduce risk by changing condition only under the flag
Co-authored-by: koba04 <koba0004@gmail.com>
This updates the experimental Scheduler postTask build to call postTask
directly, instead of managing our own custom queue and work loop.
We still use a deadline 5ms mechanism to implement `shouldYield`.
The main thing that postTask is currently missing is the continuation
feature — when yielding to the main thread, the yielding task is sent
to the back of the queue, instead of maintaining its position.
While this would be nice to have, even without it, postTask may be good
enough to replace our userspace implementation.
We'll run some tests to see.