Stacked on #28872
renderToStaticNodeStream was not originally deprecated when
renderToNodeStream was deprecated because it did not yet have a clear
analog in the modern streaming implementation for SSR. In React 19 we
have already removed renderToNodeStream. This change removes
renderToStaticNodeStream as well because you can replicate it's
semantics using renderToPipeableStream with onAllReady or
renderToReadableStream with await stream.allready.
Stacked on #28606
renderToNodeStream has been deprecated since React 18 with a warning
indicating users should upgrade to renderToPipeableStream. This change
removes renderToNodeStream
* 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
* [Fizz] Support abort reasons
Fizz supports aborting the render but does not currently accept a reason. The various render functions that use Fizz have some automatic and some user-controlled abort semantics that can be useful to communicate with the running program and users about why an Abort happened.
This change implements abort reasons for renderToReadableStream and renderToPipeable stream as well as legacy renderers such as renderToString and related implementations.
For AbortController implementations the reason passed to the abort method is forwarded to Fizz and sent to the onError handler. If no reason is provided the AbortController should construct an AbortError DOMException and as a fallback Fizz will generate a similar error in the absence of a reason
For pipeable streams, an abort function is returned alongside pipe which already accepted a reason. That reason is now forwarded to Fizz and the implementation described above.
For legacy renderers there is no exposed abort functionality but it is used internally and the reasons provided give useful context to, for instance to the fact that Suspense is not supported in renderToString-like renderers
* I forgot to call onFatalError
I can't figure out how to write a test for this because it only happens
when there is a bug in React itself which would then be fixed if we found
it.
We're also covered by the protection of ReadableStream which doesn't leak
other errors to us.
* Abort requests if the reader cancels
No need to continue computing at this point.
* Abort requests if node streams get destroyed
This is if the downstream cancels is for example.
* Rename Node APIs for Parity with allReady
The "Complete" terminology is a little misleading because not everything
has been written yet. It's just "Ready" to be written now.
onShellReady
onShellError
onAllReady
* 'close' should be enough
* Move onCompleteAll to .allReady Promise
The onCompleteAll callback can sometimes resolve before the promise that
returns the stream which is tough to coordinate. A more idiomatic API
for a one shot event is a Promise.
That way the way you render for SEO or SSG is:
const stream = await renderToReadableStream(...);
await stream.readyAll;
respondWith(stream);
Ideally this should be a sub-class of ReadableStream but we don't yet
compile these to ES6 and they'd had to be to native class to subclass
a native stream.
I have other ideas for overriding the .tee() method in a subclass anyway.
So this is inline with that strategy.
* Reject the Promise on fatal errors
* 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.
* Pass in Destination lazily in startFlowing instead of createRequest
* Delay fatal errors until we have a destination to forward them to
* Flow can now be inferred by whether there's a destination set
We can drop the destination when we're not flowing since there's nothing to
write to.
Fatal errors now close once flowing starts back up again.
* Defer fatal errors in Flight too
This makes it a lot easier to render the whole document using React without
needing to patch into the stream.
We expect that currently people will still have to patch into the stream
to do advanced things but eventually the goal is that you shouldn't
need to.
* 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.