Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2782d934e2 |
@@ -331,7 +331,6 @@ module.exports = {
|
||||
'packages/react-server-dom-turbopack/**/*.js',
|
||||
'packages/react-server-dom-parcel/**/*.js',
|
||||
'packages/react-server-dom-fb/**/*.js',
|
||||
'packages/react-server-dom-unbundled/**/*.js',
|
||||
'packages/react-test-renderer/**/*.js',
|
||||
'packages/react-debug-tools/**/*.js',
|
||||
'packages/react-devtools-extensions/**/*.js',
|
||||
@@ -593,7 +592,6 @@ module.exports = {
|
||||
mixin$Animatable: 'readonly',
|
||||
MouseEventHandler: 'readonly',
|
||||
NavigateEvent: 'readonly',
|
||||
Partial: 'readonly',
|
||||
PerformanceMeasureOptions: 'readonly',
|
||||
PropagationPhases: 'readonly',
|
||||
PropertyDescriptor: 'readonly',
|
||||
@@ -636,7 +634,6 @@ module.exports = {
|
||||
FocusOptions: 'readonly',
|
||||
OptionalEffectTiming: 'readonly',
|
||||
|
||||
__REACT_ROOT_PATH_TEST__: 'readonly',
|
||||
spyOnDev: 'readonly',
|
||||
spyOnDevAndProd: 'readonly',
|
||||
spyOnProd: 'readonly',
|
||||
|
||||
138
.github/workflows/runtime_build_and_test.yml
vendored
138
.github/workflows/runtime_build_and_test.yml
vendored
@@ -3,10 +3,6 @@ name: (Runtime) Build and Test
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags:
|
||||
# To get CI for backport releases.
|
||||
# This will duplicate CI for releases from main which is acceptable
|
||||
- "v*"
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- compiler/**
|
||||
@@ -45,7 +41,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
lookup-only: true
|
||||
- uses: actions/setup-node@v4
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
@@ -59,8 +55,10 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
- run: yarn install --frozen-lockfile
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- name: Save cache
|
||||
@@ -69,7 +67,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
|
||||
runtime_compiler_node_modules_cache:
|
||||
name: Cache Runtime, Compiler node_modules
|
||||
@@ -84,7 +82,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
lookup-only: true
|
||||
- uses: actions/setup-node@v4
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
@@ -100,8 +98,10 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-and-compiler-node_modules-v6-
|
||||
- run: yarn install --frozen-lockfile
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- run: yarn --cwd compiler install --frozen-lockfile
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
|
||||
# ----- FLOW -----
|
||||
discover_flow_inline_configs:
|
||||
@@ -154,8 +154,10 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -182,8 +184,10 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -212,7 +216,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -270,15 +274,16 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-and-compiler-node_modules-v6-
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- run: yarn --cwd compiler install --frozen-lockfile
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- run: node --version
|
||||
- run: yarn test ${{ matrix.params }} --ci --shard=${{ matrix.shard }}
|
||||
|
||||
# Hardcoded to improve parallelism
|
||||
@@ -301,7 +306,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
- name: Install runtime dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
@@ -344,8 +349,10 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-and-compiler-node_modules-v6-
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -383,6 +390,9 @@ jobs:
|
||||
-r=experimental --env=development,
|
||||
-r=experimental --env=production,
|
||||
|
||||
# Dev Tools
|
||||
--project=devtools -r=experimental,
|
||||
|
||||
# TODO: Update test config to support www build tests
|
||||
# - "-r=www-classic --env=development --variant=false"
|
||||
# - "-r=www-classic --env=production --variant=false"
|
||||
@@ -430,8 +440,10 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-and-compiler-node_modules-v6-
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -446,54 +458,8 @@ jobs:
|
||||
merge-multiple: true
|
||||
- name: Display structure of build
|
||||
run: ls -R build
|
||||
- run: node --version
|
||||
- run: yarn test --build ${{ matrix.test_params }} --shard=${{ matrix.shard }} --ci
|
||||
|
||||
test_build_devtools:
|
||||
name: yarn test-build (devtools)
|
||||
needs: [build_and_lint, runtime_node_modules_cache]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard:
|
||||
- 1/5
|
||||
- 2/5
|
||||
- 3/5
|
||||
- 4/5
|
||||
- 5/5
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
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'
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: _build_*
|
||||
path: build
|
||||
merge-multiple: true
|
||||
- name: Display structure of build
|
||||
run: ls -R build
|
||||
- run: node --version
|
||||
- run: yarn test --build --project=devtools -r=experimental --shard=${{ matrix.shard }} --ci
|
||||
|
||||
process_artifacts_combined:
|
||||
name: Process artifacts combined
|
||||
needs: [build_and_lint, runtime_node_modules_cache]
|
||||
@@ -517,8 +483,10 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -580,8 +548,10 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -618,8 +588,10 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -768,8 +740,10 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -828,8 +802,10 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -24,8 +24,6 @@ chrome-user-data
|
||||
*.swp
|
||||
*.swo
|
||||
/tmp
|
||||
/.worktrees
|
||||
.claude/*.local.*
|
||||
|
||||
packages/react-devtools-core/dist
|
||||
packages/react-devtools-extensions/chrome/build
|
||||
|
||||
18
CHANGELOG.md
18
CHANGELOG.md
@@ -1,9 +1,3 @@
|
||||
## 19.2.1 (Dec 3, 2025)
|
||||
|
||||
### React Server Components
|
||||
|
||||
- Bring React Server Component fixes to Server Actions (@sebmarkbage [#35277](https://github.com/facebook/react/pull/35277))
|
||||
|
||||
## 19.2.0 (October 1st, 2025)
|
||||
|
||||
Below is a list of all new features, APIs, and bug fixes.
|
||||
@@ -77,12 +71,6 @@ Read the [React 19.2 release post](https://react.dev/blog/2025/10/01/react-19-2)
|
||||
|
||||
- [createContainer](https://github.com/facebook/react/blob/v19.2.0/packages/react-reconciler/src/ReactFiberReconciler.js#L255-L261) and [createHydrationContainer](https://github.com/facebook/react/blob/v19.2.0/packages/react-reconciler/src/ReactFiberReconciler.js#L305-L312) had their parameter order adjusted after `on*` handlers to account for upcoming experimental APIs
|
||||
|
||||
## 19.1.2 (Dec 3, 2025)
|
||||
|
||||
### React Server Components
|
||||
|
||||
- Bring React Server Component fixes to Server Actions (@sebmarkbage [#35277](https://github.com/facebook/react/pull/35277))
|
||||
|
||||
## 19.1.1 (July 28, 2025)
|
||||
|
||||
### React
|
||||
@@ -135,12 +123,6 @@ An Owner Stack is a string representing the components that are directly respons
|
||||
* Exposed `registerServerReference` in client builds to handle server references in different environments. [#32534](https://github.com/facebook/react/pull/32534)
|
||||
* Added react-server-dom-parcel package which integrates Server Components with the [Parcel bundler](https://parceljs.org/) [#31725](https://github.com/facebook/react/pull/31725), [#32132](https://github.com/facebook/react/pull/32132), [#31799](https://github.com/facebook/react/pull/31799), [#32294](https://github.com/facebook/react/pull/32294), [#31741](https://github.com/facebook/react/pull/31741)
|
||||
|
||||
## 19.0.1 (Dec 3, 2025)
|
||||
|
||||
### React Server Components
|
||||
|
||||
- Bring React Server Component fixes to Server Actions (@sebmarkbage [#35277](https://github.com/facebook/react/pull/35277))
|
||||
|
||||
## 19.0.0 (December 5, 2024)
|
||||
|
||||
Below is a list of all new features, APIs, deprecations, and breaking changes. Read [React 19 release post](https://react.dev/blog/2024/04/25/react-19) and [React 19 upgrade guide](https://react.dev/blog/2024/04/25/react-19-upgrade-guide) for more information.
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(yarn snap:*)",
|
||||
"Bash(yarn snap:build)",
|
||||
"Bash(node scripts/enable-feature-flag.js:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
2
compiler/.gitignore
vendored
2
compiler/.gitignore
vendored
@@ -8,9 +8,7 @@ dist
|
||||
.vscode
|
||||
!packages/playground/.vscode
|
||||
testfilter.txt
|
||||
.claude/settings.local.json
|
||||
|
||||
# forgive
|
||||
*.vsix
|
||||
.vscode-test
|
||||
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
# React Compiler Knowledge Base
|
||||
|
||||
This document contains knowledge about the React Compiler gathered during development sessions. It serves as a reference for understanding the codebase architecture and key concepts.
|
||||
|
||||
## Project Structure
|
||||
|
||||
- `packages/babel-plugin-react-compiler/` - Main compiler package
|
||||
- `src/HIR/` - High-level Intermediate Representation types and utilities
|
||||
- `src/Inference/` - Effect inference passes (aliasing, mutation, etc.)
|
||||
- `src/Validation/` - Validation passes that check for errors
|
||||
- `src/Entrypoint/Pipeline.ts` - Main compilation pipeline with pass ordering
|
||||
- `src/__tests__/fixtures/compiler/` - Test fixtures
|
||||
- `error.todo-*.js` - Unsupported feature, correctly throws Todo error (graceful bailout)
|
||||
- `error.bug-*.js` - Known bug, throws wrong error type or incorrect behavior
|
||||
- `*.expect.md` - Expected output for each fixture
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
yarn snap
|
||||
|
||||
# Run tests matching a pattern
|
||||
# Example: yarn snap -p 'error.*'
|
||||
yarn snap -p <pattern>
|
||||
|
||||
# Run a single fixture in debug mode. Use the path relative to the __tests__/fixtures/compiler directory
|
||||
# For each step of compilation, outputs the step name and state of the compiled program
|
||||
# Example: yarn snap -p simple.js -d
|
||||
yarn snap -p <file-basename> -d
|
||||
|
||||
# Update fixture outputs (also works with -p)
|
||||
yarn snap -u
|
||||
```
|
||||
|
||||
## Version Control
|
||||
|
||||
This repository uses Sapling (`sl`) for version control. Sapling is similar to Mercurial: there is not staging area, but new/deleted files must be explicitlyu added/removed.
|
||||
|
||||
```bash
|
||||
# Check status
|
||||
sl status
|
||||
|
||||
# Add new files, remove deleted files
|
||||
sl addremove
|
||||
|
||||
# Commit all changes
|
||||
sl commit -m "Your commit message"
|
||||
|
||||
# Commit with multi-line message using heredoc
|
||||
sl commit -m "$(cat <<'EOF'
|
||||
Summary line
|
||||
|
||||
Detailed description here
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### HIR (High-level Intermediate Representation)
|
||||
|
||||
The compiler converts source code to HIR for analysis. Key types in `src/HIR/HIR.ts`:
|
||||
|
||||
- **HIRFunction** - A function being compiled
|
||||
- `body.blocks` - Map of BasicBlocks
|
||||
- `context` - Captured variables from outer scope
|
||||
- `params` - Function parameters
|
||||
- `returns` - The function's return place
|
||||
- `aliasingEffects` - Effects that describe the function's behavior when called
|
||||
|
||||
- **Instruction** - A single operation
|
||||
- `lvalue` - The place being assigned to
|
||||
- `value` - The instruction kind (CallExpression, FunctionExpression, LoadLocal, etc.)
|
||||
- `effects` - Array of AliasingEffects for this instruction
|
||||
|
||||
- **Terminal** - Block terminators (return, branch, etc.)
|
||||
- `effects` - Array of AliasingEffects
|
||||
|
||||
- **Place** - A reference to a value
|
||||
- `identifier.id` - Unique IdentifierId
|
||||
|
||||
- **Phi nodes** - Join points for values from different control flow paths
|
||||
- Located at `block.phis`
|
||||
- `phi.place` - The result place
|
||||
- `phi.operands` - Map of predecessor block to source place
|
||||
|
||||
### AliasingEffects System
|
||||
|
||||
Effects describe data flow and operations. Defined in `src/Inference/AliasingEffects.ts`:
|
||||
|
||||
**Data Flow Effects:**
|
||||
- `Impure` - Marks a place as containing an impure value (e.g., Date.now() result, ref.current)
|
||||
- `Capture a -> b` - Value from `a` is captured into `b` (mutable capture)
|
||||
- `Alias a -> b` - `b` aliases `a`
|
||||
- `ImmutableCapture a -> b` - Immutable capture (like Capture but read-only)
|
||||
- `Assign a -> b` - Direct assignment
|
||||
- `MaybeAlias a -> b` - Possible aliasing
|
||||
- `CreateFrom a -> b` - Created from source
|
||||
|
||||
**Mutation Effects:**
|
||||
- `Mutate value` - Value is mutated
|
||||
- `MutateTransitive value` - Value and transitive captures are mutated
|
||||
- `MutateConditionally value` - May mutate
|
||||
- `MutateTransitiveConditionally value` - May mutate transitively
|
||||
|
||||
**Other Effects:**
|
||||
- `Render place` - Place is used in render context (JSX props, component return)
|
||||
- `Freeze place` - Place is frozen (made immutable)
|
||||
- `Create place` - New value created
|
||||
- `CreateFunction` - Function expression created, includes `captures` array
|
||||
- `Apply` - Function application with receiver, function, args, and result
|
||||
|
||||
### Hook Aliasing Signatures
|
||||
|
||||
Located in `src/HIR/Globals.ts`, hooks can define custom aliasing signatures to control how data flows through them.
|
||||
|
||||
**Structure:**
|
||||
```typescript
|
||||
aliasing: {
|
||||
receiver: '@receiver', // The hook function itself
|
||||
params: ['@param0'], // Named positional parameters
|
||||
rest: '@rest', // Rest parameters (or null)
|
||||
returns: '@returns', // Return value
|
||||
temporaries: [], // Temporary values during execution
|
||||
effects: [ // Array of effects to apply when hook is called
|
||||
{kind: 'Freeze', value: '@param0', reason: ValueReason.HookCaptured},
|
||||
{kind: 'Assign', from: '@param0', into: '@returns'},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
**Common patterns:**
|
||||
|
||||
1. **RenderHookAliasing** (useState, useContext, useMemo, useCallback):
|
||||
- Freezes arguments (`Freeze @rest`)
|
||||
- Marks arguments as render-time (`Render @rest`)
|
||||
- Creates frozen return value
|
||||
- Aliases arguments to return
|
||||
|
||||
2. **EffectHookAliasing** (useEffect, useLayoutEffect, useInsertionEffect):
|
||||
- Freezes function and deps
|
||||
- Creates internal effect object
|
||||
- Captures function and deps into effect
|
||||
- Returns undefined
|
||||
|
||||
3. **Event handler hooks** (useEffectEvent):
|
||||
- Freezes callback (`Freeze @fn`)
|
||||
- Aliases input to return (`Assign @fn -> @returns`)
|
||||
- NO Render effect (callback not called during render)
|
||||
|
||||
**Example: useEffectEvent**
|
||||
```typescript
|
||||
const UseEffectEventHook = addHook(
|
||||
DEFAULT_SHAPES,
|
||||
{
|
||||
positionalParams: [Effect.Freeze], // Takes one positional param
|
||||
restParam: null,
|
||||
returnType: {kind: 'Function', ...},
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: 'useEffectEvent',
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
aliasing: {
|
||||
receiver: '@receiver',
|
||||
params: ['@fn'], // Name for the callback parameter
|
||||
rest: null,
|
||||
returns: '@returns',
|
||||
temporaries: [],
|
||||
effects: [
|
||||
{kind: 'Freeze', value: '@fn', reason: ValueReason.HookCaptured},
|
||||
{kind: 'Assign', from: '@fn', into: '@returns'},
|
||||
// Note: NO Render effect - callback is not called during render
|
||||
],
|
||||
},
|
||||
},
|
||||
BuiltInUseEffectEventId,
|
||||
);
|
||||
|
||||
// Add as both names for compatibility
|
||||
['useEffectEvent', UseEffectEventHook],
|
||||
['experimental_useEffectEvent', UseEffectEventHook],
|
||||
```
|
||||
|
||||
**Key insight:** If a hook is missing an `aliasing` config, it falls back to `DefaultNonmutatingHook` which includes a `Render` effect on all arguments. This can cause false positives for hooks like `useEffectEvent` whose callbacks are not called during render.
|
||||
|
||||
## Feature Flags
|
||||
|
||||
Feature flags are configured in `src/HIR/Environment.ts`, for example `enableJsxOutlining`. Test fixtures can override the active feature flags used for that fixture via a comment pragma on the first line of the fixture input, for example:
|
||||
|
||||
```javascript
|
||||
// enableJsxOutlining @enableChangeVariableCodegen:false
|
||||
|
||||
...code...
|
||||
```
|
||||
|
||||
Would enable the `enableJsxOutlining` feature and disable the `enableChangeVariableCodegen` feature.
|
||||
|
||||
## Debugging Tips
|
||||
|
||||
1. Run `yarn snap -p <fixture>` to see full HIR output with effects
|
||||
2. Look for `@aliasingEffects=` on FunctionExpressions
|
||||
3. Look for `Impure`, `Render`, `Capture` effects on instructions
|
||||
4. Check the pass ordering in Pipeline.ts to understand when effects are populated vs validated
|
||||
|
||||
## Error Handling for Unsupported Features
|
||||
|
||||
When the compiler encounters an unsupported but known pattern, use `CompilerError.throwTodo()` instead of `CompilerError.invariant()`. Todo errors cause graceful bailouts in production; Invariant errors are hard failures indicating unexpected/invalid states.
|
||||
|
||||
```typescript
|
||||
// Unsupported but expected pattern - graceful bailout
|
||||
CompilerError.throwTodo({
|
||||
reason: `Support [description of unsupported feature]`,
|
||||
loc: terminal.loc,
|
||||
});
|
||||
|
||||
// Invariant is for truly unexpected/invalid states - hard failure
|
||||
CompilerError.invariant(false, {
|
||||
reason: `Unexpected [thing]`,
|
||||
loc: terminal.loc,
|
||||
});
|
||||
```
|
||||
1
compiler/apps/playground/.gitignore
vendored
1
compiler/apps/playground/.gitignore
vendored
@@ -12,7 +12,6 @@
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
/next-env.d.ts
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
@@ -14,6 +14,7 @@ import React, {
|
||||
unstable_ViewTransition as ViewTransition,
|
||||
unstable_addTransitionType as addTransitionType,
|
||||
startTransition,
|
||||
Activity,
|
||||
} from 'react';
|
||||
import {Resizable} from 're-resizable';
|
||||
import {useStore, useStoreDispatch} from '../StoreContext';
|
||||
@@ -33,14 +34,9 @@ export default function ConfigEditor({
|
||||
}): React.ReactElement {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
// TODO: Add back <Activity> after upgrading next.js
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
display: isExpanded ? 'block' : 'none',
|
||||
}}>
|
||||
{/* <Activity mode={isExpanded ? 'visible' : 'hidden'}> */}
|
||||
<Activity mode={isExpanded ? 'visible' : 'hidden'}>
|
||||
<ExpandedEditor
|
||||
onToggle={() => {
|
||||
startTransition(() => {
|
||||
@@ -50,13 +46,8 @@ export default function ConfigEditor({
|
||||
}}
|
||||
formattedAppliedConfig={formattedAppliedConfig}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: !isExpanded ? 'block' : 'none',
|
||||
}}>
|
||||
{/* </Activity>
|
||||
<Activity mode={isExpanded ? 'hidden' : 'visible'}></Activity> */}
|
||||
</Activity>
|
||||
<Activity mode={isExpanded ? 'hidden' : 'visible'}>
|
||||
<CollapsedEditor
|
||||
onToggle={() => {
|
||||
startTransition(() => {
|
||||
@@ -65,8 +56,7 @@ export default function ConfigEditor({
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/* </Activity> */}
|
||||
</Activity>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -126,9 +116,8 @@ function ExpandedEditor({
|
||||
|
||||
return (
|
||||
<ViewTransition
|
||||
update={{[CONFIG_PANEL_TRANSITION]: 'slide-in', default: 'none'}}>
|
||||
{/* enter={{[CONFIG_PANEL_TRANSITION]: 'slide-in', default: 'none'}}
|
||||
exit={{[CONFIG_PANEL_TRANSITION]: 'slide-out', default: 'none'}}> */}
|
||||
enter={{[CONFIG_PANEL_TRANSITION]: 'slide-in', default: 'none'}}
|
||||
exit={{[CONFIG_PANEL_TRANSITION]: 'slide-out', default: 'none'}}>
|
||||
<Resizable
|
||||
minWidth={300}
|
||||
maxWidth={600}
|
||||
|
||||
6
compiler/apps/playground/next-env.d.ts
vendored
Normal file
6
compiler/apps/playground/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
@@ -35,13 +35,13 @@
|
||||
"lru-cache": "^11.2.2",
|
||||
"lz-string": "^1.5.0",
|
||||
"monaco-editor": "^0.52.0",
|
||||
"next": "15.5.9",
|
||||
"next": "15.6.0-canary.7",
|
||||
"notistack": "^3.0.0-alpha.7",
|
||||
"prettier": "^3.3.3",
|
||||
"pretty-format": "^29.3.1",
|
||||
"re-resizable": "^6.9.16",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3"
|
||||
"react": "19.2",
|
||||
"react-dom": "19.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "18.11.9",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
|
||||
@@ -715,10 +715,10 @@
|
||||
dependencies:
|
||||
"@monaco-editor/loader" "^1.6.1"
|
||||
|
||||
"@next/env@15.5.9":
|
||||
version "15.5.9"
|
||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-15.5.9.tgz#53c2c34dc17cd87b61f70c6cc211e303123b2ab8"
|
||||
integrity sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg==
|
||||
"@next/env@15.6.0-canary.7":
|
||||
version "15.6.0-canary.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-15.6.0-canary.7.tgz#cdbf2967a9437ef09eef755e203f315acc4d8d8f"
|
||||
integrity sha512-LNZ7Yd3Cl9rKvjYdeJmszf2HmSDP76SQmfafKep2Ux16ZXKoN5OjwVHFTltKNdsB3vt2t+XJzLP2rhw5lBoFBA==
|
||||
|
||||
"@next/eslint-plugin-next@15.5.2":
|
||||
version "15.5.2"
|
||||
@@ -727,45 +727,45 @@
|
||||
dependencies:
|
||||
fast-glob "3.3.1"
|
||||
|
||||
"@next/swc-darwin-arm64@15.5.7":
|
||||
version "15.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz#f0c9ccfec2cd87cbd4b241ce4c779a7017aed958"
|
||||
integrity sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==
|
||||
"@next/swc-darwin-arm64@15.6.0-canary.7":
|
||||
version "15.6.0-canary.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.6.0-canary.7.tgz#628cd34ce9120000f1cb5b08963426431174fc57"
|
||||
integrity sha512-POsBrxhrR3qvqXV+JZ6ZoBc8gJf8rhYe+OedceI1piPVqtJYOJa3EB4eaqcc+kMsllKRrH/goNlhLwtyhE+0Qg==
|
||||
|
||||
"@next/swc-darwin-x64@15.5.7":
|
||||
version "15.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.7.tgz#18009e9fcffc5c0687cc9db24182ddeac56280d9"
|
||||
integrity sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==
|
||||
"@next/swc-darwin-x64@15.6.0-canary.7":
|
||||
version "15.6.0-canary.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.6.0-canary.7.tgz#37d4ebab14da74a2f8028daf6d76aab410153e06"
|
||||
integrity sha512-lmk9ysBuSiPlAJZTCo/3O4mXNFosg6EDIf4GrmynIwCG2as6/KxzyD1WqFp56Exp8eFDjP7SFapD10sV43vCsA==
|
||||
|
||||
"@next/swc-linux-arm64-gnu@15.5.7":
|
||||
version "15.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.7.tgz#fe7c7e08264cf522d4e524299f6d3e63d68d579a"
|
||||
integrity sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==
|
||||
"@next/swc-linux-arm64-gnu@15.6.0-canary.7":
|
||||
version "15.6.0-canary.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.6.0-canary.7.tgz#ce700cc0e0d24763136838223105a524b36694fa"
|
||||
integrity sha512-why8k6d0SBm3AKoOD5S7ir3g+BF34l9oFKIoZrLaZaKBvNGpFcjc7Ovc2TunNMeaMJzv9k1dHYSap0EI5oSuzg==
|
||||
|
||||
"@next/swc-linux-arm64-musl@15.5.7":
|
||||
version "15.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.7.tgz#94228fe293475ec34a5a54284e1056876f43a3cf"
|
||||
integrity sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==
|
||||
"@next/swc-linux-arm64-musl@15.6.0-canary.7":
|
||||
version "15.6.0-canary.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.6.0-canary.7.tgz#c791b8e15bf2c338b4cc0387fe7afb3ef83ecfcf"
|
||||
integrity sha512-HzvTRsKvYj32Va4YuJN3n3xOxvk+6QwB63d/EsgmdkeA/vrqciUAmJDYpuzZEvRc3Yp2nyPq8KZxtHAr6ISZ2Q==
|
||||
|
||||
"@next/swc-linux-x64-gnu@15.5.7":
|
||||
version "15.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz#078c71201dfe7fcfb8fa6dc92aae6c94bc011cdc"
|
||||
integrity sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==
|
||||
"@next/swc-linux-x64-gnu@15.6.0-canary.7":
|
||||
version "15.6.0-canary.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.6.0-canary.7.tgz#c01c3a3d8e71660c49298dd053d078379b6b5919"
|
||||
integrity sha512-6yRFrg2qWXOqa+1BI53J9EmHWFzKg9U2r+5R7n7BFUp8PH5SC92WBsmYTnh/RkvAYvdupiVzMervwwswCs6kFg==
|
||||
|
||||
"@next/swc-linux-x64-musl@15.5.7":
|
||||
version "15.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz#72947f5357f9226292353e0bb775643da3c7a182"
|
||||
integrity sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==
|
||||
"@next/swc-linux-x64-musl@15.6.0-canary.7":
|
||||
version "15.6.0-canary.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.6.0-canary.7.tgz#3f4b39faef4a5f88b13e4c726b008ddc9717f819"
|
||||
integrity sha512-O/JjvOvNK/Wao/OIQaA6evDkxkmFFQgJ1/hI1dVk6/PAeKmW2/Q+6Dodh97eAkOwedS1ZdQl2mojf87TzLvzdQ==
|
||||
|
||||
"@next/swc-win32-arm64-msvc@15.5.7":
|
||||
version "15.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.7.tgz#397b912cd51c6a80e32b9c0507ecd82514353941"
|
||||
integrity sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==
|
||||
"@next/swc-win32-arm64-msvc@15.6.0-canary.7":
|
||||
version "15.6.0-canary.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.6.0-canary.7.tgz#9bc5da0907b7ce67eedda02a6d56a09d9a539ccf"
|
||||
integrity sha512-p9DvrDgnePofZCtiWVY7qZtwXxiOGJlAyy2LoGPYSGOUDhjbTG8j6XMUFXpV9UwpH+l7st522psO1BVzbpT8IQ==
|
||||
|
||||
"@next/swc-win32-x64-msvc@15.5.7":
|
||||
version "15.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.7.tgz#e02b543d9dc6c1631d4ac239cb1177245dfedfe4"
|
||||
integrity sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==
|
||||
"@next/swc-win32-x64-msvc@15.6.0-canary.7":
|
||||
version "15.6.0-canary.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.6.0-canary.7.tgz#5b271c591ccbe67d5fa966dd22db86c547414fd1"
|
||||
integrity sha512-f1ywT3xWu4StWKA1mZRyGfelu/h+W0OEEyBxQNXzXyYa0VGZb9LyCNb5cYoNKBm0Bw18Hp1PVe0bHuusemGCcw==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
@@ -3204,25 +3204,25 @@ natural-compare@^1.4.0:
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||
|
||||
next@15.5.9:
|
||||
version "15.5.9"
|
||||
resolved "https://registry.yarnpkg.com/next/-/next-15.5.9.tgz#1b80d05865cc27e710fb4dcfc6fd9e726ed12ad4"
|
||||
integrity sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==
|
||||
next@15.6.0-canary.7:
|
||||
version "15.6.0-canary.7"
|
||||
resolved "https://registry.yarnpkg.com/next/-/next-15.6.0-canary.7.tgz#bfc2ac3c9a78e23d550c303d18247a263e6b5bc1"
|
||||
integrity sha512-4ukX2mxat9wWT6E0Gw/3TOR9ULV1q399E42F86cwsPSFgTWa04ABhcTqO0r9J/QR1YWPR8WEgh9qUzmWA/1yEw==
|
||||
dependencies:
|
||||
"@next/env" "15.5.9"
|
||||
"@next/env" "15.6.0-canary.7"
|
||||
"@swc/helpers" "0.5.15"
|
||||
caniuse-lite "^1.0.30001579"
|
||||
postcss "8.4.31"
|
||||
styled-jsx "5.1.6"
|
||||
optionalDependencies:
|
||||
"@next/swc-darwin-arm64" "15.5.7"
|
||||
"@next/swc-darwin-x64" "15.5.7"
|
||||
"@next/swc-linux-arm64-gnu" "15.5.7"
|
||||
"@next/swc-linux-arm64-musl" "15.5.7"
|
||||
"@next/swc-linux-x64-gnu" "15.5.7"
|
||||
"@next/swc-linux-x64-musl" "15.5.7"
|
||||
"@next/swc-win32-arm64-msvc" "15.5.7"
|
||||
"@next/swc-win32-x64-msvc" "15.5.7"
|
||||
"@next/swc-darwin-arm64" "15.6.0-canary.7"
|
||||
"@next/swc-darwin-x64" "15.6.0-canary.7"
|
||||
"@next/swc-linux-arm64-gnu" "15.6.0-canary.7"
|
||||
"@next/swc-linux-arm64-musl" "15.6.0-canary.7"
|
||||
"@next/swc-linux-x64-gnu" "15.6.0-canary.7"
|
||||
"@next/swc-linux-x64-musl" "15.6.0-canary.7"
|
||||
"@next/swc-win32-arm64-msvc" "15.6.0-canary.7"
|
||||
"@next/swc-win32-x64-msvc" "15.6.0-canary.7"
|
||||
sharp "^0.34.3"
|
||||
|
||||
node-releases@^2.0.18:
|
||||
@@ -3582,10 +3582,10 @@ re-resizable@^6.9.16:
|
||||
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.10.0.tgz#d684a096ab438f1a93f59ad3a580a206b0ce31ee"
|
||||
integrity sha512-hysSK0xmA5nz24HBVztlk4yCqCLCvS32E6ZpWxVKop9x3tqCa4yAj1++facrmkOf62JsJHjmjABdKxXofYioCw==
|
||||
|
||||
react-dom@19.2.3:
|
||||
version "19.2.3"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.3.tgz#f0b61d7e5c4a86773889fcc1853af3ed5f215b17"
|
||||
integrity sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==
|
||||
react-dom@19.2:
|
||||
version "19.2.0"
|
||||
resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz#00ed1e959c365e9a9d48f8918377465466ec3af8"
|
||||
integrity sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==
|
||||
dependencies:
|
||||
scheduler "^0.27.0"
|
||||
|
||||
@@ -3599,10 +3599,10 @@ react-is@^18.0.0:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
|
||||
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
|
||||
|
||||
react@19.2.3:
|
||||
version "19.2.3"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-19.2.3.tgz#d83e5e8e7a258cf6b4fe28640515f99b87cd19b8"
|
||||
integrity sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==
|
||||
react@19.2:
|
||||
version "19.2.0"
|
||||
resolved "https://registry.npmjs.org/react/-/react-19.2.0.tgz#d33dd1721698f4376ae57a54098cb47fc75d93a5"
|
||||
integrity sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==
|
||||
|
||||
read-cache@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
||||
@@ -304,30 +304,6 @@ export class CompilerError extends Error {
|
||||
disabledDetails: Array<CompilerErrorDetail | CompilerDiagnostic> = [];
|
||||
printedMessage: string | null = null;
|
||||
|
||||
static simpleInvariant(
|
||||
condition: unknown,
|
||||
options: {
|
||||
reason: CompilerDiagnosticOptions['reason'];
|
||||
description?: CompilerDiagnosticOptions['description'];
|
||||
loc: SourceLocation;
|
||||
},
|
||||
): asserts condition {
|
||||
if (!condition) {
|
||||
const errors = new CompilerError();
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
reason: options.reason,
|
||||
description: options.description ?? null,
|
||||
category: ErrorCategory.Invariant,
|
||||
}).withDetails({
|
||||
kind: 'error',
|
||||
loc: options.loc,
|
||||
message: options.reason,
|
||||
}),
|
||||
);
|
||||
throw errors;
|
||||
}
|
||||
}
|
||||
static invariant(
|
||||
condition: unknown,
|
||||
options: Omit<CompilerDiagnosticOptions, 'category'>,
|
||||
@@ -600,9 +576,7 @@ function printErrorSummary(category: ErrorCategory, message: string): string {
|
||||
case ErrorCategory.Suppression:
|
||||
case ErrorCategory.Syntax:
|
||||
case ErrorCategory.UseMemo:
|
||||
case ErrorCategory.VoidUseMemo:
|
||||
case ErrorCategory.MemoDependencies:
|
||||
case ErrorCategory.EffectExhaustiveDependencies: {
|
||||
case ErrorCategory.VoidUseMemo: {
|
||||
heading = 'Error';
|
||||
break;
|
||||
}
|
||||
@@ -660,10 +634,6 @@ export enum ErrorCategory {
|
||||
* Checks that manual memoization is preserved
|
||||
*/
|
||||
PreserveManualMemo = 'PreserveManualMemo',
|
||||
/**
|
||||
* Checks for exhaustive useMemo/useCallback dependencies without extraneous values
|
||||
*/
|
||||
MemoDependencies = 'MemoDependencies',
|
||||
/**
|
||||
* Checks for known incompatible libraries
|
||||
*/
|
||||
@@ -684,10 +654,6 @@ export enum ErrorCategory {
|
||||
* Checks for memoized effect deps
|
||||
*/
|
||||
EffectDependencies = 'EffectDependencies',
|
||||
/**
|
||||
* Checks for exhaustive and extraneous effect dependencies
|
||||
*/
|
||||
EffectExhaustiveDependencies = 'EffectExhaustiveDependencies',
|
||||
/**
|
||||
* Checks for no setState in effect bodies
|
||||
*/
|
||||
@@ -843,16 +809,6 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
|
||||
preset: LintRulePreset.Off,
|
||||
};
|
||||
}
|
||||
case ErrorCategory.EffectExhaustiveDependencies: {
|
||||
return {
|
||||
category,
|
||||
severity: ErrorSeverity.Error,
|
||||
name: 'exhaustive-effect-dependencies',
|
||||
description:
|
||||
'Validates that effect dependencies are exhaustive and without extraneous values',
|
||||
preset: LintRulePreset.Off,
|
||||
};
|
||||
}
|
||||
case ErrorCategory.EffectDerivationsOfState: {
|
||||
return {
|
||||
category,
|
||||
@@ -869,9 +825,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
|
||||
severity: ErrorSeverity.Error,
|
||||
name: 'set-state-in-effect',
|
||||
description:
|
||||
'Validates against calling setState synchronously in an effect. ' +
|
||||
'This can indicate non-local derived data, a derived event pattern, or ' +
|
||||
'improper external data synchronization.',
|
||||
'Validates against calling setState synchronously in an effect, which can lead to re-renders that degrade performance',
|
||||
preset: LintRulePreset.Recommended,
|
||||
};
|
||||
}
|
||||
@@ -1077,24 +1031,6 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
|
||||
preset: LintRulePreset.RecommendedLatest,
|
||||
};
|
||||
}
|
||||
case ErrorCategory.MemoDependencies: {
|
||||
return {
|
||||
category,
|
||||
severity: ErrorSeverity.Error,
|
||||
name: 'memo-dependencies',
|
||||
description:
|
||||
'Validates that useMemo() and useCallback() specify comprehensive dependencies without extraneous values. See [`useMemo()` docs](https://react.dev/reference/react/useMemo) for more information.',
|
||||
/**
|
||||
* TODO: the "MemoDependencies" rule largely reimplements the "exhaustive-deps" non-compiler rule,
|
||||
* allowing the compiler to ensure it does not regress change behavior due to different dependencies.
|
||||
* We previously relied on the source having ESLint suppressions for any exhaustive-deps violations,
|
||||
* but it's more reliable to verify it within the compiler.
|
||||
*
|
||||
* Long-term we should de-duplicate these implementations.
|
||||
*/
|
||||
preset: LintRulePreset.Off,
|
||||
};
|
||||
}
|
||||
case ErrorCategory.IncompatibleLibrary: {
|
||||
return {
|
||||
category,
|
||||
|
||||
@@ -105,7 +105,6 @@ import {validateNoDerivedComputationsInEffects} from '../Validation/ValidateNoDe
|
||||
import {validateNoDerivedComputationsInEffects_exp} from '../Validation/ValidateNoDerivedComputationsInEffects_exp';
|
||||
import {nameAnonymousFunctions} from '../Transform/NameAnonymousFunctions';
|
||||
import {optimizeForSSR} from '../Optimization/OptimizeForSSR';
|
||||
import {validateExhaustiveDependencies} from '../Validation/ValidateExhaustiveDependencies';
|
||||
import {validateSourceLocations} from '../Validation/ValidateSourceLocations';
|
||||
|
||||
export type CompilerPipelineValue =
|
||||
@@ -279,20 +278,17 @@ function runWithEnvironment(
|
||||
validateNoSetStateInRender(hir).unwrap();
|
||||
}
|
||||
|
||||
if (
|
||||
env.config.validateNoDerivedComputationsInEffects_exp &&
|
||||
env.outputMode === 'lint'
|
||||
) {
|
||||
if (env.config.validateNoDerivedComputationsInEffects_exp) {
|
||||
env.logErrors(validateNoDerivedComputationsInEffects_exp(hir));
|
||||
} else if (env.config.validateNoDerivedComputationsInEffects) {
|
||||
validateNoDerivedComputationsInEffects(hir);
|
||||
}
|
||||
|
||||
if (env.config.validateNoSetStateInEffects && env.outputMode === 'lint') {
|
||||
if (env.config.validateNoSetStateInEffects) {
|
||||
env.logErrors(validateNoSetStateInEffects(hir, env));
|
||||
}
|
||||
|
||||
if (env.config.validateNoJSXInTryStatements && env.outputMode === 'lint') {
|
||||
if (env.config.validateNoJSXInTryStatements) {
|
||||
env.logErrors(validateNoJSXInTryStatement(hir));
|
||||
}
|
||||
|
||||
@@ -306,16 +302,6 @@ function runWithEnvironment(
|
||||
inferReactivePlaces(hir);
|
||||
log({kind: 'hir', name: 'InferReactivePlaces', value: hir});
|
||||
|
||||
if (env.enableValidations) {
|
||||
if (
|
||||
env.config.validateExhaustiveMemoizationDependencies ||
|
||||
env.config.validateExhaustiveEffectDependencies
|
||||
) {
|
||||
// NOTE: this relies on reactivity inference running first
|
||||
validateExhaustiveDependencies(hir).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
rewriteInstructionKindsBasedOnReassignment(hir);
|
||||
log({
|
||||
kind: 'hir',
|
||||
@@ -323,11 +309,7 @@ function runWithEnvironment(
|
||||
value: hir,
|
||||
});
|
||||
|
||||
if (
|
||||
env.enableValidations &&
|
||||
env.config.validateStaticComponents &&
|
||||
env.outputMode === 'lint'
|
||||
) {
|
||||
if (env.enableValidations && env.config.validateStaticComponents) {
|
||||
env.logErrors(validateStaticComponents(hir));
|
||||
}
|
||||
|
||||
|
||||
@@ -400,15 +400,7 @@ export function compileProgram(
|
||||
*/
|
||||
const suppressions = findProgramSuppressions(
|
||||
pass.comments,
|
||||
/*
|
||||
* If the compiler is validating hooks rules and exhaustive memo dependencies, we don't need to check
|
||||
* for React ESLint suppressions
|
||||
*/
|
||||
pass.opts.environment.validateExhaustiveMemoizationDependencies &&
|
||||
pass.opts.environment.validateHooksUsage
|
||||
? null
|
||||
: (pass.opts.eslintSuppressionRules ?? DEFAULT_ESLINT_SUPPRESSIONS),
|
||||
// Always bail on Flow suppressions
|
||||
pass.opts.eslintSuppressionRules ?? DEFAULT_ESLINT_SUPPRESSIONS,
|
||||
pass.opts.flowSuppressions,
|
||||
);
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ export function filterSuppressionsThatAffectFunction(
|
||||
|
||||
export function findProgramSuppressions(
|
||||
programComments: Array<t.Comment>,
|
||||
ruleNames: Array<string> | null,
|
||||
ruleNames: Array<string>,
|
||||
flowSuppressions: boolean,
|
||||
): Array<SuppressionRange> {
|
||||
const suppressionRanges: Array<SuppressionRange> = [];
|
||||
@@ -89,7 +89,7 @@ export function findProgramSuppressions(
|
||||
let disableNextLinePattern: RegExp | null = null;
|
||||
let disablePattern: RegExp | null = null;
|
||||
let enablePattern: RegExp | null = null;
|
||||
if (ruleNames != null && ruleNames.length !== 0) {
|
||||
if (ruleNames.length !== 0) {
|
||||
const rulePattern = `(${ruleNames.join('|')})`;
|
||||
disableNextLinePattern = new RegExp(
|
||||
`eslint-disable-next-line ${rulePattern}`,
|
||||
|
||||
@@ -4026,7 +4026,6 @@ function lowerAssignment(
|
||||
pattern: {
|
||||
kind: 'ArrayPattern',
|
||||
items,
|
||||
loc: lvalue.node.loc ?? GeneratedSource,
|
||||
},
|
||||
},
|
||||
value,
|
||||
@@ -4204,7 +4203,6 @@ function lowerAssignment(
|
||||
pattern: {
|
||||
kind: 'ObjectPattern',
|
||||
properties,
|
||||
loc: lvalue.node.loc ?? GeneratedSource,
|
||||
},
|
||||
},
|
||||
value,
|
||||
|
||||
@@ -218,23 +218,6 @@ export const EnvironmentConfigSchema = z.object({
|
||||
*/
|
||||
validatePreserveExistingMemoizationGuarantees: z.boolean().default(true),
|
||||
|
||||
/**
|
||||
* Validate that dependencies supplied to manual memoization calls are exhaustive.
|
||||
*/
|
||||
validateExhaustiveMemoizationDependencies: z.boolean().default(true),
|
||||
|
||||
/**
|
||||
* Validate that dependencies supplied to effect hooks are exhaustive.
|
||||
* Can be:
|
||||
* - 'off': No validation (default)
|
||||
* - 'all': Validate and report both missing and extra dependencies
|
||||
* - 'missing-only': Only report missing dependencies
|
||||
* - 'extra-only': Only report extra/unnecessary dependencies
|
||||
*/
|
||||
validateExhaustiveEffectDependencies: z
|
||||
.enum(['off', 'all', 'missing-only', 'extra-only'])
|
||||
.default('off'),
|
||||
|
||||
/**
|
||||
* When this is true, rather than pruning existing manual memoization but ensuring or validating
|
||||
* that the memoized values remain memoized, the compiler will simply not prune existing calls to
|
||||
@@ -330,12 +313,6 @@ export const EnvironmentConfigSchema = z.object({
|
||||
*/
|
||||
validateNoSetStateInRender: z.boolean().default(true),
|
||||
|
||||
/**
|
||||
* When enabled, changes the behavior of validateNoSetStateInRender to recommend
|
||||
* using useKeyedState instead of the manual pattern for resetting state.
|
||||
*/
|
||||
enableUseKeyedState: z.boolean().default(false),
|
||||
|
||||
/**
|
||||
* Validates that setState is not called synchronously within an effect (useEffect and friends).
|
||||
* Scheduling a setState (with an event listener, subscription, etc) is valid.
|
||||
@@ -707,16 +684,6 @@ export const EnvironmentConfigSchema = z.object({
|
||||
*/
|
||||
enableAllowSetStateFromRefsInEffects: z.boolean().default(true),
|
||||
|
||||
/**
|
||||
* When enabled, provides verbose error messages for setState calls within effects,
|
||||
* presenting multiple possible fixes to the user/agent since we cannot statically
|
||||
* determine which specific use-case applies:
|
||||
* 1. Non-local derived data - requires restructuring state ownership
|
||||
* 2. Derived event pattern - detecting when a prop changes
|
||||
* 3. Force update / external sync - should use useSyncExternalStore
|
||||
*/
|
||||
enableVerboseNoSetStateInEffect: z.boolean().default(false),
|
||||
|
||||
/**
|
||||
* Enables inference of event handler types for JSX props on built-in DOM elements.
|
||||
* When enabled, functions passed to event handler props (props starting with "on")
|
||||
|
||||
@@ -694,13 +694,11 @@ export type SpreadPattern = {
|
||||
export type ArrayPattern = {
|
||||
kind: 'ArrayPattern';
|
||||
items: Array<Place | SpreadPattern | Hole>;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
|
||||
export type ObjectPattern = {
|
||||
kind: 'ObjectPattern';
|
||||
properties: Array<ObjectProperty | SpreadPattern>;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
|
||||
export type ObjectPropertyKey =
|
||||
@@ -805,11 +803,9 @@ export type ManualMemoDependency = {
|
||||
| {
|
||||
kind: 'NamedLocal';
|
||||
value: Place;
|
||||
constant: boolean;
|
||||
}
|
||||
| {kind: 'Global'; identifierName: string};
|
||||
path: DependencyPath;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
|
||||
export type StartMemoize = {
|
||||
@@ -821,11 +817,6 @@ export type StartMemoize = {
|
||||
* (e.g. useMemo without a second arg)
|
||||
*/
|
||||
deps: Array<ManualMemoDependency> | null;
|
||||
/**
|
||||
* The source location of the dependencies argument. Used for
|
||||
* emitting diagnostics with a suggested replacement
|
||||
*/
|
||||
depsLoc: SourceLocation | null;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
export type FinishMemoize = {
|
||||
@@ -1689,28 +1680,6 @@ export function areEqualPaths(a: DependencyPath, b: DependencyPath): boolean {
|
||||
)
|
||||
);
|
||||
}
|
||||
export function isSubPath(
|
||||
subpath: DependencyPath,
|
||||
path: DependencyPath,
|
||||
): boolean {
|
||||
return (
|
||||
subpath.length <= path.length &&
|
||||
subpath.every(
|
||||
(item, ix) =>
|
||||
item.property === path[ix].property &&
|
||||
item.optional === path[ix].optional,
|
||||
)
|
||||
);
|
||||
}
|
||||
export function isSubPathIgnoringOptionals(
|
||||
subpath: DependencyPath,
|
||||
path: DependencyPath,
|
||||
): boolean {
|
||||
return (
|
||||
subpath.length <= path.length &&
|
||||
subpath.every((item, ix) => item.property === path[ix].property)
|
||||
);
|
||||
}
|
||||
|
||||
export function getPlaceScope(
|
||||
id: InstructionId,
|
||||
@@ -2025,11 +1994,6 @@ export function isUseInsertionEffectHookType(id: Identifier): boolean {
|
||||
id.type.shapeId === 'BuiltInUseInsertionEffectHook'
|
||||
);
|
||||
}
|
||||
export function isUseEffectEventType(id: Identifier): boolean {
|
||||
return (
|
||||
id.type.kind === 'Function' && id.type.shapeId === 'BuiltInUseEffectEvent'
|
||||
);
|
||||
}
|
||||
|
||||
export function isUseContextHookType(id: Identifier): boolean {
|
||||
return (
|
||||
|
||||
@@ -42,7 +42,7 @@ type IdentifierSidemap = {
|
||||
functions: Map<IdentifierId, TInstruction<FunctionExpression>>;
|
||||
manualMemos: Map<IdentifierId, ManualMemoCallee>;
|
||||
react: Set<IdentifierId>;
|
||||
maybeDepsLists: Map<IdentifierId, {loc: SourceLocation; deps: Array<Place>}>;
|
||||
maybeDepsLists: Map<IdentifierId, Array<Place>>;
|
||||
maybeDeps: Map<IdentifierId, ManualMemoDependency>;
|
||||
optionals: Set<IdentifierId>;
|
||||
};
|
||||
@@ -65,7 +65,6 @@ export function collectMaybeMemoDependencies(
|
||||
identifierName: value.binding.name,
|
||||
},
|
||||
path: [],
|
||||
loc: value.loc,
|
||||
};
|
||||
}
|
||||
case 'PropertyLoad': {
|
||||
@@ -75,7 +74,6 @@ export function collectMaybeMemoDependencies(
|
||||
root: object.root,
|
||||
// TODO: determine if the access is optional
|
||||
path: [...object.path, {property: value.property, optional}],
|
||||
loc: value.loc,
|
||||
};
|
||||
}
|
||||
break;
|
||||
@@ -94,10 +92,8 @@ export function collectMaybeMemoDependencies(
|
||||
root: {
|
||||
kind: 'NamedLocal',
|
||||
value: {...value.place},
|
||||
constant: false,
|
||||
},
|
||||
path: [],
|
||||
loc: value.place.loc,
|
||||
};
|
||||
}
|
||||
break;
|
||||
@@ -163,10 +159,10 @@ function collectTemporaries(
|
||||
}
|
||||
case 'ArrayExpression': {
|
||||
if (value.elements.every(e => e.kind === 'Identifier')) {
|
||||
sidemap.maybeDepsLists.set(instr.lvalue.identifier.id, {
|
||||
loc: value.loc,
|
||||
deps: value.elements as Array<Place>,
|
||||
});
|
||||
sidemap.maybeDepsLists.set(
|
||||
instr.lvalue.identifier.id,
|
||||
value.elements as Array<Place>,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -186,7 +182,6 @@ function makeManualMemoizationMarkers(
|
||||
fnExpr: Place,
|
||||
env: Environment,
|
||||
depsList: Array<ManualMemoDependency> | null,
|
||||
depsLoc: SourceLocation | null,
|
||||
memoDecl: Place,
|
||||
manualMemoId: number,
|
||||
): [TInstruction<StartMemoize>, TInstruction<FinishMemoize>] {
|
||||
@@ -202,7 +197,6 @@ function makeManualMemoizationMarkers(
|
||||
* as dependencies
|
||||
*/
|
||||
deps: depsList,
|
||||
depsLoc,
|
||||
loc: fnExpr.loc,
|
||||
},
|
||||
effects: null,
|
||||
@@ -293,85 +287,86 @@ function extractManualMemoizationArgs(
|
||||
sidemap: IdentifierSidemap,
|
||||
errors: CompilerError,
|
||||
): {
|
||||
fnPlace: Place;
|
||||
fnPlace: Place | null;
|
||||
depsList: Array<ManualMemoDependency> | null;
|
||||
depsLoc: SourceLocation | null;
|
||||
} | null {
|
||||
} {
|
||||
const [fnPlace, depsListPlace] = instr.value.args as Array<
|
||||
Place | SpreadPattern | undefined
|
||||
>;
|
||||
if (fnPlace == null || fnPlace.kind !== 'Identifier') {
|
||||
if (fnPlace == null) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.UseMemo,
|
||||
reason: `Expected a callback function to be passed to ${kind}`,
|
||||
description:
|
||||
kind === 'useCallback'
|
||||
? 'The first argument to useCallback() must be a function to cache'
|
||||
: 'The first argument to useMemo() must be a function that calculates a result to cache',
|
||||
description: `Expected a callback function to be passed to ${kind}`,
|
||||
suggestions: null,
|
||||
}).withDetails({
|
||||
kind: 'error',
|
||||
loc: instr.value.loc,
|
||||
message:
|
||||
kind === 'useCallback'
|
||||
? `Expected a callback function`
|
||||
: `Expected a memoization function`,
|
||||
message: `Expected a callback function to be passed to ${kind}`,
|
||||
}),
|
||||
);
|
||||
return null;
|
||||
return {fnPlace: null, depsList: null};
|
||||
}
|
||||
if (depsListPlace == null) {
|
||||
return {
|
||||
fnPlace,
|
||||
depsList: null,
|
||||
depsLoc: null,
|
||||
};
|
||||
}
|
||||
const maybeDepsList =
|
||||
depsListPlace.kind === 'Identifier'
|
||||
? sidemap.maybeDepsLists.get(depsListPlace.identifier.id)
|
||||
: null;
|
||||
if (maybeDepsList == null) {
|
||||
if (fnPlace.kind === 'Spread' || depsListPlace?.kind === 'Spread') {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.UseMemo,
|
||||
reason: `Expected the dependency list for ${kind} to be an array literal`,
|
||||
description: `Expected the dependency list for ${kind} to be an array literal`,
|
||||
reason: `Unexpected spread argument to ${kind}`,
|
||||
description: `Unexpected spread argument to ${kind}`,
|
||||
suggestions: null,
|
||||
}).withDetails({
|
||||
kind: 'error',
|
||||
loc:
|
||||
depsListPlace?.kind === 'Identifier' ? depsListPlace.loc : instr.loc,
|
||||
message: `Expected the dependency list for ${kind} to be an array literal`,
|
||||
loc: instr.value.loc,
|
||||
message: `Unexpected spread argument to ${kind}`,
|
||||
}),
|
||||
);
|
||||
return null;
|
||||
return {fnPlace: null, depsList: null};
|
||||
}
|
||||
const depsList: Array<ManualMemoDependency> = [];
|
||||
for (const dep of maybeDepsList.deps) {
|
||||
const maybeDep = sidemap.maybeDeps.get(dep.identifier.id);
|
||||
if (maybeDep == null) {
|
||||
let depsList: Array<ManualMemoDependency> | null = null;
|
||||
if (depsListPlace != null) {
|
||||
const maybeDepsList = sidemap.maybeDepsLists.get(
|
||||
depsListPlace.identifier.id,
|
||||
);
|
||||
if (maybeDepsList == null) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.UseMemo,
|
||||
reason: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
||||
description: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
||||
reason: `Expected the dependency list for ${kind} to be an array literal`,
|
||||
description: `Expected the dependency list for ${kind} to be an array literal`,
|
||||
suggestions: null,
|
||||
}).withDetails({
|
||||
kind: 'error',
|
||||
loc: dep.loc,
|
||||
message: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
||||
loc: depsListPlace.loc,
|
||||
message: `Expected the dependency list for ${kind} to be an array literal`,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
depsList.push(maybeDep);
|
||||
return {fnPlace, depsList: null};
|
||||
}
|
||||
depsList = [];
|
||||
for (const dep of maybeDepsList) {
|
||||
const maybeDep = sidemap.maybeDeps.get(dep.identifier.id);
|
||||
if (maybeDep == null) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.UseMemo,
|
||||
reason: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
||||
description: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
||||
suggestions: null,
|
||||
}).withDetails({
|
||||
kind: 'error',
|
||||
loc: dep.loc,
|
||||
message: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
depsList.push(maybeDep);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
fnPlace,
|
||||
depsList,
|
||||
depsLoc: maybeDepsList.loc,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -432,17 +427,16 @@ export function dropManualMemoization(
|
||||
|
||||
const manualMemo = sidemap.manualMemos.get(id);
|
||||
if (manualMemo != null) {
|
||||
const memoDetails = extractManualMemoizationArgs(
|
||||
const {fnPlace, depsList} = extractManualMemoizationArgs(
|
||||
instr as TInstruction<CallExpression> | TInstruction<MethodCall>,
|
||||
manualMemo.kind,
|
||||
sidemap,
|
||||
errors,
|
||||
);
|
||||
|
||||
if (memoDetails == null) {
|
||||
if (fnPlace == null) {
|
||||
continue;
|
||||
}
|
||||
const {fnPlace, depsList, depsLoc} = memoDetails;
|
||||
|
||||
instr.value = getManualMemoizationReplacement(
|
||||
fnPlace,
|
||||
@@ -493,7 +487,6 @@ export function dropManualMemoization(
|
||||
fnPlace,
|
||||
func.env,
|
||||
depsList,
|
||||
depsLoc,
|
||||
memoDecl,
|
||||
nextManualMemoId++,
|
||||
);
|
||||
|
||||
@@ -609,19 +609,6 @@ function evaluateInstruction(
|
||||
constantPropagationImpl(value.loweredFunc.func, constants);
|
||||
return null;
|
||||
}
|
||||
case 'StartMemoize': {
|
||||
if (value.deps != null) {
|
||||
for (const dep of value.deps) {
|
||||
if (dep.root.kind === 'NamedLocal') {
|
||||
const placeValue = read(constants, dep.root.value);
|
||||
if (placeValue != null && placeValue.kind === 'Primitive') {
|
||||
dep.root.constant = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
default: {
|
||||
// TODO: handle more cases
|
||||
return null;
|
||||
|
||||
@@ -515,7 +515,6 @@ function emitDestructureProps(
|
||||
pattern: {
|
||||
kind: 'ObjectPattern',
|
||||
properties,
|
||||
loc: GeneratedSource,
|
||||
},
|
||||
kind: InstructionKind.Let,
|
||||
},
|
||||
|
||||
@@ -702,7 +702,7 @@ function codegenReactiveScope(
|
||||
outputComments.push(name.name);
|
||||
if (!cx.hasDeclared(identifier)) {
|
||||
statements.push(
|
||||
t.variableDeclaration('let', [createVariableDeclarator(name, null)]),
|
||||
t.variableDeclaration('let', [t.variableDeclarator(name)]),
|
||||
);
|
||||
}
|
||||
cacheLoads.push({name, index, value: wrapCacheDep(cx, name)});
|
||||
@@ -1387,7 +1387,7 @@ function codegenInstructionNullable(
|
||||
suggestions: null,
|
||||
});
|
||||
return createVariableDeclaration(instr.loc, 'const', [
|
||||
createVariableDeclarator(codegenLValue(cx, lvalue), value),
|
||||
t.variableDeclarator(codegenLValue(cx, lvalue), value),
|
||||
]);
|
||||
}
|
||||
case InstructionKind.Function: {
|
||||
@@ -1451,7 +1451,7 @@ function codegenInstructionNullable(
|
||||
suggestions: null,
|
||||
});
|
||||
return createVariableDeclaration(instr.loc, 'let', [
|
||||
createVariableDeclarator(codegenLValue(cx, lvalue), value),
|
||||
t.variableDeclarator(codegenLValue(cx, lvalue), value),
|
||||
]);
|
||||
}
|
||||
case InstructionKind.Reassign: {
|
||||
@@ -1691,9 +1691,6 @@ function withLoc<T extends (...args: Array<any>) => t.Node>(
|
||||
};
|
||||
}
|
||||
|
||||
const createIdentifier = withLoc(t.identifier);
|
||||
const createArrayPattern = withLoc(t.arrayPattern);
|
||||
const createObjectPattern = withLoc(t.objectPattern);
|
||||
const createBinaryExpression = withLoc(t.binaryExpression);
|
||||
const createExpressionStatement = withLoc(t.expressionStatement);
|
||||
const _createLabelledStatement = withLoc(t.labeledStatement);
|
||||
@@ -1725,31 +1722,6 @@ const createTryStatement = withLoc(t.tryStatement);
|
||||
const createBreakStatement = withLoc(t.breakStatement);
|
||||
const createContinueStatement = withLoc(t.continueStatement);
|
||||
|
||||
function createVariableDeclarator(
|
||||
id: t.LVal,
|
||||
init?: t.Expression | null,
|
||||
): t.VariableDeclarator {
|
||||
const node = t.variableDeclarator(id, init);
|
||||
|
||||
/*
|
||||
* The variable declarator location is not preserved in HIR, however, we can use the
|
||||
* start location of the id and the end location of the init to recreate the
|
||||
* exact original variable declarator location.
|
||||
*
|
||||
* Or if init is null, we likely have a declaration without an initializer, so we can use the id.loc.end as the end location.
|
||||
*/
|
||||
if (id.loc && (init === null || init?.loc)) {
|
||||
node.loc = {
|
||||
start: id.loc.start,
|
||||
end: init?.loc?.end ?? id.loc.end,
|
||||
filename: id.loc.filename,
|
||||
identifierName: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
function createHookGuard(
|
||||
guard: ExternalFunction,
|
||||
context: ProgramContext,
|
||||
@@ -1857,7 +1829,7 @@ function codegenInstruction(
|
||||
);
|
||||
} else {
|
||||
return createVariableDeclaration(instr.loc, 'const', [
|
||||
createVariableDeclarator(
|
||||
t.variableDeclarator(
|
||||
convertIdentifier(instr.lvalue.identifier),
|
||||
expressionValue,
|
||||
),
|
||||
@@ -2784,7 +2756,7 @@ function codegenArrayPattern(
|
||||
): t.ArrayPattern {
|
||||
const hasHoles = !pattern.items.every(e => e.kind !== 'Hole');
|
||||
if (hasHoles) {
|
||||
const result = createArrayPattern(pattern.loc, []);
|
||||
const result = t.arrayPattern([]);
|
||||
/*
|
||||
* Older versions of babel have a validation bug fixed by
|
||||
* https://github.com/babel/babel/pull/10917
|
||||
@@ -2805,8 +2777,7 @@ function codegenArrayPattern(
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return createArrayPattern(
|
||||
pattern.loc,
|
||||
return t.arrayPattern(
|
||||
pattern.items.map(item => {
|
||||
if (item.kind === 'Hole') {
|
||||
return null;
|
||||
@@ -2826,8 +2797,7 @@ function codegenLValue(
|
||||
return codegenArrayPattern(cx, pattern);
|
||||
}
|
||||
case 'ObjectPattern': {
|
||||
return createObjectPattern(
|
||||
pattern.loc,
|
||||
return t.objectPattern(
|
||||
pattern.properties.map(property => {
|
||||
if (property.kind === 'ObjectProperty') {
|
||||
const key = codegenObjectPropertyKey(cx, property.key);
|
||||
@@ -2946,7 +2916,7 @@ function convertIdentifier(identifier: Identifier): t.Identifier {
|
||||
suggestions: null,
|
||||
},
|
||||
);
|
||||
return createIdentifier(identifier.loc, identifier.name.value);
|
||||
return t.identifier(identifier.name.value);
|
||||
}
|
||||
|
||||
function compareScopeDependency(
|
||||
|
||||
@@ -389,6 +389,14 @@ export function findDisjointMutableValues(
|
||||
*/
|
||||
operand.identifier.mutableRange.start > 0
|
||||
) {
|
||||
if (
|
||||
instr.value.kind === 'FunctionExpression' ||
|
||||
instr.value.kind === 'ObjectMethod'
|
||||
) {
|
||||
if (operand.identifier.type.kind === 'Primitive') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
operands.push(operand.identifier);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,6 @@ import {
|
||||
IdentifierId,
|
||||
isSetStateType,
|
||||
isUseEffectHookType,
|
||||
isUseEffectEventType,
|
||||
isUseInsertionEffectHookType,
|
||||
isUseLayoutEffectHookType,
|
||||
isUseRefType,
|
||||
@@ -99,20 +98,7 @@ export function validateNoSetStateInEffects(
|
||||
instr.value.kind === 'MethodCall'
|
||||
? instr.value.receiver
|
||||
: instr.value.callee;
|
||||
|
||||
if (isUseEffectEventType(callee.identifier)) {
|
||||
const arg = instr.value.args[0];
|
||||
if (arg !== undefined && arg.kind === 'Identifier') {
|
||||
const setState = setStateFunctions.get(arg.identifier.id);
|
||||
if (setState !== undefined) {
|
||||
/**
|
||||
* This effect event function calls setState synchonously,
|
||||
* treat it as a setState function for transitive tracking
|
||||
*/
|
||||
setStateFunctions.set(instr.lvalue.identifier.id, setState);
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
if (
|
||||
isUseEffectHookType(callee.identifier) ||
|
||||
isUseLayoutEffectHookType(callee.identifier) ||
|
||||
isUseInsertionEffectHookType(callee.identifier)
|
||||
@@ -121,58 +107,26 @@ export function validateNoSetStateInEffects(
|
||||
if (arg !== undefined && arg.kind === 'Identifier') {
|
||||
const setState = setStateFunctions.get(arg.identifier.id);
|
||||
if (setState !== undefined) {
|
||||
const enableVerbose =
|
||||
env.config.enableVerboseNoSetStateInEffect;
|
||||
if (enableVerbose) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.EffectSetState,
|
||||
reason:
|
||||
'Calling setState synchronously within an effect can trigger cascading renders',
|
||||
description:
|
||||
'Effects are intended to synchronize state between React and external systems. ' +
|
||||
'Calling setState synchronously causes cascading renders that hurt performance.\n\n' +
|
||||
'This pattern may indicate one of several issues:\n\n' +
|
||||
'**1. Non-local derived data**: If the value being set could be computed from props/state ' +
|
||||
'but requires data from a parent component, consider restructuring state ownership so the ' +
|
||||
'derivation can happen during render in the component that owns the relevant state.\n\n' +
|
||||
"**2. Derived event pattern**: If you're detecting when a prop changes (e.g., `isPlaying` " +
|
||||
'transitioning from false to true), this often indicates the parent should provide an event ' +
|
||||
'callback (like `onPlay`) instead of just the current state. Request access to the original event.\n\n' +
|
||||
"**3. Force update / external sync**: If you're forcing a re-render to sync with an external " +
|
||||
'data source (mutable values outside React), use `useSyncExternalStore` to properly subscribe ' +
|
||||
'to external state changes.\n\n' +
|
||||
'See: https://react.dev/learn/you-might-not-need-an-effect',
|
||||
suggestions: null,
|
||||
}).withDetails({
|
||||
kind: 'error',
|
||||
loc: setState.loc,
|
||||
message:
|
||||
'Avoid calling setState() directly within an effect',
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.EffectSetState,
|
||||
reason:
|
||||
'Calling setState synchronously within an effect can trigger cascading renders',
|
||||
description:
|
||||
'Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. ' +
|
||||
'In general, the body of an effect should do one or both of the following:\n' +
|
||||
'* Update external systems with the latest state from React.\n' +
|
||||
'* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\n' +
|
||||
'Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. ' +
|
||||
'(https://react.dev/learn/you-might-not-need-an-effect)',
|
||||
suggestions: null,
|
||||
}).withDetails({
|
||||
kind: 'error',
|
||||
loc: setState.loc,
|
||||
message:
|
||||
'Avoid calling setState() directly within an effect',
|
||||
}),
|
||||
);
|
||||
}
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.EffectSetState,
|
||||
reason:
|
||||
'Calling setState synchronously within an effect can trigger cascading renders',
|
||||
description:
|
||||
'Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. ' +
|
||||
'In general, the body of an effect should do one or both of the following:\n' +
|
||||
'* Update external systems with the latest state from React.\n' +
|
||||
'* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\n' +
|
||||
'Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. ' +
|
||||
'(https://react.dev/learn/you-might-not-need-an-effect)',
|
||||
suggestions: null,
|
||||
}).withDetails({
|
||||
kind: 'error',
|
||||
loc: setState.loc,
|
||||
message:
|
||||
'Avoid calling setState() directly within an effect',
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,40 +155,20 @@ function validateNoSetStateInRenderImpl(
|
||||
}),
|
||||
);
|
||||
} else if (unconditionalBlocks.has(block.id)) {
|
||||
const enableUseKeyedState = fn.env.config.enableUseKeyedState;
|
||||
if (enableUseKeyedState) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.RenderSetState,
|
||||
reason: 'Cannot call setState during render',
|
||||
description:
|
||||
'Calling setState during render may trigger an infinite loop.\n' +
|
||||
'* To reset state when other state/props change, use `const [state, setState] = useKeyedState(initialState, key)` to reset `state` when `key` changes.\n' +
|
||||
'* To derive data from other state/props, compute the derived data during render without using state',
|
||||
suggestions: null,
|
||||
}).withDetails({
|
||||
kind: 'error',
|
||||
loc: callee.loc,
|
||||
message: 'Found setState() in render',
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.RenderSetState,
|
||||
reason: 'Cannot call setState during render',
|
||||
description:
|
||||
'Calling setState during render may trigger an infinite loop.\n' +
|
||||
'* To reset state when other state/props change, store the previous value in state and update conditionally: https://react.dev/reference/react/useState#storing-information-from-previous-renders\n' +
|
||||
'* To derive data from other state/props, compute the derived data during render without using state',
|
||||
suggestions: null,
|
||||
}).withDetails({
|
||||
kind: 'error',
|
||||
loc: callee.loc,
|
||||
message: 'Found setState() in render',
|
||||
}),
|
||||
);
|
||||
}
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.RenderSetState,
|
||||
reason:
|
||||
'Calling setState during render may trigger an infinite loop',
|
||||
description:
|
||||
'Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState)',
|
||||
suggestions: null,
|
||||
}).withDetails({
|
||||
kind: 'error',
|
||||
loc: callee.loc,
|
||||
message: 'Found setState() in render',
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -242,7 +242,6 @@ function validateInferredDep(
|
||||
normalizedDep = {
|
||||
root: maybeNormalizedRoot.root,
|
||||
path: [...maybeNormalizedRoot.path, ...dep.path],
|
||||
loc: maybeNormalizedRoot.loc,
|
||||
};
|
||||
} else {
|
||||
CompilerError.invariant(dep.identifier.name?.kind === 'named', {
|
||||
@@ -268,10 +267,8 @@ function validateInferredDep(
|
||||
effect: Effect.Read,
|
||||
reactive: false,
|
||||
},
|
||||
constant: false,
|
||||
},
|
||||
path: [...dep.path],
|
||||
loc: GeneratedSource,
|
||||
};
|
||||
}
|
||||
for (const decl of declsWithinMemoBlock) {
|
||||
@@ -382,10 +379,8 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
|
||||
root: {
|
||||
kind: 'NamedLocal',
|
||||
value: storeTarget,
|
||||
constant: false,
|
||||
},
|
||||
path: [],
|
||||
loc: storeTarget.loc,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -413,10 +408,8 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
|
||||
root: {
|
||||
kind: 'NamedLocal',
|
||||
value: {...lvalue},
|
||||
constant: false,
|
||||
},
|
||||
path: [],
|
||||
loc: lvalue.loc,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,11 +27,7 @@ import {Result} from '../Utils/Result';
|
||||
|
||||
/**
|
||||
* Some common node types that are important for coverage tracking.
|
||||
* Based on istanbul-lib-instrument + some other common nodes we expect to be present in the generated AST.
|
||||
*
|
||||
* Note: For VariableDeclaration, VariableDeclarator, and Identifier, we enforce stricter validation
|
||||
* that requires both the source location AND node type to match in the generated AST. This ensures
|
||||
* that variable declarations maintain their structural integrity through compilation.
|
||||
* Based on istanbul-lib-instrument
|
||||
*/
|
||||
const IMPORTANT_INSTRUMENTED_TYPES = new Set([
|
||||
'ArrowFunctionExpression',
|
||||
@@ -58,14 +54,6 @@ const IMPORTANT_INSTRUMENTED_TYPES = new Set([
|
||||
'LabeledStatement',
|
||||
'ConditionalExpression',
|
||||
'LogicalExpression',
|
||||
|
||||
/**
|
||||
* Note: these aren't important for coverage tracking,
|
||||
* but we still want to track them to ensure we aren't regressing them when
|
||||
* we fix the source location tracking for other nodes.
|
||||
*/
|
||||
'VariableDeclaration',
|
||||
'Identifier',
|
||||
]);
|
||||
|
||||
/**
|
||||
@@ -126,13 +114,10 @@ export function validateSourceLocations(
|
||||
): Result<void, CompilerError> {
|
||||
const errors = new CompilerError();
|
||||
|
||||
/*
|
||||
* Step 1: Collect important locations from the original source
|
||||
* Note: Multiple node types can share the same location (e.g. VariableDeclarator and Identifier)
|
||||
*/
|
||||
// Step 1: Collect important locations from the original source
|
||||
const importantOriginalLocations = new Map<
|
||||
string,
|
||||
{loc: t.SourceLocation; nodeTypes: Set<string>}
|
||||
{loc: t.SourceLocation; nodeType: string}
|
||||
>();
|
||||
|
||||
func.traverse({
|
||||
@@ -152,31 +137,20 @@ export function validateSourceLocations(
|
||||
// Collect the location if it exists
|
||||
if (node.loc) {
|
||||
const key = locationKey(node.loc);
|
||||
const existing = importantOriginalLocations.get(key);
|
||||
if (existing) {
|
||||
existing.nodeTypes.add(node.type);
|
||||
} else {
|
||||
importantOriginalLocations.set(key, {
|
||||
loc: node.loc,
|
||||
nodeTypes: new Set([node.type]),
|
||||
});
|
||||
}
|
||||
importantOriginalLocations.set(key, {
|
||||
loc: node.loc,
|
||||
nodeType: node.type,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Step 2: Collect all locations from the generated AST with their node types
|
||||
const generatedLocations = new Map<string, Set<string>>();
|
||||
// Step 2: Collect all locations from the generated AST
|
||||
const generatedLocations = new Set<string>();
|
||||
|
||||
function collectGeneratedLocations(node: t.Node): void {
|
||||
if (node.loc) {
|
||||
const key = locationKey(node.loc);
|
||||
const nodeTypes = generatedLocations.get(key);
|
||||
if (nodeTypes) {
|
||||
nodeTypes.add(node.type);
|
||||
} else {
|
||||
generatedLocations.set(key, new Set([node.type]));
|
||||
}
|
||||
generatedLocations.add(locationKey(node.loc));
|
||||
}
|
||||
|
||||
// Use Babel's VISITOR_KEYS to traverse only actual node properties
|
||||
@@ -209,86 +183,22 @@ export function validateSourceLocations(
|
||||
collectGeneratedLocations(outlined.fn.body);
|
||||
}
|
||||
|
||||
/*
|
||||
* Step 3: Validate that all important locations are preserved
|
||||
* For certain node types, also validate that the node type matches
|
||||
*/
|
||||
const strictNodeTypes = new Set([
|
||||
'VariableDeclaration',
|
||||
'VariableDeclarator',
|
||||
'Identifier',
|
||||
]);
|
||||
|
||||
const reportMissingLocation = (
|
||||
loc: t.SourceLocation,
|
||||
nodeType: string,
|
||||
): void => {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.Todo,
|
||||
reason: 'Important source location missing in generated code',
|
||||
description:
|
||||
`Source location for ${nodeType} is missing in the generated output. This can cause coverage instrumentation ` +
|
||||
`to fail to track this code properly, resulting in inaccurate coverage reports.`,
|
||||
}).withDetails({
|
||||
kind: 'error',
|
||||
loc,
|
||||
message: null,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const reportWrongNodeType = (
|
||||
loc: t.SourceLocation,
|
||||
expectedType: string,
|
||||
actualTypes: Set<string>,
|
||||
): void => {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.Todo,
|
||||
reason:
|
||||
'Important source location has wrong node type in generated code',
|
||||
description:
|
||||
`Source location for ${expectedType} exists in the generated output but with wrong node type(s): ${Array.from(actualTypes).join(', ')}. ` +
|
||||
`This can cause coverage instrumentation to fail to track this code properly, resulting in inaccurate coverage reports.`,
|
||||
}).withDetails({
|
||||
kind: 'error',
|
||||
loc,
|
||||
message: null,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
for (const [key, {loc, nodeTypes}] of importantOriginalLocations) {
|
||||
const generatedNodeTypes = generatedLocations.get(key);
|
||||
|
||||
if (!generatedNodeTypes) {
|
||||
// Location is completely missing
|
||||
reportMissingLocation(loc, Array.from(nodeTypes).join(', '));
|
||||
} else {
|
||||
// Location exists, check each node type
|
||||
for (const nodeType of nodeTypes) {
|
||||
if (
|
||||
strictNodeTypes.has(nodeType) &&
|
||||
!generatedNodeTypes.has(nodeType)
|
||||
) {
|
||||
/*
|
||||
* For strict node types, the specific node type must be present
|
||||
* Check if any generated node type is also an important original node type
|
||||
*/
|
||||
const hasValidNodeType = Array.from(generatedNodeTypes).some(
|
||||
genType => nodeTypes.has(genType),
|
||||
);
|
||||
|
||||
if (hasValidNodeType) {
|
||||
// At least one generated node type is valid (also in original), so this is just missing
|
||||
reportMissingLocation(loc, nodeType);
|
||||
} else {
|
||||
// None of the generated node types are in original - this is wrong node type
|
||||
reportWrongNodeType(loc, nodeType, generatedNodeTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Step 3: Validate that all important locations are preserved
|
||||
for (const [key, {loc, nodeType}] of importantOriginalLocations) {
|
||||
if (!generatedLocations.has(key)) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.Todo,
|
||||
reason: 'Important source location missing in generated code',
|
||||
description:
|
||||
`Source location for ${nodeType} is missing in the generated output. This can cause coverage instrumentation ` +
|
||||
`to fail to track this code properly, resulting in inaccurate coverage reports.`,
|
||||
}).withDetails({
|
||||
kind: 'error',
|
||||
loc,
|
||||
message: null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,10 @@ function Component() {
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const a = makeObject_Primitives();
|
||||
|
||||
const x = [];
|
||||
x.push(a);
|
||||
|
||||
mutate(x);
|
||||
t0 = [x, a];
|
||||
$[0] = t0;
|
||||
|
||||
@@ -33,6 +33,7 @@ function Component() {
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const x = [];
|
||||
x.push(a);
|
||||
|
||||
t1 = [x, a];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
|
||||
@@ -85,10 +85,14 @@ function Component(t0) {
|
||||
let t1;
|
||||
if ($[0] !== prop) {
|
||||
const obj = shallowCopy(prop);
|
||||
|
||||
const aliasedObj = identity(obj);
|
||||
|
||||
const getId = () => obj.id;
|
||||
|
||||
mutate(aliasedObj);
|
||||
setPropertyByKey(aliasedObj, "id", prop.id + 1);
|
||||
|
||||
t1 = <Stringify getId={getId} shouldInvokeFns={true} />;
|
||||
$[0] = prop;
|
||||
$[1] = t1;
|
||||
|
||||
@@ -181,9 +181,12 @@ function Component(t0) {
|
||||
if ($[0] !== prop) {
|
||||
const obj = shallowCopy(prop);
|
||||
const aliasedObj = identity(obj);
|
||||
|
||||
const id = [obj.id];
|
||||
|
||||
mutate(aliasedObj);
|
||||
setPropertyByKey(aliasedObj, "id", prop.id + 1);
|
||||
|
||||
t1 = <Stringify id={id} />;
|
||||
$[0] = prop;
|
||||
$[1] = t1;
|
||||
|
||||
@@ -54,6 +54,7 @@ function Foo(t0) {
|
||||
let t1;
|
||||
if ($[0] !== cond1 || $[1] !== cond2) {
|
||||
const arr = makeArray({ a: 2 }, 2, []);
|
||||
|
||||
t1 = cond1 ? (
|
||||
<>
|
||||
<div>{identity("foo")}</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
|
||||
// @enablePreserveExistingMemoizationGuarantees:false
|
||||
import {useMemo} from 'react';
|
||||
|
||||
const someGlobal = {value: 0};
|
||||
@@ -33,7 +33,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
|
||||
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
|
||||
import { useMemo } from "react";
|
||||
|
||||
const someGlobal = { value: 0 };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
|
||||
// @enablePreserveExistingMemoizationGuarantees:false
|
||||
import {useMemo} from 'react';
|
||||
|
||||
const someGlobal = {value: 0};
|
||||
|
||||
@@ -49,6 +49,7 @@ function Component() {
|
||||
ref.current = "";
|
||||
}
|
||||
};
|
||||
|
||||
t0 = () => {
|
||||
setRef();
|
||||
};
|
||||
|
||||
@@ -49,6 +49,7 @@ function Component() {
|
||||
ref.current.value = "";
|
||||
}
|
||||
};
|
||||
|
||||
t0 = () => {
|
||||
setRef();
|
||||
};
|
||||
|
||||
@@ -74,6 +74,7 @@ function Component() {
|
||||
console.log(ref.current.value);
|
||||
}
|
||||
};
|
||||
|
||||
t0 = (
|
||||
<>
|
||||
<input ref={ref} />
|
||||
|
||||
@@ -36,6 +36,7 @@ function useArrayOfRef() {
|
||||
const callback = (value) => {
|
||||
ref.current = value;
|
||||
};
|
||||
|
||||
t0 = [callback];
|
||||
$[0] = t0;
|
||||
} else {
|
||||
|
||||
@@ -35,6 +35,7 @@ function Component(props) {
|
||||
const arr = [...bar(props)];
|
||||
return arr.at(x);
|
||||
};
|
||||
|
||||
t1 = fn();
|
||||
$[2] = props;
|
||||
$[3] = x;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {makeObject_Primitives, ValidateMemoization} from 'shared-runtime';
|
||||
|
||||
@@ -36,7 +36,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
|
||||
import { useMemo } from "react";
|
||||
import { makeObject_Primitives, ValidateMemoization } from "shared-runtime";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {makeObject_Primitives, ValidateMemoization} from 'shared-runtime';
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ function useBar(t0) {
|
||||
if ($[0] !== arg) {
|
||||
const s = new Set([1, 5, 4]);
|
||||
const mutableIterator = s.values();
|
||||
|
||||
t1 = [arg, ...mutableIterator];
|
||||
$[0] = arg;
|
||||
$[1] = t1;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateExhaustiveMemoizationDependencies:false
|
||||
import {useMemo} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
@@ -31,7 +30,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateExhaustiveMemoizationDependencies:false
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useMemo } from "react";
|
||||
|
||||
function Component(props) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// @validateExhaustiveMemoizationDependencies:false
|
||||
import {useMemo} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
|
||||
@@ -28,6 +28,7 @@ function Component(props) {
|
||||
const a = [];
|
||||
const b = {};
|
||||
foo(a, b);
|
||||
|
||||
foo(b);
|
||||
t0 = <div a={a} b={b} />;
|
||||
$[0] = t0;
|
||||
|
||||
@@ -45,6 +45,7 @@ function useKeyCommand() {
|
||||
const nextPosition = direction === "left" ? addOne(position) : position;
|
||||
currentPosition.current = nextPosition;
|
||||
};
|
||||
|
||||
const moveLeft = { handler: handleKey("left") };
|
||||
const moveRight = { handler: handleKey("right") };
|
||||
t0 = [moveLeft, moveRight];
|
||||
|
||||
@@ -45,6 +45,7 @@ function Component(t0) {
|
||||
z.a = 2;
|
||||
mutate(y.b);
|
||||
};
|
||||
|
||||
x();
|
||||
t1 = [y, z];
|
||||
$[0] = a;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @expectNothingCompiled @compilationMode:"infer"
|
||||
// @compilationMode:"infer"
|
||||
class Component {
|
||||
_renderMessage = () => {
|
||||
const Message = () => {
|
||||
@@ -22,7 +22,7 @@ class Component {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @expectNothingCompiled @compilationMode:"infer"
|
||||
// @compilationMode:"infer"
|
||||
class Component {
|
||||
_renderMessage = () => {
|
||||
const Message = () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @expectNothingCompiled @compilationMode:"infer"
|
||||
// @compilationMode:"infer"
|
||||
class Component {
|
||||
_renderMessage = () => {
|
||||
const Message = () => {
|
||||
|
||||
@@ -29,6 +29,7 @@ function MyComponentName(props) {
|
||||
const x = {};
|
||||
foo(x, props.a);
|
||||
foo(x, props.b);
|
||||
|
||||
y = [];
|
||||
y.push(x);
|
||||
$[0] = props.a;
|
||||
|
||||
@@ -34,6 +34,7 @@ function useTest() {
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
let w = {};
|
||||
|
||||
const t1 = (w = 42);
|
||||
const t2 = w;
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ function useTest() {
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const w = {};
|
||||
|
||||
const t1 = (w.x = 42);
|
||||
const t2 = w.x;
|
||||
|
||||
|
||||
@@ -44,9 +44,11 @@ function ComponentA(props) {
|
||||
if (b) {
|
||||
a.push(props.p0);
|
||||
}
|
||||
|
||||
if (props.p1) {
|
||||
b.push(props.p2);
|
||||
}
|
||||
|
||||
t0 = <Foo a={a} b={b} />;
|
||||
$[0] = props.p0;
|
||||
$[1] = props.p1;
|
||||
@@ -67,9 +69,11 @@ function ComponentB(props) {
|
||||
if (mayMutate(b)) {
|
||||
a.push(props.p0);
|
||||
}
|
||||
|
||||
if (props.p1) {
|
||||
b.push(props.p2);
|
||||
}
|
||||
|
||||
t0 = <Foo a={a} b={b} />;
|
||||
$[0] = props.p0;
|
||||
$[1] = props.p1;
|
||||
|
||||
@@ -28,6 +28,7 @@ function Component(props) {
|
||||
const a = [];
|
||||
const b = {};
|
||||
new Foo(a, b);
|
||||
|
||||
new Foo(b);
|
||||
t0 = <div a={a} b={b} />;
|
||||
$[0] = t0;
|
||||
|
||||
@@ -11,7 +11,7 @@ function Component(props) {
|
||||
|
||||
Component = useMemo(() => {
|
||||
return Component;
|
||||
}, [Component]);
|
||||
});
|
||||
|
||||
return <Component {...props} />;
|
||||
}
|
||||
@@ -36,7 +36,6 @@ function Component(props) {
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
Component = Stringify;
|
||||
|
||||
Component;
|
||||
Component = Component;
|
||||
$[0] = Component;
|
||||
} else {
|
||||
|
||||
@@ -7,7 +7,7 @@ function Component(props) {
|
||||
|
||||
Component = useMemo(() => {
|
||||
return Component;
|
||||
}, [Component]);
|
||||
});
|
||||
|
||||
return <Component {...props} />;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ function Component(props) {
|
||||
const callback = () => {
|
||||
console.log(x);
|
||||
};
|
||||
|
||||
x = {};
|
||||
t0 = <Stringify callback={callback} shouldInvokeFns={true} />;
|
||||
$[0] = t0;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @expectNothingCompiled @customOptOutDirectives:["use todo memo"]
|
||||
// @customOptOutDirectives:["use todo memo"]
|
||||
function Component() {
|
||||
'use todo memo';
|
||||
return <div>hello world!</div>;
|
||||
@@ -18,7 +18,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @expectNothingCompiled @customOptOutDirectives:["use todo memo"]
|
||||
// @customOptOutDirectives:["use todo memo"]
|
||||
function Component() {
|
||||
"use todo memo";
|
||||
return <div>hello world!</div>;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @expectNothingCompiled @customOptOutDirectives:["use todo memo"]
|
||||
// @customOptOutDirectives:["use todo memo"]
|
||||
function Component() {
|
||||
'use todo memo';
|
||||
return <div>hello world!</div>;
|
||||
|
||||
@@ -75,6 +75,7 @@ function Component(props) {
|
||||
let t0;
|
||||
if ($[0] !== post) {
|
||||
const allUrls = [];
|
||||
|
||||
const { media: t1, comments: t2, urls: t3 } = post;
|
||||
const media = t1 === undefined ? null : t1;
|
||||
let t4;
|
||||
@@ -101,6 +102,7 @@ function Component(props) {
|
||||
if (!comments.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(comments.length);
|
||||
};
|
||||
$[6] = comments.length;
|
||||
@@ -109,6 +111,7 @@ function Component(props) {
|
||||
t6 = $[7];
|
||||
}
|
||||
const onClick = t6;
|
||||
|
||||
allUrls.push(...urls);
|
||||
t0 = <Stringify media={media} allUrls={allUrls} onClick={onClick} />;
|
||||
$[0] = post;
|
||||
|
||||
@@ -53,6 +53,7 @@ function Component(props) {
|
||||
let t0;
|
||||
if ($[0] !== post) {
|
||||
const allUrls = [];
|
||||
|
||||
const { media, comments, urls } = post;
|
||||
let t1;
|
||||
if ($[2] !== comments.length) {
|
||||
@@ -60,6 +61,7 @@ function Component(props) {
|
||||
if (!comments.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(comments.length);
|
||||
};
|
||||
$[2] = comments.length;
|
||||
@@ -68,6 +70,7 @@ function Component(props) {
|
||||
t1 = $[3];
|
||||
}
|
||||
const onClick = t1;
|
||||
|
||||
allUrls.push(...urls);
|
||||
t0 = <Media media={media} onClick={onClick} />;
|
||||
$[0] = post;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @expectNothingCompiled @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
// @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {makeObject_Primitives, ValidateMemoization} from 'shared-runtime';
|
||||
|
||||
@@ -37,7 +37,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @expectNothingCompiled @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
// @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
import { useMemo } from "react";
|
||||
import { makeObject_Primitives, ValidateMemoization } from "shared-runtime";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @expectNothingCompiled @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
// @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {makeObject_Primitives, ValidateMemoization} from 'shared-runtime';
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ function Component(t0) {
|
||||
let y;
|
||||
if ($[0] !== a || $[1] !== b || $[2] !== c) {
|
||||
x = [];
|
||||
|
||||
if (a) {
|
||||
let t1;
|
||||
if ($[5] !== b) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component({value, enabled}) {
|
||||
@@ -29,21 +29,43 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function Component({ value, enabled }) {
|
||||
function Component(t0) {
|
||||
const $ = _c(6);
|
||||
const { value, enabled } = t0;
|
||||
const [localValue, setLocalValue] = useState("");
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[0] !== enabled || $[1] !== value) {
|
||||
t1 = () => {
|
||||
if (enabled) {
|
||||
setLocalValue(value);
|
||||
} else {
|
||||
setLocalValue("disabled");
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (enabled) {
|
||||
setLocalValue(value);
|
||||
} else {
|
||||
setLocalValue("disabled");
|
||||
}
|
||||
}, [value, enabled]);
|
||||
|
||||
return <div>{localValue}</div>;
|
||||
t2 = [value, enabled];
|
||||
$[0] = enabled;
|
||||
$[1] = value;
|
||||
$[2] = t1;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
t2 = $[3];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
let t3;
|
||||
if ($[4] !== localValue) {
|
||||
t3 = <div>{localValue}</div>;
|
||||
$[4] = localValue;
|
||||
$[5] = t3;
|
||||
} else {
|
||||
t3 = $[5];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
@@ -56,8 +78,8 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [value]\n\nData Flow Tree:\n└── value (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":9,"column":6,"index":263},"end":{"line":9,"column":19,"index":276},"filename":"derived-state-conditionally-in-effect.ts","identifierName":"setLocalValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":16,"column":1,"index":397},"filename":"derived-state-conditionally-in-effect.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [value]\n\nData Flow Tree:\n└── value (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":9,"column":6,"index":244},"end":{"line":9,"column":19,"index":257},"filename":"derived-state-conditionally-in-effect.ts","identifierName":"setLocalValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":16,"column":1,"index":378},"filename":"derived-state-conditionally-in-effect.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component({value, enabled}) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
export default function Component({input = 'empty'}) {
|
||||
@@ -26,18 +26,38 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function Component({ input = "empty" }) {
|
||||
export default function Component(t0) {
|
||||
const $ = _c(5);
|
||||
const { input: t1 } = t0;
|
||||
const input = t1 === undefined ? "empty" : t1;
|
||||
const [currInput, setCurrInput] = useState(input);
|
||||
const localConst = "local const";
|
||||
|
||||
useEffect(() => {
|
||||
setCurrInput(input + localConst);
|
||||
}, [input, localConst]);
|
||||
|
||||
return <div>{currInput}</div>;
|
||||
let t2;
|
||||
let t3;
|
||||
if ($[0] !== input) {
|
||||
t2 = () => {
|
||||
setCurrInput(input + "local const");
|
||||
};
|
||||
t3 = [input, "local const"];
|
||||
$[0] = input;
|
||||
$[1] = t2;
|
||||
$[2] = t3;
|
||||
} else {
|
||||
t2 = $[1];
|
||||
t3 = $[2];
|
||||
}
|
||||
useEffect(t2, t3);
|
||||
let t4;
|
||||
if ($[3] !== currInput) {
|
||||
t4 = <div>{currInput}</div>;
|
||||
$[3] = currInput;
|
||||
$[4] = t4;
|
||||
} else {
|
||||
t4 = $[4];
|
||||
}
|
||||
return t4;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
@@ -50,8 +70,8 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [input]\n\nData Flow Tree:\n└── input (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":9,"column":4,"index":295},"end":{"line":9,"column":16,"index":307},"filename":"derived-state-from-default-props.ts","identifierName":"setCurrInput"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":141},"end":{"line":13,"column":1,"index":391},"filename":"derived-state-from-default-props.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [input]\n\nData Flow Tree:\n└── input (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":9,"column":4,"index":276},"end":{"line":9,"column":16,"index":288},"filename":"derived-state-from-default-props.ts","identifierName":"setCurrInput"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":122},"end":{"line":13,"column":1,"index":372},"filename":"derived-state-from-default-props.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
export default function Component({input = 'empty'}) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
@@ -23,20 +23,45 @@ function Component({shouldChange}) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function Component({ shouldChange }) {
|
||||
function Component(t0) {
|
||||
const $ = _c(7);
|
||||
const { shouldChange } = t0;
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldChange) {
|
||||
setCount(count + 1);
|
||||
}
|
||||
}, [count]);
|
||||
|
||||
return <div>{count}</div>;
|
||||
let t1;
|
||||
if ($[0] !== count || $[1] !== shouldChange) {
|
||||
t1 = () => {
|
||||
if (shouldChange) {
|
||||
setCount(count + 1);
|
||||
}
|
||||
};
|
||||
$[0] = count;
|
||||
$[1] = shouldChange;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
let t2;
|
||||
if ($[3] !== count) {
|
||||
t2 = [count];
|
||||
$[3] = count;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
let t3;
|
||||
if ($[5] !== count) {
|
||||
t3 = <div>{count}</div>;
|
||||
$[5] = count;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
```
|
||||
@@ -44,8 +69,8 @@ function Component({ shouldChange }) {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [count]\n\nData Flow Tree:\n└── count (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":10,"column":6,"index":256},"end":{"line":10,"column":14,"index":264},"filename":"derived-state-from-local-state-in-effect.ts","identifierName":"setCount"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":127},"end":{"line":15,"column":1,"index":329},"filename":"derived-state-from-local-state-in-effect.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [count]\n\nData Flow Tree:\n└── count (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":10,"column":6,"index":237},"end":{"line":10,"column":14,"index":245},"filename":"derived-state-from-local-state-in-effect.ts","identifierName":"setCount"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":108},"end":{"line":15,"column":1,"index":310},"filename":"derived-state-from-local-state-in-effect.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component({firstName}) {
|
||||
@@ -33,25 +33,68 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function Component({ firstName }) {
|
||||
function Component(t0) {
|
||||
const $ = _c(12);
|
||||
const { firstName } = t0;
|
||||
const [lastName, setLastName] = useState("Doe");
|
||||
const [fullName, setFullName] = useState("John");
|
||||
|
||||
const middleName = "D.";
|
||||
|
||||
useEffect(() => {
|
||||
setFullName(firstName + " " + middleName + " " + lastName);
|
||||
}, [firstName, middleName, lastName]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input value={lastName} onChange={(e) => setLastName(e.target.value)} />
|
||||
<div>{fullName}</div>
|
||||
</div>
|
||||
);
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[0] !== firstName || $[1] !== lastName) {
|
||||
t1 = () => {
|
||||
setFullName(firstName + " " + "D." + " " + lastName);
|
||||
};
|
||||
t2 = [firstName, "D.", lastName];
|
||||
$[0] = firstName;
|
||||
$[1] = lastName;
|
||||
$[2] = t1;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
t2 = $[3];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
let t3;
|
||||
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = (e) => setLastName(e.target.value);
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
let t4;
|
||||
if ($[5] !== lastName) {
|
||||
t4 = <input value={lastName} onChange={t3} />;
|
||||
$[5] = lastName;
|
||||
$[6] = t4;
|
||||
} else {
|
||||
t4 = $[6];
|
||||
}
|
||||
let t5;
|
||||
if ($[7] !== fullName) {
|
||||
t5 = <div>{fullName}</div>;
|
||||
$[7] = fullName;
|
||||
$[8] = t5;
|
||||
} else {
|
||||
t5 = $[8];
|
||||
}
|
||||
let t6;
|
||||
if ($[9] !== t4 || $[10] !== t5) {
|
||||
t6 = (
|
||||
<div>
|
||||
{t4}
|
||||
{t5}
|
||||
</div>
|
||||
);
|
||||
$[9] = t4;
|
||||
$[10] = t5;
|
||||
$[11] = t6;
|
||||
} else {
|
||||
t6 = $[11];
|
||||
}
|
||||
return t6;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
@@ -64,8 +107,8 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [firstName]\nState: [lastName]\n\nData Flow Tree:\n├── firstName (Prop)\n└── lastName (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":11,"column":4,"index":316},"end":{"line":11,"column":15,"index":327},"filename":"derived-state-from-prop-local-state-and-component-scope.ts","identifierName":"setFullName"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":20,"column":1,"index":561},"filename":"derived-state-from-prop-local-state-and-component-scope.ts"},"fnName":"Component","memoSlots":12,"memoBlocks":5,"memoValues":6,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [firstName]\nState: [lastName]\n\nData Flow Tree:\n├── firstName (Prop)\n└── lastName (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":11,"column":4,"index":297},"end":{"line":11,"column":15,"index":308},"filename":"derived-state-from-prop-local-state-and-component-scope.ts","identifierName":"setFullName"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":20,"column":1,"index":542},"filename":"derived-state-from-prop-local-state-and-component-scope.ts"},"fnName":"Component","memoSlots":12,"memoBlocks":5,"memoValues":6,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component({firstName}) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component({initialName}) {
|
||||
@@ -29,21 +29,48 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function Component({ initialName }) {
|
||||
function Component(t0) {
|
||||
const $ = _c(6);
|
||||
const { initialName } = t0;
|
||||
const [name, setName] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
setName(initialName);
|
||||
}, [initialName]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input value={name} onChange={(e) => setName(e.target.value)} />
|
||||
</div>
|
||||
);
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[0] !== initialName) {
|
||||
t1 = () => {
|
||||
setName(initialName);
|
||||
};
|
||||
t2 = [initialName];
|
||||
$[0] = initialName;
|
||||
$[1] = t1;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
t2 = $[2];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
let t3;
|
||||
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = (e) => setName(e.target.value);
|
||||
$[3] = t3;
|
||||
} else {
|
||||
t3 = $[3];
|
||||
}
|
||||
let t4;
|
||||
if ($[4] !== name) {
|
||||
t4 = (
|
||||
<div>
|
||||
<input value={name} onChange={t3} />
|
||||
</div>
|
||||
);
|
||||
$[4] = name;
|
||||
$[5] = t4;
|
||||
} else {
|
||||
t4 = $[5];
|
||||
}
|
||||
return t4;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
@@ -56,7 +83,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":16,"column":1,"index":378},"filename":"derived-state-from-prop-setter-call-outside-effect-no-error.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":3,"memoValues":4,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":16,"column":1,"index":359},"filename":"derived-state-from-prop-setter-call-outside-effect-no-error.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":3,"memoValues":4,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component({initialName}) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp
|
||||
|
||||
function Component({value}) {
|
||||
const [checked, setChecked] = useState('');
|
||||
@@ -19,16 +19,36 @@ function Component({value}) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp
|
||||
|
||||
function Component({ value }) {
|
||||
function Component(t0) {
|
||||
const $ = _c(5);
|
||||
const { value } = t0;
|
||||
const [checked, setChecked] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
setChecked(value === "" ? [] : value.split(","));
|
||||
}, [value]);
|
||||
|
||||
return <div>{checked}</div>;
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[0] !== value) {
|
||||
t1 = () => {
|
||||
setChecked(value === "" ? [] : value.split(","));
|
||||
};
|
||||
t2 = [value];
|
||||
$[0] = value;
|
||||
$[1] = t1;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
t2 = $[2];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
let t3;
|
||||
if ($[3] !== checked) {
|
||||
t3 = <div>{checked}</div>;
|
||||
$[3] = checked;
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp
|
||||
|
||||
function Component({value}) {
|
||||
const [checked, setChecked] = useState('');
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function MockComponent({onSet}) {
|
||||
@@ -28,20 +28,50 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function MockComponent({ onSet }) {
|
||||
return <div onClick={() => onSet("clicked")}>Mock Component</div>;
|
||||
function MockComponent(t0) {
|
||||
const $ = _c(2);
|
||||
const { onSet } = t0;
|
||||
let t1;
|
||||
if ($[0] !== onSet) {
|
||||
t1 = <div onClick={() => onSet("clicked")}>Mock Component</div>;
|
||||
$[0] = onSet;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
function Component({ propValue }) {
|
||||
const [value, setValue] = useState(null);
|
||||
useEffect(() => {
|
||||
setValue(propValue);
|
||||
}, [propValue]);
|
||||
|
||||
return <MockComponent onSet={setValue} />;
|
||||
function Component(t0) {
|
||||
const $ = _c(4);
|
||||
const { propValue } = t0;
|
||||
const [, setValue] = useState(null);
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[0] !== propValue) {
|
||||
t1 = () => {
|
||||
setValue(propValue);
|
||||
};
|
||||
t2 = [propValue];
|
||||
$[0] = propValue;
|
||||
$[1] = t1;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
t2 = $[2];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
let t3;
|
||||
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = <MockComponent onSet={setValue} />;
|
||||
$[3] = t3;
|
||||
} else {
|
||||
t3 = $[3];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
@@ -54,8 +84,8 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":6,"column":1,"index":230},"filename":"derived-state-from-prop-setter-used-outside-effect-no-error.ts"},"fnName":"MockComponent","memoSlots":2,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":8,"column":0,"index":232},"end":{"line":15,"column":1,"index":421},"filename":"derived-state-from-prop-setter-used-outside-effect-no-error.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":6,"column":1,"index":211},"filename":"derived-state-from-prop-setter-used-outside-effect-no-error.ts"},"fnName":"MockComponent","memoSlots":2,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":8,"column":0,"index":213},"end":{"line":15,"column":1,"index":402},"filename":"derived-state-from-prop-setter-used-outside-effect-no-error.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function MockComponent({onSet}) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component({value}) {
|
||||
@@ -26,18 +26,38 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function Component({ value }) {
|
||||
function Component(t0) {
|
||||
const $ = _c(5);
|
||||
const { value } = t0;
|
||||
const [localValue, setLocalValue] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
setLocalValue(value);
|
||||
document.title = `Value: ${value}`;
|
||||
}, [value]);
|
||||
|
||||
return <div>{localValue}</div>;
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[0] !== value) {
|
||||
t1 = () => {
|
||||
setLocalValue(value);
|
||||
document.title = `Value: ${value}`;
|
||||
};
|
||||
t2 = [value];
|
||||
$[0] = value;
|
||||
$[1] = t1;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
t2 = $[2];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
let t3;
|
||||
if ($[3] !== localValue) {
|
||||
t3 = <div>{localValue}</div>;
|
||||
$[3] = localValue;
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
@@ -50,8 +70,8 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [value]\n\nData Flow Tree:\n└── value (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":8,"column":4,"index":233},"end":{"line":8,"column":17,"index":246},"filename":"derived-state-from-prop-with-side-effect.ts","identifierName":"setLocalValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":13,"column":1,"index":346},"filename":"derived-state-from-prop-with-side-effect.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [value]\n\nData Flow Tree:\n└── value (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":8,"column":4,"index":214},"end":{"line":8,"column":17,"index":227},"filename":"derived-state-from-prop-with-side-effect.ts","identifierName":"setLocalValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":13,"column":1,"index":327},"filename":"derived-state-from-prop-with-side-effect.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component({value}) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState, useRef} from 'react';
|
||||
|
||||
export default function Component({test}) {
|
||||
@@ -27,19 +27,39 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
|
||||
export default function Component({ test }) {
|
||||
export default function Component(t0) {
|
||||
const $ = _c(5);
|
||||
const { test } = t0;
|
||||
const [local, setLocal] = useState("");
|
||||
|
||||
const myRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
setLocal(myRef.current + test);
|
||||
}, [test]);
|
||||
|
||||
return <>{local}</>;
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[0] !== test) {
|
||||
t1 = () => {
|
||||
setLocal(myRef.current + test);
|
||||
};
|
||||
t2 = [test];
|
||||
$[0] = test;
|
||||
$[1] = t1;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
t2 = $[2];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
let t3;
|
||||
if ($[3] !== local) {
|
||||
t3 = <>{local}</>;
|
||||
$[3] = local;
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
@@ -52,7 +72,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":149},"end":{"line":14,"column":1,"index":347},"filename":"derived-state-from-ref-and-state-no-error.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":130},"end":{"line":14,"column":1,"index":328},"filename":"derived-state-from-ref-and-state-no-error.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState, useRef} from 'react';
|
||||
|
||||
export default function Component({test}) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component({propValue}) {
|
||||
@@ -30,22 +30,48 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function Component({ propValue }) {
|
||||
function Component(t0) {
|
||||
const $ = _c(6);
|
||||
const { propValue } = t0;
|
||||
const [value, setValue] = useState(null);
|
||||
|
||||
function localFunction() {
|
||||
console.log("local function");
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = function localFunction() {
|
||||
console.log("local function");
|
||||
};
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setValue(propValue);
|
||||
localFunction();
|
||||
}, [propValue]);
|
||||
|
||||
return <div>{value}</div>;
|
||||
const localFunction = t1;
|
||||
let t2;
|
||||
let t3;
|
||||
if ($[1] !== propValue) {
|
||||
t2 = () => {
|
||||
setValue(propValue);
|
||||
localFunction();
|
||||
};
|
||||
t3 = [propValue];
|
||||
$[1] = propValue;
|
||||
$[2] = t2;
|
||||
$[3] = t3;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
t3 = $[3];
|
||||
}
|
||||
useEffect(t2, t3);
|
||||
let t4;
|
||||
if ($[4] !== value) {
|
||||
t4 = <div>{value}</div>;
|
||||
$[4] = value;
|
||||
$[5] = t4;
|
||||
} else {
|
||||
t4 = $[5];
|
||||
}
|
||||
return t4;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
@@ -58,8 +84,8 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [propValue]\n\nData Flow Tree:\n└── propValue (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":12,"column":4,"index":298},"end":{"line":12,"column":12,"index":306},"filename":"effect-contains-local-function-call.ts","identifierName":"setValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":17,"column":1,"index":390},"filename":"effect-contains-local-function-call.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":3,"memoValues":4,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [propValue]\n\nData Flow Tree:\n└── propValue (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":12,"column":4,"index":279},"end":{"line":12,"column":12,"index":287},"filename":"effect-contains-local-function-call.ts","identifierName":"setValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":17,"column":1,"index":371},"filename":"effect-contains-local-function-call.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":3,"memoValues":4,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component({propValue}) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component({propValue, onChange}) {
|
||||
@@ -25,17 +25,43 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function Component({ propValue, onChange }) {
|
||||
function Component(t0) {
|
||||
const $ = _c(7);
|
||||
const { propValue, onChange } = t0;
|
||||
const [value, setValue] = useState(null);
|
||||
useEffect(() => {
|
||||
setValue(propValue);
|
||||
onChange();
|
||||
}, [propValue]);
|
||||
|
||||
return <div>{value}</div>;
|
||||
let t1;
|
||||
if ($[0] !== onChange || $[1] !== propValue) {
|
||||
t1 = () => {
|
||||
setValue(propValue);
|
||||
onChange();
|
||||
};
|
||||
$[0] = onChange;
|
||||
$[1] = propValue;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
let t2;
|
||||
if ($[3] !== propValue) {
|
||||
t2 = [propValue];
|
||||
$[3] = propValue;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
let t3;
|
||||
if ($[5] !== value) {
|
||||
t3 = <div>{value}</div>;
|
||||
$[5] = value;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
@@ -48,8 +74,8 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":12,"column":1,"index":325},"filename":"effect-contains-prop-function-call-no-error.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":16,"column":41,"index":421},"end":{"line":16,"column":49,"index":429},"filename":"effect-contains-prop-function-call-no-error.ts"},"fnName":null,"memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":12,"column":1,"index":306},"filename":"effect-contains-prop-function-call-no-error.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":16,"column":41,"index":402},"end":{"line":16,"column":49,"index":410},"filename":"effect-contains-prop-function-call-no-error.ts"},"fnName":null,"memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component({propValue, onChange}) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
|
||||
function Component({prop}) {
|
||||
const [s, setS] = useState(0);
|
||||
@@ -18,15 +18,36 @@ function Component({prop}) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
|
||||
function Component({ prop }) {
|
||||
const [s, setS] = useState(0);
|
||||
useEffect(() => {
|
||||
setS(prop);
|
||||
}, [prop, setS]);
|
||||
|
||||
return <div>{prop}</div>;
|
||||
function Component(t0) {
|
||||
const $ = _c(5);
|
||||
const { prop } = t0;
|
||||
const [, setS] = useState(0);
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[0] !== prop) {
|
||||
t1 = () => {
|
||||
setS(prop);
|
||||
};
|
||||
t2 = [prop, setS];
|
||||
$[0] = prop;
|
||||
$[1] = t1;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
t2 = $[2];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
let t3;
|
||||
if ($[3] !== prop) {
|
||||
t3 = <div>{prop}</div>;
|
||||
$[3] = prop;
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
```
|
||||
@@ -34,8 +55,8 @@ function Component({ prop }) {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [prop]\n\nData Flow Tree:\n└── prop (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":6,"column":4,"index":169},"end":{"line":6,"column":8,"index":173},"filename":"effect-used-in-dep-array-still-errors.ts","identifierName":"setS"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":3,"column":0,"index":83},"end":{"line":10,"column":1,"index":231},"filename":"effect-used-in-dep-array-still-errors.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [prop]\n\nData Flow Tree:\n└── prop (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":6,"column":4,"index":150},"end":{"line":6,"column":8,"index":154},"filename":"effect-used-in-dep-array-still-errors.ts","identifierName":"setS"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":3,"column":0,"index":64},"end":{"line":10,"column":1,"index":212},"filename":"effect-used-in-dep-array-still-errors.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
|
||||
function Component({prop}) {
|
||||
const [s, setS] = useState(0);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
@@ -29,26 +29,39 @@ function Component(file: File) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function Component(file: File) {
|
||||
function Component(file) {
|
||||
const $ = _c(5);
|
||||
const [imageUrl, setImageUrl] = useState(null);
|
||||
|
||||
/*
|
||||
* Cleaning up the variable or a source of the variable used to setState
|
||||
* inside the effect communicates that we always need to clean up something
|
||||
* which is a valid use case for useEffect. In which case we want to
|
||||
* avoid an throwing
|
||||
*/
|
||||
useEffect(() => {
|
||||
const imageUrlPrepared = URL.createObjectURL(file);
|
||||
setImageUrl(imageUrlPrepared);
|
||||
return () => URL.revokeObjectURL(imageUrlPrepared);
|
||||
}, [file]);
|
||||
|
||||
return <Image src={imageUrl} xstyle={styles.imageSizeLimits} />;
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[0] !== file) {
|
||||
t0 = () => {
|
||||
const imageUrlPrepared = URL.createObjectURL(file);
|
||||
setImageUrl(imageUrlPrepared);
|
||||
return () => URL.revokeObjectURL(imageUrlPrepared);
|
||||
};
|
||||
t1 = [file];
|
||||
$[0] = file;
|
||||
$[1] = t0;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
t1 = $[2];
|
||||
}
|
||||
useEffect(t0, t1);
|
||||
let t2;
|
||||
if ($[3] !== imageUrl) {
|
||||
t2 = <Image src={imageUrl} xstyle={styles.imageSizeLimits} />;
|
||||
$[3] = imageUrl;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
```
|
||||
@@ -56,7 +69,7 @@ function Component(file: File) {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":127},"end":{"line":21,"column":1,"index":719},"filename":"effect-with-cleanup-function-depending-on-derived-computation-value.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":108},"end":{"line":21,"column":1,"index":700},"filename":"effect-with-cleanup-function-depending-on-derived-computation-value.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component({propValue}) {
|
||||
@@ -25,17 +25,38 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function Component({ propValue }) {
|
||||
function Component(t0) {
|
||||
const $ = _c(5);
|
||||
const { propValue } = t0;
|
||||
const [value, setValue] = useState(null);
|
||||
useEffect(() => {
|
||||
setValue(propValue);
|
||||
globalCall();
|
||||
}, [propValue]);
|
||||
|
||||
return <div>{value}</div>;
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[0] !== propValue) {
|
||||
t1 = () => {
|
||||
setValue(propValue);
|
||||
globalCall();
|
||||
};
|
||||
t2 = [propValue];
|
||||
$[0] = propValue;
|
||||
$[1] = t1;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
t2 = $[2];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
let t3;
|
||||
if ($[3] !== value) {
|
||||
t3 = <div>{value}</div>;
|
||||
$[3] = value;
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
@@ -48,7 +69,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":12,"column":1,"index":317},"filename":"effect-with-global-function-call-no-error.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":12,"column":1,"index":298},"filename":"effect-with-global-function-call-no-error.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component({propValue}) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @enableTreatSetIdentifiersAsStateSetters @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @enableTreatSetIdentifiersAsStateSetters @loggerTestOnly
|
||||
|
||||
function Component({setParentState, prop}) {
|
||||
useEffect(() => {
|
||||
@@ -17,14 +17,40 @@ function Component({setParentState, prop}) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @enableTreatSetIdentifiersAsStateSetters @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @enableTreatSetIdentifiersAsStateSetters @loggerTestOnly
|
||||
|
||||
function Component({ setParentState, prop }) {
|
||||
useEffect(() => {
|
||||
setParentState(prop);
|
||||
}, [prop]);
|
||||
|
||||
return <div>{prop}</div>;
|
||||
function Component(t0) {
|
||||
const $ = _c(7);
|
||||
const { setParentState, prop } = t0;
|
||||
let t1;
|
||||
if ($[0] !== prop || $[1] !== setParentState) {
|
||||
t1 = () => {
|
||||
setParentState(prop);
|
||||
};
|
||||
$[0] = prop;
|
||||
$[1] = setParentState;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
let t2;
|
||||
if ($[3] !== prop) {
|
||||
t2 = [prop];
|
||||
$[3] = prop;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
let t3;
|
||||
if ($[5] !== prop) {
|
||||
t3 = <div>{prop}</div>;
|
||||
$[5] = prop;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
```
|
||||
@@ -32,7 +58,7 @@ function Component({ setParentState, prop }) {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":3,"column":0,"index":124},"end":{"line":9,"column":1,"index":259},"filename":"from-props-setstate-in-effect-no-error.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":3,"column":0,"index":105},"end":{"line":9,"column":1,"index":240},"filename":"from-props-setstate-in-effect-no-error.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @enableTreatSetIdentifiersAsStateSetters @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @enableTreatSetIdentifiersAsStateSetters @loggerTestOnly
|
||||
|
||||
function Component({setParentState, prop}) {
|
||||
useEffect(() => {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
|
||||
function Component() {
|
||||
const [foo, setFoo] = useState({});
|
||||
@@ -40,37 +40,66 @@ function Component() {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
|
||||
function Component() {
|
||||
const [foo, setFoo] = useState({});
|
||||
const [bar, setBar] = useState(new Set());
|
||||
const $ = _c(9);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = {};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const [foo, setFoo] = useState(t0);
|
||||
let t1;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = new Set();
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const [bar] = useState(t1);
|
||||
let t2;
|
||||
let t3;
|
||||
if ($[2] !== bar || $[3] !== foo) {
|
||||
t2 = () => {
|
||||
let isChanged = false;
|
||||
|
||||
/*
|
||||
* isChanged is considered context of the effect's function expression,
|
||||
* if we don't bail out of effect mutation derivation tracking, isChanged
|
||||
* will inherit the sources of the effect's function expression.
|
||||
*
|
||||
* This is innacurate and with the multiple passes ends up causing an infinite loop.
|
||||
*/
|
||||
useEffect(() => {
|
||||
let isChanged = false;
|
||||
const newData = foo.map((val) => {
|
||||
bar.someMethod(val);
|
||||
isChanged = true;
|
||||
});
|
||||
|
||||
const newData = foo.map((val) => {
|
||||
bar.someMethod(val);
|
||||
isChanged = true;
|
||||
});
|
||||
if (isChanged) {
|
||||
setFoo(newData);
|
||||
}
|
||||
};
|
||||
|
||||
if (isChanged) {
|
||||
setFoo(newData);
|
||||
}
|
||||
}, [foo, bar]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{foo}, {bar}
|
||||
</div>
|
||||
);
|
||||
t3 = [foo, bar];
|
||||
$[2] = bar;
|
||||
$[3] = foo;
|
||||
$[4] = t2;
|
||||
$[5] = t3;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
t3 = $[5];
|
||||
}
|
||||
useEffect(t2, t3);
|
||||
let t4;
|
||||
if ($[6] !== bar || $[7] !== foo) {
|
||||
t4 = (
|
||||
<div>
|
||||
{foo}, {bar}
|
||||
</div>
|
||||
);
|
||||
$[6] = bar;
|
||||
$[7] = foo;
|
||||
$[8] = t4;
|
||||
} else {
|
||||
t4 = $[8];
|
||||
}
|
||||
return t4;
|
||||
}
|
||||
|
||||
```
|
||||
@@ -78,8 +107,8 @@ function Component() {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [foo, bar]\n\nData Flow Tree:\n└── newData\n ├── foo (State)\n └── bar (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":23,"column":6,"index":682},"end":{"line":23,"column":12,"index":688},"filename":"function-expression-mutation-edge-case.ts","identifierName":"setFoo"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":3,"column":0,"index":83},"end":{"line":32,"column":1,"index":781},"filename":"function-expression-mutation-edge-case.ts"},"fnName":"Component","memoSlots":9,"memoBlocks":4,"memoValues":5,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [foo, bar]\n\nData Flow Tree:\n└── newData\n ├── foo (State)\n └── bar (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":23,"column":6,"index":663},"end":{"line":23,"column":12,"index":669},"filename":"function-expression-mutation-edge-case.ts","identifierName":"setFoo"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":3,"column":0,"index":64},"end":{"line":32,"column":1,"index":762},"filename":"function-expression-mutation-edge-case.ts"},"fnName":"Component","memoSlots":9,"memoBlocks":4,"memoValues":5,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
|
||||
function Component() {
|
||||
const [foo, setFoo] = useState({});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component() {
|
||||
@@ -28,20 +28,38 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint"
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function Component() {
|
||||
const [firstName, setFirstName] = useState("Taylor");
|
||||
const lastName = "Swift";
|
||||
const $ = _c(5);
|
||||
const [firstName] = useState("Taylor");
|
||||
|
||||
// 🔴 Avoid: redundant state and unnecessary Effect
|
||||
const [fullName, setFullName] = useState("");
|
||||
useEffect(() => {
|
||||
setFullName(firstName + " " + lastName);
|
||||
}, [firstName, lastName]);
|
||||
|
||||
return <div>{fullName}</div>;
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[0] !== firstName) {
|
||||
t0 = () => {
|
||||
setFullName(firstName + " " + "Swift");
|
||||
};
|
||||
t1 = [firstName, "Swift"];
|
||||
$[0] = firstName;
|
||||
$[1] = t0;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
t1 = $[2];
|
||||
}
|
||||
useEffect(t0, t1);
|
||||
let t2;
|
||||
if ($[3] !== fullName) {
|
||||
t2 = <div>{fullName}</div>;
|
||||
$[3] = fullName;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
@@ -54,8 +72,8 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [firstName]\n\nData Flow Tree:\n└── firstName (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":11,"column":4,"index":360},"end":{"line":11,"column":15,"index":371},"filename":"invalid-derived-computation-in-effect.ts","identifierName":"setFullName"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":15,"column":1,"index":464},"filename":"invalid-derived-computation-in-effect.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [firstName]\n\nData Flow Tree:\n└── firstName (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":11,"column":4,"index":341},"end":{"line":11,"column":15,"index":352},"filename":"invalid-derived-computation-in-effect.ts","identifierName":"setFullName"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":15,"column":1,"index":445},"filename":"invalid-derived-computation-in-effect.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user