Commit Graph

167 Commits

Author SHA1 Message Date
Sebastian Markbåge 5690932765
Add onErrorShell Callback (#23247)
This indicates that an error has happened before the shell completed and
there's no point in emitting the result of this stream.

This is not quite the same as other fatal errors that can happen even
after streaming as started.

It's also not quite the same as onError before onCompleteShell because
onError can be called for an error inside a Suspense boundary before the
shell completes.

Implement shell error handling in Node SSR fixtures

Instead of hanging indefinitely.

Update Browser Fixture

Expose onErrorShell to the Node build

This API is not Promisified so it's just a separate callback instead.

Promisify the Browser Fizz API

It's now a Promise of a readable stream. The Promise resolves when the
shell completes. If the shell errors, the Promise is rejected.
2022-02-08 22:38:14 -05:00
Andrew Clark 848e802d20
Add onRecoverableError option to hydrateRoot, createRoot (#23207)
* [RFC] Add onHydrationError option to hydrateRoot

This is not the final API but I'm pushing it for discussion purposes.

When an error is thrown during hydration, we fallback to client
rendering, without triggering an error boundary. This is good because,
in many cases, the UI will recover and the user won't even notice that
something has gone wrong behind the scenes.

However, we shouldn't recover from these errors silently, because the
underlying cause might be pretty serious. Server-client mismatches are
not supposed to happen, even if UI doesn't break from the users
perspective. Ignoring them could lead to worse problems later. De-opting
from server to client rendering could also be a significant performance
regression, depending on the scope of the UI it affects.

So we need a way to log when hydration errors occur.

This adds a new option for `hydrateRoot` called `onHydrationError`. It's
symmetrical to the server renderer's `onError` option, and serves the
same purpose.

When no option is provided, the default behavior is to schedule a
browser task and rethrow the error. This will trigger the normal browser
behavior for errors, including dispatching an error event. If the app
already has error monitoring, this likely will just work as expected
without additional configuration.

However, we can also expose additional metadata about these errors, like
which Suspense boundaries were affected by the de-opt to client
rendering. (I have not exposed any metadata in this commit; API needs
more design work.)

There are other situations besides hydration where we recover from an
error without surfacing it to the user, or notifying an error boundary.
For example, if an error occurs during a concurrent render, it could be
due to a data race, so we try again synchronously in case that fixes it.
We should probably expose a way to log these types of errors, too. (Also
not implemented in this commit.)

* Log all recoverable errors

This expands the scope of onHydrationError to include all errors that
are not surfaced to the UI (an error boundary). In addition to errors
that occur during hydration, this also includes errors that recoverable
by de-opting to synchronous rendering. Typically (or really, by
definition) these errors are the result of a concurrent data race;
blocking the main thread fixes them by prevents subsequent races.

The logic for de-opting to synchronous rendering already existed. The
only thing that has changed is that we now log the errors instead of
silently proceeding.

The logging API has been renamed from onHydrationError
to onRecoverableError.

* Don't log recoverable errors until commit phase

If the render is interrupted and restarts, we don't want to log the
errors multiple times.

This change only affects errors that are recovered by de-opting to
synchronous rendering; we'll have to do something else for errors
during hydration, since they use a different recovery path.

* Only log hydration error if client render succeeds

Similar to previous step.

When an error occurs during hydration, we only want to log it if falling
back to client rendering _succeeds_. If client rendering fails,
the error will get reported to the nearest error boundary, so there's
no need for a duplicate log.

To implement this, I added a list of errors to the hydration context.
If the Suspense boundary successfully completes, they are added to
the main recoverable errors queue (the one I added in the
previous step.)

* Log error with queueMicrotask instead of Scheduler

If onRecoverableError is not provided, we default to rethrowing the
error in a separate task. Originally, I scheduled the task with
idle priority, but @sebmarkbage made the good point that if there are
multiple errors logs, we want to preserve the original order. So I've
switched it to a microtask. The priority can be lowered in userspace
by scheduling an additional task inside onRecoverableError.

* Only use host config method for default behavior

Redefines the contract of the host config's logRecoverableError method
to be a default implementation for onRecoverableError if a user-provided
one is not provided when the root is created.

* Log with reportError instead of rethrowing

In modern browsers, reportError will dispatch an error event, emulating
an uncaught JavaScript error. We can do this instead of rethrowing
recoverable errors in a microtask, which is nice because it avoids any
subtle ordering issues.

In older browsers and test environments, we'll fall back
to console.error.

* Naming nits

queueRecoverableHydrationErrors -> upgradeHydrationErrorsToRecoverable
2022-02-04 07:57:33 -08:00
salazarm 3a44621296
Disable avoidThisFallback support in Fizz (#23224)
* disable fizz avoidThisFallback support

* true
2022-02-01 18:22:04 -05:00
Fran Dios 529dc3ce84
Fix context providers in SSR when handling multiple requests (#23171)
* add failing test for renderToPipeableStream

* Fix context providers in SSR when handling multiple requests. Closes #23089

* Add sibling regression test

Co-authored-by: zhuyi01 <zhuyi01@ke.com>
Co-authored-by: Dan Abramov <dan.abramov@me.com>
2022-01-24 17:52:51 +00:00
Andrew Clark 4729ff6d1f
Implement identifierPrefix option for useId (#22855)
When an `identifierPrefix` option is given, React will add it to the
beginning of ids generated by `useId`.

The main use case is to avoid conflicts when there are multiple React
roots on a single page.

The server API already supported an `identifierPrefix` option. It's not
only used by `useId`, but also for React-generated ids that are used to
stitch together chunks of HTML, among other things. I added a
corresponding option to the client.

You must pass the same prefix option to both the server and client.
Eventually we may make this automatic by sending the prefix from the
server as part of the HTML stream.
2021-12-02 17:49:43 -08:00
180909 e6f60d2add
fix typos (#22715) 2021-11-15 10:58:21 -05:00
Andrew Clark 00ced1e2b7
Fix useId in strict mode (#22681)
* Fix: useId in strict mode

In strict mode, `renderWithHooks` is called twice to flush out
side effects.

Modying the tree context (`pushTreeId` and `pushTreeFork`) is effectful,
so before this fix, the tree context was allocating two slots for a
materialized id instead of one.

To address, I lifted those calls outside of `renderWithHooks`. This
is how I had originally structured it, and it's how Fizz is structured,
too. The other solution would be to reset the stack in between the calls
but that's also a bit weird because we usually only ever reset the
stack during unwind or complete.

* Add test for render phase updates

Noticed this while fixing the previous bug
2021-11-02 17:59:17 -07:00
Andrew Clark 75f3ddebfa
Remove experimental useOpaqueIdentifier API (#22672)
useId is the updated version of this API.
2021-11-01 15:02:39 -07:00
Andrew Clark ebf9ae8579
useId (#22644)
* Add useId to dispatcher

* Initial useId implementation

Ids are base 32 strings whose binary representation corresponds to the
position of a node in a tree.

Every time the tree forks into multiple children, we add additional bits
to the left of the sequence that represent the position of the child
within the current level of children.

    00101       00010001011010101
    ╰─┬─╯       ╰───────┬───────╯
  Fork 5 of 20       Parent id

The leading 0s are important. In the above example, you only need 3 bits
to represent slot 5. However, you need 5 bits to represent all the forks
at the current level, so we must account for the empty bits at the end.

For this same reason, slots are 1-indexed instead of 0-indexed.
Otherwise, the zeroth id at a level would be indistinguishable from
its parent.

If a node has only one child, and does not materialize an id (i.e. does
not contain a useId hook), then we don't need to allocate any space in
the sequence. It's treated as a transparent indirection. For example,
these two trees produce the same ids:

<>                          <>
  <Indirection>               <A />
    <A />                     <B />
  </Indirection>            </>
  <B />
</>

However, we cannot skip any materializes an id. Otherwise, a parent id
that does not fork would be indistinguishable from its child id. For
example, this tree does not fork, but the parent and child must have
different ids.

<Parent>
  <Child />
</Parent>

To handle this scenario, every time we materialize an id, we allocate a
new level with a single slot. You can think of this as a fork with only
one prong, or an array of children with length 1.

It's possible for the the size of the sequence to exceed 32 bits, the
max size for bitwise operations. When this happens, we make more room by
converting the right part of the id to a string and storing it in an
overflow variable. We use a base 32 string representation, because 32 is
the largest power of 2 that is supported by toString(). We want the base
to be large so that the resulting ids are compact, and we want the base
to be a power of 2 because every log2(base) bits corresponds to a single
character, i.e. every log2(32) = 5 bits. That means we can lop bits off
the end 5 at a time without affecting the final result.

* Incremental hydration

Stores the tree context on the dehydrated Suspense boundary's state
object so it resume where it left off.

* Add useId to react-debug-tools

* Add selective hydration test

Demonstrates that selective hydration works and ids are preserved even
after subsequent client updates.
2021-11-01 13:30:44 -07:00
btea 90e5d36388
chore: fix comment typo (#22615) 2021-10-22 12:44:31 -04:00
Sebastian Markbåge cdb8a1d19d
[Fizz] Add option to inject bootstrapping script tags after the shell is injected (#22594)
* Add option to inject bootstrap scripts

These are emitted right after the shell as flushed.

* Update ssr fixtures to use bootstrapScripts instead of manual script tag

* Add option to FB renderer too
2021-10-19 19:36:10 -07:00
Andrew Clark a724a3b578
[RFC] Codemod invariant -> throw new Error (#22435)
* 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.
2021-09-30 12:01:28 -07:00
Sebastian Markbåge 7843b142ac
[Fizz/Flight] Pass in Destination lazily to startFlowing instead of in createRequest (#22449)
* 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
2021-09-28 15:32:09 -07:00
Sebastian Markbåge eba248c390
[Fizz/Flight] Remove reentrancy hack (#22446)
* Remove reentrant check from Fizz/Flight

* Make startFlowing explicit in Flight

This is already an explicit call in Fizz. This moves flowing to be explicit.

That way we can avoid calling it in start() for web streams and therefore
avoid the reentrant call.

* Add regression test

This test doesn't actually error due to the streams polyfill not behaving
like Chrome but rather according to spec.

* Update the Web Streams polyfill

Not that we need this but just in case there are differences that are fixed.
2021-09-27 17:47:56 -07:00
Sebastian Markbåge 2cc6d79c98
Rename onReadyToStream to onCompleteShell (#22443) 2021-09-27 12:38:22 -07:00
Justin Grant c88fb49d37
Improve DEV errors if string coercion throws (Temporal.*, Symbol, etc.) (#22064)
* 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.
2021-09-27 10:05:07 -07:00
Sebastian Markbåge d47339ea36
[Fizz] Remove assignID mechanism (#22410)
* Remove pushEmpty

This is only used to support the assignID mechanism.

* Remove assignID mechanism

This effectively isn't used anyway because we always insert a dummy tag
into the fallback.

* Emit the template tag with an ID directly in pending boundaries

This ensures that assigning the ID is deterministic since it's done during
writing.

This also avoids emitting it for client rendered boundaries that start as
client rendered since we never need to refer to them.

* Move lazy ID initialization to the core implementation

We never need an ID before we write a pending boundary. This also ensures
that ID generation is deterministic by moving it to the write phase.

* Simplify the inserted scripts

We can assume that there are no text nodes before the template tag so this
simplifies the script that finds the comment node. It should be the direct
previous child.
2021-09-24 10:22:02 -04:00
Andrew Clark 82c8fa90be
Add back useMutableSource temporarily (#22396)
Recoil uses useMutableSource behind a flag. I thought this was fine
because Recoil isn't used in any concurrent roots, so the behavior
would be the same, but it turns out that it is used by concurrent
roots in a few places.

I'm not expecting it to be hard to migrate to useSyncExternalStore, but
to de-risk the change I'm going to roll it out gradually with a flag. In
the meantime, I've added back the useMutableSource API.
2021-09-21 20:38:24 -07:00
salazarm 64e70f82e9
[Fizz] add avoidThisFallback support (#22318) 2021-09-20 15:44:48 -04:00
Andrew Clark 86b3e2461d
Implement useSyncExternalStore on server (#22347)
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.
2021-09-20 08:31:02 -07:00
Andrew Clark 8209de2695
Delete useMutableSource implementation (#22292)
This API was replaced by useSyncExternalStore
2021-09-19 21:11:50 -07:00
Ricky 263cfa6ecb
[Experimental] Add useInsertionEffect (#21913) 2021-09-14 10:27:09 -04:00
Andrew Clark 77912d9a05
Wire up the native API for useSyncExternalStore (#22237)
Adds useSyncExternalStore to the internal dispatcher, and exports
the native API from the React package without yet implementing it.
2021-09-07 10:20:24 -07:00
Sebastian Markbåge 321087d134
[Fizz] Don't add aborted segments to the completedSegments list (#21976)
* Don't add aborted segments to the completedSegments list

* Update error message to include aborted status
2021-07-27 21:53:37 -04:00
Deniz Susman 3f62dec84a
Typo fix (#21729)
emitted instead of emmitted
2021-07-19 17:19:52 -04:00
Ricky 14bac6193a
Allow components to render undefined (#21869) 2021-07-13 15:48:11 -04:00
Sebastian Markbåge 502f8a2a07
[Fizz/Flight] Don't use default args (#21681)
* Don't use default args

* Hoist out creation for better inlining

The closures prevent inlining otherwise.
2021-06-14 15:28:20 -07:00
Sebastian Markbåge dbe3363ccd
[Fizz] Implement Legacy renderToString and renderToNodeStream on top of Fizz (#21276)
* 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.
2021-06-14 12:54:30 -07:00
Dan Abramov cc4d24ab0b
[Fizz] Always call flush() if it exists (#21625) 2021-06-04 20:17:57 +01:00
Sebastian Markbåge e0d9b28999
[Fizz] Minor Fixes for Warning Parity (#21618) 2021-06-03 19:54:31 -04:00
Sebastian Markbåge 1b7b3592f4
[Fizz] Implement Component Stacks in DEV for warnings (#21610)
* Implement component stacks

This uses a reverse linked list in DEV-only to keep track of where we're
currently executing.

* Fix bug that wasn't picking up the right stack at suspended boundaries

This makes it more explicit which stack we pass in to be retained by the
task.
2021-06-03 13:02:41 -07:00
Sebastian Markbåge 4a8deb0836
Switch the isPrimaryRender flag based on the stream config (#21357) 2021-04-26 22:37:05 -04:00
Sebastian Markbåge bd4f056a3e
[Fizz] Implement lazy components and nodes (#21355)
* Implement lazy components

* Implement lazy elements / nodes

This is used by Flight to encode not yet resolved nodes of any kind.
2021-04-26 18:46:46 -07:00
Sebastian Markbåge 9cd52b27fe
Restore context after an error happens (#21341)
Typically we don't need to restore the context here because we assume that
we'll terminate the rest of the subtree so we don't need the correct
context since we're not rendering any siblings.

However, after a nested suspense boundary we need to restore the context.
The boundary could do this but since we're already doing this in the
suspense branch of renderNode, we might as well do it in the error case
which isn't very perf sensitive anyway.
2021-04-23 12:24:10 -07:00
Sebastian Markbåge 709f948412
[Fizz] Add FB specific streaming API and build (#21337)
Add FB specific streaming API and build
2021-04-22 16:54:29 -07:00
Ricky a632f7de3b
Flip tuple order of useTransition (#20976) 2021-04-20 12:21:44 -04:00
Sebastian Markbåge cc4b431dab
Mark boundary as client rendered even if aborting fallback (#21294) 2021-04-15 19:16:40 -04:00
Sebastian Markbåge 9eddfbf5af
[Fizz] Two More Fixes (#21288)
* Emit value of option tags

* Mask the legacy context passed to classes
2021-04-15 10:26:49 -07:00
Sebastian Markbåge 11b07597ee
Fix classes (#21283) 2021-04-15 08:06:31 -07:00
Sebastian Markbåge 96d00b9bba
[Fizz] Random Fixes (#21277) 2021-04-14 23:29:30 -04:00
Sebastian Markbåge 81ef539535
Always insert a dummy node with an ID into fallbacks (#21272) 2021-04-14 15:39:51 -07:00
Sebastian Markbåge a4a940d7a1
[Fizz] Add unsupported Portal/Scope components (#21261)
* Update Portal error message

* Add Scope Component
2021-04-14 14:35:36 -07:00
Sebastian Markbåge f4d7a0f1ea
Implement useOpaqueIdentifier (#21260)
The format of this ID is specific to the format.
2021-04-14 14:25:42 -07:00
Sebastian Markbåge dde875dfb1
[Fizz] Implement Hooks (#21257)
* Implement Fizz Hooks

This is pretty much just a copy of the partial renderer Hooks.

* Implement forward ref and memo
2021-04-14 14:16:41 -07:00
Sebastian Markbåge a597c2f5dc
[Fizz] Fix reentrancy bug (#21270)
* Fix reentrancy bug

* Fix another reentrancy bug

There's also an issue if we try to schedule something to be client
rendered if its fallback hasn't rendered yet. So we don't do it
in that case.
2021-04-14 13:49:14 -07:00
Sebastian Markbåge 4f76a28c93
[Fizz] Implement New Context (#21255)
* 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
2021-04-14 11:45:42 -07:00
Sebastian Markbåge dbadfa2c36
[Fizz] Classes Follow Up (#21253)
* Port Classes from Fiber to Fizz

* Test
2021-04-13 13:57:36 -07:00
Sebastian Markbåge 343710c923
[Fizz] Fragments and Iterable support (#21228) 2021-04-10 15:50:42 -04:00
Sebastian Markbåge b0407b55ff
Support more empty types (#21225)
Undefined errors as a direct return value.

This changes semantics for "true" and functions to mirror the client.
2021-04-09 16:23:37 -07:00
Sebastian Markbåge cf45a623a1
[Fizz] Implement Classes (#21200)
* Legacy context

* Port Classes from Fiber to Fizz
2021-04-08 10:42:37 -07:00
Sebastian Markbåge ad6e6ec7bb
[Fizz] Prepare Recursive Loop for More Types (#21186)
* Split out into helper functions

This is similar to the structure of beginWork in Fiber.

* Split the rendering of a node from recursively rendering a node

This lets us reuse render node at the root which doesn't spawn new work.
2021-04-07 11:29:06 -07:00
Sebastian Markbåge 172e89b4bf
Reland Remove redundant initial of isArray (#21188)
* Remove redundant initial of isArray (#21163)

* Reapply prettier

* Type the isArray function with refinement support

This ensures that an argument gets refined just like it does if isArray is
used directly.

I'm not sure how to express with just a direct reference so I added a
function wrapper and confirmed that this does get inlined properly by
closure compiler.

* A few more

* Rename unit test to internal

This is not testing a bundle.

Co-authored-by: Behnam Mohammadi <itten@live.com>
2021-04-07 07:57:43 -07:00
Sebastian Markbage b4f119cdf1 Revert "Remove redundant initial of isArray (#21163)"
This reverts commit b130a0f5cd.
2021-04-01 15:19:00 -04:00
Behnam Mohammadi b130a0f5cd
Remove redundant initial of isArray (#21163) 2021-04-01 10:50:48 -07:00
Sebastian Markbåge 1cf9978d89
Don't pass internals to callbacks (#21161)
I noticed that I accidentally pass the request object to public API callbacks
as "this".
2021-04-01 08:43:12 -07:00
Sebastian Markbåge b9e4c10e99
[Fizz] Implement all the DOM attributes and special cases (#21153)
* 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
2021-03-31 17:39:38 -07:00
Sebastian Markbåge 0853aab74d
Log all errors to console.error by default (#21130) 2021-03-29 19:39:55 -07:00
Sebastian Markbåge d1294c9d40
[Flight] Add global onError handler (#21129)
* Add onError option to Flight Server

The callback is called any time an error is generated in a server component.

This allows it to be logged on a server if needed. It'll still be rethrown
on the client so it can be logged there too but in case it never reaches
the client, here's a way to make sure it doesn't get lost.

* Add fatal error handling
2021-03-29 19:36:16 -07:00
Sebastian Markbåge 32d6f39edd
[Fizz] Support special HTML/SVG/MathML tags to suspend (#21113)
* 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
2021-03-27 10:50:38 -07:00
Sebastian Markbåge 556644e237
Fix plurals (#21106) 2021-03-25 22:21:41 -04:00
Sebastian Markbåge 8b741437b1
Rename SuspendedWork to Task (#21105) 2021-03-25 18:39:59 -07:00
Sebastian Markbåge 38a1aedb49
[Fizz] Add FormatContext and Refactor Work (#21103)
* Add format context

* Let the Work node hold all working state for the recursive loop

Stacks are nice and all but there's a cost to maintaining each frame
both in terms of stack size usage and writing to it.

* Move current format context into work

* Synchronously render children of a Suspense boundary

We don't have to spawn work and snapshot the context. Instead we can try
to render the boundary immediately in case it works.

* Lazily create the fallback work

Instead of eagerly create the fallback work and then immediately abort it.
We can just avoid creating it if we finish synchronously.
2021-03-25 18:38:43 -07:00
Sebastian Markbåge 435cff9866
[Fizz] Expose callbacks in options for when various stages of the content is done (#21056)
* Report errors to a global handler

This allows you to log errors or set things like status codes.

* Add complete callback

* onReadyToStream callback

This is typically not needed because if you want to stream when the
root is ready you can just start writing immediately.

* Rename onComplete -> onCompleteAll
2021-03-23 11:39:38 -07:00
Sebastian Markbåge 8fe7810e70
Remove already completed comment (#21054) 2021-03-22 15:41:22 -07:00
Sebastian Markbåge 6c3202b1e1
[Fizz] Use identifierPrefix to avoid conflicts within the same response (#21037)
* Use identifierPrefix to avoid conflicts within the same response

identifierPrefix as an option exists to avoid useOpaqueIdentifier conflicting
when different renders are used within one HTML response.

This lets this be configured for the DOM renderer specifically since it's DOM
specific whether they will conflict across trees or not.

* Add test for using multiple containers in one HTML document
2021-03-22 13:10:57 -07:00
Sebastian Markbåge f6bc9c8243
[Fizz] Expose maxBoundarySize as an option (#21029)
* Expose maxBoundarySize as an option

* Adjust the heuristic

* Rename to progressiveChunkSize
2021-03-19 09:41:25 -07:00
Sebastian Markbåge 154b85213a
[Fizz] Expose a method to explicitly start writing to a Node stream (#21028)
* Expose an explicit point when to start writing in the Node API

* Add a previously failing test
2021-03-18 12:43:40 -07:00
Sebastian Markbåge cf485e6f6b
[Fizz] Expose a method to abort a pending request (#21027)
* Track all suspended work while it's still pending

This allows us to abort work and put everything into client rendered mode
if we don't want to wait for further I/O.

It also allows us to cancel fallbacks if we complete the main content
before the fallback.

* Expose abort API to the browser streams

Since this API already returns a value, we need to use destructuring to
expose more options.

* Add a test including the client actually client rendering it

* Use AbortSignal option for W3C streams instead of external control

* Clean up listener after it's used once
2021-03-18 09:46:15 -07:00
Sebastian Markbåge 1d1e49cfa4
[Fizz] Assign an ID to the first DOM element in a fallback or insert a dummy (and testing infra) (#21020)
* Patches

* Add Fizz testing infra structure

* Assign an ID to the first DOM node in a fallback or insert a dummy

* unstable_createRoot
2021-03-16 14:05:52 -07:00
Sebastian Markbåge b9c4a01f71
Allow the streaming config to decide how to precompute or compute chunks (#21008)
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.
2021-03-15 10:36:23 -07:00
Sebastian Markbåge 14e4fd1ff2
[Fizz] Move DOM/Native format configs to their respective packages (#20994)
* Move DOM/Native format configs to their respective packages

The streaming configs (Node/Browser) are different because they operate at
another dimension that exists in each package.

* Use escapeTextForBrowser to encode dynamic strings

We can now use local dependencies
2021-03-13 06:54:59 -08:00
Sebastian Markbåge f2b6bf7c86
[Fizz] Destroy the stream with an error if the root throws (#20992)
* Destroy the stream with an error if the root throws

But not if the error happens inside a suspense boundary.

* Try rewriting the test to see if it works in other Node envs
2021-03-12 15:21:02 -08:00
Sebastian Markbåge 10cc400184
Basic Fizz Architecture (#20970)
* 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
2021-03-11 12:01:41 -08:00
Sebastian Markbåge a511dc7090
Error for deferred value and transition in Server Components (#20657) 2021-01-25 13:58:47 -08:00
Sebastian Markbåge fb3f63f1ab
Remove lazy invokation of segments (#20656)
This is a remainder from Blocks when these were separate query functions.
2021-01-25 13:04:36 -08:00
Andrew Clark efc57e5cbb
Add built-in Suspense cache with support for invalidation (refreshing) (#20456) 2020-12-18 10:57:24 -08:00
Dan Abramov daf38ecdfc
[Flight] Use lazy reference for existing modules (#20445) 2020-12-11 22:45:18 +00:00
Dan Abramov 1b5ca99063
Fix module ID deduplication (#20406) 2020-12-08 13:19:59 +00:00
Sebastian Markbåge 5fd9db732d
[Flight] Rename react-transport-... packages to react-server-... (#20403)
* 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
2020-12-08 08:08:57 -05:00
Dan Abramov e23673b511
[Flight] Add getCacheForType() to the dispatcher (#20315)
* 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
2020-12-03 03:44:56 +00:00
Sebastian Markbåge 163199d8cc
Dedupe module id generation (#20172) 2020-11-10 19:58:58 -08:00
Sebastian Markbåge 76a6dbcb9a
[Flight] Encode Symbols as special rows that can be referenced by models … (#20171)
* 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
2020-11-10 19:56:50 -08:00
Sebastian Markbåge 16e6dadba6
Encode throwing server components as lazy throwing references (#20217)
This ensures that if this server component was the child of a client
component that has an error boundary, it doesn't trigger the error until
this gets rendered so it happens as deep as possible.
2020-11-10 16:35:27 -08:00
Sebastian Markbåge c3e20f18fe
Add Relay specific React Native build of Flight (#20149)
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.
2020-11-02 18:49:48 -08:00
Sebastian Markbåge 56e9feead0
Remove Blocks (#20138)
* 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
2020-10-30 23:03:45 -07:00
Sebastian Markbåge 3fbd47b862
Serialize pending server components by reference (lazy component) (#20137)
This now means that if a server component suspends, its value becomes a
React.lazy object. I.e. the element that rendered the server component
gets replaced with a lazy node.

As of #19033 lazy objects can be rendered in the node position. This allows
us to suspend at the location of the server component while we're waiting
on its content.

Now server components has the same capabilities as Blocks to progressively
reveal its content.
2020-10-30 17:19:46 -07:00
Sebastian Markbåge 930ce7c15a
Allow values to be encoded by "reference" to a value rather than the value itself (#20136)
These references are currently transformed into React.lazy values. We can use these in
React positions like element type or node position.

This could be expanded to a more general concept like Suspensey Promises, asset references or JSResourceReferences.

For now it's only used in React Element type position.

The purpose of these is to let you suspend deeper in the tree.
2020-10-30 13:02:03 -07:00
Sebastian Markbåge ffd8423356
[Flight] Add support for Module References in transport protocol (#20121)
* 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
2020-10-29 17:57:31 -07:00
Dan Abramov f021a983aa
Bump versions for 17 (#20062) 2020-10-20 21:41:18 +01:00
Sebastian Markbåge 0a4c7c5651
[Flight] Don't warn for key, but error for ref (#19986)
* 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
2020-10-08 17:02:23 -07:00
Sebastian Markbåge 40c52de960
[Flight] Add Runtime Errors for Non-serializable Values (#19980)
* Error on encoding non-serializable props

* Add DEV time warnings to enforce that values are plain objects
2020-10-08 11:11:15 -07:00
Joseph Savona d38ec17b1d
[Flight] Set dispatcher for duration of performWork() (#19776) 2020-09-07 19:41:22 -04:00
Dan Abramov c1ac052158
[Flight] Support more element types and Hooks for Server and Hybrid Components (#19711)
* Shim support for more element types

* Shim commonly used Hooks that are safe

* Flow

* Oopsie
2020-08-27 20:19:13 +01:00
Brian Vaughn a89854bc93
Fix Suspense-wrapping heuristic (and bump version numbers) (#19373)
* Fixed suspense wrapping heuristic

* Bump package numbers 16.13.1 -> 17.0.0-alpha.0 to fix DevTools Suspense heuristic
2020-07-15 12:25:27 -04:00
Sebastian Markbåge 64d4b84204
Rename Flight to Transport (#18808)
* Rename Flight to Transport

Flight is still the codename for the implementation details (like Fiber).

However, now the public package is react-transport-... which is only
intended to be used directly by integrators.

* Rename names
2020-05-03 11:33:48 -07:00
Sebastian Markbåge e2dd30898e
[Flight] Lazily parse models and allow any value to suspend (#18476)
* Lazily initialize models as they're read intead of eagerly when received

This ensures that we don't spend CPU cycles processing models that we're
not going to end up rendering.

This model will also allow us to suspend during this initialization if
data is not yet available to satisfy the model.

* Refactoring carefully to ensure bundles still compile to something optimal

* Remove generic from Response

The root model needs to be cast at one point or another same as othe
chunks. So we can parameterize the read instead of the whole Response.

* Read roots from the 0 key of the map

The special case to read the root isn't worth the field and code.

* Store response on each Chunk

Instead of storing it on the data tuple which is kind of dynamic, we store
it on each Chunk. This uses more memory. Especially compared to just making
initializeBlock a closure, but overall is simpler.

* Rename private fields to underscores

Response objects are exposed.

* Encode server components as delayed references

This allows us to stream in server components one after another over the
wire. It also allows parallelizing their fetches and resuming only the
server component instead of the whole parent block.

This doesn't yet allow us to suspend deeper while waiting on this content
because we don't have "lazy elements".
2020-04-03 14:58:02 -07:00
Sebastian Markbåge 3e94bce765
Enable prefer-const lint rules (#18451)
* Enable prefer-const rule

Stylistically I don't like this but Closure Compiler takes advantage of
this information.

* Auto-fix lints

* Manually fix the remaining callsites
2020-04-01 12:35:52 -07:00
Sebastian Markbåge ce6fe50b01
Add server-runtime to create Server Blocks (#18392)
This is equivalent to the jsx-runtime in that this is what the compiled
output on the server is supposed to target.

It's really just the same code for all the different Flights, but they
have different types in their arguments so each one gets their own entry
point. We might use this to add runtime warnings per entry point.

Unlike the client-side React.block call this doesn't provide the factory
function that curries the load function. The compiler is expected to wrap
this call in the currying factory.
2020-03-25 19:03:31 -07:00
Sebastian Markbåge a317bd033f
Flip the arguments of Blocks and make the query optional (#18374)
* Flip the arguments of Blocks and make the query optional

* Rename Query to Load
2020-03-24 10:58:23 -07:00
Sebastian Markbåge a56309fb88
[Flight] Integrate Blocks into Flight (#18371)
* Resolve Server-side Blocks instead of Components

React elements should no longer be used to extract arbitrary data but only
for prerendering trees.

Blocks are used to create asynchronous behavior.

* Resolve Blocks in the Client

* Tests

* Bug fix relay JSON traversal

It's supposed to pass the original object and not the new one.

* Lint

* Move Noop Module Test Helpers to top level entry points

This module has shared state. It needs to be external from builds.

This lets us test the built versions of the Noop renderer.
2020-03-23 17:53:45 -07:00