Compare commits

..

32 Commits

Author SHA1 Message Date
Joe Savona
1517c63a78 [compiler] Enable additional lints by default
Enable more validations to help catch bad patterns, but only in the linter. These rules are already enabled by default in the compiler _if_ violations could produce unsafe output.
2025-07-24 14:43:03 -07:00
Joe Savona
5fbe88ab40 [compiler] Validate against setState in all effect types 2025-07-24 14:43:03 -07:00
Sebastian Markbåge
4f34cc4a2e [Fiber] Don't throw away the Error object retaining the owner stack (#33976)
We currently throw away the Error once we've used to the owner stack of
a Fiber once. This maybe helps a bit with memory and redoing it but we
really don't expect most Fibers to hit this at all. It's not very hot.

If we throw away the Error, then we can't use native debugger protocols
to inspect the native stack. Instead, we'd have to maintain a url to
resource map indefinitely like what Chrome DevTools does to map a url to
a resource. Technically it's not even technically correct since the file
path might not be reversible and could in theory conflict.
2025-07-24 13:33:03 -04:00
Sebastian Markbåge
3d14fcf03f [Flight] Use about: protocol instead of rsc: protocol for fake evals (#33977)
Chrome DevTools Extensions has a silly problem where they block access
to load Resources from all protocols except [an allow
list](eb970fbc64/front_end/models/extensions/ExtensionServer.ts (L60)).

https://issues.chromium.org/issues/416196401

Even though these are `eval()` and not actually loaded from the network
they're blocked. They can really be any string. We just have to pick one
of:

```js
'http:', 'https:', 'file:', 'data:', 'chrome-extension:', 'about:'
```

That way React DevTools extensions can load this content to source map
them.

Webpack has the same issue with its `webpack://` and
`webpack-internal://` urls.
2025-07-24 11:07:11 -04:00
Sebastian Markbåge
edac0dded9 [DevTools] Add a Code Editor Sidebar Pane in the Chrome Sources Tab (#33968)
This adds a "Code Editor" pane for the Chrome extension in the bottom
right corner of the "Sources" panel. If you end up getting linked to the
"Sources" panel from stack traces in console, performance tab, stacks in
React Component tab like the one added in #33954 basically everywhere
there's a link to source code. Then going from there to open in a code
editor should be more convenient. This adds a button to open the current
file.

<img width="1387" height="389" alt="Screenshot 2025-07-22 at 10 22
19 PM"
src="https://github.com/user-attachments/assets/fe01f84c-83c2-4639-9b64-4af1a90c3f7d"
/>

This only makes sense in the extensions since in standalone it needs to
always open by default in an editor. Unfortunately Firefox doesn't
support extending the Sources panel.

Chrome is also a bit buggy where it doesn't send a selection update
event when you switch tabs in the Sources panel. Only when the actual
cursor position changes. This means that the link can be lagging behind
sometimes. We also have some general bugs where if React DevTools loses
connection it can break the UI which includes this pane too.

This has a small inline configuration too so that it's discoverable:

<img width="559" height="143" alt="Screenshot 2025-07-22 at 10 22 42 PM"
src="https://github.com/user-attachments/assets/1270bda8-ce10-4f9d-9fcb-080c0198366a"
/>

<img width="527" height="123" alt="Screenshot 2025-07-22 at 10 22 30 PM"
src="https://github.com/user-attachments/assets/45848c95-afd8-495f-a7cf-eb2f46e698f2"
/>

Since we can't add a separate link to open-in-editor or open-in-sources
everywhere I plan on adding an option to open in editor by default in a
follow up. That option needs to be even more discoverable.

I moved the configuration from the Components settings to the General
settings since this is now a much more general features for opening
links to resources in all types of panes.

<img width="673" height="311" alt="Screenshot 2025-07-22 at 10 22 57 PM"
src="https://github.com/user-attachments/assets/ea2c0871-942c-4b55-a362-025835d2c2bd"
/>
2025-07-23 10:28:11 -04:00
Sebastian Markbåge
3586a7f9e8 [DevTools] Allow file:/// urls to be opened in editor (#33965)
If a `file:///` path is specified as the url of a file, like after
source mapping into an ESM file, then we should be able to open it in a
code editor.
2025-07-23 10:21:50 -04:00
Sebastian "Sebbie" Silbermann
f6fb1a07a5 [Flight] Remove superfluous whitespace when console method is called with non-strings (#33953) 2025-07-23 10:07:37 +02:00
Sebastian Markbåge
7513996f20 [DevTools] Unify by using ReactFunctionLocation type instead of Source (#33955)
In RSC and other stacks now we use a lot of `ReactFunctionLocation` type
to represent the location of a function. I.e. the location of the
beginning of the function (the enclosing line/col) that is represented
by the "Source" of the function. This is also what the parent Component
Stacks represents.

As opposed to `ReactCallSite` which is what normal stack traces and
owner stacks represent. I.e. the line/column number of the callsite into
the next function.

We can start sharing more code by using the `ReactFunctionLocation` type
to represent the component source location and it also helps clarify
which ones are function locations and which ones are callsites as we
start adding more stack traces (e.g. for async debug info and owner
stack traces).
2025-07-22 10:53:08 -04:00
Sebastian Markbåge
bb4418d647 [DevTools] Linkify Source View (#33954)
This makes it so you can click the source location itself to view the
source. This is similar styling as the link to jump to function props
like events and actions. We're going to need a lot more linkifying to
jump to various source locations. Also, I always was trying to click
this file anyway.

Hover state:

<img width="485" height="382" alt="Screenshot 2025-07-21 at 4 36 10 PM"
src="https://github.com/user-attachments/assets/1f0f8f8c-6866-4e62-ab84-1fb5ba012986"
/>
2025-07-21 17:36:37 -04:00
Jordan Brown
074e92777c Change autodeps configuration (#33800) 2025-07-21 13:04:02 -07:00
Sebastian "Sebbie" Silbermann
ac7da9d46d [Flight] Make it more obvious what the short name in the I/O description represents (#33944) 2025-07-21 19:53:58 +02:00
Sebastian Markbåge
0dca9c2471 [Flight] Use the Promise of the first await even if that is cut off (#33948)
We need a "value" to represent the I/O that was loaded. We don't
normally actually use the Promise at the callsite that started the I/O
because that's usually deep inside internals. Instead we override the
value of the I/O entry with the Promise that was first awaited in user
space. This means that you could potentially have different values
depending on if multiple things await the same I/O. We just take one of
them. (Maybe we should actually just write the first user space awaited
Promise as the I/O entry? This might instead have other implications
like less deduping.)

When you pass a Promise forward, we may skip the awaits that happened in
earlier components because they're not part of the currently rendering
component. That's mainly for the stack and time stamps though. The value
is still probably conceptually the best value because it represents the
I/O value as far user space is concerned.

This writes the I/O early with the first await we find in user space
even if we're not going to use that particular await for the stack.
2025-07-21 13:22:10 -04:00
Sebastian Markbåge
b9af1404ea [Flight] Use the JSX as the await stack if an await is not available (#33947)
If you pass a promise to a client component to be rendered `<Client
promise={promise} />` then there's an internal await inside Flight.
There might also be user space awaits but those awaits may already have
happened before we render this component. Conceptually they were part of
the parent component and not this component. It's tricky to attribute
which await should be used for the stack in this case.

If we can't find an await we can use the JSX callsite as the stack
frame.

However, we don't want to do this for simple cases like if you return a
non-native Promise from a Server Component. Since that would now use the
stack of the thing that rendered the Server Component which is worse
than the stack of the I/O. To fix this, I update the
`debugOwner`/`debugTask`/`debugStack` when we start rendering inside the
Server Component. Conceptually these represent the "parent" component
and is used for errors referring to the parent like when we serialize
client component props the parent is the JSX of the client component.
However, when we're directly inside the Server Component we don't have a
callsite of the parent really. Conceptually it would be the return call
of the Server Component. This might negatively affect other types of
errors but I think this is ok since this feature mainly exists for the
case when you enter the child JSX.
2025-07-21 13:21:17 -04:00
Rubén Norte
e9638c33d7 Clean up feature flag to use lazy public instances in Fabric (#33943)
## Summary

We have thoroughly tested this flag in production and proved stability
and performance, so we can clean it up and "ship it".
2025-07-21 10:27:46 +01:00
Sebastian Markbåge
28d4bc496b [Flight] Make debug info and console log resolve in predictable order (#33665)
This resolves an outstanding issue where it was possible for debug info
and console logs to become out of order if they up blocked. E.g. by a
future reference or a client reference that hasn't loaded yet. Such as
if you console.log a client reference followed by one that doesn't. This
encodes the order similar to how the stream chunks work.

This also blocks the main chunk from resolving until the last debug info
has fully loaded, including future references and client references.
This also ensures that we could send some of that data in a different
stream, since then it can come out of order.
2025-07-19 20:13:26 -04:00
Jordan Brown
dffacc7b80 InferEffectDeps takes a React.AUTODEPS sigil (#33799)
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33799).
* #33800
* __->__ #33799
2025-07-17 05:31:52 -07:00
Sebastian Markbåge
da7487b681 [Flight] Skip the stack frame of built-in wrappers that create or await Promises (#33798)
We already do this with `"new Promise"` and `"Promise.then"`. There are
also many helpers that both create promises and awaits other promises
inside of it like `Promise.all`.

The way this is filtered is different from just filtering out all
anonymous stacks since they're used to determine where the boundary is
between ignore listed and user space.

Ideally we'd cover more wrappers that are internal to Promise libraries.
2025-07-16 15:57:22 -04:00
Ruslan Lesiutin
9fec565a9b fix: log renders from passive effects for only newly finished work (#33797)
This fixes displaying incorrect component render entries on a timeline,
when we are reconnecting passive effects.

### Before
<img width="2318" height="1127" alt="1"
src="https://github.com/user-attachments/assets/9b6b2824-d2de-43a3-8615-2c45d67c3668"
/>

The cloned nodes will persist original `actualStartTime`, when these
were first mounted. When we "replay", the end time will be "now" or
whatever the actual start time of the sibling. Depending on when this is
being recorded, the diff between end and start could be tens of seconds
and doesn't represent what React was doing.

We shouldn't log these entries at all.

### After
We are only logging newly finished renders, but could potentially loose
renders that never commit.
2025-07-16 18:09:35 +01:00
Jack Pope
996d0eb055 Allow runtime_build_and_test action to trigger manually (#33796) 2025-07-16 12:41:35 -04:00
Sebastian "Sebbie" Silbermann
d85ec5f5bd [Flight] Assume __turbopack_load_by_url__ returns a cached Promise (#33792) 2025-07-16 13:20:10 +02:00
Henry Q. Dineen
fe813143e2 [compiler] Check TSAsExpression and TSNonNullExpression reorderability (#33788)
## Summary

The `TSAsExpression` and `TSNonNullExpression` nodes are supported by
`lowerExpression()` but `isReorderableExpression()` does not check if
they can be reordered. This PR updates `isReorderableExpression()` to
handle these two node types by adding cases that fall through to the
existing `TypeCastExpression` case.

We ran `react-compiler-healthcheck` at scale on several of our repos and
found dozens of `` (BuildHIR::node.lowerReorderableExpression)
Expression type `TSAsExpression` cannot be safely reordered`` errors and
a handful for `TSNonNullExpression`.


## How did you test this change?

In this case I added two fixture tests
2025-07-15 11:50:20 -07:00
Sebastian Markbåge
2f0e7e570d [Flight] Don't block on debug channel if it's not wired up (#33757)
React Elements reference debug data (their stack and owner) in the debug
channel. If the debug channel isn't wired up this can block the client
from resolving.

We can infer that if there's no debug channel wired up and the reference
wasn't emitted before the element, then it's probably because it's in
the debug channel. So we can skip it.

This should also apply to debug chunks but they're not yet blocking
until #33665 lands.
2025-07-15 11:45:34 -04:00
Sebastian "Sebbie" Silbermann
56d0ddae18 [Flight] Switch to __turbopack_load_by_url__ (#33791) 2025-07-15 16:55:31 +02:00
Sebastian "Sebbie" Silbermann
345ca24f13 [Flight] Remove unused fork configs (#33785) 2025-07-15 07:23:00 +02:00
Jordan Brown
97cdd5d3c3 [eslint] Do not allow useEffectEvent fns to be called in arbitrary closures (#33544)
Summary:

useEffectEvent is meant to be used specifically in combination with
useEffect, and using
the feature in arbitrary closures can lead to surprising reactivity
semantics. In order to
minimize risk in the experimental rollout, we are going to restrict its
usage to being
called directly inside an effect or another useEffectEvent, effectively
enforcing the function
coloring statically. Without an effect system this is the best we can
do.
2025-07-10 16:51:12 -04:00
Sebastian Markbåge
eb7f8b42c9 [Flight] Add Separate Outgoing Debug Channel (#33754)
This lets us pass a writable on the server side and readable on the
client side to send debug info through a separate channel so that it
doesn't interfere with the main payload as much. The main payload refers
to chunks defined in the debug info which means it's still blocked on it
though. This ensures that the debug data has loaded by the time the
value is rendered so that the next step can forward the data.

This will be a bit fragile to race conditions until #33665 lands.
Another follow up needed is the ability to skip the debug channel on the
receiving side. Right now it'll block forever if you don't provide one
since we're blocking on the debug data.
2025-07-10 16:22:44 -04:00
Sebastian Markbåge
eed2560762 [Flight] Treat empty message as a close signal (#33756)
We typically treat an empty message as closing the debug channel stream
but for the Noop renderer we don't use an intermediate stream but just
pass the message through.


bbc13fa17b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js (L59-L60)

For that simple case we should just treat it as a close without an
intermediate stream.
2025-07-10 16:16:57 -04:00
Josh Story
463b808176 [Fizz] Reset the segent id assignment when postponing the root (#33755)
When postponing the root we encode the segment Id into the postponed
state but we should really be reseting it to zero so we can restart the
counter from the beginning when the resume is actually just a re-render.

This also no longer assigns the root segment id based on the postponed
state when resuming the root for the same reason. In the future we may
use the embedded replay segment id if we implement resuming the root
without re-rendering everything but that is not yet implemented or
planned.
2025-07-10 12:12:09 -07:00
Joseph Savona
96c61b7f1f [compiler] Add CompilerError.UnsupportedJS variant (#33750)
We use this variant for syntax we intentionally don't support: with
statements, eval, and inline class declarations.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33750).
* #33753
* #33752
* #33751
* __->__ #33750
* #33748
2025-07-09 22:24:20 -07:00
Joseph Savona
0bfa404bac [compiler] More precise errors for invalid import/export/namespace statements (#33748)
import, export, and TS namespace statements can only be used at the
top-level of a module, which is enforced by parsers already. Here we add
a backup validation of that. As of this PR, we now have only major
statement type (class declarations) listed as a todo.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33748).
* #33753
* #33752
* #33751
* #33750
* __->__ #33748
2025-07-09 22:24:07 -07:00
Joseph Savona
81e1ee7476 [compiler] Support inline enums (flow/ts), type declarations (#33747)
Supports inline enum declarations in both Flow and TS by treating the
node as pass-through (enums can't capture values mutably). Related, this
PR extends the set of type-related declarations that we ignore.
Previously we threw a todo for things like DeclareClass or
DeclareVariable, but these are type related and can simply be dropped
just like we dropped TypeAlias.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33747).
* #33753
* #33752
* #33751
* #33750
* #33748
* __->__ #33747
2025-07-09 22:21:02 -07:00
Joseph Savona
4a3ff8eed6 [compiler] Errors for eval(), with statments, class declarations (#33746)
* Error for `eval()`
* More specific error message for `with (expr) { ... }` syntax
* More specific error message for class declarations

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33746).
* #33752
* #33751
* #33750
* #33748
* #33747
* __->__ #33746
2025-07-09 22:18:30 -07:00
221 changed files with 4012 additions and 1750 deletions

View File

@@ -474,7 +474,7 @@ module.exports = {
{
files: ['packages/react-server-dom-turbopack/**/*.js'],
globals: {
__turbopack_load__: 'readonly',
__turbopack_load_by_url__: 'readonly',
__turbopack_require__: 'readonly',
},
},

View File

@@ -6,6 +6,12 @@ on:
pull_request:
paths-ignore:
- compiler/**
workflow_dispatch:
inputs:
commit_sha:
required: false
type: string
default: ''
permissions: {}
@@ -28,7 +34,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- name: Check cache hit
uses: actions/cache/restore@v4
id: node_modules
@@ -69,7 +75,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- name: Check cache hit
uses: actions/cache/restore@v4
id: node_modules
@@ -117,7 +123,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/github-script@v7
id: set-matrix
with:
@@ -136,7 +142,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -166,7 +172,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -198,7 +204,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -254,7 +260,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -325,7 +331,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -420,7 +426,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -465,7 +471,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -493,7 +499,7 @@ jobs:
merge-multiple: true
- name: Display structure of build
run: ls -R build
- run: echo ${{ github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA
- run: echo ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA
- name: Scrape warning messages
run: |
mkdir -p ./build/__test_utils__
@@ -530,7 +536,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -570,7 +576,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -607,7 +613,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -648,7 +654,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -722,7 +728,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -779,7 +785,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -824,7 +830,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -873,7 +879,7 @@ jobs:
node ./scripts/print-warnings/print-warnings.js > build/__test_utils__/ReactAllWarnings.js
- name: Display structure of build for PR
run: ls -R build
- run: echo ${{ github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA
- run: echo ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA
- run: node ./scripts/tasks/danger
- name: Archive sizebot results
uses: actions/upload-artifact@v4

View File

@@ -15,6 +15,11 @@ export enum ErrorSeverity {
* misunderstanding on the users part.
*/
InvalidJS = 'InvalidJS',
/**
* JS syntax that is not supported and which we do not plan to support. Developers should
* rewrite to use supported forms.
*/
UnsupportedJS = 'UnsupportedJS',
/**
* Code that breaks the rules of React.
*/
@@ -241,12 +246,16 @@ export class CompilerError extends Error {
case ErrorSeverity.InvalidJS:
case ErrorSeverity.InvalidReact:
case ErrorSeverity.InvalidConfig:
case ErrorSeverity.UnsupportedJS: {
return true;
}
case ErrorSeverity.CannotPreserveMemoization:
case ErrorSeverity.Todo:
case ErrorSeverity.Todo: {
return false;
default:
}
default: {
assertExhaustive(detail.severity, 'Unhandled error severity');
}
}
});
}

View File

@@ -94,7 +94,7 @@ import {validateLocalsNotReassignedAfterRender} from '../Validation/ValidateLoca
import {outlineFunctions} from '../Optimization/OutlineFunctions';
import {propagatePhiTypes} from '../TypeInference/PropagatePhiTypes';
import {lowerContextAccess} from '../Optimization/LowerContextAccess';
import {validateNoSetStateInPassiveEffects} from '../Validation/ValidateNoSetStateInPassiveEffects';
import {validateNoSetStateInEffects} from '../Validation/ValidateNoSetStateInEffects';
import {validateNoJSXInTryStatement} from '../Validation/ValidateNoJSXInTryStatement';
import {propagateScopeDependenciesHIR} from '../HIR/PropagateScopeDependenciesHIR';
import {outlineJSX} from '../Optimization/OutlineJsx';
@@ -292,8 +292,8 @@ function runWithEnvironment(
validateNoSetStateInRender(hir).unwrap();
}
if (env.config.validateNoSetStateInPassiveEffects) {
env.logErrors(validateNoSetStateInPassiveEffects(hir));
if (env.config.validateNoSetStateInEffects) {
env.logErrors(validateNoSetStateInEffects(hir));
}
if (env.config.validateNoJSXInTryStatements) {

View File

@@ -35,8 +35,41 @@ function throwInvalidReact(
});
CompilerError.throw(detail);
}
function isAutodepsSigil(
arg: NodePath<t.ArgumentPlaceholder | t.SpreadElement | t.Expression>,
): boolean {
// Check for AUTODEPS identifier imported from React
if (arg.isIdentifier() && arg.node.name === 'AUTODEPS') {
const binding = arg.scope.getBinding(arg.node.name);
if (binding && binding.path.isImportSpecifier()) {
const importSpecifier = binding.path.node as t.ImportSpecifier;
if (importSpecifier.imported.type === 'Identifier') {
return (importSpecifier.imported as t.Identifier).name === 'AUTODEPS';
}
}
return false;
}
// Check for React.AUTODEPS member expression
if (arg.isMemberExpression() && !arg.node.computed) {
const object = arg.get('object');
const property = arg.get('property');
if (
object.isIdentifier() &&
object.node.name === 'React' &&
property.isIdentifier() &&
property.node.name === 'AUTODEPS'
) {
return true;
}
}
return false;
}
function assertValidEffectImportReference(
numArgs: number,
autodepsIndex: number,
paths: Array<NodePath<t.Node>>,
context: TraversalState,
): void {
@@ -49,11 +82,10 @@ function assertValidEffectImportReference(
maybeCalleeLoc != null &&
context.inferredEffectLocations.has(maybeCalleeLoc);
/**
* Only error on untransformed references of the form `useMyEffect(...)`
* or `moduleNamespace.useMyEffect(...)`, with matching argument counts.
* TODO: do we also want a mode to also hard error on non-call references?
* Error on effect calls that still have AUTODEPS in their args
*/
if (args.length === numArgs && !hasInferredEffect) {
const hasAutodepsArg = args.some(isAutodepsSigil);
if (hasAutodepsArg && !hasInferredEffect) {
const maybeErrorDiagnostic = matchCompilerDiagnostic(
path,
context.transformErrors,
@@ -128,12 +160,12 @@ export default function validateNoUntransformedReferences(
if (env.inferEffectDependencies) {
for (const {
function: {source, importSpecifierName},
numRequiredArgs,
autodepsIndex,
} of env.inferEffectDependencies) {
const module = getOrInsertWith(moduleLoadChecks, source, () => new Map());
module.set(
importSpecifierName,
assertValidEffectImportReference.bind(null, numRequiredArgs),
assertValidEffectImportReference.bind(null, autodepsIndex),
);
}
}

View File

@@ -1359,7 +1359,7 @@ function lowerStatement(
builder.errors.push({
reason: `JavaScript 'with' syntax is not supported`,
description: `'with' syntax is considered deprecated and removed from JavaScript standards, consider alternatives`,
severity: ErrorSeverity.InvalidJS,
severity: ErrorSeverity.UnsupportedJS,
loc: stmtPath.node.loc ?? null,
suggestions: null,
});
@@ -1371,13 +1371,15 @@ function lowerStatement(
return;
}
case 'ClassDeclaration': {
/*
* We can in theory support nested classes, similarly to functions where we track values
* captured by the class and consider mutations of the instances to mutate the class itself
/**
* In theory we could support inline class declarations, but this is rare enough in practice
* and complex enough to support that we don't anticipate supporting anytime soon. Developers
* are encouraged to lift classes out of component/hook declarations.
*/
builder.errors.push({
reason: `Support nested class declarations`,
severity: ErrorSeverity.Todo,
reason: 'Inline `class` declarations are not supported',
description: `Move class declarations outside of components/hooks`,
severity: ErrorSeverity.UnsupportedJS,
loc: stmtPath.node.loc ?? null,
suggestions: null,
});
@@ -1388,10 +1390,48 @@ function lowerStatement(
});
return;
}
case 'TypeAlias':
case 'TSInterfaceDeclaration':
case 'TSTypeAliasDeclaration': {
// We do not preserve type annotations/syntax through transformation
case 'EnumDeclaration':
case 'TSEnumDeclaration': {
lowerValueToTemporary(builder, {
kind: 'UnsupportedNode',
loc: stmtPath.node.loc ?? GeneratedSource,
node: stmtPath.node,
});
return;
}
case 'ExportAllDeclaration':
case 'ExportDefaultDeclaration':
case 'ExportNamedDeclaration':
case 'ImportDeclaration':
case 'TSExportAssignment':
case 'TSImportEqualsDeclaration': {
builder.errors.push({
reason:
'JavaScript `import` and `export` statements may only appear at the top level of a module',
severity: ErrorSeverity.InvalidJS,
loc: stmtPath.node.loc ?? null,
suggestions: null,
});
lowerValueToTemporary(builder, {
kind: 'UnsupportedNode',
loc: stmtPath.node.loc ?? GeneratedSource,
node: stmtPath.node,
});
return;
}
case 'TSNamespaceExportDeclaration': {
builder.errors.push({
reason:
'TypeScript `namespace` statements may only appear at the top level of a module',
severity: ErrorSeverity.InvalidJS,
loc: stmtPath.node.loc ?? null,
suggestions: null,
});
lowerValueToTemporary(builder, {
kind: 'UnsupportedNode',
loc: stmtPath.node.loc ?? GeneratedSource,
node: stmtPath.node,
});
return;
}
case 'DeclareClass':
@@ -1404,30 +1444,14 @@ function lowerStatement(
case 'DeclareOpaqueType':
case 'DeclareTypeAlias':
case 'DeclareVariable':
case 'EnumDeclaration':
case 'ExportAllDeclaration':
case 'ExportDefaultDeclaration':
case 'ExportNamedDeclaration':
case 'ImportDeclaration':
case 'InterfaceDeclaration':
case 'OpaqueType':
case 'TSDeclareFunction':
case 'TSEnumDeclaration':
case 'TSExportAssignment':
case 'TSImportEqualsDeclaration':
case 'TSInterfaceDeclaration':
case 'TSModuleDeclaration':
case 'TSNamespaceExportDeclaration': {
builder.errors.push({
reason: `(BuildHIR::lowerStatement) Handle ${stmtPath.type} statements`,
severity: ErrorSeverity.Todo,
loc: stmtPath.node.loc ?? null,
suggestions: null,
});
lowerValueToTemporary(builder, {
kind: 'UnsupportedNode',
loc: stmtPath.node.loc ?? GeneratedSource,
node: stmtPath.node,
});
case 'TSTypeAliasDeclaration':
case 'TypeAlias': {
// We do not preserve type annotations/syntax through transformation
return;
}
default: {
@@ -2977,6 +3001,8 @@ function isReorderableExpression(
}
}
}
case 'TSAsExpression':
case 'TSNonNullExpression':
case 'TypeCastExpression': {
return isReorderableExpression(
builder,
@@ -3538,7 +3564,7 @@ function lowerIdentifier(
reason: `The 'eval' function is not supported`,
description:
'Eval is an anti-pattern in JavaScript, and the code executed cannot be evaluated by React Compiler',
severity: ErrorSeverity.InvalidJS,
severity: ErrorSeverity.UnsupportedJS,
loc: exprPath.node.loc ?? null,
suggestions: null,
});

View File

@@ -265,21 +265,19 @@ export const EnvironmentConfigSchema = z.object({
* {
* module: 'react',
* imported: 'useEffect',
* numRequiredArgs: 1,
* autodepsIndex: 1,
* },{
* module: 'MyExperimentalEffectHooks',
* imported: 'useExperimentalEffect',
* numRequiredArgs: 2,
* autodepsIndex: 2,
* },
* ]
* would insert dependencies for calls of `useEffect` imported from `react` and calls of
* useExperimentalEffect` from `MyExperimentalEffectHooks`.
*
* `numRequiredArgs` tells the compiler the amount of arguments required to append a dependency
* array to the end of the call. With the configuration above, we'd insert dependencies for
* `useEffect` if it is only given a single argument and it would be appended to the argument list.
*
* numRequiredArgs must always be greater than 0, otherwise there is no function to analyze for dependencies
* `autodepsIndex` tells the compiler which index we expect the AUTODEPS to appear in.
* With the configuration above, we'd insert dependencies for `useEffect` if it has two
* arguments, and the second is AUTODEPS.
*
* Still experimental.
*/
@@ -288,7 +286,7 @@ export const EnvironmentConfigSchema = z.object({
z.array(
z.object({
function: ExternalFunctionSchema,
numRequiredArgs: z.number().min(1, 'numRequiredArgs must be > 0'),
autodepsIndex: z.number().min(1, 'autodepsIndex must be > 0'),
}),
),
)
@@ -320,10 +318,10 @@ export const EnvironmentConfigSchema = z.object({
validateNoSetStateInRender: z.boolean().default(true),
/**
* Validates that setState is not called directly within a passive effect (useEffect).
* Validates that setState is not called synchronously within an effect (useEffect and friends).
* Scheduling a setState (with an event listener, subscription, etc) is valid.
*/
validateNoSetStateInPassiveEffects: z.boolean().default(false),
validateNoSetStateInEffects: z.boolean().default(false),
/**
* Validates against creating JSX within a try block and recommends using an error boundary

View File

@@ -9,6 +9,7 @@ import {Effect, ValueKind, ValueReason} from './HIR';
import {
BUILTIN_SHAPES,
BuiltInArrayId,
BuiltInAutodepsId,
BuiltInFireFunctionId,
BuiltInFireId,
BuiltInMapId,
@@ -780,6 +781,7 @@ const REACT_APIS: Array<[string, BuiltInType]> = [
BuiltInUseEffectEventId,
),
],
['AUTODEPS', addObject(DEFAULT_SHAPES, BuiltInAutodepsId, [])],
];
TYPED_GLOBALS.push(

View File

@@ -384,6 +384,7 @@ export const BuiltInFireId = 'BuiltInFire';
export const BuiltInFireFunctionId = 'BuiltInFireFunction';
export const BuiltInUseEffectEventId = 'BuiltInUseEffectEvent';
export const BuiltinEffectEventId = 'BuiltInEffectEventFunction';
export const BuiltInAutodepsId = 'BuiltInAutoDepsId';
// See getReanimatedModuleType() in Globals.ts — this is part of supporting Reanimated's ref-like types
export const ReanimatedSharedValueId = 'ReanimatedSharedValueId';

View File

@@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
import generate from '@babel/generator';
import {CompilerError} from '../CompilerError';
import {printReactiveScopeSummary} from '../ReactiveScopes/PrintReactiveFunction';
import DisjointSet from '../Utils/DisjointSet';
@@ -466,7 +465,7 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
break;
}
case 'UnsupportedNode': {
value = `UnsupportedNode(${generate(instrValue.node).code})`;
value = `UnsupportedNode ${instrValue.node.type}`;
break;
}
case 'LoadLocal': {

View File

@@ -57,6 +57,8 @@ import {
} from '../HIR/visitors';
import {empty} from '../Utils/Stack';
import {getOrInsertWith} from '../Utils/utils';
import {deadCodeElimination} from '../Optimization';
import {BuiltInAutodepsId} from '../HIR/ObjectShape';
/**
* Infers reactive dependencies captured by useEffect lambdas and adds them as
@@ -77,7 +79,7 @@ export function inferEffectDependencies(fn: HIRFunction): void {
);
moduleTargets.set(
effectTarget.function.importSpecifierName,
effectTarget.numRequiredArgs,
effectTarget.autodepsIndex,
);
}
const autodepFnLoads = new Map<IdentifierId, number>();
@@ -135,7 +137,6 @@ export function inferEffectDependencies(fn: HIRFunction): void {
}
} else if (value.kind === 'LoadGlobal') {
loadGlobals.add(lvalue.identifier.id);
/*
* TODO: Handle properties on default exports, like
* import React from 'react';
@@ -169,8 +170,22 @@ export function inferEffectDependencies(fn: HIRFunction): void {
) {
const callee =
value.kind === 'CallExpression' ? value.callee : value.property;
const autodepsArgIndex = value.args.findIndex(
arg =>
arg.kind === 'Identifier' &&
arg.identifier.type.kind === 'Object' &&
arg.identifier.type.shapeId === BuiltInAutodepsId,
);
const autodepsArgExpectedIndex = autodepFnLoads.get(
callee.identifier.id,
);
if (
value.args.length === autodepFnLoads.get(callee.identifier.id) &&
value.args.length > 0 &&
autodepsArgExpectedIndex != null &&
autodepsArgIndex === autodepsArgExpectedIndex &&
autodepFnLoads.has(callee.identifier.id) &&
value.args[0].kind === 'Identifier'
) {
// We have a useEffect call with no deps array, so we need to infer the deps
@@ -260,7 +275,10 @@ export function inferEffectDependencies(fn: HIRFunction): void {
effects: null,
},
});
value.args.push({...depsPlace, effect: Effect.Freeze});
value.args[autodepsArgIndex] = {
...depsPlace,
effect: Effect.Freeze,
};
fn.env.inferredEffectLocations.add(callee.loc);
} else if (loadGlobals.has(value.args[0].identifier.id)) {
// Global functions have no reactive dependencies, so we can insert an empty array
@@ -275,7 +293,10 @@ export function inferEffectDependencies(fn: HIRFunction): void {
effects: null,
},
});
value.args.push({...depsPlace, effect: Effect.Freeze});
value.args[autodepsArgIndex] = {
...depsPlace,
effect: Effect.Freeze,
};
fn.env.inferredEffectLocations.add(callee.loc);
}
} else if (
@@ -323,6 +344,7 @@ export function inferEffectDependencies(fn: HIRFunction): void {
// Renumber instructions and fix scope ranges
markInstructionIds(fn.body);
fixScopeAndIdentifierRanges(fn.body);
deadCodeElimination(fn);
fn.env.hasInferredEffect = true;
}
@@ -408,6 +430,7 @@ function rewriteSplices(
rewriteBlocks.push(currBlock);
let cursor = 0;
for (const rewrite of splices) {
while (originalInstrs[cursor].id < rewrite.location) {
CompilerError.invariant(
@@ -429,7 +452,7 @@ function rewriteSplices(
if (rewrite.kind === 'instr') {
currBlock.instructions.push(rewrite.value);
} else {
} else if (rewrite.kind === 'block') {
const {entry, blocks} = rewrite.value;
const entryBlock = blocks.get(entry)!;
// splice in all instructions from the entry block

View File

@@ -829,12 +829,14 @@ class CollectDependenciesVisitor extends ReactiveFunctionVisitor<
};
}
case 'UnsupportedNode': {
CompilerError.invariant(false, {
reason: `Unexpected unsupported node`,
description: null,
loc: value.loc,
suggestions: null,
});
const lvalues = [];
if (lvalue !== null) {
lvalues.push({place: lvalue, level: MemoizationLevel.Never});
}
return {
lvalues,
rvalues: [],
};
}
default: {
assertExhaustive(

View File

@@ -75,21 +75,21 @@ const testComplexConfigDefaults: PartialEnvironmentConfig = {
source: 'react',
importSpecifierName: 'useEffect',
},
numRequiredArgs: 1,
autodepsIndex: 1,
},
{
function: {
source: 'shared-runtime',
importSpecifierName: 'useSpecialEffect',
},
numRequiredArgs: 2,
autodepsIndex: 2,
},
{
function: {
source: 'useEffectWrapper',
importSpecifierName: 'default',
},
numRequiredArgs: 1,
autodepsIndex: 1,
},
],
};

View File

@@ -11,20 +11,22 @@ import {
IdentifierId,
isSetStateType,
isUseEffectHookType,
isUseInsertionEffectHookType,
isUseLayoutEffectHookType,
Place,
} from '../HIR';
import {eachInstructionValueOperand} from '../HIR/visitors';
import {Result} from '../Utils/Result';
/**
* Validates against calling setState in the body of a *passive* effect (useEffect),
* Validates against calling setState in the body of an effect (useEffect and friends),
* while allowing calling setState in callbacks scheduled by the effect.
*
* Calling setState during execution of a useEffect triggers a re-render, which is
* often bad for performance and frequently has more efficient and straightforward
* alternatives. See https://react.dev/learn/you-might-not-need-an-effect for examples.
*/
export function validateNoSetStateInPassiveEffects(
export function validateNoSetStateInEffects(
fn: HIRFunction,
): Result<void, CompilerError> {
const setStateFunctions: Map<IdentifierId, Place> = new Map();
@@ -79,7 +81,11 @@ export function validateNoSetStateInPassiveEffects(
instr.value.kind === 'MethodCall'
? instr.value.receiver
: instr.value.callee;
if (isUseEffectHookType(callee.identifier)) {
if (
isUseEffectHookType(callee.identifier) ||
isUseLayoutEffectHookType(callee.identifier) ||
isUseInsertionEffectHookType(callee.identifier)
) {
const arg = instr.value.args[0];
if (arg !== undefined && arg.kind === 'Identifier') {
const setState = setStateFunctions.get(arg.identifier.id);

View File

@@ -33,12 +33,12 @@ describe('parseConfigPragma()', () => {
source: 'react',
importSpecifierName: 'useEffect',
},
numRequiredArgs: 0,
autodepsIndex: 0,
},
],
} as any);
}).toThrowErrorMatchingInlineSnapshot(
`"InvalidConfig: Could not validate environment config. Update React Compiler config to fix the error. Validation error: numRequiredArgs must be > 0 at "inferEffectDependencies[0].numRequiredArgs""`,
`"InvalidConfig: Could not validate environment config. Update React Compiler config to fix the error. Validation error: autodepsIndex must be > 0 at "inferEffectDependencies[0].autodepsIndex""`,
);
});

View File

@@ -15,7 +15,7 @@ function Component(props) {
```
1 | function Component(props) {
> 2 | eval('props.x = true');
| ^^^^ InvalidJS: The 'eval' function is not supported. Eval is an anti-pattern in JavaScript, and the code executed cannot be evaluated by React Compiler (2:2)
| ^^^^ UnsupportedJS: The 'eval' function is not supported. Eval is an anti-pattern in JavaScript, and the code executed cannot be evaluated by React Compiler (2:2)
3 | return <div />;
4 | }
5 |

View File

@@ -84,7 +84,7 @@ let moduleLocal = false;
> 3 | var x = [];
| ^^^^^^^^^^^ Todo: (BuildHIR::lowerStatement) Handle var kinds in VariableDeclaration (3:3)
Todo: Support nested class declarations (5:10)
UnsupportedJS: Inline `class` declarations are not supported. Move class declarations outside of components/hooks (5:10)
Todo: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement (20:22)

View File

@@ -0,0 +1,60 @@
## Input
```javascript
// @flow
function Component(props) {
enum Bool {
True = 'true',
False = 'false',
}
let bool: Bool = Bool.False;
if (props.value) {
bool = Bool.True;
}
return <div>{bool}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{value: true}],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
function Component(props) {
const $ = _c(2);
enum Bool {
True = "true",
False = "false",
}
let bool = Bool.False;
if (props.value) {
bool = Bool.True;
}
let t0;
if ($[0] !== bool) {
t0 = <div>{bool}</div>;
$[0] = bool;
$[1] = t0;
} else {
t0 = $[1];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ value: true }],
};
```
### Eval output
(kind: exception) Bool is not defined

View File

@@ -0,0 +1,18 @@
// @flow
function Component(props) {
enum Bool {
True = 'true',
False = 'false',
}
let bool: Bool = Bool.False;
if (props.value) {
bool = Bool.True;
}
return <div>{bool}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{value: true}],
};

View File

@@ -3,13 +3,13 @@
```javascript
// @dynamicGating:{"source":"shared-runtime"} @panicThreshold:"none" @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function ReactiveVariable({propVal}) {
'use memo if(invalid identifier)';
const arr = [propVal];
useEffect(() => print(arr));
useEffect(() => print(arr), AUTODEPS);
}
export const FIXTURE_ENTRYPOINT = {
@@ -25,8 +25,8 @@ export const FIXTURE_ENTRYPOINT = {
```
6 | 'use memo if(invalid identifier)';
7 | const arr = [propVal];
> 8 | useEffect(() => print(arr));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (8:8)
> 8 | useEffect(() => print(arr), AUTODEPS);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (8:8)
9 | }
10 |
11 | export const FIXTURE_ENTRYPOINT = {

View File

@@ -1,11 +1,11 @@
// @dynamicGating:{"source":"shared-runtime"} @panicThreshold:"none" @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function ReactiveVariable({propVal}) {
'use memo if(invalid identifier)';
const arr = [propVal];
useEffect(() => print(arr));
useEffect(() => print(arr), AUTODEPS);
}
export const FIXTURE_ENTRYPOINT = {

View File

@@ -4,9 +4,10 @@
```javascript
// @inferEffectDependencies @compilationMode:"infer" @panicThreshold:"none"
import useMyEffect from 'useEffectWrapper';
import {AUTODEPS} from 'react';
function nonReactFn(arg) {
useMyEffect(() => [1, 2, arg]);
useMyEffect(() => [1, 2, arg], AUTODEPS);
}
```
@@ -15,12 +16,12 @@ function nonReactFn(arg) {
## Error
```
3 |
4 | function nonReactFn(arg) {
> 5 | useMyEffect(() => [1, 2, arg]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (5:5)
6 | }
7 |
4 |
5 | function nonReactFn(arg) {
> 6 | useMyEffect(() => [1, 2, arg], AUTODEPS);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:6)
7 | }
8 |
```

View File

@@ -1,6 +1,7 @@
// @inferEffectDependencies @compilationMode:"infer" @panicThreshold:"none"
import useMyEffect from 'useEffectWrapper';
import {AUTODEPS} from 'react';
function nonReactFn(arg) {
useMyEffect(() => [1, 2, arg]);
useMyEffect(() => [1, 2, arg], AUTODEPS);
}

View File

@@ -3,10 +3,10 @@
```javascript
// @inferEffectDependencies @compilationMode:"infer" @panicThreshold:"none"
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
function nonReactFn(arg) {
useEffect(() => [1, 2, arg]);
useEffect(() => [1, 2, arg], AUTODEPS);
}
```
@@ -17,8 +17,8 @@ function nonReactFn(arg) {
```
3 |
4 | function nonReactFn(arg) {
> 5 | useEffect(() => [1, 2, arg]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (5:5)
> 5 | useEffect(() => [1, 2, arg], AUTODEPS);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (5:5)
6 | }
7 |
```

View File

@@ -1,6 +1,6 @@
// @inferEffectDependencies @compilationMode:"infer" @panicThreshold:"none"
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
function nonReactFn(arg) {
useEffect(() => [1, 2, arg]);
useEffect(() => [1, 2, arg], AUTODEPS);
}

View File

@@ -3,7 +3,7 @@
```javascript
// @inferEffectDependencies @panicThreshold:"none"
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
/**
* Error on non-inlined effect functions:
@@ -21,7 +21,7 @@ function Component({foo}) {
}
// No inferred dep array, the argument is not a lambda
useEffect(f);
useEffect(f, AUTODEPS);
}
```
@@ -32,8 +32,8 @@ function Component({foo}) {
```
18 |
19 | // No inferred dep array, the argument is not a lambda
> 20 | useEffect(f);
| ^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (20:20)
> 20 | useEffect(f, AUTODEPS);
| ^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (20:20)
21 | }
22 |
```

View File

@@ -1,5 +1,5 @@
// @inferEffectDependencies @panicThreshold:"none"
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
/**
* Error on non-inlined effect functions:
@@ -17,5 +17,5 @@ function Component({foo}) {
}
// No inferred dep array, the argument is not a lambda
useEffect(f);
useEffect(f, AUTODEPS);
}

View File

@@ -5,6 +5,7 @@
// @dynamicGating:{"source":"shared-runtime"} @inferEffectDependencies @panicThreshold:"none"
import useEffectWrapper from 'useEffectWrapper';
import {AUTODEPS} from 'react';
/**
* TODO: run the non-forget enabled version through the effect inference
@@ -13,7 +14,7 @@ import useEffectWrapper from 'useEffectWrapper';
function Component({foo}) {
'use memo if(getTrue)';
const arr = [];
useEffectWrapper(() => arr.push(foo));
useEffectWrapper(() => arr.push(foo), AUTODEPS);
arr.push(2);
return arr;
}
@@ -30,13 +31,13 @@ export const FIXTURE_ENTRYPOINT = {
## Error
```
10 | 'use memo if(getTrue)';
11 | const arr = [];
> 12 | useEffectWrapper(() => arr.push(foo));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (12:12)
13 | arr.push(2);
14 | return arr;
15 | }
11 | 'use memo if(getTrue)';
12 | const arr = [];
> 13 | useEffectWrapper(() => arr.push(foo), AUTODEPS);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (13:13)
14 | arr.push(2);
15 | return arr;
16 | }
```

View File

@@ -1,6 +1,7 @@
// @dynamicGating:{"source":"shared-runtime"} @inferEffectDependencies @panicThreshold:"none"
import useEffectWrapper from 'useEffectWrapper';
import {AUTODEPS} from 'react';
/**
* TODO: run the non-forget enabled version through the effect inference
@@ -9,7 +10,7 @@ import useEffectWrapper from 'useEffectWrapper';
function Component({foo}) {
'use memo if(getTrue)';
const arr = [];
useEffectWrapper(() => arr.push(foo));
useEffectWrapper(() => arr.push(foo), AUTODEPS);
arr.push(2);
return arr;
}

View File

@@ -4,6 +4,7 @@
```javascript
// @gating @inferEffectDependencies @panicThreshold:"none"
import useEffectWrapper from 'useEffectWrapper';
import {AUTODEPS} from 'react';
/**
* TODO: run the non-forget enabled version through the effect inference
@@ -11,7 +12,7 @@ import useEffectWrapper from 'useEffectWrapper';
*/
function Component({foo}) {
const arr = [];
useEffectWrapper(() => arr.push(foo));
useEffectWrapper(() => arr.push(foo), AUTODEPS);
arr.push(2);
return arr;
}
@@ -28,13 +29,13 @@ export const FIXTURE_ENTRYPOINT = {
## Error
```
8 | function Component({foo}) {
9 | const arr = [];
> 10 | useEffectWrapper(() => arr.push(foo));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (10:10)
11 | arr.push(2);
12 | return arr;
13 | }
9 | function Component({foo}) {
10 | const arr = [];
> 11 | useEffectWrapper(() => arr.push(foo), AUTODEPS);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (11:11)
12 | arr.push(2);
13 | return arr;
14 | }
```

View File

@@ -1,5 +1,6 @@
// @gating @inferEffectDependencies @panicThreshold:"none"
import useEffectWrapper from 'useEffectWrapper';
import {AUTODEPS} from 'react';
/**
* TODO: run the non-forget enabled version through the effect inference
@@ -7,7 +8,7 @@ import useEffectWrapper from 'useEffectWrapper';
*/
function Component({foo}) {
const arr = [];
useEffectWrapper(() => arr.push(foo));
useEffectWrapper(() => arr.push(foo), AUTODEPS);
arr.push(2);
return arr;
}

View File

@@ -7,7 +7,7 @@ import React from 'react';
function NonReactiveDepInEffect() {
const obj = makeObject_Primitives();
React.useEffect(() => print(obj));
React.useEffect(() => print(obj), React.AUTODEPS);
}
```
@@ -18,8 +18,8 @@ function NonReactiveDepInEffect() {
```
4 | function NonReactiveDepInEffect() {
5 | const obj = makeObject_Primitives();
> 6 | React.useEffect(() => print(obj));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:6)
> 6 | React.useEffect(() => print(obj), React.AUTODEPS);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:6)
7 | }
8 |
```

View File

@@ -3,5 +3,5 @@ import React from 'react';
function NonReactiveDepInEffect() {
const obj = makeObject_Primitives();
React.useEffect(() => print(obj));
React.useEffect(() => print(obj), React.AUTODEPS);
}

View File

@@ -4,6 +4,7 @@
```javascript
// @inferEffectDependencies @panicThreshold:"none"
import {useSpecialEffect} from 'shared-runtime';
import {AUTODEPS} from 'react';
/**
* Note that a react compiler-based transform still has limitations on JS syntax.
@@ -11,13 +12,17 @@ import {useSpecialEffect} from 'shared-runtime';
*/
function Component({prop1}) {
'use memo';
useSpecialEffect(() => {
try {
console.log(prop1);
} finally {
console.log('exiting');
}
}, [prop1]);
useSpecialEffect(
() => {
try {
console.log(prop1);
} finally {
console.log('exiting');
}
},
[prop1],
AUTODEPS
);
return <div>{prop1}</div>;
}
@@ -27,25 +32,33 @@ function Component({prop1}) {
## Error
```
8 | function Component({prop1}) {
9 | 'use memo';
> 10 | useSpecialEffect(() => {
| ^^^^^^^^^^^^^^^^^^^^^^^^
> 11 | try {
| ^^^^^^^^^
> 12 | console.log(prop1);
| ^^^^^^^^^
> 13 | } finally {
| ^^^^^^^^^
> 14 | console.log('exiting');
| ^^^^^^^^^
> 15 | }
| ^^^^^^^^^
> 16 | }, [prop1]);
| ^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics.. (Bailout reason: Todo: (BuildHIR::lowerStatement) Handle TryStatement without a catch clause (11:15)) (10:16)
17 | return <div>{prop1}</div>;
18 | }
19 |
9 | function Component({prop1}) {
10 | 'use memo';
> 11 | useSpecialEffect(
| ^^^^^^^^^^^^^^^^^
> 12 | () => {
| ^^^^^^^^^^^
> 13 | try {
| ^^^^^^^^^^^
> 14 | console.log(prop1);
| ^^^^^^^^^^^
> 15 | } finally {
| ^^^^^^^^^^^
> 16 | console.log('exiting');
| ^^^^^^^^^^^
> 17 | }
| ^^^^^^^^^^^
> 18 | },
| ^^^^^^^^^^^
> 19 | [prop1],
| ^^^^^^^^^^^
> 20 | AUTODEPS
| ^^^^^^^^^^^
> 21 | );
| ^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics.. (Bailout reason: Todo: (BuildHIR::lowerStatement) Handle TryStatement without a catch clause (13:17)) (11:21)
22 | return <div>{prop1}</div>;
23 | }
24 |
```

View File

@@ -1,5 +1,6 @@
// @inferEffectDependencies @panicThreshold:"none"
import {useSpecialEffect} from 'shared-runtime';
import {AUTODEPS} from 'react';
/**
* Note that a react compiler-based transform still has limitations on JS syntax.
@@ -7,12 +8,16 @@ import {useSpecialEffect} from 'shared-runtime';
*/
function Component({prop1}) {
'use memo';
useSpecialEffect(() => {
try {
console.log(prop1);
} finally {
console.log('exiting');
}
}, [prop1]);
useSpecialEffect(
() => {
try {
console.log(prop1);
} finally {
console.log('exiting');
}
},
[prop1],
AUTODEPS
);
return <div>{prop1}</div>;
}

View File

@@ -3,11 +3,11 @@
```javascript
// @inferEffectDependencies @panicThreshold:"none"
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
function Component({propVal}) {
'use no memo';
useEffect(() => [propVal]);
useEffect(() => [propVal], AUTODEPS);
}
```
@@ -18,8 +18,8 @@ function Component({propVal}) {
```
4 | function Component({propVal}) {
5 | 'use no memo';
> 6 | useEffect(() => [propVal]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:6)
> 6 | useEffect(() => [propVal], AUTODEPS);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:6)
7 | }
8 |
```

View File

@@ -1,7 +1,7 @@
// @inferEffectDependencies @panicThreshold:"none"
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
function Component({propVal}) {
'use no memo';
useEffect(() => [propVal]);
useEffect(() => [propVal], AUTODEPS);
}

View File

@@ -3,14 +3,14 @@
```javascript
// @inferEffectDependencies @panicThreshold:"none"
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function Component({foo}) {
const arr = [];
// Taking either arr[0].value or arr as a dependency is reasonable
// as long as developers know what to expect.
useEffect(() => print(arr[0].value));
useEffect(() => print(arr[0].value), AUTODEPS);
arr.push({value: foo});
return arr;
}
@@ -21,7 +21,7 @@ function Component({foo}) {
```javascript
// @inferEffectDependencies @panicThreshold:"none"
import { useEffect } from "react";
import { useEffect, AUTODEPS } from "react";
import { print } from "shared-runtime";
function Component(t0) {

View File

@@ -1,12 +1,12 @@
// @inferEffectDependencies @panicThreshold:"none"
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function Component({foo}) {
const arr = [];
// Taking either arr[0].value or arr as a dependency is reasonable
// as long as developers know what to expect.
useEffect(() => print(arr[0].value));
useEffect(() => print(arr[0].value), AUTODEPS);
arr.push({value: foo});
return arr;
}

View File

@@ -3,14 +3,14 @@
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function Component({foo}) {
const arr = [];
// Taking either arr[0].value or arr as a dependency is reasonable
// as long as developers know what to expect.
useEffect(() => print(arr[0]?.value));
useEffect(() => print(arr[0]?.value), AUTODEPS);
arr.push({value: foo});
return arr;
}
@@ -26,7 +26,7 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import { useEffect } from "react";
import { useEffect, AUTODEPS } from "react";
import { print } from "shared-runtime";
function Component(t0) {
@@ -48,9 +48,9 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":139},"end":{"line":12,"column":1,"index":384},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"reason":"Updating a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the mutation before calling useEffect()","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":10,"column":2,"index":345},"end":{"line":10,"column":5,"index":348},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":9,"column":2,"index":304},"end":{"line":9,"column":39,"index":341},"filename":"mutate-after-useeffect-optional-chain.ts"},"decorations":[{"start":{"line":9,"column":24,"index":326},"end":{"line":9,"column":27,"index":329},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":139},"end":{"line":12,"column":1,"index":384},"filename":"mutate-after-useeffect-optional-chain.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":149},"end":{"line":12,"column":1,"index":404},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"reason":"Updating a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the mutation before calling useEffect()","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":10,"column":2,"index":365},"end":{"line":10,"column":5,"index":368},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":9,"column":2,"index":314},"end":{"line":9,"column":49,"index":361},"filename":"mutate-after-useeffect-optional-chain.ts"},"decorations":[{"start":{"line":9,"column":24,"index":336},"end":{"line":9,"column":27,"index":339},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":149},"end":{"line":12,"column":1,"index":404},"filename":"mutate-after-useeffect-optional-chain.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```
### Eval output

View File

@@ -1,12 +1,12 @@
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function Component({foo}) {
const arr = [];
// Taking either arr[0].value or arr as a dependency is reasonable
// as long as developers know what to expect.
useEffect(() => print(arr[0]?.value));
useEffect(() => print(arr[0]?.value), AUTODEPS);
arr.push({value: foo});
return arr;
}

View File

@@ -4,12 +4,12 @@
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import {useEffect, useRef} from 'react';
import {useEffect, useRef, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function Component({arrRef}) {
// Avoid taking arr.current as a dependency
useEffect(() => print(arrRef.current));
useEffect(() => print(arrRef.current), AUTODEPS);
arrRef.current.val = 2;
return arrRef;
}
@@ -26,7 +26,7 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import { useEffect, useRef } from "react";
import { useEffect, useRef, AUTODEPS } from "react";
import { print } from "shared-runtime";
function Component(t0) {
@@ -47,9 +47,9 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":148},"end":{"line":11,"column":1,"index":311},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"reason":"Mutating component props or hook arguments is not allowed. Consider using a local variable instead","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":9,"column":2,"index":269},"end":{"line":9,"column":16,"index":283},"filename":"mutate-after-useeffect-ref-access.ts"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":227},"end":{"line":8,"column":40,"index":265},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":249},"end":{"line":8,"column":30,"index":255},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":148},"end":{"line":11,"column":1,"index":311},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":158},"end":{"line":11,"column":1,"index":331},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"reason":"Mutating component props or hook arguments is not allowed. Consider using a local variable instead","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":9,"column":2,"index":289},"end":{"line":9,"column":16,"index":303},"filename":"mutate-after-useeffect-ref-access.ts"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":237},"end":{"line":8,"column":50,"index":285},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":259},"end":{"line":8,"column":30,"index":265},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":158},"end":{"line":11,"column":1,"index":331},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```
### Eval output

View File

@@ -1,11 +1,11 @@
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import {useEffect, useRef} from 'react';
import {useEffect, useRef, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function Component({arrRef}) {
// Avoid taking arr.current as a dependency
useEffect(() => print(arrRef.current));
useEffect(() => print(arrRef.current), AUTODEPS);
arrRef.current.val = 2;
return arrRef;
}

View File

@@ -3,13 +3,13 @@
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
function Component({foo}) {
const arr = [];
useEffect(() => {
arr.push(foo);
});
}, AUTODEPS);
arr.push(2);
return arr;
}
@@ -25,7 +25,7 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import { useEffect } from "react";
import { useEffect, AUTODEPS } from "react";
function Component(t0) {
const { foo } = t0;
@@ -47,9 +47,9 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":101},"end":{"line":11,"column":1,"index":222},"filename":"mutate-after-useeffect.ts"},"detail":{"reason":"Updating a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the mutation before calling useEffect()","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":9,"column":2,"index":194},"end":{"line":9,"column":5,"index":197},"filename":"mutate-after-useeffect.ts","identifierName":"arr"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":6,"column":2,"index":149},"end":{"line":8,"column":4,"index":190},"filename":"mutate-after-useeffect.ts"},"decorations":[{"start":{"line":7,"column":4,"index":171},"end":{"line":7,"column":7,"index":174},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":4,"index":171},"end":{"line":7,"column":7,"index":174},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":13,"index":180},"end":{"line":7,"column":16,"index":183},"filename":"mutate-after-useeffect.ts","identifierName":"foo"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":101},"end":{"line":11,"column":1,"index":222},"filename":"mutate-after-useeffect.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":111},"end":{"line":11,"column":1,"index":242},"filename":"mutate-after-useeffect.ts"},"detail":{"reason":"Updating a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the mutation before calling useEffect()","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":9,"column":2,"index":214},"end":{"line":9,"column":5,"index":217},"filename":"mutate-after-useeffect.ts","identifierName":"arr"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":6,"column":2,"index":159},"end":{"line":8,"column":14,"index":210},"filename":"mutate-after-useeffect.ts"},"decorations":[{"start":{"line":7,"column":4,"index":181},"end":{"line":7,"column":7,"index":184},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":4,"index":181},"end":{"line":7,"column":7,"index":184},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":13,"index":190},"end":{"line":7,"column":16,"index":193},"filename":"mutate-after-useeffect.ts","identifierName":"foo"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":111},"end":{"line":11,"column":1,"index":242},"filename":"mutate-after-useeffect.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```
### Eval output

View File

@@ -1,11 +1,11 @@
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
function Component({foo}) {
const arr = [];
useEffect(() => {
arr.push(foo);
});
}, AUTODEPS);
arr.push(2);
return arr;
}

View File

@@ -0,0 +1,26 @@
## Input
```javascript
// @inferEffectDependencies
import {useEffect, AUTODEPS} from 'react';
function Component({foo}) {
useEffect(AUTODEPS);
}
```
## Error
```
3 |
4 | function Component({foo}) {
> 5 | useEffect(AUTODEPS);
| ^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (5:5)
6 | }
7 |
```

View File

@@ -0,0 +1,6 @@
// @inferEffectDependencies
import {useEffect, AUTODEPS} from 'react';
function Component({foo}) {
useEffect(AUTODEPS);
}

View File

@@ -0,0 +1,45 @@
## Input
```javascript
// @inferEffectDependencies
import {AUTODEPS} from 'react';
import useEffectWrapper from 'useEffectWrapper';
function Component({foo}) {
useEffectWrapper(
() => {
console.log(foo);
},
[foo],
AUTODEPS
);
}
```
## Error
```
4 |
5 | function Component({foo}) {
> 6 | useEffectWrapper(
| ^^^^^^^^^^^^^^^^^
> 7 | () => {
| ^^^^^^^^^^^
> 8 | console.log(foo);
| ^^^^^^^^^^^
> 9 | },
| ^^^^^^^^^^^
> 10 | [foo],
| ^^^^^^^^^^^
> 11 | AUTODEPS
| ^^^^^^^^^^^
> 12 | );
| ^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:12)
13 | }
14 |
```

View File

@@ -0,0 +1,13 @@
// @inferEffectDependencies
import {AUTODEPS} from 'react';
import useEffectWrapper from 'useEffectWrapper';
function Component({foo}) {
useEffectWrapper(
() => {
console.log(foo);
},
[foo],
AUTODEPS
);
}

View File

@@ -3,7 +3,7 @@
```javascript
// @inferEffectDependencies
import {useEffect, useRef} from 'react';
import {useEffect, useRef, AUTODEPS} from 'react';
function useCustomRef() {
const ref = useRef();
return ref;
@@ -12,7 +12,7 @@ function NonReactiveWrapper() {
const ref = useCustomRef();
useEffect(() => {
print(ref);
});
}, AUTODEPS);
}
```
@@ -21,7 +21,7 @@ function NonReactiveWrapper() {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect, useRef } from "react";
import { useEffect, useRef, AUTODEPS } from "react";
function useCustomRef() {
const ref = useRef();
return ref;

View File

@@ -1,5 +1,5 @@
// @inferEffectDependencies
import {useEffect, useRef} from 'react';
import {useEffect, useRef, AUTODEPS} from 'react';
function useCustomRef() {
const ref = useRef();
return ref;
@@ -8,5 +8,5 @@ function NonReactiveWrapper() {
const ref = useCustomRef();
useEffect(() => {
print(ref);
});
}, AUTODEPS);
}

View File

@@ -8,8 +8,8 @@ import * as SharedRuntime from 'shared-runtime';
function NonReactiveDepInEffect() {
const obj = makeObject_Primitives();
React.useEffect(() => print(obj));
SharedRuntime.useSpecialEffect(() => print(obj), [obj]);
React.useEffect(() => print(obj), React.AUTODEPS);
SharedRuntime.useSpecialEffect(() => print(obj), [obj], React.AUTODEPS);
}
```

View File

@@ -4,6 +4,6 @@ import * as SharedRuntime from 'shared-runtime';
function NonReactiveDepInEffect() {
const obj = makeObject_Primitives();
React.useEffect(() => print(obj));
SharedRuntime.useSpecialEffect(() => print(obj), [obj]);
React.useEffect(() => print(obj), React.AUTODEPS);
SharedRuntime.useSpecialEffect(() => print(obj), [obj], React.AUTODEPS);
}

View File

@@ -4,10 +4,11 @@
```javascript
// @inferEffectDependencies
import {print, useSpecialEffect} from 'shared-runtime';
import {AUTODEPS} from 'react';
function CustomConfig({propVal}) {
// Insertion
useSpecialEffect(() => print(propVal), [propVal]);
useSpecialEffect(() => print(propVal), [propVal], AUTODEPS);
// No insertion
useSpecialEffect(() => print(propVal), [propVal], [propVal]);
}
@@ -19,6 +20,7 @@ function CustomConfig({propVal}) {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { print, useSpecialEffect } from "shared-runtime";
import { AUTODEPS } from "react";
function CustomConfig(t0) {
const $ = _c(7);

View File

@@ -1,9 +1,10 @@
// @inferEffectDependencies
import {print, useSpecialEffect} from 'shared-runtime';
import {AUTODEPS} from 'react';
function CustomConfig({propVal}) {
// Insertion
useSpecialEffect(() => print(propVal), [propVal]);
useSpecialEffect(() => print(propVal), [propVal], AUTODEPS);
// No insertion
useSpecialEffect(() => print(propVal), [propVal], [propVal]);
}

View File

@@ -3,7 +3,7 @@
```javascript
// @inferEffectDependencies
import {useEffect, useRef} from 'react';
import {useEffect, useRef, AUTODEPS} from 'react';
import useEffectWrapper from 'useEffectWrapper';
const moduleNonReactive = 0;
@@ -24,7 +24,7 @@ function Component({foo, bar}) {
console.log(ref.current);
console.log(localNonPrimitiveReactive);
console.log(localNonPrimitiveNonreactive);
});
}, AUTODEPS);
// Optional chains and property accesses
// TODO: we may be able to save bytes by omitting property accesses if the
@@ -32,11 +32,11 @@ function Component({foo, bar}) {
useEffect(() => {
console.log(bar?.baz);
console.log(bar.qux);
});
}, AUTODEPS);
useEffectWrapper(() => {
console.log(foo);
});
}, AUTODEPS);
}
```
@@ -45,7 +45,7 @@ function Component({foo, bar}) {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect, useRef } from "react";
import { useEffect, useRef, AUTODEPS } from "react";
import useEffectWrapper from "useEffectWrapper";
const moduleNonReactive = 0;

View File

@@ -1,5 +1,5 @@
// @inferEffectDependencies
import {useEffect, useRef} from 'react';
import {useEffect, useRef, AUTODEPS} from 'react';
import useEffectWrapper from 'useEffectWrapper';
const moduleNonReactive = 0;
@@ -20,7 +20,7 @@ function Component({foo, bar}) {
console.log(ref.current);
console.log(localNonPrimitiveReactive);
console.log(localNonPrimitiveNonreactive);
});
}, AUTODEPS);
// Optional chains and property accesses
// TODO: we may be able to save bytes by omitting property accesses if the
@@ -28,9 +28,9 @@ function Component({foo, bar}) {
useEffect(() => {
console.log(bar?.baz);
console.log(bar.qux);
});
}, AUTODEPS);
useEffectWrapper(() => {
console.log(foo);
});
}, AUTODEPS);
}

View File

@@ -5,10 +5,11 @@
// @inferEffectDependencies @noEmit
import {print} from 'shared-runtime';
import useEffectWrapper from 'useEffectWrapper';
import {AUTODEPS} from 'react';
function ReactiveVariable({propVal}) {
const arr = [propVal];
useEffectWrapper(() => print(arr));
useEffectWrapper(() => print(arr), AUTODEPS);
}
```
@@ -19,10 +20,11 @@ function ReactiveVariable({propVal}) {
// @inferEffectDependencies @noEmit
import { print } from "shared-runtime";
import useEffectWrapper from "useEffectWrapper";
import { AUTODEPS } from "react";
function ReactiveVariable({ propVal }) {
const arr = [propVal];
useEffectWrapper(() => print(arr));
useEffectWrapper(() => print(arr), AUTODEPS);
}
```

View File

@@ -1,8 +1,9 @@
// @inferEffectDependencies @noEmit
import {print} from 'shared-runtime';
import useEffectWrapper from 'useEffectWrapper';
import {AUTODEPS} from 'react';
function ReactiveVariable({propVal}) {
const arr = [propVal];
useEffectWrapper(() => print(arr));
useEffectWrapper(() => print(arr), AUTODEPS);
}

View File

@@ -5,13 +5,14 @@
// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
import {print} from 'shared-runtime';
import useEffectWrapper from 'useEffectWrapper';
import {AUTODEPS} from 'react';
function Foo({propVal}) {
const arr = [propVal];
useEffectWrapper(() => print(arr));
useEffectWrapper(() => print(arr), AUTODEPS);
const arr2 = [];
useEffectWrapper(() => arr2.push(propVal));
useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
arr2.push(2);
return {arr, arr2};
}
@@ -30,13 +31,14 @@ export const FIXTURE_ENTRYPOINT = {
// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
import { print } from "shared-runtime";
import useEffectWrapper from "useEffectWrapper";
import { AUTODEPS } from "react";
function Foo({ propVal }) {
const arr = [propVal];
useEffectWrapper(() => print(arr));
useEffectWrapper(() => print(arr), AUTODEPS);
const arr2 = [];
useEffectWrapper(() => arr2.push(propVal));
useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
arr2.push(2);
return { arr, arr2 };
}
@@ -52,10 +54,10 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":163},"end":{"line":13,"column":1,"index":357},"filename":"retry-no-emit.ts"},"detail":{"reason":"Updating a value previously passed as an argument to a hook is not allowed. Consider moving the mutation before calling the hook","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":11,"column":2,"index":320},"end":{"line":11,"column":6,"index":324},"filename":"retry-no-emit.ts","identifierName":"arr2"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":7,"column":2,"index":216},"end":{"line":7,"column":36,"index":250},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":7,"column":31,"index":245},"end":{"line":7,"column":34,"index":248},"filename":"retry-no-emit.ts","identifierName":"arr"}]}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":10,"column":2,"index":274},"end":{"line":10,"column":44,"index":316},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":10,"column":25,"index":297},"end":{"line":10,"column":29,"index":301},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":10,"column":25,"index":297},"end":{"line":10,"column":29,"index":301},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":10,"column":35,"index":307},"end":{"line":10,"column":42,"index":314},"filename":"retry-no-emit.ts","identifierName":"propVal"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":163},"end":{"line":13,"column":1,"index":357},"filename":"retry-no-emit.ts"},"fnName":"Foo","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":195},"end":{"line":14,"column":1,"index":409},"filename":"retry-no-emit.ts"},"detail":{"reason":"Updating a value previously passed as an argument to a hook is not allowed. Consider moving the mutation before calling the hook","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":12,"column":2,"index":372},"end":{"line":12,"column":6,"index":376},"filename":"retry-no-emit.ts","identifierName":"arr2"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":248},"end":{"line":8,"column":46,"index":292},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":8,"column":31,"index":277},"end":{"line":8,"column":34,"index":280},"filename":"retry-no-emit.ts","identifierName":"arr"}]}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":11,"column":2,"index":316},"end":{"line":11,"column":54,"index":368},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":11,"column":25,"index":339},"end":{"line":11,"column":29,"index":343},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":11,"column":25,"index":339},"end":{"line":11,"column":29,"index":343},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":11,"column":35,"index":349},"end":{"line":11,"column":42,"index":356},"filename":"retry-no-emit.ts","identifierName":"propVal"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":195},"end":{"line":14,"column":1,"index":409},"filename":"retry-no-emit.ts"},"fnName":"Foo","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```
### Eval output

View File

@@ -1,13 +1,14 @@
// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
import {print} from 'shared-runtime';
import useEffectWrapper from 'useEffectWrapper';
import {AUTODEPS} from 'react';
function Foo({propVal}) {
const arr = [propVal];
useEffectWrapper(() => print(arr));
useEffectWrapper(() => print(arr), AUTODEPS);
const arr2 = [];
useEffectWrapper(() => arr2.push(propVal));
useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
arr2.push(2);
return {arr, arr2};
}

View File

@@ -4,15 +4,16 @@
```javascript
// @compilationMode:"all" @inferEffectDependencies @panicThreshold:"none" @noEmit
import {print} from 'shared-runtime';
import {AUTODEPS} from 'react';
import useEffectWrapper from 'useEffectWrapper';
function Foo({propVal}) {
'use memo';
const arr = [propVal];
useEffectWrapper(() => print(arr));
useEffectWrapper(() => print(arr), AUTODEPS);
const arr2 = [];
useEffectWrapper(() => arr2.push(propVal));
useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
arr2.push(2);
return {arr, arr2};
@@ -31,15 +32,16 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
// @compilationMode:"all" @inferEffectDependencies @panicThreshold:"none" @noEmit
import { print } from "shared-runtime";
import { AUTODEPS } from "react";
import useEffectWrapper from "useEffectWrapper";
function Foo({ propVal }) {
"use memo";
const arr = [propVal];
useEffectWrapper(() => print(arr));
useEffectWrapper(() => print(arr), AUTODEPS);
const arr2 = [];
useEffectWrapper(() => arr2.push(propVal));
useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
arr2.push(2);
return { arr, arr2 };

View File

@@ -1,14 +1,15 @@
// @compilationMode:"all" @inferEffectDependencies @panicThreshold:"none" @noEmit
import {print} from 'shared-runtime';
import {AUTODEPS} from 'react';
import useEffectWrapper from 'useEffectWrapper';
function Foo({propVal}) {
'use memo';
const arr = [propVal];
useEffectWrapper(() => print(arr));
useEffectWrapper(() => print(arr), AUTODEPS);
const arr2 = [];
useEffectWrapper(() => arr2.push(propVal));
useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
arr2.push(2);
return {arr, arr2};

View File

@@ -3,7 +3,7 @@
```javascript
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {makeObject_Primitives, print} from 'shared-runtime';
/**
@@ -25,7 +25,7 @@ import {makeObject_Primitives, print} from 'shared-runtime';
*/
function NonReactiveDepInEffect() {
const obj = makeObject_Primitives();
useEffect(() => print(obj));
useEffect(() => print(obj), AUTODEPS);
}
```
@@ -34,7 +34,7 @@ function NonReactiveDepInEffect() {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect } from "react";
import { useEffect, AUTODEPS } from "react";
import { makeObject_Primitives, print } from "shared-runtime";
/**

View File

@@ -1,5 +1,5 @@
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {makeObject_Primitives, print} from 'shared-runtime';
/**
@@ -21,5 +21,5 @@ import {makeObject_Primitives, print} from 'shared-runtime';
*/
function NonReactiveDepInEffect() {
const obj = makeObject_Primitives();
useEffect(() => print(obj));
useEffect(() => print(obj), AUTODEPS);
}

View File

@@ -3,7 +3,7 @@
```javascript
// @inferEffectDependencies
import {useEffect, useEffectEvent} from 'react';
import {useEffect, useEffectEvent, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
/**
@@ -11,7 +11,7 @@ import {print} from 'shared-runtime';
*/
function NonReactiveEffectEvent() {
const fn = useEffectEvent(() => print('hello world'));
useEffect(() => fn());
useEffect(() => fn(), AUTODEPS);
}
```
@@ -20,7 +20,7 @@ function NonReactiveEffectEvent() {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect, useEffectEvent } from "react";
import { useEffect, useEffectEvent, AUTODEPS } from "react";
import { print } from "shared-runtime";
/**

View File

@@ -1,5 +1,5 @@
// @inferEffectDependencies
import {useEffect, useEffectEvent} from 'react';
import {useEffect, useEffectEvent, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
/**
@@ -7,5 +7,5 @@ import {print} from 'shared-runtime';
*/
function NonReactiveEffectEvent() {
const fn = useEffectEvent(() => print('hello world'));
useEffect(() => fn());
useEffect(() => fn(), AUTODEPS);
}

View File

@@ -3,7 +3,7 @@
```javascript
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
/**
@@ -19,7 +19,7 @@ function RefsInEffects() {
useEffect(() => {
print(ref.current);
print(wrapped.foo.current);
});
}, AUTODEPS);
}
function useRefHelper() {
@@ -36,7 +36,7 @@ function useDeeperRefHelper() {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect } from "react";
import { useEffect, AUTODEPS } from "react";
import { print } from "shared-runtime";
/**

View File

@@ -1,5 +1,5 @@
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
/**
@@ -15,7 +15,7 @@ function RefsInEffects() {
useEffect(() => {
print(ref.current);
print(wrapped.foo.current);
});
}, AUTODEPS);
}
function useRefHelper() {

View File

@@ -3,7 +3,7 @@
```javascript
// @inferEffectDependencies
import {useEffect, useRef} from 'react';
import {useEffect, useRef, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
/**
@@ -14,7 +14,7 @@ import {print} from 'shared-runtime';
*/
function NonReactiveRefInEffect() {
const ref = useRef('initial value');
useEffect(() => print(ref.current));
useEffect(() => print(ref.current), AUTODEPS);
}
```
@@ -23,7 +23,7 @@ function NonReactiveRefInEffect() {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect, useRef } from "react";
import { useEffect, useRef, AUTODEPS } from "react";
import { print } from "shared-runtime";
/**

View File

@@ -1,5 +1,5 @@
// @inferEffectDependencies
import {useEffect, useRef} from 'react';
import {useEffect, useRef, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
/**
@@ -10,5 +10,5 @@ import {print} from 'shared-runtime';
*/
function NonReactiveRefInEffect() {
const ref = useRef('initial value');
useEffect(() => print(ref.current));
useEffect(() => print(ref.current), AUTODEPS);
}

View File

@@ -3,7 +3,7 @@
```javascript
// @inferEffectDependencies
import {useEffect, useState} from 'react';
import {useEffect, useState, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
/**
@@ -14,7 +14,7 @@ import {print} from 'shared-runtime';
*/
function NonReactiveSetStateInEffect() {
const [_, setState] = useState('initial value');
useEffect(() => print(setState));
useEffect(() => print(setState), AUTODEPS);
}
```
@@ -23,7 +23,7 @@ function NonReactiveSetStateInEffect() {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect, useState } from "react";
import { useEffect, useState, AUTODEPS } from "react";
import { print } from "shared-runtime";
/**

View File

@@ -1,5 +1,5 @@
// @inferEffectDependencies
import {useEffect, useState} from 'react';
import {useEffect, useState, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
/**
@@ -10,5 +10,5 @@ import {print} from 'shared-runtime';
*/
function NonReactiveSetStateInEffect() {
const [_, setState] = useState('initial value');
useEffect(() => print(setState));
useEffect(() => print(setState), AUTODEPS);
}

View File

@@ -3,7 +3,7 @@
```javascript
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
/**
* This compiled output is technically incorrect but this is currently the same
@@ -14,7 +14,7 @@ import {print} from 'shared-runtime';
* before OutlineFunctions
*/
function OutlinedFunctionInEffect() {
useEffect(() => print('hello world!'));
useEffect(() => print('hello world!'), AUTODEPS);
}
```
@@ -23,7 +23,7 @@ function OutlinedFunctionInEffect() {
```javascript
// @inferEffectDependencies
import { useEffect } from "react";
import { useEffect, AUTODEPS } from "react";
import { print } from "shared-runtime";
/**
* This compiled output is technically incorrect but this is currently the same

View File

@@ -1,5 +1,5 @@
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
/**
* This compiled output is technically incorrect but this is currently the same
@@ -10,5 +10,5 @@ import {print} from 'shared-runtime';
* before OutlineFunctions
*/
function OutlinedFunctionInEffect() {
useEffect(() => print('hello world!'));
useEffect(() => print('hello world!'), AUTODEPS);
}

View File

@@ -4,7 +4,7 @@
```javascript
// @inferEffectDependencies
import {useIdentity, mutate, makeObject} from 'shared-runtime';
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
/**
* When a semantically non-reactive value has a pruned scope (i.e. the object
@@ -48,7 +48,7 @@ function PrunedNonReactive() {
useIdentity(null);
mutate(obj);
useEffect(() => print(obj.value));
useEffect(() => print(obj.value), AUTODEPS);
}
```
@@ -58,7 +58,7 @@ function PrunedNonReactive() {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useIdentity, mutate, makeObject } from "shared-runtime";
import { useEffect } from "react";
import { useEffect, AUTODEPS } from "react";
/**
* When a semantically non-reactive value has a pruned scope (i.e. the object

View File

@@ -1,6 +1,6 @@
// @inferEffectDependencies
import {useIdentity, mutate, makeObject} from 'shared-runtime';
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
/**
* When a semantically non-reactive value has a pruned scope (i.e. the object
@@ -44,5 +44,5 @@ function PrunedNonReactive() {
useIdentity(null);
mutate(obj);
useEffect(() => print(obj.value));
useEffect(() => print(obj.value), AUTODEPS);
}

View File

@@ -3,12 +3,12 @@
```javascript
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function ReactiveMemberExprMerge({propVal}) {
const obj = {a: {b: propVal}};
useEffect(() => print(obj.a, obj.a.b));
useEffect(() => print(obj.a, obj.a.b), AUTODEPS);
}
```
@@ -17,7 +17,7 @@ function ReactiveMemberExprMerge({propVal}) {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect } from "react";
import { useEffect, AUTODEPS } from "react";
import { print } from "shared-runtime";
function ReactiveMemberExprMerge(t0) {

View File

@@ -1,8 +1,8 @@
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function ReactiveMemberExprMerge({propVal}) {
const obj = {a: {b: propVal}};
useEffect(() => print(obj.a, obj.a.b));
useEffect(() => print(obj.a, obj.a.b), AUTODEPS);
}

View File

@@ -3,12 +3,12 @@
```javascript
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function ReactiveMemberExpr({propVal}) {
const obj = {a: {b: propVal}};
useEffect(() => print(obj.a.b));
useEffect(() => print(obj.a.b), AUTODEPS);
}
```
@@ -17,7 +17,7 @@ function ReactiveMemberExpr({propVal}) {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect } from "react";
import { useEffect, AUTODEPS } from "react";
import { print } from "shared-runtime";
function ReactiveMemberExpr(t0) {

View File

@@ -1,8 +1,8 @@
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function ReactiveMemberExpr({propVal}) {
const obj = {a: {b: propVal}};
useEffect(() => print(obj.a.b));
useEffect(() => print(obj.a.b), AUTODEPS);
}

View File

@@ -3,15 +3,16 @@
```javascript
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print, shallowCopy} from 'shared-runtime';
function ReactiveMemberExpr({cond, propVal}) {
const obj = {a: cond ? {b: propVal} : null, c: null};
const other = shallowCopy({a: {b: {c: {d: {e: {f: propVal + 1}}}}}});
const primitive = shallowCopy(propVal);
useEffect(() =>
print(obj.a?.b, other?.a?.b?.c?.d?.e.f, primitive.a?.b.c?.d?.e.f)
useEffect(
() => print(obj.a?.b, other?.a?.b?.c?.d?.e.f, primitive.a?.b.c?.d?.e.f),
AUTODEPS
);
}
@@ -26,7 +27,7 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect } from "react";
import { useEffect, AUTODEPS } from "react";
import { print, shallowCopy } from "shared-runtime";
function ReactiveMemberExpr(t0) {

View File

@@ -1,13 +1,14 @@
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print, shallowCopy} from 'shared-runtime';
function ReactiveMemberExpr({cond, propVal}) {
const obj = {a: cond ? {b: propVal} : null, c: null};
const other = shallowCopy({a: {b: {c: {d: {e: {f: propVal + 1}}}}}});
const primitive = shallowCopy(propVal);
useEffect(() =>
print(obj.a?.b, other?.a?.b?.c?.d?.e.f, primitive.a?.b.c?.d?.e.f)
useEffect(
() => print(obj.a?.b, other?.a?.b?.c?.d?.e.f, primitive.a?.b.c?.d?.e.f),
AUTODEPS
);
}

View File

@@ -3,13 +3,13 @@
```javascript
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function ReactiveMemberExpr({cond, propVal}) {
const obj = {a: cond ? {b: propVal} : null, c: null};
useEffect(() => print(obj.a?.b));
useEffect(() => print(obj.c?.d));
useEffect(() => print(obj.a?.b), AUTODEPS);
useEffect(() => print(obj.c?.d), AUTODEPS);
}
export const FIXTURE_ENTRYPOINT = {
@@ -23,7 +23,7 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect } from "react";
import { useEffect, AUTODEPS } from "react";
import { print } from "shared-runtime";
function ReactiveMemberExpr(t0) {

View File

@@ -1,11 +1,11 @@
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function ReactiveMemberExpr({cond, propVal}) {
const obj = {a: cond ? {b: propVal} : null, c: null};
useEffect(() => print(obj.a?.b));
useEffect(() => print(obj.c?.d));
useEffect(() => print(obj.a?.b), AUTODEPS);
useEffect(() => print(obj.c?.d), AUTODEPS);
}
export const FIXTURE_ENTRYPOINT = {

View File

@@ -3,7 +3,7 @@
```javascript
// @inferEffectDependencies
import {useRef, useEffect} from 'react';
import {useRef, useEffect, AUTODEPS} from 'react';
import {print, mutate} from 'shared-runtime';
function Component({cond}) {
@@ -14,7 +14,7 @@ function Component({cond}) {
useEffect(() => {
mutate(derived.current);
print(derived.current);
});
}, AUTODEPS);
return arr;
}
@@ -24,7 +24,7 @@ function Component({cond}) {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useRef, useEffect } from "react";
import { useRef, useEffect, AUTODEPS } from "react";
import { print, mutate } from "shared-runtime";
function Component(t0) {

View File

@@ -1,5 +1,5 @@
// @inferEffectDependencies
import {useRef, useEffect} from 'react';
import {useRef, useEffect, AUTODEPS} from 'react';
import {print, mutate} from 'shared-runtime';
function Component({cond}) {
@@ -10,6 +10,6 @@ function Component({cond}) {
useEffect(() => {
mutate(derived.current);
print(derived.current);
});
}, AUTODEPS);
return arr;
}

View File

@@ -3,7 +3,7 @@
```javascript
// @inferEffectDependencies
import {useEffect, useRef} from 'react';
import {useEffect, useRef, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
/*
@@ -18,7 +18,7 @@ function ReactiveRefInEffect(props) {
} else {
ref = ref2;
}
useEffect(() => print(ref));
useEffect(() => print(ref), AUTODEPS);
}
```
@@ -27,7 +27,7 @@ function ReactiveRefInEffect(props) {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect, useRef } from "react";
import { useEffect, useRef, AUTODEPS } from "react";
import { print } from "shared-runtime";
/*

View File

@@ -1,5 +1,5 @@
// @inferEffectDependencies
import {useEffect, useRef} from 'react';
import {useEffect, useRef, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
/*
@@ -14,5 +14,5 @@ function ReactiveRefInEffect(props) {
} else {
ref = ref2;
}
useEffect(() => print(ref));
useEffect(() => print(ref), AUTODEPS);
}

View File

@@ -3,7 +3,7 @@
```javascript
// @inferEffectDependencies
import {useEffect, useState} from 'react';
import {useEffect, useState, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
/*
@@ -18,7 +18,7 @@ function ReactiveRefInEffect(props) {
} else {
setState = setState2;
}
useEffect(() => print(setState));
useEffect(() => print(setState), AUTODEPS);
}
```
@@ -27,7 +27,7 @@ function ReactiveRefInEffect(props) {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect, useState } from "react";
import { useEffect, useState, AUTODEPS } from "react";
import { print } from "shared-runtime";
/*

View File

@@ -1,5 +1,5 @@
// @inferEffectDependencies
import {useEffect, useState} from 'react';
import {useEffect, useState, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
/*
@@ -14,5 +14,5 @@ function ReactiveRefInEffect(props) {
} else {
setState = setState2;
}
useEffect(() => print(setState));
useEffect(() => print(setState), AUTODEPS);
}

View File

@@ -3,12 +3,12 @@
```javascript
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function ReactiveVariable({propVal}) {
const arr = [propVal];
useEffect(() => print(arr));
useEffect(() => print(arr), AUTODEPS);
}
```
@@ -17,7 +17,7 @@ function ReactiveVariable({propVal}) {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect } from "react";
import { useEffect, AUTODEPS } from "react";
import { print } from "shared-runtime";
function ReactiveVariable(t0) {

View File

@@ -1,8 +1,8 @@
// @inferEffectDependencies
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function ReactiveVariable({propVal}) {
const arr = [propVal];
useEffect(() => print(arr));
useEffect(() => print(arr), AUTODEPS);
}

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @loggerTestOnly @validateNoSetStateInPassiveEffects
// @loggerTestOnly @validateNoSetStateInEffects
import {useEffect, useState} from 'react';
function Component() {
@@ -24,7 +24,7 @@ function Component() {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @loggerTestOnly @validateNoSetStateInPassiveEffects
import { c as _c } from "react/compiler-runtime"; // @loggerTestOnly @validateNoSetStateInEffects
import { useEffect, useState } from "react";
function Component() {
@@ -65,8 +65,8 @@ function _temp(s) {
## Logs
```
{"kind":"CompileError","detail":{"options":{"reason":"Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":13,"column":4,"index":272},"end":{"line":13,"column":5,"index":273},"filename":"invalid-setState-in-useEffect-transitive.ts","identifierName":"g"}}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":99},"end":{"line":16,"column":1,"index":300},"filename":"invalid-setState-in-useEffect-transitive.ts"},"fnName":"Component","memoSlots":2,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0}
{"kind":"CompileError","detail":{"options":{"reason":"Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":13,"column":4,"index":265},"end":{"line":13,"column":5,"index":266},"filename":"invalid-setState-in-useEffect-transitive.ts","identifierName":"g"}}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":92},"end":{"line":16,"column":1,"index":293},"filename":"invalid-setState-in-useEffect-transitive.ts"},"fnName":"Component","memoSlots":2,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0}
```
### Eval output

View File

@@ -1,4 +1,4 @@
// @loggerTestOnly @validateNoSetStateInPassiveEffects
// @loggerTestOnly @validateNoSetStateInEffects
import {useEffect, useState} from 'react';
function Component() {

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @loggerTestOnly @validateNoSetStateInPassiveEffects
// @loggerTestOnly @validateNoSetStateInEffects
import {useEffect, useState} from 'react';
function Component() {
@@ -18,7 +18,7 @@ function Component() {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @loggerTestOnly @validateNoSetStateInPassiveEffects
import { c as _c } from "react/compiler-runtime"; // @loggerTestOnly @validateNoSetStateInEffects
import { useEffect, useState } from "react";
function Component() {
@@ -45,8 +45,8 @@ function _temp(s) {
## Logs
```
{"kind":"CompileError","detail":{"options":{"reason":"Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":7,"column":4,"index":187},"end":{"line":7,"column":12,"index":195},"filename":"invalid-setState-in-useEffect.ts","identifierName":"setState"}}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":99},"end":{"line":10,"column":1,"index":232},"filename":"invalid-setState-in-useEffect.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
{"kind":"CompileError","detail":{"options":{"reason":"Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":7,"column":4,"index":180},"end":{"line":7,"column":12,"index":188},"filename":"invalid-setState-in-useEffect.ts","identifierName":"setState"}}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":92},"end":{"line":10,"column":1,"index":225},"filename":"invalid-setState-in-useEffect.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
```
### Eval output

View File

@@ -1,4 +1,4 @@
// @loggerTestOnly @validateNoSetStateInPassiveEffects
// @loggerTestOnly @validateNoSetStateInEffects
import {useEffect, useState} from 'react';
function Component() {

View File

@@ -3,14 +3,14 @@
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function Component({foo}) {
const arr = [];
// Taking either arr[0].value or arr as a dependency is reasonable
// as long as developers know what to expect.
useEffect(() => print(arr[0]?.value));
useEffect(() => print(arr[0]?.value), AUTODEPS);
arr.push({value: foo});
return arr;
}
@@ -26,7 +26,7 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import { useEffect } from "react";
import { useEffect, AUTODEPS } from "react";
import { print } from "shared-runtime";
function Component(t0) {
@@ -48,9 +48,9 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":171},"end":{"line":12,"column":1,"index":416},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"reason":"Updating a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the mutation before calling useEffect()","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":10,"column":2,"index":377},"end":{"line":10,"column":5,"index":380},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":9,"column":2,"index":336},"end":{"line":9,"column":39,"index":373},"filename":"mutate-after-useeffect-optional-chain.ts"},"decorations":[{"start":{"line":9,"column":24,"index":358},"end":{"line":9,"column":27,"index":361},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":171},"end":{"line":12,"column":1,"index":416},"filename":"mutate-after-useeffect-optional-chain.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":181},"end":{"line":12,"column":1,"index":436},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"reason":"Updating a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the mutation before calling useEffect()","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":10,"column":2,"index":397},"end":{"line":10,"column":5,"index":400},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":9,"column":2,"index":346},"end":{"line":9,"column":49,"index":393},"filename":"mutate-after-useeffect-optional-chain.ts"},"decorations":[{"start":{"line":9,"column":24,"index":368},"end":{"line":9,"column":27,"index":371},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":181},"end":{"line":12,"column":1,"index":436},"filename":"mutate-after-useeffect-optional-chain.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```
### Eval output

View File

@@ -1,12 +1,12 @@
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import {useEffect} from 'react';
import {useEffect, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function Component({foo}) {
const arr = [];
// Taking either arr[0].value or arr as a dependency is reasonable
// as long as developers know what to expect.
useEffect(() => print(arr[0]?.value));
useEffect(() => print(arr[0]?.value), AUTODEPS);
arr.push({value: foo});
return arr;
}

View File

@@ -4,12 +4,12 @@
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import {useEffect, useRef} from 'react';
import {useEffect, useRef, AUTODEPS} from 'react';
import {print} from 'shared-runtime';
function Component({arrRef}) {
// Avoid taking arr.current as a dependency
useEffect(() => print(arrRef.current));
useEffect(() => print(arrRef.current), AUTODEPS);
arrRef.current.val = 2;
return arrRef;
}
@@ -26,7 +26,7 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import { useEffect, useRef } from "react";
import { useEffect, useRef, AUTODEPS } from "react";
import { print } from "shared-runtime";
function Component(t0) {
@@ -47,9 +47,9 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":180},"end":{"line":11,"column":1,"index":343},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"reason":"Mutating component props or hook arguments is not allowed. Consider using a local variable instead","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":9,"column":2,"index":301},"end":{"line":9,"column":16,"index":315},"filename":"mutate-after-useeffect-ref-access.ts"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":259},"end":{"line":8,"column":40,"index":297},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":281},"end":{"line":8,"column":30,"index":287},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":180},"end":{"line":11,"column":1,"index":343},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":190},"end":{"line":11,"column":1,"index":363},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"reason":"Mutating component props or hook arguments is not allowed. Consider using a local variable instead","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":9,"column":2,"index":321},"end":{"line":9,"column":16,"index":335},"filename":"mutate-after-useeffect-ref-access.ts"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":269},"end":{"line":8,"column":50,"index":317},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":291},"end":{"line":8,"column":30,"index":297},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":190},"end":{"line":11,"column":1,"index":363},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```
### Eval output

Some files were not shown because too many files have changed in this diff Show More