* 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
* 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.
* Reduce code to necessities
* Switch to postTask API
* Add SchedulerPostTask tests
* Updates from review
* Fix typo from review
* Generate build of unstable_post_task