Commit Graph
19463 Commits
Author SHA1 Message Date
Andrew Clark b5810163e9 Change yarn test to default to experimental (#26741)
The idea is that the default `yarn test` command should be the one that
includes the most bleeding edge features, because during development you
probably want as many features enabled as possible.

That used to be `www-modern` but nowadays it's `experimental` because
we've landed a bunch of async actions stuff in experimental but it isn't
yet being tested at Meta.

So this switches the default to `experimental`.
2023-05-09 21:50:14 -04:00
Mofei Zhang f3d4d16918 [lower] Bail out on computed lvalues in destructure
--- 

Currently, we're silently producing incorrect output - 

```js 

// source 

function Component(props) { 

const computedKey = props.key; 

const { [computedKey]: x } = props.val; 

return x; 

} 

// compiles to 

function Component(props) { 

const { computedKey: x } = props.val; 

return x; 

} 

```
2023-05-09 14:38:15 -04:00
mofeiZ 04cee98166 [snap] patch bug in clearing require cache
Snap currently has a bug in which the require cache is not correctly cleared 
when running in filter mode (#tests < 2 * #workers). 

- We're currently clearing all entries in the require cache of worker threads, 
including `jest-worker` and `snap/dist/...` files 

- jest-worker seems to `require` these files on every dispatch (i.e. 
`worker.compile` seems to call `require(`compiler-worker`).compile`)
2023-05-11 13:53:06 -04:00
Joe Savona 5de02b881b Support unused conditional/logical expressions assigned to variable
I noticed some instances of this error when running forget on an internal 
product. I previously fixed the case if a logical/conditional used only for side 
effects (not assigned to a variable) but the new cases were assigned to an 
unused variable. I double-checked and we’ve actually fixed all the steps after 
these invariants so we can just remove them and support these cases.
2023-05-09 10:58:51 -07:00
Joe Savona 50c3bd9d98 Fix capturedRefs collection for lambdas
When we calculate the dependencies of a FunctionExpression we were only adding 
new items if the binding identifier had not been seen yet. That is correct for 
`capturedIds` since its the set of identifiers, but incorrect for `capturedRefs` 
since its an array of all the distinct places. This meant that if a function 
expression referenced multiple properties of the same binding, we'd only record 
the first one. We now correctly record all of them.
2023-05-09 10:38:56 -07:00
Jan Kassens fda1f0b902 Flow upgrade to 0.205.1 (#26796)
Just a small upgrade to keep us current and remove unused suppressions
(probably fixed by some upgrade since).

- `*` is no longer allowed and has been an alias for `any` for a while
now.
2023-05-09 10:45:50 -04:00
Joe Savona 90109b3572 Clean up dominator/post-dominator impl
Tidies up the implementation a bit, splitting the single function and class into 
distinct computeDominatorTree() and computePostDominatorTree() functions and 
helper classes.
2023-05-08 15:35:48 -07:00
Joe Savona 597e70d69f Improve eslint derived fixtures 2023-05-08 14:10:07 -07:00
Joe Savona 8920fc0b2f Dont count throw as an exit node (React semantics)
React will retry or abort components that throw (depending on a few conditions), 
so from React's perspective a `throw` statement is not a normal exit node. Thus 
the Rules of Hooks really have a caveat: the set of hooks that are called _in an 
execution that returns successfully_ must be consistent. Examples such as the 
following are therefore allowed: 

```javascript 

function Component(props) { 

if (props.cond) { 

throw new Error(...); 

} 

useHook(); 

} 

``` 

By modeling `throw` as an exit node, we rejected cases such as this. This diff 
changes to not model throws as exit nodes. #1584  changes this to make it an 
option, since some cases will want to consider throw as an exit node.
2023-05-08 13:58:46 -07:00
Joe Savona 7dedc6cc72 Fix test for hook name to match lint rule 2023-05-08 12:14:30 -07:00
Joe Savona fd007be0d0 Fixtures from ESLint rule
Incorporates the fixtures from eslint-plugin-react-hooks using a script, so that 
we can easily update them in the future. For each fixture we run the compiler 
with and without hooks validation first so that we know if the fixture is 
expected to pass — we have some false positives and false negatives that i can 
work through. For example we accidentally think that `userFetch()` is a hook, 
oops. Fixtures that should pass but error, or that should error but pass, are 
marked as `todo.<name>` or `todo.error.<name>`. 

While i was here i added the ability to have fixtures in subdirectories for 
grouping purposes.
2023-05-08 12:10:43 -07:00
Jan Kassens 7ac5e9a602 Small flowconfig fixes (#26784)
- The whole project root is included by default anyway, the include
section should be redundant and just misleading.
- The generated ignore paths ignore more than intended as they didn't
escape the `.` for regex.

Test Plan:
- wait for CI
- tested the ignore pattern change with renaming files and seeing the
expected files ignored for flow
2023-05-08 12:07:48 -04:00
Joe Savona 6bbe3111c0 ValidateUnconditionalHooks pass using dominators
See the code comments for more, but the basic idea here is that we use the post 
dominator tree to find the set of basic blocks which are guaranteed reachable in 
each function. Those are the only blocks where it is safe to call hooks, and we 
error for hook calls in any other blocks.
2023-05-08 08:55:14 -07:00
Joe Savona 2ddd00dbb1 Implement dominator/post-dominator tree calculation
Implements an efficient algorithm for computing the dominator (or post 
dominator) tree of a CFG, following 
https://www.cs.rice.edu/~keith/Embed/dom.pdf. This is used/tested in the next PR 
to validate that hooks are called unconditionally. 

note: I clean up the implementation quite a bit late in the stack in #1584
2023-05-08 08:55:13 -07:00
Joe Savona 5b0f566941 Example of "unnecessary" memoization of lambdas
I noticed this while demoing Forget to React Org alum Christoph Nakazawa — in 
array.map calls (and other APIs that take a lambda as input) we sometimes end up 
memoizing the lambda. It's technically correct since the function _could_ return 
the lambda, and then we'd need it to be memoized. It's tricky because array.map 
is often called on nested objects, where even if we had type inference on the 
outer value we wouldn't know for sure that the inner property is an Array and 
not some other data type with a custom .map. For example in 
`data.feedback.comments.edges.map(edge => ...)`, even if we knew that `data` was 
an Object, we wouldn't know that data.feedback.comments.edges is an Array 
without cross-file type knowledge. 

But it's definitely wasteful to memoize these lambdas, so we should brainstorm 
options. One option that stands out right away: if the lambda has zero 
dependencies, then we could lift it out to module scope and refer to it by name.
2023-05-10 23:09:17 -06:00
Mofei Zhang 11cee99968 [snap runner][easy] reverse diff order 2023-05-08 14:58:32 -04:00
Andrew Clark 16d053d592 Add useFormStatus to server rendering stub (#26788)
This was an oversight when I set up the hook in #26719.
2023-05-06 20:39:08 -04:00
dan efb381bbf9 [Release Script] Print a hint where to get the token (#26783)
I always forget where to get it.
2023-05-05 20:08:14 +01:00
Samuel SuslaandDan Abramov b00e27342d Use native scheduler if defined in global scope (#26554)
Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
2023-05-05 14:01:31 +01:00
Joe Savona 2332155161 ValidateHooksUsage
Adds a validation pass to check that the only thing you can do with hooks is 
call them. A follow-up PR (still early WIP) will check the other aspect of the 
rules of hooks, that they are not called conditionally. That's a more involved 
algorithm.
2023-05-04 13:33:43 -07:00
Joe Savona f75cf6fe38 Fix fbt (again) 2023-05-04 15:19:41 -07:00
Ruslan Lesiutin 783e7fcfa3 React DevTools 4.27.6 -> 4.27.7 (#26780)
List of changes:
* DevTools: fix backend activation ([hoxyq](https://github.com/hoxyq) in
[#26779](https://github.com/facebook/react/pull/26779))
* fix[dynamic-scripts-injection]: unregister content scripts before
registration ([hoxyq](https://github.com/hoxyq) in
[#26765](https://github.com/facebook/react/pull/26765))
2023-05-04 18:22:47 +01:00
Ruslan Lesiutin 377c5175f7 DevTools: fix backend activation (#26779)
## Summary
We have a case:
1. Open components tab
2. Close Chrome / Firefox devtools window completely
3. Reopen browser devtools panel
4. Open components tab

Currently, in version 4.27.6, we cannot load the components tree.

This PR contains two changes:
- non-functional refactoring in
`react-devtools-shared/src/devtools/store.js`: removed some redundant
type castings.
- fixed backend manager logic (introduced in
https://github.com/facebook/react/pull/26615) to activate already
registered backends. Looks like frontend of devtools also depends on
`renderer-attached` event, without it component tree won't load.

## How did you test this change?
This fixes the case mentioned prior. Currently in 4.27.6 version it is
not working, we need to refresh the page to make it work.

I've tested this in several environments: chrome, firefox, standalone
with RN application.
2023-05-04 17:34:57 +01:00
Sebastian Markbåge aef7ce5547 [Flight] Progressively Enhanced Server Actions (#26774)
This automatically exposes `$$FORM_ACTIONS` on Server References coming
from Flight. So that when they're used in a form action, we can encode
the ID for the server reference as a hidden field or as part of the name
of a button.

If the Server Action is a bound function it can have complex data
associated with it. In this case this additional data is encoded as
additional form fields.

To process a POST on the server there's now a `decodeAction` helper that
can take one of these progressive posts from FormData and give you a
function that is prebound with the correct closure and FormData so that
you can just invoke it.

I updated the fixture which now has a "Server State" that gets
automatically refreshed. This also lets us visualize form fields.
There's no "Action State" here for showing error messages that are not
thrown, that's still up to user space.
2023-05-03 18:36:57 -04:00
Sebastian Markbåge c10010a6a0 [Fizz] Gracefully handle suspending in DOM configs (#26768)
E.g. if we suspend (throw a promise) in pushStartInstance today we might
have already pushed some chunks (or even child segments potentially). We
should revert back to where we were.

This doesn't usually happen because when we suspend in a component it
doesn't write anything itself, it'll always defer to som host instance
to do the writing.

There was a todo about this already but I'm not 100% sure it's always
safe when suspending. It should be safe when suspending just regularly
because it's just a noop. We might not even want "throwing a promise" in
this mechanism to be supported longer term but for now that's how a
suspend in internals.
2023-05-03 18:13:47 -04:00
Andrew Clark f533cee8cb Add useFormStatus to Flight fixture (#26773) 2023-05-03 15:29:32 -04:00
Andrew Gadzik 2c1117a8d0 Reuse request so that a ReabableStream body does not become disturbed (#26771) 2023-05-03 15:06:10 -04:00
Mofei Zhang 407e7b0c62 [snap runner] debug mode 2023-05-03 14:50:27 -04:00
Mofei Zhang 7e990419c1 [snap tester] Handle unexpected errors by failing fixture 2023-05-03 14:50:25 -04:00
Joe Savona 253b05f28d Remove restriction on optional nesting
We previously disallowed OptionalMemberExpression inside a normal 
MemberExpression, eg `(a?.b).c`. The new representation handles this case 
correctly so we can remove the restriction.
2023-05-03 17:10:31 -07:00
Joe Savona 7ecee6a091 Support complex computed properties in OptionalMemberExpression
Our previous lowering for OptionalMemberExpression reordered the evaluation of 
properties, such that we had to restrict the allowed properties to those that 
were safe for reordering. With the new representation we preserve order of 
evaluation, so we can relax the restriction. This unblocks a few cases in an 
internal product.
2023-05-03 17:10:28 -07:00
Joe Savona e192930cd4 Remove unnecessary optional property on loads
Now that _all_ optional expression types use the new representation, the 
optionality of all PropertyLoad and ComputedLoad is modeled via control flow (in 
HIR) and the structure of OptionalExpression (in ReactiveFunction). Thus we no 
longer need the `optional` properties on these load instructions — they're 
optional if they're part of an OptionalExpression.
2023-05-03 17:10:27 -07:00
Joe Savona a43fc2bcf3 Consistently use new lowering for OptionalMemberExpression
Earlier PRs in the stack change the way we lower OptionalMemberExpression, but 
only when they ultimately appear inside some OptionalCallExpression. This PR 
ensures that _all_ OptionalMemberExpressions get the new lowering. Note that one 
test case has what is arguably a regression, but the new behavior is also 
reasonable: if we see both `a.b?.c` and `a.b.c.` as dependencies of a scope, we 
previously inferred `a.b.c` as the dependency, but we now infer `a.b` as the 
dependency. This isn't as optimal as what we had before, but it also seems good 
enough for now. Also note that some cases are improved: `foo(a.b?.c)` would 
previously have taken `a.b` as a dependency, we now take the full value of 
`a.b?.c` as a dependency - more precise. 

So overall i'm inclined to land and follow-up on the one regression, since the 
overall model is more cohesive.
2023-05-03 17:10:26 -07:00
Sebastian Markbåge fa7a447b9c [Fizz] Check for nullish values on ReactCustomFormAction (#26770)
Usually we don't have to do this since we only set these in the loop but
the ReactCustomFormAction props are optional so they might be undefined.

Also moved it to a general type since it's a semi-public API.
2023-05-03 14:35:26 -04:00
Andrew Clark b7972822b5 useOptimisticState -> useOptimistic (#26772)
Drop the "state". Just "useOptimistic". It's cleaner.

This is still an experimental API. May not be the final name.
2023-05-03 14:26:00 -04:00
Andrew Clark 388686f291 Add "canary" to list of allowed npm dist tags (#26767)
Forgot this allowlist existed. It's an extra safeguard, in case we mess
up the configuration somehow.
2023-05-03 12:36:34 -04:00
Ruslan Lesiutin 8a25302c66 fix[dynamic-scripts-injection]: unregister content scripts before registration (#26765)
## Summary
Fixes #26756.

DevTools is failing to inject `__REACT_DEVTOOLS_GLOBAL_HOOK__` hook in
incognito mode. This is not happening straight-forward, but if extension
is toggled on and off, the next time I try to open it I am receiving an
error that content script was already registered.

<img width="676" alt="Screenshot 2023-05-02 at 14 36 53"
src="https://user-images.githubusercontent.com/28902667/235877692-51c5d284-79d9-4b00-b62e-d25d5bb5e056.png">

- Unregistering content scripts before attempting to register them
again. We need to inject `__REACT_DEVTOOLS_GLOBAL_HOOK__` on each page,
so this should be expected behaviour.
- Fixed error logging
 
## How did you test this change?
Local build of extension for Chrome, trying the same steps, which
resulted in an error.
No regression in performance, tested on react.dev, still the same.
2023-05-03 17:27:39 +01:00
Andrew Clark 2c2476834a Rename "next" prerelease channel to "canary" (#26761)
The "next" prerelease channel represents what will be published the next
time we do a stable release. We publish a new "next" release every day
using a timed CI workflow.

When we introduced this prerelease channel a few years ago, another name
we considered was "canary". But I proposed "next" instead to create a
greater distinction between this channel and the "experimental" channel
(which is published at the same cadence, but includes extra experimental
features), because some other projects use "canary" to refer to releases
that are more unstable than how we would use it.

The main downside of "next" is someone might mistakenly assume the name
refers to Next.js. We were aware of this risk at the time but didn't
think it would be an issue in practice.

However, colloquially, we've ended up referring to this as the "canary"
channel anyway to avoid precisely that confusion.

So after further discussion, we've agreed to rename to "canary".

This affects the label used in the version string (e.g.
`18.3.0-next-a1c2d3e4` becomes `18.3.0-canary-a1c2d3e4`) as well as the
npm dist tags used to publish the releases. For now, I've chosen to
publish the canaries using both `@canary` and `@next` dist tags, so that
downstream consumers who might depend on `@next` have time to adjust. We
can remove that later after the change has been communicated.
2023-05-03 12:10:32 -04:00
Sathya Gunasekaran e5c07ad3e1 [babel] Skip compilation if there's no function name 2023-05-03 14:32:58 +01:00
Joe Savona 404f627c2c Improve conditional dependency tracking for optional member expr inside optional
call 

When we traverse an OptionalExpression in PropagateScopeDependencies, we 
previously considered the entire value to be optional. With the changes in this 
stack to more accurately model OptionalMemberExpression, the `object` portion of 
an OptionalMemberExpression is now evaluated within the OptionalExpression. This 
PR refines the handling of OptionalExpression accordingly, so that we only treat 
the optional portion as conditional.
2023-05-02 16:49:54 -07:00
Joe Savona a1902b263d Rename OptionalCall -> Optional for clarity
The previous OptionalCall terminal and reactive value kinds are now used not 
just for optional calls, but for optional member expressions that appear within 
an optional call. This PR renames those data types to OptionalTerminal and 
OptionalExpression for clarity.
2023-05-02 16:31:31 -07:00
Joe Savona c46157b00b More accurately model OptionalMemberExpression
Extends the new modeling of the previous diff to OptionalMemberExpression. In an 
example such as `a?.b?.c`, we now model that not only is the `.c` conditional, 
but our control flow graph accurately reflects the fact that the `.c` is only 
evaluated if `a.b` exists. Previously we knew it was conditional but the CFG 
allowed a path from a being null through to evaluation of `.c`.
2023-05-02 15:44:06 -07:00
Joe Savona 73a203c199 More accurately model nested OptionalCallExpression
More accurately models nested OptionalCallExpression. Consider: 

```javascript 

a?.(b)?.(c) 

``` 

Our previous representation modeled it such that we treated the second function 
call as if it would be called regardless of whether `a` existed or not. We knew 
that the second call was conditional, so our test output was correct, but the 
control-flow graph didn't faithfully model the semantics. That bothered me. 

The new representation correctly models the control flow, and the fact that if 
`a` is null/undefined execution immediately aborts (not reaching the second call 
at all, nor the evaluation of its args), and evaluates the whole outer 
OptionalCallExpression to `undefined`. 

Note that nested optional member expressions still have the previous model — 
that's next to address.
2023-05-02 15:00:12 -07:00
Joe Savona 9a12e8ba58 Support OptionalCallExpression as LHS of LogicalExpression
A common idiom is to map over some possibly-missing list of items from a data 
payload and fall back to an empty array: 

```javascript 

const renderedItems = data?.items?.map(renderItem) ?? []; 

``` 

The way we were lowering OptionalCallExpression meant that in this case, we'd 
end up with an OptionalCallTerminal as the terminal of the logical expression's 
test block, which violates our internal invariant. Logical test blocks must end 
in a Branch! This PR fixes the immediate issue, which is that the callee - in 
this case `data?.items?.map` — was being lowered prior to the 
OptionalCallTerminal instead of inside its test block. Changing that fixes the 
shape of the IR and makes this example work. 

As part of investigating this I realized that the way I originally handled 
lowering of optional call isn't quite right. The difference isn't observable 
unless we did more sophisticated DCE but we don't correctly model the fact that 
if `data.items` is null that the `map()` call won't occur. That is technically 
fine bc we do model the fact that the `map()` call is conditional, and notably 
its arguments are only conditional dependencies. So it's good enough. But in a 
follow-up I'll change to model the fact that `data.items` is null, that the map 
call isn't reachable at all.
2023-05-02 15:00:11 -07:00
Andrew Clark fa4314841e Remove deprecated workflow key from Circle config (#26762)
This key was only valid during the 2.0 beta period so we can remove it.
2023-05-02 14:59:36 -04:00
Joe Savona 8292c65d84 Add missing traverseValue()
I don't think this one matters but just in case, we should always call 
traverseValue() when overriding visitValue().
2023-05-02 11:07:12 -07:00
Joe Savona dd6a9d6a03 Dont memoize hook calls in logical expressions
Fix the previous bug — this was a simple oversight, where FlattenScopesWithHooks 
overrode `visitValue()` but failed to call `traverseValue()`. This meant that 
when we reached compound expressions such as LogicalExpressions that we didn't 
traverse into their nested values, and didn't see the hooks hidden there.
2023-05-02 11:01:37 -07:00
Joe Savona bfb84eed3b Repro for memoized hook within logical expression
Repro of a bug in which we incorrect memoize hook calls that are inside logical 
expressions (though the bug could occur for ternaries, optional calls, and 
sequence expressions too).
2023-05-02 10:47:48 -07:00
Joe Savona c0b0119356 Start of architecture doc
This is an attempt to get down some of the principles and goals that we've had 
partially written down, partially just thoroughly discussed amongst the team. 
It's rough draft quality but better than nothing, and gives us someplace to add 
to.
2023-05-02 09:33:19 -07:00
Samuel Susla 5dd90c5623 Use content hash for react-native builds (#26734) 2023-05-02 12:04:02 +01:00