Compare commits
13 Commits
repro-bug-
...
pr32662
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
269325c04f | ||
|
|
829e8918a6 | ||
|
|
5644e81f27 | ||
|
|
208905257f | ||
|
|
891a6332e3 | ||
|
|
a4842c92ea | ||
|
|
ada8bbbd6a | ||
|
|
e9c3b27b4b | ||
|
|
b0446ff06a | ||
|
|
db7dfe0550 | ||
|
|
646835fb59 | ||
|
|
c2a1961747 | ||
|
|
6584a6eec4 |
@@ -336,11 +336,11 @@ module.exports = {
|
||||
'packages/react-devtools-extensions/**/*.js',
|
||||
'packages/react-devtools-timeline/**/*.js',
|
||||
'packages/react-native-renderer/**/*.js',
|
||||
'packages/eslint-plugin-react-hooks/**/*.js',
|
||||
'packages/jest-react/**/*.js',
|
||||
'packages/internal-test-utils/**/*.js',
|
||||
'packages/**/__tests__/*.js',
|
||||
'packages/**/npm/*.js',
|
||||
'compiler/packages/eslint-plugin-react-hooks/**/*.js',
|
||||
],
|
||||
rules: {
|
||||
'react-internal/prod-error-codes': OFF,
|
||||
@@ -515,7 +515,7 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/eslint-plugin-react-hooks/src/**/*'],
|
||||
files: ['compiler/packages/eslint-plugin-react-hooks/src/**/*'],
|
||||
extends: ['plugin:@typescript-eslint/recommended'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint', 'eslint-plugin'],
|
||||
|
||||
2
.github/workflows/compiler_playground.yml
vendored
2
.github/workflows/compiler_playground.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: npx playwright install --with-deps chromium
|
||||
- run: CI=true yarn test
|
||||
|
||||
2
.github/workflows/compiler_prereleases.yml
vendored
2
.github/workflows/compiler_prereleases.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- name: Publish packages to npm
|
||||
run: |
|
||||
|
||||
6
.github/workflows/compiler_typescript.yml
vendored
6
.github/workflows/compiler_typescript.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn workspace babel-plugin-react-compiler lint
|
||||
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn workspace babel-plugin-react-compiler jest
|
||||
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: xvfb-run -a yarn workspace ${{ matrix.workspace_name }} test
|
||||
if: runner.os == 'Linux' && matrix.workspace_name == 'react-forgive'
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
|
||||
key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ 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
|
||||
@@ -123,7 +123,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- name: Restore all archived build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -158,7 +158,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- name: Restore all archived build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
169
.github/workflows/runtime_build_and_test.yml
vendored
169
.github/workflows/runtime_build_and_test.yml
vendored
@@ -17,6 +17,75 @@ env:
|
||||
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
|
||||
|
||||
jobs:
|
||||
# ----- NODE_MODULES CACHE -----
|
||||
# Centralize the node_modules cache so it is saved once and each subsequent job only needs to
|
||||
# restore the cache. Prevents race conditions where multiple workflows try to write to the cache.
|
||||
runtime_node_modules_cache:
|
||||
name: Cache Runtime node_modules
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Check cache hit
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
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'
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- run: yarn install --frozen-lockfile
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- name: Save cache
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
|
||||
runtime_compiler_node_modules_cache:
|
||||
name: Cache Runtime, Compiler node_modules
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Check cache hit
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
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'
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: yarn
|
||||
cache-dependency-path: |
|
||||
yarn.lock
|
||||
compiler/yarn.lock
|
||||
- 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'
|
||||
- name: Save cache
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
|
||||
# ----- FLOW -----
|
||||
discover_flow_inline_configs:
|
||||
name: Discover flow inline configs
|
||||
@@ -36,7 +105,7 @@ jobs:
|
||||
|
||||
flow:
|
||||
name: Flow check ${{ matrix.flow_inline_config_shortname }}
|
||||
needs: discover_flow_inline_configs
|
||||
needs: [discover_flow_inline_configs, runtime_node_modules_cache]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -52,20 +121,22 @@ jobs:
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ 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
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- run: node ./scripts/tasks/flow-ci ${{ matrix.flow_inline_config_shortname }}
|
||||
|
||||
# ----- FIZZ -----
|
||||
check_generated_fizz_runtime:
|
||||
name: Confirm generated inline Fizz runtime is up to date
|
||||
needs: [runtime_node_modules_cache]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -77,15 +148,16 @@ jobs:
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ 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
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- run: |
|
||||
yarn generate-inline-fizz-runtime
|
||||
git diff --quiet || (echo "There was a change to the Fizz runtime. Run `yarn generate-inline-fizz-runtime` and check in the result." && false)
|
||||
@@ -93,6 +165,7 @@ jobs:
|
||||
# ----- FEATURE FLAGS -----
|
||||
flags:
|
||||
name: Check flags
|
||||
needs: [runtime_node_modules_cache]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -104,20 +177,22 @@ jobs:
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ 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
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- run: yarn flags
|
||||
|
||||
# ----- TESTS -----
|
||||
test:
|
||||
name: yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }})
|
||||
needs: [runtime_compiler_node_modules_cache]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -160,27 +235,30 @@ jobs:
|
||||
yarn.lock
|
||||
compiler/yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v5-${{ 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: 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: yarn test ${{ matrix.params }} --ci --shard=${{ matrix.shard }}
|
||||
|
||||
# ----- BUILD -----
|
||||
build_and_lint:
|
||||
name: yarn build and lint
|
||||
needs: [runtime_compiler_node_modules_cache]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# yml is dumb. update the --total arg to yarn build if you change the number of workers
|
||||
worker_id: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
|
||||
worker_id: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]
|
||||
release_channel: [stable, experimental]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -198,17 +276,19 @@ jobs:
|
||||
distribution: temurin
|
||||
java-version: 11.0.22
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v5-${{ 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: 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
|
||||
- run: yarn build --index=${{ matrix.worker_id }} --total=20 --r=${{ matrix.release_channel }} --ci
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- run: yarn build --index=${{ matrix.worker_id }} --total=25 --r=${{ matrix.release_channel }} --ci
|
||||
env:
|
||||
CI: github
|
||||
RELEASE_CHANNEL: ${{ matrix.release_channel }}
|
||||
@@ -225,7 +305,7 @@ jobs:
|
||||
|
||||
test_build:
|
||||
name: yarn test-build
|
||||
needs: build_and_lint
|
||||
needs: [build_and_lint, runtime_compiler_node_modules_cache]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -276,16 +356,18 @@ jobs:
|
||||
yarn.lock
|
||||
compiler/yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v5-${{ 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: 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'
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -298,7 +380,7 @@ jobs:
|
||||
|
||||
process_artifacts_combined:
|
||||
name: Process artifacts combined
|
||||
needs: build_and_lint
|
||||
needs: [build_and_lint, runtime_node_modules_cache]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -310,15 +392,16 @@ jobs:
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ 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
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -346,7 +429,7 @@ jobs:
|
||||
|
||||
check_error_codes:
|
||||
name: Search build artifacts for unminified errors
|
||||
needs: build_and_lint
|
||||
needs: [build_and_lint, runtime_node_modules_cache]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -358,15 +441,16 @@ jobs:
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ 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
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -382,7 +466,7 @@ jobs:
|
||||
|
||||
check_release_dependencies:
|
||||
name: Check release dependencies
|
||||
needs: build_and_lint
|
||||
needs: [build_and_lint, runtime_node_modules_cache]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -394,15 +478,16 @@ jobs:
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ 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
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -427,15 +512,16 @@ jobs:
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v4 # note: this does not reuse centralized cache since it has unique cache key
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: fixtures_dom-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
key: fixtures_dom-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn --cwd fixtures/dom install --frozen-lockfile
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -470,15 +556,16 @@ jobs:
|
||||
# That means dependencies of the built packages are not installed.
|
||||
# We need to install dependencies of the workroot to fulfill all dependency constraints
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v4 # note: this does not reuse centralized cache since it has unique cache key
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: fixtures_flight-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
key: fixtures_flight-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- 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:
|
||||
@@ -517,7 +604,7 @@ jobs:
|
||||
# ----- DEVTOOLS -----
|
||||
build_devtools_and_process_artifacts:
|
||||
name: Build DevTools and process artifacts
|
||||
needs: build_and_lint
|
||||
needs: [build_and_lint, runtime_node_modules_cache]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -533,15 +620,16 @@ jobs:
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ 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
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -573,7 +661,7 @@ jobs:
|
||||
|
||||
run_devtools_e2e_tests:
|
||||
name: Run DevTools e2e tests
|
||||
needs: build_and_lint
|
||||
needs: [build_and_lint, runtime_node_modules_cache]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -585,15 +673,16 @@ jobs:
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ 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
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -611,7 +700,7 @@ jobs:
|
||||
sizebot:
|
||||
if: ${{ github.event_name == 'pull_request' && github.ref_name != 'main' && github.event.pull_request.base.ref == 'main' }}
|
||||
name: Run sizebot
|
||||
needs: [build_and_lint]
|
||||
needs: [build_and_lint, runtime_node_modules_cache]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -623,15 +712,18 @@ jobs:
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
|
||||
- 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 scripts/release install --frozen-lockfile
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- name: Download artifacts for base revision
|
||||
run: |
|
||||
GH_TOKEN=${{ github.token }} scripts/release/download-experimental-build.js --commit=$(git rev-parse ${{ github.event.pull_request.base.sha }})
|
||||
@@ -647,6 +739,7 @@ jobs:
|
||||
- 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 for PR
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
|
||||
22
.github/workflows/runtime_commit_artifacts.yml
vendored
22
.github/workflows/runtime_commit_artifacts.yml
vendored
@@ -80,7 +80,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
|
||||
key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -246,15 +246,19 @@ jobs:
|
||||
run: |
|
||||
echo ":"
|
||||
git status -u
|
||||
- name: Check commit message
|
||||
if: inputs.dry_run
|
||||
run: |
|
||||
git fetch origin --quiet
|
||||
git show ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} --no-patch --pretty=format:"%B"
|
||||
- name: Commit changes to branch
|
||||
if: inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true'
|
||||
run: |
|
||||
git config --global user.email "${{ format('{0}@users.noreply.github.com', github.triggering_actor) }}"
|
||||
git config --global user.name "${{ github.triggering_actor }}"
|
||||
|
||||
git commit -m "${{ github.event.workflow_run.head_commit.message || format('Manual build of {0}', github.event.workflow_run.head_sha || github.sha) }}
|
||||
|
||||
DiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha }})" || echo "No changes to commit"
|
||||
git fetch origin --quiet
|
||||
git commit -m "$(git show ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} --no-patch --pretty=format:'%B%n%nDiffTrain build for [${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha}})')" || echo "No changes to commit"
|
||||
- name: Push changes to branch
|
||||
if: inputs.dry_run == false && (inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true')
|
||||
run: git push
|
||||
@@ -413,15 +417,19 @@ jobs:
|
||||
run: |
|
||||
git add .
|
||||
git status
|
||||
- name: Check commit message
|
||||
if: inputs.dry_run
|
||||
run: |
|
||||
git fetch origin --quiet
|
||||
git show ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} --no-patch --pretty=format:"%B"
|
||||
- name: Commit changes to branch
|
||||
if: inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true'
|
||||
run: |
|
||||
git config --global user.email "${{ format('{0}@users.noreply.github.com', github.triggering_actor) }}"
|
||||
git config --global user.name "${{ github.triggering_actor }}"
|
||||
|
||||
git commit -m "${{ github.event.workflow_run.head_commit.message || format('Manual build of {0}', github.event.workflow_run.head_sha || github.sha) }}
|
||||
|
||||
DiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha }})" || echo "No changes to commit"
|
||||
git fetch origin --quiet
|
||||
git commit -m "$(git show ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} --no-patch --pretty=format:'%B%n%nDiffTrain build for [${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha}})')" || echo "No changes to commit"
|
||||
- name: Push changes to branch
|
||||
if: inputs.dry_run == false && (inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true')
|
||||
run: git push
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-eslint_e2e-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
key: runtime-and-compiler-eslint_e2e-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
|
||||
2
.github/workflows/runtime_prereleases.yml
vendored
2
.github/workflows/runtime_prereleases.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
|
||||
key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
|
||||
key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
|
||||
35
.github/workflows/shared_cleanup_branch_caches.yml
vendored
Normal file
35
.github/workflows/shared_cleanup_branch_caches.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# https://github.com/actions/cache/blob/main/tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy
|
||||
|
||||
name: (Shared) Cleanup Branch Caches
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# `actions:write` permission is required to delete caches
|
||||
# See also: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#delete-a-github-actions-cache-for-a-repository-using-a-cache-id
|
||||
actions: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Cleanup
|
||||
run: |
|
||||
echo "Fetching list of cache key"
|
||||
cacheKeysForPR=$(gh cache list --ref $BRANCH --limit 100 --json id --jq '.[].id')
|
||||
|
||||
## Setting this to not fail the workflow while deleting cache keys.
|
||||
set +e
|
||||
echo "Deleting caches..."
|
||||
for cacheKey in $cacheKeysForPR
|
||||
do
|
||||
gh cache delete $cacheKey
|
||||
done
|
||||
echo "Done"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||
8
.github/workflows/shared_lint.yml
vendored
8
.github/workflows/shared_lint.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
key: shared-lint-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
key: shared-lint-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
key: shared-lint-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
key: shared-lint-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
|
||||
@@ -33,7 +33,6 @@ const canaryChannelLabel = 'canary';
|
||||
const rcNumber = 0;
|
||||
|
||||
const stablePackages = {
|
||||
'eslint-plugin-react-hooks': '5.2.0',
|
||||
'jest-react': '0.17.0',
|
||||
react: ReactVersion,
|
||||
'react-art': ReactVersion,
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* HACK: @poteto React Compiler inlines Zod in its build artifact. Zod spreads values passed to .map
|
||||
* which causes issues in @babel/plugin-transform-spread in loose mode, as it will result in
|
||||
* {undefined: undefined} which fails to parse.
|
||||
*
|
||||
* [@babel/plugin-transform-block-scoping', {throwIfClosureRequired: true}] also causes issues with
|
||||
* the built version of the compiler. The minimal set of plugins needed for this file is reexported
|
||||
* from babel.config-ts.
|
||||
*
|
||||
* I will remove this hack later when we move eslint-plugin-react-hooks into the compiler directory.
|
||||
**/
|
||||
|
||||
const baseConfig = require('./babel.config-ts');
|
||||
|
||||
module.exports = {
|
||||
plugins: baseConfig.plugins,
|
||||
};
|
||||
@@ -16,7 +16,7 @@
|
||||
"snap:build": "yarn workspace snap run build",
|
||||
"snap:ci": "yarn snap:build && yarn snap",
|
||||
"ts:analyze-trace": "scripts/ts-analyze-trace.sh",
|
||||
"lint": "yarn eslint src",
|
||||
"lint": "../../node_modules/eslint-v8/bin/eslint.js src",
|
||||
"watch": "yarn build --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -43,7 +43,7 @@
|
||||
"babel-jest": "^29.0.3",
|
||||
"babel-plugin-fbt": "^1.0.0",
|
||||
"babel-plugin-fbt-runtime": "^1.0.0",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-v8": "npm:eslint@^8.57.1",
|
||||
"invariant": "^2.2.4",
|
||||
"jest": "^29.0.3",
|
||||
"jest-environment-jsdom": "^29.0.3",
|
||||
|
||||
@@ -13,11 +13,13 @@ import {
|
||||
BlockId,
|
||||
DependencyPathEntry,
|
||||
GeneratedSource,
|
||||
getHookKind,
|
||||
HIRFunction,
|
||||
Identifier,
|
||||
IdentifierId,
|
||||
InstructionId,
|
||||
InstructionValue,
|
||||
LoweredFunction,
|
||||
PropertyLiteral,
|
||||
ReactiveScopeDependency,
|
||||
ScopeId,
|
||||
@@ -112,6 +114,9 @@ export function collectHoistablePropertyLoads(
|
||||
hoistableFromOptionals,
|
||||
registry,
|
||||
nestedFnImmutableContext: null,
|
||||
assumedInvokedFns: fn.env.config.enableTreatFunctionDepsAsConditional
|
||||
? new Set()
|
||||
: getAssumedInvokedFunctions(fn),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -127,6 +132,11 @@ type CollectHoistablePropertyLoadsContext = {
|
||||
* but are currently kept separate for readability.
|
||||
*/
|
||||
nestedFnImmutableContext: ReadonlySet<IdentifierId> | null;
|
||||
/**
|
||||
* Functions which are assumed to be eventually called (as opposed to ones which might
|
||||
* not be called, e.g. the 0th argument of Array.map)
|
||||
*/
|
||||
assumedInvokedFns: ReadonlySet<LoweredFunction>;
|
||||
};
|
||||
function collectHoistablePropertyLoadsImpl(
|
||||
fn: HIRFunction,
|
||||
@@ -338,7 +348,13 @@ function collectNonNullsInBlocks(
|
||||
context.registry.getOrCreateIdentifier(identifier),
|
||||
);
|
||||
}
|
||||
const nodes = new Map<BlockId, BlockInfo>();
|
||||
const nodes = new Map<
|
||||
BlockId,
|
||||
{
|
||||
block: BasicBlock;
|
||||
assumedNonNullObjects: Set<PropertyPathNode>;
|
||||
}
|
||||
>();
|
||||
for (const [_, block] of fn.body.blocks) {
|
||||
const assumedNonNullObjects = new Set<PropertyPathNode>(
|
||||
knownNonNullIdentifiers,
|
||||
@@ -358,32 +374,30 @@ function collectNonNullsInBlocks(
|
||||
) {
|
||||
assumedNonNullObjects.add(maybeNonNull);
|
||||
}
|
||||
if (
|
||||
(instr.value.kind === 'FunctionExpression' ||
|
||||
instr.value.kind === 'ObjectMethod') &&
|
||||
!fn.env.config.enableTreatFunctionDepsAsConditional
|
||||
) {
|
||||
if (instr.value.kind === 'FunctionExpression') {
|
||||
const innerFn = instr.value.loweredFunc;
|
||||
const innerHoistableMap = collectHoistablePropertyLoadsImpl(
|
||||
innerFn.func,
|
||||
{
|
||||
...context,
|
||||
nestedFnImmutableContext:
|
||||
context.nestedFnImmutableContext ??
|
||||
new Set(
|
||||
innerFn.func.context
|
||||
.filter(place =>
|
||||
isImmutableAtInstr(place.identifier, instr.id, context),
|
||||
)
|
||||
.map(place => place.identifier.id),
|
||||
),
|
||||
},
|
||||
);
|
||||
const innerHoistables = assertNonNull(
|
||||
innerHoistableMap.get(innerFn.func.body.entry),
|
||||
);
|
||||
for (const entry of innerHoistables.assumedNonNullObjects) {
|
||||
assumedNonNullObjects.add(entry);
|
||||
if (context.assumedInvokedFns.has(innerFn)) {
|
||||
const innerHoistableMap = collectHoistablePropertyLoadsImpl(
|
||||
innerFn.func,
|
||||
{
|
||||
...context,
|
||||
nestedFnImmutableContext:
|
||||
context.nestedFnImmutableContext ??
|
||||
new Set(
|
||||
innerFn.func.context
|
||||
.filter(place =>
|
||||
isImmutableAtInstr(place.identifier, instr.id, context),
|
||||
)
|
||||
.map(place => place.identifier.id),
|
||||
),
|
||||
},
|
||||
);
|
||||
const innerHoistables = assertNonNull(
|
||||
innerHoistableMap.get(innerFn.func.body.entry),
|
||||
);
|
||||
for (const entry of innerHoistables.assumedNonNullObjects) {
|
||||
assumedNonNullObjects.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -591,3 +605,130 @@ function reduceMaybeOptionalChains(
|
||||
}
|
||||
} while (changed);
|
||||
}
|
||||
|
||||
function getAssumedInvokedFunctions(
|
||||
fn: HIRFunction,
|
||||
temporaries: Map<
|
||||
IdentifierId,
|
||||
{fn: LoweredFunction; mayInvoke: Set<LoweredFunction>}
|
||||
> = new Map(),
|
||||
): ReadonlySet<LoweredFunction> {
|
||||
const hoistableFunctions = new Set<LoweredFunction>();
|
||||
/**
|
||||
* Step 1: Conservatively collect identifier to function expression mappings
|
||||
*/
|
||||
for (const block of fn.body.blocks.values()) {
|
||||
for (const {lvalue, value} of block.instructions) {
|
||||
/**
|
||||
* Conservatively only match function expressions which can have guaranteed ssa.
|
||||
* ObjectMethods and ObjectProperties do not.
|
||||
*/
|
||||
if (value.kind === 'FunctionExpression') {
|
||||
temporaries.set(lvalue.identifier.id, {
|
||||
fn: value.loweredFunc,
|
||||
mayInvoke: new Set(),
|
||||
});
|
||||
} else if (value.kind === 'StoreLocal') {
|
||||
const lvalue = value.lvalue.place.identifier;
|
||||
const maybeLoweredFunc = temporaries.get(value.value.identifier.id);
|
||||
if (maybeLoweredFunc != null) {
|
||||
temporaries.set(lvalue.id, maybeLoweredFunc);
|
||||
}
|
||||
} else if (value.kind === 'LoadLocal') {
|
||||
const maybeLoweredFunc = temporaries.get(value.place.identifier.id);
|
||||
if (maybeLoweredFunc != null) {
|
||||
temporaries.set(lvalue.identifier.id, maybeLoweredFunc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Step 2: Forward pass to do analysis of assumed function calls. Note that
|
||||
* this is conservative and does not count indirect references through
|
||||
* containers (e.g. `return {cb: () => {...}})`).
|
||||
*/
|
||||
for (const block of fn.body.blocks.values()) {
|
||||
for (const {lvalue, value} of block.instructions) {
|
||||
if (value.kind === 'CallExpression') {
|
||||
const callee = value.callee;
|
||||
const maybeHook = getHookKind(fn.env, callee.identifier);
|
||||
const maybeLoweredFunc = temporaries.get(callee.identifier.id);
|
||||
if (maybeLoweredFunc != null) {
|
||||
// Direct calls
|
||||
hoistableFunctions.add(maybeLoweredFunc.fn);
|
||||
} else if (maybeHook != null) {
|
||||
/**
|
||||
* Assume arguments to all hooks are safe to invoke
|
||||
*/
|
||||
for (const arg of value.args) {
|
||||
if (arg.kind === 'Identifier') {
|
||||
const maybeLoweredFunc = temporaries.get(arg.identifier.id);
|
||||
if (maybeLoweredFunc != null) {
|
||||
hoistableFunctions.add(maybeLoweredFunc.fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (value.kind === 'JsxExpression') {
|
||||
/**
|
||||
* Assume JSX attributes and children are safe to invoke
|
||||
*/
|
||||
for (const attr of value.props) {
|
||||
if (attr.kind === 'JsxSpreadAttribute') {
|
||||
continue;
|
||||
}
|
||||
const maybeLoweredFunc = temporaries.get(attr.place.identifier.id);
|
||||
if (maybeLoweredFunc != null) {
|
||||
hoistableFunctions.add(maybeLoweredFunc.fn);
|
||||
}
|
||||
}
|
||||
for (const child of value.children ?? []) {
|
||||
const maybeLoweredFunc = temporaries.get(child.identifier.id);
|
||||
if (maybeLoweredFunc != null) {
|
||||
hoistableFunctions.add(maybeLoweredFunc.fn);
|
||||
}
|
||||
}
|
||||
} else if (value.kind === 'FunctionExpression') {
|
||||
/**
|
||||
* Recursively traverse into other function expressions which may invoke
|
||||
* or pass already declared functions to react (e.g. as JSXAttributes).
|
||||
*
|
||||
* If lambda A calls lambda B, we assume lambda B is safe to invoke if
|
||||
* lambda A is -- even if lambda B is conditionally called. (see
|
||||
* `conditional-call-chain` fixture for example).
|
||||
*/
|
||||
const loweredFunc = value.loweredFunc.func;
|
||||
const lambdasCalled = getAssumedInvokedFunctions(
|
||||
loweredFunc,
|
||||
temporaries,
|
||||
);
|
||||
const maybeLoweredFunc = temporaries.get(lvalue.identifier.id);
|
||||
if (maybeLoweredFunc != null) {
|
||||
for (const called of lambdasCalled) {
|
||||
maybeLoweredFunc.mayInvoke.add(called);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (block.terminal.kind === 'return') {
|
||||
/**
|
||||
* Assume directly returned functions are safe to call
|
||||
*/
|
||||
const maybeLoweredFunc = temporaries.get(
|
||||
block.terminal.value.identifier.id,
|
||||
);
|
||||
if (maybeLoweredFunc != null) {
|
||||
hoistableFunctions.add(maybeLoweredFunc.fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [_, {fn, mayInvoke}] of temporaries) {
|
||||
if (hoistableFunctions.has(fn)) {
|
||||
for (const called of mayInvoke) {
|
||||
hoistableFunctions.add(called);
|
||||
}
|
||||
}
|
||||
}
|
||||
return hoistableFunctions;
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
let t0;
|
||||
if ($[0] !== props.name) {
|
||||
if ($[0] !== props) {
|
||||
t0 = function () {
|
||||
return <div>{props.name}</div>;
|
||||
};
|
||||
$[0] = props.name;
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Named lambdas (e.g. cb1) may be defined in the top scope of a function and
|
||||
* used in a different lambda (getArrMap1).
|
||||
*
|
||||
* Here, we should try to determine if cb1 is actually called. In this case:
|
||||
* - getArrMap1 is assumed to be called as it's passed to JSX
|
||||
* - cb1 is not assumed to be called since it's only used as a call operand
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const cb1 = e => arr1[0].value + e.value;
|
||||
const getArrMap1 = () => arr1.map(cb1);
|
||||
const cb2 = e => arr2[0].value + e.value;
|
||||
const getArrMap2 = () => arr1.map(cb2);
|
||||
return (
|
||||
<Stringify
|
||||
getArrMap1={getArrMap1}
|
||||
getArrMap2={getArrMap2}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Named lambdas (e.g. cb1) may be defined in the top scope of a function and
|
||||
* used in a different lambda (getArrMap1).
|
||||
*
|
||||
* Here, we should try to determine if cb1 is actually called. In this case:
|
||||
* - getArrMap1 is assumed to be called as it's passed to JSX
|
||||
* - cb1 is not assumed to be called since it's only used as a call operand
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(13);
|
||||
const { arr1, arr2 } = t0;
|
||||
let t1;
|
||||
if ($[0] !== arr1[0]) {
|
||||
t1 = (e) => arr1[0].value + e.value;
|
||||
$[0] = arr1[0];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const cb1 = t1;
|
||||
let t2;
|
||||
if ($[2] !== arr1 || $[3] !== cb1) {
|
||||
t2 = () => arr1.map(cb1);
|
||||
$[2] = arr1;
|
||||
$[3] = cb1;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
const getArrMap1 = t2;
|
||||
let t3;
|
||||
if ($[5] !== arr2) {
|
||||
t3 = (e_0) => arr2[0].value + e_0.value;
|
||||
$[5] = arr2;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
const cb2 = t3;
|
||||
let t4;
|
||||
if ($[7] !== arr1 || $[8] !== cb2) {
|
||||
t4 = () => arr1.map(cb2);
|
||||
$[7] = arr1;
|
||||
$[8] = cb2;
|
||||
$[9] = t4;
|
||||
} else {
|
||||
t4 = $[9];
|
||||
}
|
||||
const getArrMap2 = t4;
|
||||
let t5;
|
||||
if ($[10] !== getArrMap1 || $[11] !== getArrMap2) {
|
||||
t5 = (
|
||||
<Stringify
|
||||
getArrMap1={getArrMap1}
|
||||
getArrMap2={getArrMap2}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
$[10] = getArrMap1;
|
||||
$[11] = getArrMap2;
|
||||
$[12] = t5;
|
||||
} else {
|
||||
t5 = $[12];
|
||||
}
|
||||
return t5;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ arr1: [], arr2: [] }],
|
||||
sequentialRenders: [
|
||||
{ arr1: [], arr2: [] },
|
||||
{ arr1: [], arr2: null },
|
||||
{ arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"getArrMap1":{"kind":"Function","result":[]},"getArrMap2":{"kind":"Function","result":[]},"shouldInvokeFns":true}</div>
|
||||
<div>{"getArrMap1":{"kind":"Function","result":[]},"getArrMap2":{"kind":"Function","result":[]},"shouldInvokeFns":true}</div>
|
||||
<div>{"getArrMap1":{"kind":"Function","result":[2,3]},"getArrMap2":{"kind":"Function","result":[0,1]},"shouldInvokeFns":true}</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Named lambdas (e.g. cb1) may be defined in the top scope of a function and
|
||||
* used in a different lambda (getArrMap1).
|
||||
*
|
||||
* Here, we should try to determine if cb1 is actually called. In this case:
|
||||
* - getArrMap1 is assumed to be called as it's passed to JSX
|
||||
* - cb1 is not assumed to be called since it's only used as a call operand
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const cb1 = e => arr1[0].value + e.value;
|
||||
const getArrMap1 = () => arr1.map(cb1);
|
||||
const cb2 = e => arr2[0].value + e.value;
|
||||
const getArrMap2 = () => arr1.map(cb2);
|
||||
return (
|
||||
<Stringify
|
||||
getArrMap1={getArrMap1}
|
||||
getArrMap2={getArrMap2}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Whether lambdas are named or passed inline shouldn't affect whether we expect
|
||||
* it to be called.
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const cb1 = e => arr1[0].value + e.value;
|
||||
const x = arr1.map(cb1);
|
||||
const cb2 = e => arr2[0].value + e.value;
|
||||
const y = arr1.map(cb2);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; /**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Whether lambdas are named or passed inline shouldn't affect whether we expect
|
||||
* it to be called.
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(13);
|
||||
const { arr1, arr2 } = t0;
|
||||
let t1;
|
||||
if ($[0] !== arr1[0]) {
|
||||
t1 = (e) => arr1[0].value + e.value;
|
||||
$[0] = arr1[0];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const cb1 = t1;
|
||||
let t2;
|
||||
if ($[2] !== arr1 || $[3] !== cb1) {
|
||||
t2 = arr1.map(cb1);
|
||||
$[2] = arr1;
|
||||
$[3] = cb1;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
const x = t2;
|
||||
let t3;
|
||||
if ($[5] !== arr2) {
|
||||
t3 = (e_0) => arr2[0].value + e_0.value;
|
||||
$[5] = arr2;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
const cb2 = t3;
|
||||
let t4;
|
||||
if ($[7] !== arr1 || $[8] !== cb2) {
|
||||
t4 = arr1.map(cb2);
|
||||
$[7] = arr1;
|
||||
$[8] = cb2;
|
||||
$[9] = t4;
|
||||
} else {
|
||||
t4 = $[9];
|
||||
}
|
||||
const y = t4;
|
||||
let t5;
|
||||
if ($[10] !== x || $[11] !== y) {
|
||||
t5 = [x, y];
|
||||
$[10] = x;
|
||||
$[11] = y;
|
||||
$[12] = t5;
|
||||
} else {
|
||||
t5 = $[12];
|
||||
}
|
||||
return t5;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ arr1: [], arr2: [] }],
|
||||
sequentialRenders: [
|
||||
{ arr1: [], arr2: [] },
|
||||
{ arr1: [], arr2: null },
|
||||
{ arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[],[]]
|
||||
[[],[]]
|
||||
[[2,3],[0,1]]
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Whether lambdas are named or passed inline shouldn't affect whether we expect
|
||||
* it to be called.
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const cb1 = e => arr1[0].value + e.value;
|
||||
const x = arr1.map(cb1);
|
||||
const cb2 = e => arr2[0].value + e.value;
|
||||
const y = arr1.map(cb2);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Here, getVal1 has a known callsite in `cb1`, but `cb1` isn't known to be
|
||||
* called (it's only passed to array.map). In this case, we should be
|
||||
* conservative and assume that all named lambdas are conditionally called.
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const getVal1 = () => arr1[0].value;
|
||||
const cb1 = e => getVal1() + e.value;
|
||||
const x = arr1.map(cb1);
|
||||
const getVal2 = () => arr2[0].value;
|
||||
const cb2 = e => getVal2() + e.value;
|
||||
const y = arr1.map(cb2);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; /**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Here, getVal1 has a known callsite in `cb1`, but `cb1` isn't known to be
|
||||
* called (it's only passed to array.map). In this case, we should be
|
||||
* conservative and assume that all named lambdas are conditionally called.
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(17);
|
||||
const { arr1, arr2 } = t0;
|
||||
let t1;
|
||||
if ($[0] !== arr1[0]) {
|
||||
t1 = () => arr1[0].value;
|
||||
$[0] = arr1[0];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const getVal1 = t1;
|
||||
let t2;
|
||||
if ($[2] !== getVal1) {
|
||||
t2 = (e) => getVal1() + e.value;
|
||||
$[2] = getVal1;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
const cb1 = t2;
|
||||
let t3;
|
||||
if ($[4] !== arr1 || $[5] !== cb1) {
|
||||
t3 = arr1.map(cb1);
|
||||
$[4] = arr1;
|
||||
$[5] = cb1;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
const x = t3;
|
||||
let t4;
|
||||
if ($[7] !== arr2) {
|
||||
t4 = () => arr2[0].value;
|
||||
$[7] = arr2;
|
||||
$[8] = t4;
|
||||
} else {
|
||||
t4 = $[8];
|
||||
}
|
||||
const getVal2 = t4;
|
||||
let t5;
|
||||
if ($[9] !== getVal2) {
|
||||
t5 = (e_0) => getVal2() + e_0.value;
|
||||
$[9] = getVal2;
|
||||
$[10] = t5;
|
||||
} else {
|
||||
t5 = $[10];
|
||||
}
|
||||
const cb2 = t5;
|
||||
let t6;
|
||||
if ($[11] !== arr1 || $[12] !== cb2) {
|
||||
t6 = arr1.map(cb2);
|
||||
$[11] = arr1;
|
||||
$[12] = cb2;
|
||||
$[13] = t6;
|
||||
} else {
|
||||
t6 = $[13];
|
||||
}
|
||||
const y = t6;
|
||||
let t7;
|
||||
if ($[14] !== x || $[15] !== y) {
|
||||
t7 = [x, y];
|
||||
$[14] = x;
|
||||
$[15] = y;
|
||||
$[16] = t7;
|
||||
} else {
|
||||
t7 = $[16];
|
||||
}
|
||||
return t7;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ arr1: [], arr2: [] }],
|
||||
sequentialRenders: [
|
||||
{ arr1: [], arr2: [] },
|
||||
{ arr1: [], arr2: null },
|
||||
{ arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[],[]]
|
||||
[[],[]]
|
||||
[[2,3],[0,1]]
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Here, getVal1 has a known callsite in `cb1`, but `cb1` isn't known to be
|
||||
* called (it's only passed to array.map). In this case, we should be
|
||||
* conservative and assume that all named lambdas are conditionally called.
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const getVal1 = () => arr1[0].value;
|
||||
const cb1 = e => getVal1() + e.value;
|
||||
const x = arr1.map(cb1);
|
||||
const getVal2 = () => arr2[0].value;
|
||||
const cb2 = e => getVal2() + e.value;
|
||||
const y = arr1.map(cb2);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,111 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Test that we're not hoisting property reads from lambdas that are created to
|
||||
* pass to opaque functions, which often have maybe-invoke semantics.
|
||||
*
|
||||
* In this example, we shouldn't hoist `arr[0].value` out of the lambda.
|
||||
* ```js
|
||||
* e => arr[0].value + e.value <-- created to pass to map
|
||||
* arr.map(<cb>) <-- argument only invoked if array is non-empty
|
||||
* ```
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const x = arr1.map(e => arr1[0].value + e.value);
|
||||
const y = arr1.map(e => arr2[0].value + e.value);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; /**
|
||||
* Test that we're not hoisting property reads from lambdas that are created to
|
||||
* pass to opaque functions, which often have maybe-invoke semantics.
|
||||
*
|
||||
* In this example, we shouldn't hoist `arr[0].value` out of the lambda.
|
||||
* ```js
|
||||
* e => arr[0].value + e.value <-- created to pass to map
|
||||
* arr.map(<cb>) <-- argument only invoked if array is non-empty
|
||||
* ```
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(12);
|
||||
const { arr1, arr2 } = t0;
|
||||
let t1;
|
||||
if ($[0] !== arr1) {
|
||||
let t2;
|
||||
if ($[2] !== arr1[0]) {
|
||||
t2 = (e) => arr1[0].value + e.value;
|
||||
$[2] = arr1[0];
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
t1 = arr1.map(t2);
|
||||
$[0] = arr1;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const x = t1;
|
||||
let t2;
|
||||
if ($[4] !== arr1 || $[5] !== arr2) {
|
||||
let t3;
|
||||
if ($[7] !== arr2) {
|
||||
t3 = (e_0) => arr2[0].value + e_0.value;
|
||||
$[7] = arr2;
|
||||
$[8] = t3;
|
||||
} else {
|
||||
t3 = $[8];
|
||||
}
|
||||
t2 = arr1.map(t3);
|
||||
$[4] = arr1;
|
||||
$[5] = arr2;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
const y = t2;
|
||||
let t3;
|
||||
if ($[9] !== x || $[10] !== y) {
|
||||
t3 = [x, y];
|
||||
$[9] = x;
|
||||
$[10] = y;
|
||||
$[11] = t3;
|
||||
} else {
|
||||
t3 = $[11];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ arr1: [], arr2: [] }],
|
||||
sequentialRenders: [
|
||||
{ arr1: [], arr2: [] },
|
||||
{ arr1: [], arr2: null },
|
||||
{ arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[],[]]
|
||||
[[],[]]
|
||||
[[2,3],[0,1]]
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Test that we're not hoisting property reads from lambdas that are created to
|
||||
* pass to opaque functions, which often have maybe-invoke semantics.
|
||||
*
|
||||
* In this example, we shouldn't hoist `arr[0].value` out of the lambda.
|
||||
* ```js
|
||||
* e => arr[0].value + e.value <-- created to pass to map
|
||||
* arr.map(<cb>) <-- argument only invoked if array is non-empty
|
||||
* ```
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const x = arr1.map(e => arr1[0].value + e.value);
|
||||
const y = arr1.map(e => arr2[0].value + e.value);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useRef} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Component({a, b}) {
|
||||
const logA = () => {
|
||||
console.log(a.value);
|
||||
};
|
||||
const logB = () => {
|
||||
console.log(b.value);
|
||||
};
|
||||
const hasLogged = useRef(false);
|
||||
const log = () => {
|
||||
if (!hasLogged.current) {
|
||||
logA();
|
||||
logB();
|
||||
hasLogged.current = true;
|
||||
}
|
||||
};
|
||||
return <Stringify log={log} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: {value: 1}, b: {value: 2}}],
|
||||
sequentialRenders: [
|
||||
{a: {value: 1}, b: {value: 2}},
|
||||
{a: {value: 3}, b: {value: 4}},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useRef } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(9);
|
||||
const { a, b } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a.value) {
|
||||
t1 = () => {
|
||||
console.log(a.value);
|
||||
};
|
||||
$[0] = a.value;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const logA = t1;
|
||||
let t2;
|
||||
if ($[2] !== b.value) {
|
||||
t2 = () => {
|
||||
console.log(b.value);
|
||||
};
|
||||
$[2] = b.value;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
const logB = t2;
|
||||
|
||||
const hasLogged = useRef(false);
|
||||
let t3;
|
||||
if ($[4] !== logA || $[5] !== logB) {
|
||||
t3 = () => {
|
||||
if (!hasLogged.current) {
|
||||
logA();
|
||||
logB();
|
||||
hasLogged.current = true;
|
||||
}
|
||||
};
|
||||
$[4] = logA;
|
||||
$[5] = logB;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
const log = t3;
|
||||
let t4;
|
||||
if ($[7] !== log) {
|
||||
t4 = <Stringify log={log} shouldInvokeFns={true} />;
|
||||
$[7] = log;
|
||||
$[8] = t4;
|
||||
} else {
|
||||
t4 = $[8];
|
||||
}
|
||||
return t4;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ a: { value: 1 }, b: { value: 2 } }],
|
||||
sequentialRenders: [
|
||||
{ a: { value: 1 }, b: { value: 2 } },
|
||||
{ a: { value: 3 }, b: { value: 4 } },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"log":{"kind":"Function"},"shouldInvokeFns":true}</div>
|
||||
<div>{"log":{"kind":"Function"},"shouldInvokeFns":true}</div>
|
||||
logs: [1,2]
|
||||
@@ -0,0 +1,29 @@
|
||||
import {useRef} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Component({a, b}) {
|
||||
const logA = () => {
|
||||
console.log(a.value);
|
||||
};
|
||||
const logB = () => {
|
||||
console.log(b.value);
|
||||
};
|
||||
const hasLogged = useRef(false);
|
||||
const log = () => {
|
||||
if (!hasLogged.current) {
|
||||
logA();
|
||||
logB();
|
||||
hasLogged.current = true;
|
||||
}
|
||||
};
|
||||
return <Stringify log={log} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: {value: 1}, b: {value: 2}}],
|
||||
sequentialRenders: [
|
||||
{a: {value: 1}, b: {value: 2}},
|
||||
{a: {value: 3}, b: {value: 4}},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useState} from 'react';
|
||||
import {useIdentity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that conditionally called functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback({obj}: {obj: {value: number}}) {
|
||||
const [state, setState] = useState(0);
|
||||
const cb = () => {
|
||||
if (obj.value !== 0) setState(obj.value);
|
||||
};
|
||||
useIdentity(null);
|
||||
if (state === 0) {
|
||||
cb();
|
||||
}
|
||||
return {cb};
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}}],
|
||||
sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useState } from "react";
|
||||
import { useIdentity } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Assume that conditionally called functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(4);
|
||||
const { obj } = t0;
|
||||
const [state, setState] = useState(0);
|
||||
let t1;
|
||||
if ($[0] !== obj.value) {
|
||||
t1 = () => {
|
||||
if (obj.value !== 0) {
|
||||
setState(obj.value);
|
||||
}
|
||||
};
|
||||
$[0] = obj.value;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const cb = t1;
|
||||
|
||||
useIdentity(null);
|
||||
if (state === 0) {
|
||||
cb();
|
||||
}
|
||||
let t2;
|
||||
if ($[2] !== cb) {
|
||||
t2 = { cb };
|
||||
$[2] = cb;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{ obj: { value: 1 } }],
|
||||
sequentialRenders: [{ obj: { value: 1 } }, { obj: { value: 2 } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) {"cb":"[[ function params=0 ]]"}
|
||||
{"cb":"[[ function params=0 ]]"}
|
||||
@@ -0,0 +1,23 @@
|
||||
import {useState} from 'react';
|
||||
import {useIdentity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that conditionally called functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback({obj}: {obj: {value: number}}) {
|
||||
const [state, setState] = useState(0);
|
||||
const cb = () => {
|
||||
if (obj.value !== 0) setState(obj.value);
|
||||
};
|
||||
useIdentity(null);
|
||||
if (state === 0) {
|
||||
cb();
|
||||
}
|
||||
return {cb};
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}}],
|
||||
sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that conditionally returned functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
shouldMakeCb,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
shouldMakeCb: boolean;
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
const cb = () => setState(obj.value);
|
||||
if (shouldMakeCb) return cb;
|
||||
else return null;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, shouldMakeCb: true, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, shouldMakeCb: true, setState},
|
||||
{obj: {value: 2}, shouldMakeCb: true, setState},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { createHookWrapper } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Assume that conditionally returned functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(3);
|
||||
const { obj, shouldMakeCb, setState } = t0;
|
||||
let t1;
|
||||
if ($[0] !== obj.value || $[1] !== setState) {
|
||||
t1 = () => setState(obj.value);
|
||||
$[0] = obj.value;
|
||||
$[1] = setState;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
const cb = t1;
|
||||
if (shouldMakeCb) {
|
||||
return cb;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
"use no memo";
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{ obj: { value: 1 }, shouldMakeCb: true, setState }],
|
||||
sequentialRenders: [
|
||||
{ obj: { value: 1 }, shouldMakeCb: true, setState },
|
||||
{ obj: { value: 2 }, shouldMakeCb: true, setState },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"result":{"kind":"Function","result":1},"shouldInvokeFns":true}</div>
|
||||
<div>{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}</div>
|
||||
@@ -0,0 +1,32 @@
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that conditionally returned functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
shouldMakeCb,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
shouldMakeCb: boolean;
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
const cb = () => setState(obj.value);
|
||||
if (shouldMakeCb) return cb;
|
||||
else return null;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, shouldMakeCb: true, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, shouldMakeCb: true, setState},
|
||||
{obj: {value: 2}, shouldMakeCb: true, setState},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useState} from 'react';
|
||||
import {useIdentity} from 'shared-runtime';
|
||||
|
||||
function useMakeCallback({obj}: {obj: {value: number}}) {
|
||||
const [state, setState] = useState(0);
|
||||
const cb = () => {
|
||||
if (obj.value !== state) setState(obj.value);
|
||||
};
|
||||
useIdentity();
|
||||
cb();
|
||||
return [cb];
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}}],
|
||||
sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useState } from "react";
|
||||
import { useIdentity } from "shared-runtime";
|
||||
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(5);
|
||||
const { obj } = t0;
|
||||
const [state, setState] = useState(0);
|
||||
let t1;
|
||||
if ($[0] !== obj.value || $[1] !== state) {
|
||||
t1 = () => {
|
||||
if (obj.value !== state) {
|
||||
setState(obj.value);
|
||||
}
|
||||
};
|
||||
$[0] = obj.value;
|
||||
$[1] = state;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
const cb = t1;
|
||||
|
||||
useIdentity();
|
||||
cb();
|
||||
let t2;
|
||||
if ($[3] !== cb) {
|
||||
t2 = [cb];
|
||||
$[3] = cb;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{ obj: { value: 1 } }],
|
||||
sequentialRenders: [{ obj: { value: 1 } }, { obj: { value: 2 } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) ["[[ function params=0 ]]"]
|
||||
["[[ function params=0 ]]"]
|
||||
@@ -0,0 +1,17 @@
|
||||
import {useState} from 'react';
|
||||
import {useIdentity} from 'shared-runtime';
|
||||
|
||||
function useMakeCallback({obj}: {obj: {value: number}}) {
|
||||
const [state, setState] = useState(0);
|
||||
const cb = () => {
|
||||
if (obj.value !== state) setState(obj.value);
|
||||
};
|
||||
useIdentity();
|
||||
cb();
|
||||
return [cb];
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}}],
|
||||
sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* (Given that the returned lambda is assumed to be invoked, see
|
||||
* return-function)
|
||||
*
|
||||
* If lambda A conditionally calls lambda B, optimistically assume that property
|
||||
* loads from lambda B has the same hoistability of ones from lambda A. This
|
||||
* helps optimize components / hooks that create and chain many helper
|
||||
* functions.
|
||||
*
|
||||
* Type systems and code readability encourage developers to colocate length and
|
||||
* null checks values in the same function as where values are used. i.e.
|
||||
* developers are unlikely to write the following code.
|
||||
* ```js
|
||||
* function useFoo(obj, objNotNullAndHasElements) {
|
||||
* // ...
|
||||
* const get0th = () => obj.arr[0].value;
|
||||
* return () => objNotNullAndHasElements ? get0th : undefined;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In Meta code, this assumption helps reduce the number of memo dependency
|
||||
* deopts.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
cond,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
cond: boolean;
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
const cb = () => setState(obj.value);
|
||||
// cb's property loads are assumed to be hoistable to the start of this lambda
|
||||
return () => (cond ? cb() : undefined);
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, cond: true, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, cond: true, setState},
|
||||
{obj: {value: 2}, cond: true, setState},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { createHookWrapper } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* (Given that the returned lambda is assumed to be invoked, see
|
||||
* return-function)
|
||||
*
|
||||
* If lambda A conditionally calls lambda B, optimistically assume that property
|
||||
* loads from lambda B has the same hoistability of ones from lambda A. This
|
||||
* helps optimize components / hooks that create and chain many helper
|
||||
* functions.
|
||||
*
|
||||
* Type systems and code readability encourage developers to colocate length and
|
||||
* null checks values in the same function as where values are used. i.e.
|
||||
* developers are unlikely to write the following code.
|
||||
* ```js
|
||||
* function useFoo(obj, objNotNullAndHasElements) {
|
||||
* // ...
|
||||
* const get0th = () => obj.arr[0].value;
|
||||
* return () => objNotNullAndHasElements ? get0th : undefined;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In Meta code, this assumption helps reduce the number of memo dependency
|
||||
* deopts.
|
||||
*/
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(6);
|
||||
const { obj, cond, setState } = t0;
|
||||
let t1;
|
||||
if ($[0] !== obj.value || $[1] !== setState) {
|
||||
t1 = () => setState(obj.value);
|
||||
$[0] = obj.value;
|
||||
$[1] = setState;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
const cb = t1;
|
||||
let t2;
|
||||
if ($[3] !== cb || $[4] !== cond) {
|
||||
t2 = () => (cond ? cb() : undefined);
|
||||
$[3] = cb;
|
||||
$[4] = cond;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
"use no memo";
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{ obj: { value: 1 }, cond: true, setState }],
|
||||
sequentialRenders: [
|
||||
{ obj: { value: 1 }, cond: true, setState },
|
||||
{ obj: { value: 2 }, cond: true, setState },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"result":{"kind":"Function","result":1},"shouldInvokeFns":true}</div>
|
||||
<div>{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}</div>
|
||||
@@ -0,0 +1,51 @@
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* (Given that the returned lambda is assumed to be invoked, see
|
||||
* return-function)
|
||||
*
|
||||
* If lambda A conditionally calls lambda B, optimistically assume that property
|
||||
* loads from lambda B has the same hoistability of ones from lambda A. This
|
||||
* helps optimize components / hooks that create and chain many helper
|
||||
* functions.
|
||||
*
|
||||
* Type systems and code readability encourage developers to colocate length and
|
||||
* null checks values in the same function as where values are used. i.e.
|
||||
* developers are unlikely to write the following code.
|
||||
* ```js
|
||||
* function useFoo(obj, objNotNullAndHasElements) {
|
||||
* // ...
|
||||
* const get0th = () => obj.arr[0].value;
|
||||
* return () => objNotNullAndHasElements ? get0th : undefined;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In Meta code, this assumption helps reduce the number of memo dependency
|
||||
* deopts.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
cond,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
cond: boolean;
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
const cb = () => setState(obj.value);
|
||||
// cb's property loads are assumed to be hoistable to the start of this lambda
|
||||
return () => (cond ? cb() : undefined);
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, cond: true, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, cond: true, setState},
|
||||
{obj: {value: 2}, cond: true, setState},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {createHookWrapper, useIdentity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that functions passed hook arguments are invoked and that their
|
||||
* property loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
const cb = useIdentity(() => setState(obj.value));
|
||||
return cb;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, setState},
|
||||
{obj: {value: 2}, setState},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { createHookWrapper, useIdentity } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Assume that functions passed hook arguments are invoked and that their
|
||||
* property loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(3);
|
||||
const { obj, setState } = t0;
|
||||
let t1;
|
||||
if ($[0] !== obj.value || $[1] !== setState) {
|
||||
t1 = () => setState(obj.value);
|
||||
$[0] = obj.value;
|
||||
$[1] = setState;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
const cb = useIdentity(t1);
|
||||
return cb;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
"use no memo";
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{ obj: { value: 1 }, setState }],
|
||||
sequentialRenders: [
|
||||
{ obj: { value: 1 }, setState },
|
||||
{ obj: { value: 2 }, setState },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"result":{"kind":"Function","result":1},"shouldInvokeFns":true}</div>
|
||||
<div>{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}</div>
|
||||
@@ -0,0 +1,29 @@
|
||||
import {createHookWrapper, useIdentity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that functions passed hook arguments are invoked and that their
|
||||
* property loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
const cb = useIdentity(() => setState(obj.value));
|
||||
return cb;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, setState},
|
||||
{obj: {value: 2}, setState},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
function useFoo({arr1}) {
|
||||
const cb1 = e => arr1[0].value + e.value;
|
||||
const x = arr1.map(cb1);
|
||||
return [x, cb1];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useFoo),
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { createHookWrapper } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(8);
|
||||
const { arr1 } = t0;
|
||||
let t1;
|
||||
if ($[0] !== arr1[0]) {
|
||||
t1 = (e) => arr1[0].value + e.value;
|
||||
$[0] = arr1[0];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const cb1 = t1;
|
||||
let t2;
|
||||
if ($[2] !== arr1 || $[3] !== cb1) {
|
||||
t2 = arr1.map(cb1);
|
||||
$[2] = arr1;
|
||||
$[3] = cb1;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
const x = t2;
|
||||
let t3;
|
||||
if ($[5] !== cb1 || $[6] !== x) {
|
||||
t3 = [x, cb1];
|
||||
$[5] = cb1;
|
||||
$[6] = x;
|
||||
$[7] = t3;
|
||||
} else {
|
||||
t3 = $[7];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useFoo),
|
||||
params: [{ arr1: [], arr2: [] }],
|
||||
sequentialRenders: [
|
||||
{ arr1: [], arr2: [] },
|
||||
{ arr1: [], arr2: null },
|
||||
{ arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"result":[[],"[[ function params=1 ]]"],"shouldInvokeFns":true}</div>
|
||||
<div>{"result":[[],"[[ function params=1 ]]"],"shouldInvokeFns":true}</div>
|
||||
<div>{"result":[[2,3],"[[ function params=1 ]]"],"shouldInvokeFns":true}</div>
|
||||
@@ -0,0 +1,17 @@
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
function useFoo({arr1}) {
|
||||
const cb1 = e => arr1[0].value + e.value;
|
||||
const x = arr1.map(cb1);
|
||||
return [x, cb1];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useFoo),
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @flow
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that functions captured directly as jsx attributes are invoked and
|
||||
* that their property loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
return <Stringify cb={() => setState(obj.value)} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, setState},
|
||||
{obj: {value: 2}, setState},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(3);
|
||||
const { obj, setState } = t0;
|
||||
let t1;
|
||||
if ($[0] !== obj.value || $[1] !== setState) {
|
||||
t1 = <Stringify cb={() => setState(obj.value)} shouldInvokeFns={true} />;
|
||||
$[0] = obj.value;
|
||||
$[1] = setState;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
"use no memo";
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{ obj: { value: 1 }, setState }],
|
||||
sequentialRenders: [
|
||||
{ obj: { value: 1 }, setState },
|
||||
{ obj: { value: 2 }, setState },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"cb":{"kind":"Function","result":1},"shouldInvokeFns":true}</div>
|
||||
<div>{"cb":{"kind":"Function","result":2},"shouldInvokeFns":true}</div>
|
||||
@@ -0,0 +1,29 @@
|
||||
// @flow
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that functions captured directly as jsx attributes are invoked and
|
||||
* that their property loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
return <Stringify cb={() => setState(obj.value)} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, setState},
|
||||
{obj: {value: 2}, setState},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that directly returned functions are invoked and that their property
|
||||
* loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
return () => setState(obj.value);
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, setState},
|
||||
{obj: {value: 2}, setState},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { createHookWrapper } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Assume that directly returned functions are invoked and that their property
|
||||
* loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(3);
|
||||
const { obj, setState } = t0;
|
||||
let t1;
|
||||
if ($[0] !== obj.value || $[1] !== setState) {
|
||||
t1 = () => setState(obj.value);
|
||||
$[0] = obj.value;
|
||||
$[1] = setState;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
"use no memo";
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{ obj: { value: 1 }, setState }],
|
||||
sequentialRenders: [
|
||||
{ obj: { value: 1 }, setState },
|
||||
{ obj: { value: 2 }, setState },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"result":{"kind":"Function","result":1},"shouldInvokeFns":true}</div>
|
||||
<div>{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}</div>
|
||||
@@ -0,0 +1,28 @@
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that directly returned functions are invoked and that their property
|
||||
* loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
return () => setState(obj.value);
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, setState},
|
||||
{obj: {value: 2}, setState},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useState, useMemo} from 'react';
|
||||
import {useIdentity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that conditionally called functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
shouldSynchronizeState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
shouldSynchronizeState: boolean;
|
||||
}) {
|
||||
const [state, setState] = useState(0);
|
||||
const cb = useMemo(() => {
|
||||
return () => {
|
||||
if (obj.value !== 0) setState(obj.value);
|
||||
};
|
||||
}, [obj.value, shouldSynchronizeState]);
|
||||
useIdentity(null);
|
||||
return cb;
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}}],
|
||||
sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useState, useMemo } from "react";
|
||||
import { useIdentity } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Assume that conditionally called functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(2);
|
||||
const { obj, shouldSynchronizeState } = t0;
|
||||
|
||||
const [, setState] = useState(0);
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[0] !== obj.value) {
|
||||
t2 = () => {
|
||||
if (obj.value !== 0) {
|
||||
setState(obj.value);
|
||||
}
|
||||
};
|
||||
$[0] = obj.value;
|
||||
$[1] = t2;
|
||||
} else {
|
||||
t2 = $[1];
|
||||
}
|
||||
t1 = t2;
|
||||
const cb = t1;
|
||||
|
||||
useIdentity(null);
|
||||
return cb;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{ obj: { value: 1 } }],
|
||||
sequentialRenders: [{ obj: { value: 1 } }, { obj: { value: 2 } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) "[[ function params=0 ]]"
|
||||
"[[ function params=0 ]]"
|
||||
@@ -0,0 +1,28 @@
|
||||
import {useState, useMemo} from 'react';
|
||||
import {useIdentity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that conditionally called functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
shouldSynchronizeState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
shouldSynchronizeState: boolean;
|
||||
}) {
|
||||
const [state, setState] = useState(0);
|
||||
const cb = useMemo(() => {
|
||||
return () => {
|
||||
if (obj.value !== 0) setState(obj.value);
|
||||
};
|
||||
}, [obj.value, shouldSynchronizeState]);
|
||||
useIdentity(null);
|
||||
return cb;
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}}],
|
||||
sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function useFoo({arr1, arr2}) {
|
||||
const cb = e => arr2[0].value + e.value;
|
||||
const y = [];
|
||||
for (let i = 0; i < arr1.length; i++) {
|
||||
y.push(cb(arr1[i]));
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function useFoo(t0) {
|
||||
const $ = _c(5);
|
||||
const { arr1, arr2 } = t0;
|
||||
let t1;
|
||||
if ($[0] !== arr2[0].value) {
|
||||
t1 = (e) => arr2[0].value + e.value;
|
||||
$[0] = arr2[0].value;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const cb = t1;
|
||||
let y;
|
||||
if ($[2] !== arr1 || $[3] !== cb) {
|
||||
y = [];
|
||||
for (let i = 0; i < arr1.length; i++) {
|
||||
y.push(cb(arr1[i]));
|
||||
}
|
||||
$[2] = arr1;
|
||||
$[3] = cb;
|
||||
$[4] = y;
|
||||
} else {
|
||||
y = $[4];
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ arr1: [], arr2: [] }],
|
||||
sequentialRenders: [
|
||||
{ arr1: [], arr2: [] },
|
||||
{ arr1: [], arr2: null },
|
||||
{ arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
function useFoo({arr1, arr2}) {
|
||||
const cb = e => arr2[0].value + e.value;
|
||||
const y = [];
|
||||
for (let i = 0; i < arr1.length; i++) {
|
||||
y.push(cb(arr1[i]));
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Assume that only directly returned functions or JSX attributes are invoked.
|
||||
* Conservatively estimate that functions wrapped in objects or other containers
|
||||
* might never be called (and therefore their property loads are not hoistable).
|
||||
*/
|
||||
function useMakeCallback({arr}) {
|
||||
return {
|
||||
getElement0: () => arr[0].value,
|
||||
getElement1: () => arr[1].value,
|
||||
};
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{arr: [1, 2]}],
|
||||
sequentialRenders: [{arr: [1, 2]}, {arr: []}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; /**
|
||||
* Assume that only directly returned functions or JSX attributes are invoked.
|
||||
* Conservatively estimate that functions wrapped in objects or other containers
|
||||
* might never be called (and therefore their property loads are not hoistable).
|
||||
*/
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(2);
|
||||
const { arr } = t0;
|
||||
let t1;
|
||||
if ($[0] !== arr) {
|
||||
t1 = { getElement0: () => arr[0].value, getElement1: () => arr[1].value };
|
||||
$[0] = arr;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{ arr: [1, 2] }],
|
||||
sequentialRenders: [{ arr: [1, 2] }, { arr: [] }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) {"getElement0":"[[ function params=0 ]]","getElement1":"[[ function params=0 ]]"}
|
||||
{"getElement0":"[[ function params=0 ]]","getElement1":"[[ function params=0 ]]"}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Assume that only directly returned functions or JSX attributes are invoked.
|
||||
* Conservatively estimate that functions wrapped in objects or other containers
|
||||
* might never be called (and therefore their property loads are not hoistable).
|
||||
*/
|
||||
function useMakeCallback({arr}) {
|
||||
return {
|
||||
getElement0: () => arr[0].value,
|
||||
getElement1: () => arr[1].value,
|
||||
};
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{arr: [1, 2]}],
|
||||
sequentialRenders: [{arr: [1, 2]}, {arr: []}],
|
||||
};
|
||||
@@ -41,9 +41,9 @@ function useFoo(t0) {
|
||||
local = $[1];
|
||||
}
|
||||
let t1;
|
||||
if ($[2] !== local.b.c) {
|
||||
if ($[2] !== local) {
|
||||
t1 = () => [() => local.b.c];
|
||||
$[2] = local.b.c;
|
||||
$[2] = local;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
|
||||
@@ -34,13 +34,13 @@ function useFoo(t0) {
|
||||
const $ = _c(4);
|
||||
const { a } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a.b.c) {
|
||||
if ($[0] !== a) {
|
||||
t1 = {
|
||||
fn() {
|
||||
return identity(a.b.c);
|
||||
},
|
||||
};
|
||||
$[0] = a.b.c;
|
||||
$[0] = a;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
|
||||
@@ -51,7 +51,7 @@ import { identity } from "shared-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
let x;
|
||||
if ($[0] !== props.cond) {
|
||||
if ($[0] !== props) {
|
||||
const f = () => {
|
||||
if (props.cond) {
|
||||
x = 1;
|
||||
@@ -62,7 +62,7 @@ function Component(props) {
|
||||
|
||||
const f2 = identity(f);
|
||||
f2();
|
||||
$[0] = props.cond;
|
||||
$[0] = props;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
|
||||
@@ -82,9 +82,9 @@ function Component(t0) {
|
||||
contextVar = $[2];
|
||||
}
|
||||
let t1;
|
||||
if ($[3] !== contextVar.val) {
|
||||
if ($[3] !== contextVar) {
|
||||
t1 = { cb: () => contextVar.val * 4 };
|
||||
$[3] = contextVar.val;
|
||||
$[3] = contextVar;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
|
||||
@@ -43,7 +43,7 @@ const t0 = "module_t0";
|
||||
const c_0 = "module_c_0";
|
||||
function useFoo(props) {
|
||||
const $0 = _c(2);
|
||||
const c_00 = $0[0] !== props.value;
|
||||
const c_00 = $0[0] !== props;
|
||||
let t1;
|
||||
if (c_00) {
|
||||
const a = {
|
||||
@@ -61,7 +61,7 @@ function useFoo(props) {
|
||||
};
|
||||
|
||||
t1 = a.foo().bar();
|
||||
$0[0] = props.value;
|
||||
$0[0] = props;
|
||||
$0[1] = t1;
|
||||
} else {
|
||||
t1 = $0[1];
|
||||
|
||||
@@ -35,7 +35,7 @@ function Component(props) {
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableTransitivelyFreezeFunctionExpressions:false
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(9);
|
||||
const $ = _c(7);
|
||||
const item = useMutable(props.itemId);
|
||||
const dispatch = useDispatch();
|
||||
useFreeze(dispatch);
|
||||
@@ -51,7 +51,8 @@ function Component(props) {
|
||||
}
|
||||
const exit = t0;
|
||||
let t1;
|
||||
if ($[2] !== exit || $[3] !== item.value) {
|
||||
let t2;
|
||||
if ($[2] !== exit || $[3] !== item) {
|
||||
t1 = () => {
|
||||
const cleanup = GlobalEventEmitter.addListener("onInput", () => {
|
||||
if (item.value) {
|
||||
@@ -60,30 +61,24 @@ function Component(props) {
|
||||
});
|
||||
return () => cleanup.remove();
|
||||
};
|
||||
t2 = [exit, item];
|
||||
$[2] = exit;
|
||||
$[3] = item.value;
|
||||
$[3] = item;
|
||||
$[4] = t1;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
let t2;
|
||||
if ($[5] !== exit || $[6] !== item) {
|
||||
t2 = [exit, item];
|
||||
$[5] = exit;
|
||||
$[6] = item;
|
||||
$[7] = t2;
|
||||
} else {
|
||||
t2 = $[7];
|
||||
t2 = $[5];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
|
||||
maybeMutate(item);
|
||||
let t3;
|
||||
if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = <div />;
|
||||
$[8] = t3;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[8];
|
||||
t3 = $[6];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import {ErrorSeverity} from 'babel-plugin-react-compiler/src';
|
||||
import {RuleTester as ESLintTester} from 'eslint';
|
||||
import ReactCompilerRule from '../src/rules/ReactCompilerRule';
|
||||
|
||||
const ESLintTesterV8 = require('eslint-v8').RuleTester;
|
||||
|
||||
/**
|
||||
* A string template tag that removes padding from the left side of multi-line strings
|
||||
* @param {Array} strings array of code strings (only one expected)
|
||||
@@ -309,7 +311,7 @@ const tests: CompilerTestCases = {
|
||||
],
|
||||
};
|
||||
|
||||
const eslintTester = new ESLintTester({
|
||||
const eslintTester = new ESLintTesterV8({
|
||||
parser: require.resolve('hermes-eslint'),
|
||||
parserOptions: {
|
||||
ecmaVersion: 2015,
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
import {RuleTester} from 'eslint';
|
||||
import ReactCompilerRule from '../src/rules/ReactCompilerRule';
|
||||
|
||||
const ESLintTesterV8 = require('eslint-v8').RuleTester;
|
||||
|
||||
/**
|
||||
* A string template tag that removes padding from the left side of multi-line strings
|
||||
* @param {Array} strings array of code strings (only one expected)
|
||||
@@ -70,7 +72,7 @@ const tests: CompilerTestCases = {
|
||||
],
|
||||
};
|
||||
|
||||
const eslintTester = new RuleTester({
|
||||
const eslintTester = new ESLintTesterV8({
|
||||
parser: require.resolve('@typescript-eslint/parser'),
|
||||
});
|
||||
eslintTester.run('react-compiler', ReactCompilerRule, tests);
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@types/eslint": "^8.56.12",
|
||||
"@types/node": "^20.2.5",
|
||||
"babel-jest": "^29.0.3",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-v8": "npm:eslint@^8.57.1",
|
||||
"hermes-eslint": "^0.25.1",
|
||||
"jest": "^29.5.0"
|
||||
},
|
||||
|
||||
@@ -25,7 +25,7 @@ const configs = {
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'react-compiler/react-compiler': 'error',
|
||||
'react-compiler/react-compiler': 'error' as const,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -7675,61 +7675,60 @@ const tests = {
|
||||
],
|
||||
};
|
||||
|
||||
if (__EXPERIMENTAL__) {
|
||||
tests.valid = [
|
||||
...tests.valid,
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function MyComponent({ theme }) {
|
||||
const onStuff = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
useEffect(() => {
|
||||
onStuff();
|
||||
}, []);
|
||||
}
|
||||
`,
|
||||
},
|
||||
];
|
||||
tests.valid = [
|
||||
...tests.valid,
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function MyComponent({ theme }) {
|
||||
const onStuff = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
useEffect(() => {
|
||||
onStuff();
|
||||
}, []);
|
||||
}
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
tests.invalid = [
|
||||
...tests.invalid,
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function MyComponent({ theme }) {
|
||||
const onStuff = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
useEffect(() => {
|
||||
onStuff();
|
||||
}, [onStuff]);
|
||||
}
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
message:
|
||||
'Functions returned from `useEffectEvent` must not be included in the dependency array. ' +
|
||||
'Remove `onStuff` from the list.',
|
||||
suggestions: [
|
||||
{
|
||||
desc: 'Remove the dependency `onStuff`',
|
||||
output: normalizeIndent`
|
||||
function MyComponent({ theme }) {
|
||||
const onStuff = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
useEffect(() => {
|
||||
onStuff();
|
||||
}, []);
|
||||
}
|
||||
`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
// useEffectEvent
|
||||
tests.invalid = [
|
||||
...tests.invalid,
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function MyComponent({ theme }) {
|
||||
const onStuff = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
useEffect(() => {
|
||||
onStuff();
|
||||
}, [onStuff]);
|
||||
}
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
message:
|
||||
'Functions returned from `useEffectEvent` must not be included in the dependency array. ' +
|
||||
'Remove `onStuff` from the list.',
|
||||
suggestions: [
|
||||
{
|
||||
desc: 'Remove the dependency `onStuff`',
|
||||
output: normalizeIndent`
|
||||
function MyComponent({ theme }) {
|
||||
const onStuff = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
useEffect(() => {
|
||||
onStuff();
|
||||
}, []);
|
||||
}
|
||||
`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// Tests that are only valid/invalid across parsers supporting Flow
|
||||
const testsFlow = {
|
||||
@@ -8370,16 +8369,16 @@ describe('rules-of-hooks/exhaustive-deps', () => {
|
||||
testsTypescriptEslintParser
|
||||
);
|
||||
|
||||
new ESLintTesterV9({
|
||||
languageOptions: {
|
||||
...languageOptionsV9,
|
||||
parser: require('@typescript-eslint/parser-v3'),
|
||||
},
|
||||
}).run(
|
||||
'eslint: v9, parser: @typescript-eslint/parser@3.x',
|
||||
ReactHooksESLintRule,
|
||||
testsTypescriptEslintParser
|
||||
);
|
||||
// new ESLintTesterV9({
|
||||
// languageOptions: {
|
||||
// ...languageOptionsV9,
|
||||
// parser: require('@typescript-eslint/parser-v3'),
|
||||
// },
|
||||
// }).run(
|
||||
// 'eslint: v9, parser: @typescript-eslint/parser@3.x',
|
||||
// ReactHooksESLintRule,
|
||||
// testsTypescriptEslintParser
|
||||
// );
|
||||
|
||||
new ESLintTesterV7({
|
||||
parser: require.resolve('@typescript-eslint/parser-v4'),
|
||||
@@ -1286,180 +1286,181 @@ const tests = {
|
||||
],
|
||||
};
|
||||
|
||||
if (__EXPERIMENTAL__) {
|
||||
tests.valid = [
|
||||
...tests.valid,
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Valid because functions created with useEffectEvent can be called in a useEffect.
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
useEffect(() => {
|
||||
onClick();
|
||||
});
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Valid because functions created with useEffectEvent can be called in closures.
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
return <Child onClick={() => onClick()}></Child>;
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Valid because functions created with useEffectEvent can be called in closures.
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
const onClick2 = () => { onClick() };
|
||||
const onClick3 = useCallback(() => onClick(), []);
|
||||
return <>
|
||||
<Child onClick={onClick2}></Child>
|
||||
<Child onClick={onClick3}></Child>
|
||||
</>;
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Valid because functions created with useEffectEvent can be passed by reference in useEffect
|
||||
// and useEffectEvent.
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
const onClick2 = useEffectEvent(() => {
|
||||
debounce(onClick);
|
||||
});
|
||||
useEffect(() => {
|
||||
let id = setInterval(onClick, 100);
|
||||
return () => clearInterval(onClick);
|
||||
}, []);
|
||||
return <Child onClick={() => onClick2()} />
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
const MyComponent = ({theme}) => {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
return <Child onClick={() => onClick()}></Child>;
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function MyComponent({ theme }) {
|
||||
const notificationService = useNotifications();
|
||||
const showNotification = useEffectEvent((text) => {
|
||||
notificationService.notify(theme, text);
|
||||
});
|
||||
const onClick = useEffectEvent((text) => {
|
||||
showNotification(text);
|
||||
});
|
||||
return <Child onClick={(text) => onClick(text)} />
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function MyComponent({ theme }) {
|
||||
useEffect(() => {
|
||||
onClick();
|
||||
});
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
}
|
||||
`,
|
||||
},
|
||||
];
|
||||
tests.invalid = [
|
||||
...tests.invalid,
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
return <Child onClick={onClick}></Child>;
|
||||
}
|
||||
`,
|
||||
errors: [useEffectEventError('onClick')],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// This should error even though it shares an identifier name with the below
|
||||
function MyComponent({theme}) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme)
|
||||
});
|
||||
return <Child onClick={onClick} />
|
||||
}
|
||||
// useEffectEvent
|
||||
tests.valid = [
|
||||
...tests.valid,
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Valid because functions created with useEffectEvent can be called in a useEffect.
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
useEffect(() => {
|
||||
onClick();
|
||||
});
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Valid because functions created with useEffectEvent can be called in closures.
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
return <Child onClick={() => onClick()}></Child>;
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Valid because functions created with useEffectEvent can be called in closures.
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
const onClick2 = () => { onClick() };
|
||||
const onClick3 = useCallback(() => onClick(), []);
|
||||
return <>
|
||||
<Child onClick={onClick2}></Child>
|
||||
<Child onClick={onClick3}></Child>
|
||||
</>;
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Valid because functions created with useEffectEvent can be passed by reference in useEffect
|
||||
// and useEffectEvent.
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
const onClick2 = useEffectEvent(() => {
|
||||
debounce(onClick);
|
||||
});
|
||||
useEffect(() => {
|
||||
let id = setInterval(onClick, 100);
|
||||
return () => clearInterval(onClick);
|
||||
}, []);
|
||||
return <Child onClick={() => onClick2()} />
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
const MyComponent = ({theme}) => {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
return <Child onClick={() => onClick()}></Child>;
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function MyComponent({ theme }) {
|
||||
const notificationService = useNotifications();
|
||||
const showNotification = useEffectEvent((text) => {
|
||||
notificationService.notify(theme, text);
|
||||
});
|
||||
const onClick = useEffectEvent((text) => {
|
||||
showNotification(text);
|
||||
});
|
||||
return <Child onClick={(text) => onClick(text)} />
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function MyComponent({ theme }) {
|
||||
useEffect(() => {
|
||||
onClick();
|
||||
});
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
}
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
// The useEffectEvent function shares an identifier name with the above
|
||||
function MyOtherComponent({theme}) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme)
|
||||
});
|
||||
return <Child onClick={() => onClick()} />
|
||||
}
|
||||
`,
|
||||
errors: [{...useEffectEventError('onClick'), line: 7}],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
const MyComponent = ({ theme }) => {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
return <Child onClick={onClick}></Child>;
|
||||
}
|
||||
`,
|
||||
errors: [useEffectEventError('onClick')],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Invalid because onClick is being aliased to foo but not invoked
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
let foo = onClick;
|
||||
return <Bar onClick={foo} />
|
||||
}
|
||||
`,
|
||||
errors: [{...useEffectEventError('onClick'), line: 7}],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Should error because it's being passed down to JSX, although it's been referenced once
|
||||
// in an effect
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(them);
|
||||
});
|
||||
useEffect(() => {
|
||||
setTimeout(onClick, 100);
|
||||
});
|
||||
return <Child onClick={onClick} />
|
||||
}
|
||||
`,
|
||||
errors: [useEffectEventError('onClick')],
|
||||
},
|
||||
];
|
||||
}
|
||||
// useEffectEvent
|
||||
tests.invalid = [
|
||||
...tests.invalid,
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
return <Child onClick={onClick}></Child>;
|
||||
}
|
||||
`,
|
||||
errors: [useEffectEventError('onClick')],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// This should error even though it shares an identifier name with the below
|
||||
function MyComponent({theme}) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme)
|
||||
});
|
||||
return <Child onClick={onClick} />
|
||||
}
|
||||
|
||||
// The useEffectEvent function shares an identifier name with the above
|
||||
function MyOtherComponent({theme}) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme)
|
||||
});
|
||||
return <Child onClick={() => onClick()} />
|
||||
}
|
||||
`,
|
||||
errors: [{...useEffectEventError('onClick'), line: 7}],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
const MyComponent = ({ theme }) => {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
return <Child onClick={onClick}></Child>;
|
||||
}
|
||||
`,
|
||||
errors: [useEffectEventError('onClick')],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Invalid because onClick is being aliased to foo but not invoked
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
let foo = onClick;
|
||||
return <Bar onClick={foo} />
|
||||
}
|
||||
`,
|
||||
errors: [{...useEffectEventError('onClick'), line: 7}],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Should error because it's being passed down to JSX, although it's been referenced once
|
||||
// in an effect
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(them);
|
||||
});
|
||||
useEffect(() => {
|
||||
setTimeout(onClick, 100);
|
||||
});
|
||||
return <Child onClick={onClick} />
|
||||
}
|
||||
`,
|
||||
errors: [useEffectEventError('onClick')],
|
||||
},
|
||||
];
|
||||
|
||||
function conditionalError(hook, hasPreviousFinalizer = false) {
|
||||
return {
|
||||
@@ -1623,16 +1624,16 @@ describe('rules-of-hooks/rules-of-hooks', () => {
|
||||
tests
|
||||
);
|
||||
|
||||
new ESLintTesterV9({
|
||||
languageOptions: {
|
||||
...languageOptionsV9,
|
||||
parser: require('@typescript-eslint/parser-v3'),
|
||||
},
|
||||
}).run(
|
||||
'eslint: v9, parser: @typescript-eslint/parser@3.x',
|
||||
ReactHooksESLintRule,
|
||||
tests
|
||||
);
|
||||
// new ESLintTesterV9({
|
||||
// languageOptions: {
|
||||
// ...languageOptionsV9,
|
||||
// parser: require('@typescript-eslint/parser-v3'),
|
||||
// },
|
||||
// }).run(
|
||||
// 'eslint: v9, parser: @typescript-eslint/parser@3.x',
|
||||
// ReactHooksESLintRule,
|
||||
// tests
|
||||
// );
|
||||
|
||||
new ESLintTesterV7({
|
||||
parser: require.resolve('@typescript-eslint/parser-v4'),
|
||||
21
compiler/packages/eslint-plugin-react-hooks/babel.config.js
Normal file
21
compiler/packages/eslint-plugin-react-hooks/babel.config.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
presets: [
|
||||
['@babel/preset-env', {targets: {esmodules: false, node: 'current'}}],
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-syntax-jsx',
|
||||
'@babel/plugin-transform-flow-strip-types',
|
||||
['@babel/plugin-proposal-class-properties', {loose: true}],
|
||||
'@babel/plugin-transform-classes',
|
||||
['@babel/plugin-transform-private-property-in-object', {loose: true}],
|
||||
['@babel/plugin-transform-private-methods', {loose: true}],
|
||||
],
|
||||
};
|
||||
11
compiler/packages/eslint-plugin-react-hooks/jest.config.js
Normal file
11
compiler/packages/eslint-plugin-react-hooks/jest.config.js
Normal file
@@ -0,0 +1,11 @@
|
||||
/** @type {import('jest').Config} */
|
||||
const config = {
|
||||
transform: {
|
||||
'\\.[jt]sx?$': [
|
||||
'babel-jest',
|
||||
{configFile: require.resolve('./babel.config.js')},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
@@ -5,7 +5,7 @@
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/react.git",
|
||||
"directory": "packages/eslint-plugin-react-hooks"
|
||||
"directory": "compiler/packages/eslint-plugin-react-hooks"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
@@ -21,9 +21,11 @@
|
||||
"react"
|
||||
],
|
||||
"scripts": {
|
||||
"build:compiler": "cd ../../compiler && yarn workspace babel-plugin-react-compiler build",
|
||||
"build:compiler": "yarn workspace babel-plugin-react-compiler build",
|
||||
"build": "rimraf dist && tsup",
|
||||
"test": "yarn build:compiler && jest",
|
||||
"typecheck": "tsc --noEmit"
|
||||
"typecheck": "tsc --noEmit",
|
||||
"watch": "yarn build --watch"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
@@ -48,6 +50,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.11.4",
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
"@babel/types": "^7.19.0",
|
||||
"@tsconfig/strictest": "^2.0.5",
|
||||
@@ -60,6 +63,7 @@
|
||||
"@types/estree-jsx": "^1.0.5",
|
||||
"@types/node": "^20.2.5",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-jest": "^29.7.0",
|
||||
"eslint-v7": "npm:eslint@^7.7.0",
|
||||
"eslint-v8": "npm:eslint@^8.57.1",
|
||||
"eslint-v9": "npm:eslint@^9.0.0",
|
||||
@@ -2056,10 +2056,7 @@ function isAncestorNodeOf(a: Node, b: Node): boolean {
|
||||
}
|
||||
|
||||
function isUseEffectEventIdentifier(node: Node): boolean {
|
||||
if (__EXPERIMENTAL__) {
|
||||
return node.type === 'Identifier' && node.name === 'useEffectEvent';
|
||||
}
|
||||
return false;
|
||||
return node.type === 'Identifier' && node.name === 'useEffectEvent';
|
||||
}
|
||||
|
||||
function getUnknownDependenciesMessage(reactiveHookName: string): string {
|
||||
@@ -109,10 +109,7 @@ function isInsideDoWhileLoop(node: Node | undefined): node is DoWhileStatement {
|
||||
}
|
||||
|
||||
function isUseEffectEventIdentifier(node: Node): boolean {
|
||||
if (__EXPERIMENTAL__) {
|
||||
return node.type === 'Identifier' && node.name === 'useEffectEvent';
|
||||
}
|
||||
return false;
|
||||
return node.type === 'Identifier' && node.name === 'useEffectEvent';
|
||||
}
|
||||
|
||||
function isUseIdentifier(node: Node): boolean {
|
||||
@@ -9,7 +9,7 @@
|
||||
"types": ["estree-jsx", "node"],
|
||||
"downlevelIteration": true,
|
||||
"paths": {
|
||||
"babel-plugin-react-compiler": ["../../compiler/packages/babel-plugin-react-compiler/src"]
|
||||
"babel-plugin-react-compiler": ["../babel-plugin-react-compiler/src"]
|
||||
},
|
||||
"jsx": "react-jsxdev",
|
||||
"rootDir": "../..",
|
||||
36
compiler/packages/eslint-plugin-react-hooks/tsup.config.ts
Normal file
36
compiler/packages/eslint-plugin-react-hooks/tsup.config.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import {defineConfig} from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['./src/index.ts'],
|
||||
outDir: './dist',
|
||||
external: [
|
||||
'@babel/core',
|
||||
'@babel/parser',
|
||||
'@babel/plugin-proposal-private-methods',
|
||||
'hermes-parser',
|
||||
'zod',
|
||||
'zod-validation-error',
|
||||
],
|
||||
splitting: false,
|
||||
sourcemap: false,
|
||||
dts: false,
|
||||
bundle: true,
|
||||
format: 'cjs',
|
||||
platform: 'node',
|
||||
banner: {
|
||||
js: `/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @lightSyntaxTransform
|
||||
* @noflow
|
||||
* @nolint
|
||||
* @preventMunge
|
||||
* @preserve-invariant-messages
|
||||
*/
|
||||
|
||||
"use no memo";`,
|
||||
},
|
||||
});
|
||||
@@ -450,6 +450,7 @@ const skipFilter = new Set([
|
||||
'invalid-jsx-lowercase-localvar',
|
||||
|
||||
// bugs
|
||||
'inner-function/nullable-objects/bug-invalid-array-map-manual',
|
||||
'bug-object-expression-computed-key-modified-during-after-construction-hoisted-sequence-expr',
|
||||
`bug-capturing-func-maybealias-captured-mutate`,
|
||||
'bug-aliased-capture-aliased-mutate',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const PUBLISHABLE_PACKAGES = [
|
||||
'babel-plugin-react-compiler',
|
||||
'eslint-plugin-react-compiler',
|
||||
'eslint-plugin-react-hooks',
|
||||
'react-compiler-healthcheck',
|
||||
'react-compiler-runtime',
|
||||
];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,3 +7,10 @@ body {
|
||||
padding: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
::view-transition-group(*),
|
||||
::view-transition-image-pair(*),
|
||||
::view-transition-old(*),
|
||||
::view-transition-new(*) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,6 @@ export default function SwipeRecognizer({
|
||||
width: axis === 'x' ? '100%' : null,
|
||||
height: axis === 'y' ? '100%' : null,
|
||||
overflow: 'scroll hidden',
|
||||
touchAction: 'pan-' + direction,
|
||||
// Disable overscroll on Safari which moves the sticky content.
|
||||
// Unfortunately, this also means that we disable chaining. We should only disable
|
||||
// it if the parent is not scrollable in this axis.
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* This file is purely being used for local jest runs, and doesn't participate in the build process.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
extends: '../../babel.config-ts.js',
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
process.env.NODE_ENV = 'development';
|
||||
|
||||
module.exports = {
|
||||
setupFiles: [require.resolve('../../scripts/jest/setupEnvironment.js')],
|
||||
moduleFileExtensions: ['ts', 'js', 'json'],
|
||||
};
|
||||
@@ -261,7 +261,7 @@ function scheduleFibersWithFamiliesRecursively(
|
||||
staleFamilies: Set<Family>,
|
||||
): void {
|
||||
if (__DEV__) {
|
||||
const {alternate, child, sibling, tag, type, elementType} = fiber;
|
||||
const {alternate, child, sibling, tag, type} = fiber;
|
||||
|
||||
let candidateType = null;
|
||||
switch (tag) {
|
||||
@@ -283,11 +283,6 @@ function scheduleFibersWithFamiliesRecursively(
|
||||
|
||||
let needsRender = false;
|
||||
let needsRemount = false;
|
||||
if (staleFamilies.has(resolveFamily(elementType))) {
|
||||
console.log('gonna mark stale fiber for remount');
|
||||
needsRemount = true;
|
||||
}
|
||||
|
||||
if (candidateType !== null) {
|
||||
const family = resolveFamily(candidateType);
|
||||
if (family !== undefined) {
|
||||
|
||||
@@ -146,12 +146,6 @@ function canPreserveStateBetween(prevType: any, nextType: any) {
|
||||
if (isReactClass(prevType) || isReactClass(nextType)) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
typeof prevType !== typeof nextType ||
|
||||
getProperty(prevType, '$$typeof') !== getProperty(nextType, '$$typeof')
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (haveEqualSignatures(prevType, nextType)) {
|
||||
return true;
|
||||
}
|
||||
@@ -221,7 +215,6 @@ export function performReactRefresh(): RefreshUpdate | null {
|
||||
if (canPreserveStateBetween(prevType, nextType)) {
|
||||
updatedFamilies.add(family);
|
||||
} else {
|
||||
console.log('mark family as stale', family);
|
||||
staleFamilies.add(family);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -699,244 +699,6 @@ describe('ReactFresh', () => {
|
||||
}
|
||||
});
|
||||
|
||||
fit('can remount when change function to memo', async () => {
|
||||
if (__DEV__) {
|
||||
await act(async () => {
|
||||
await render(() => {
|
||||
function Test() {
|
||||
return <p>hi test</p>;
|
||||
}
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
// Check the initial render
|
||||
const el = container.firstChild;
|
||||
expect(el.textContent).toBe('hi test');
|
||||
|
||||
// Patch to change function to memo
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test2() {
|
||||
return <p>hi memo</p>;
|
||||
}
|
||||
const Test = React.memo(Test2);
|
||||
$RefreshReg$(Test2, 'Test2');
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
// Check remount
|
||||
expect(container.firstChild !== el).toBe(true);
|
||||
const nextEl = container.firstChild;
|
||||
expect(nextEl.textContent).toBe('hi memo');
|
||||
|
||||
console.log('patch to original');
|
||||
|
||||
// Patch back to original function
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test() {
|
||||
return <p>hi test</p>;
|
||||
}
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
// Check final remount
|
||||
expect(container.firstChild !== nextEl).toBe(true);
|
||||
const newEl = container.firstChild;
|
||||
expect(newEl.textContent).toBe('hi test');
|
||||
}
|
||||
});
|
||||
|
||||
it('can remount when change memo to forwardRef', async () => {
|
||||
if (__DEV__) {
|
||||
await act(async () => {
|
||||
await render(() => {
|
||||
function Test2() {
|
||||
return <p>hi memo</p>;
|
||||
}
|
||||
const Test = React.memo(Test2);
|
||||
$RefreshReg$(Test2, 'Test2');
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
// Check the initial render
|
||||
const el = container.firstChild;
|
||||
expect(el.textContent).toBe('hi memo');
|
||||
|
||||
// Patch to change memo to forwardRef
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test2() {
|
||||
return <p>hi forwardRef</p>;
|
||||
}
|
||||
const Test = React.forwardRef(Test2);
|
||||
$RefreshReg$(Test2, 'Test2');
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
// Check remount
|
||||
expect(container.firstChild).not.toBe(el);
|
||||
const nextEl = container.firstChild;
|
||||
expect(nextEl.textContent).toBe('hi forwardRef');
|
||||
|
||||
// Patch back to memo
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test2() {
|
||||
return <p>hi memo</p>;
|
||||
}
|
||||
const Test = React.memo(Test2);
|
||||
$RefreshReg$(Test2, 'Test2');
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
// Check final remount
|
||||
expect(container.firstChild).not.toBe(nextEl);
|
||||
const newEl = container.firstChild;
|
||||
expect(newEl.textContent).toBe('hi memo');
|
||||
}
|
||||
});
|
||||
|
||||
it('can remount when change function to forwardRef', async () => {
|
||||
if (__DEV__) {
|
||||
await act(async () => {
|
||||
await render(() => {
|
||||
function Test() {
|
||||
return <p>hi test</p>;
|
||||
}
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
// Check the initial render
|
||||
const el = container.firstChild;
|
||||
expect(el.textContent).toBe('hi test');
|
||||
|
||||
// Patch to change function to forwardRef
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test2() {
|
||||
return <p>hi forwardRef</p>;
|
||||
}
|
||||
const Test = React.forwardRef(Test2);
|
||||
$RefreshReg$(Test2, 'Test2');
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
// Check remount
|
||||
expect(container.firstChild).not.toBe(el);
|
||||
const nextEl = container.firstChild;
|
||||
expect(nextEl.textContent).toBe('hi forwardRef');
|
||||
|
||||
// Patch back to a new function
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test() {
|
||||
return <p>hi test1</p>;
|
||||
}
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
// Check final remount
|
||||
expect(container.firstChild).not.toBe(nextEl);
|
||||
const newEl = container.firstChild;
|
||||
expect(newEl.textContent).toBe('hi test1');
|
||||
}
|
||||
});
|
||||
|
||||
it('resets state when switching between different component types', async () => {
|
||||
if (__DEV__) {
|
||||
await act(async () => {
|
||||
await render(() => {
|
||||
function Test() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
return (
|
||||
<div onClick={() => setCount(c => c + 1)}>count: {count}</div>
|
||||
);
|
||||
}
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
expect(container.firstChild.textContent).toBe('count: 0');
|
||||
await act(async () => {
|
||||
container.firstChild.click();
|
||||
});
|
||||
expect(container.firstChild.textContent).toBe('count: 1');
|
||||
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test2() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
return (
|
||||
<div onClick={() => setCount(c => c + 1)}>count: {count}</div>
|
||||
);
|
||||
}
|
||||
const Test = React.memo(Test2);
|
||||
$RefreshReg$(Test2, 'Test2');
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
expect(container.firstChild.textContent).toBe('count: 0');
|
||||
await act(async () => {
|
||||
container.firstChild.click();
|
||||
});
|
||||
expect(container.firstChild.textContent).toBe('count: 1');
|
||||
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
const Test = React.forwardRef((props, ref) => {
|
||||
const [count, setCount] = React.useState(0);
|
||||
const handleClick = () => setCount(c => c + 1);
|
||||
|
||||
// Ensure ref is extensible
|
||||
const divRef = React.useRef(null);
|
||||
React.useEffect(() => {
|
||||
if (ref) {
|
||||
if (typeof ref === 'function') {
|
||||
ref(divRef.current);
|
||||
} else if (Object.isExtensible(ref)) {
|
||||
ref.current = divRef.current;
|
||||
}
|
||||
}
|
||||
}, [ref]);
|
||||
|
||||
return (
|
||||
<div ref={divRef} onClick={handleClick}>
|
||||
count: {count}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
expect(container.firstChild.textContent).toBe('count: 0');
|
||||
await act(async () => {
|
||||
container.firstChild.click();
|
||||
});
|
||||
expect(container.firstChild.textContent).toBe('count: 1');
|
||||
}
|
||||
});
|
||||
|
||||
it('can update simple memo function in isolation', async () => {
|
||||
if (__DEV__) {
|
||||
await render(() => {
|
||||
|
||||
@@ -7,12 +7,6 @@ module.exports = {
|
||||
'<rootDir>/scripts/bench/',
|
||||
],
|
||||
transform: {
|
||||
'^.+babel-plugin-react-compiler/dist/index.js$': [
|
||||
'babel-jest',
|
||||
{
|
||||
configFile: require.resolve('../../babel.config-react-compiler.js'),
|
||||
},
|
||||
],
|
||||
'^.+\\.ts$': [
|
||||
'babel-jest',
|
||||
{configFile: require.resolve('../../babel.config-ts.js')},
|
||||
|
||||
@@ -26,7 +26,6 @@ const {asyncRimRaf} = require('./utils');
|
||||
const codeFrame = require('@babel/code-frame').default;
|
||||
const Wrappers = require('./wrappers');
|
||||
const commonjs = require('@rollup/plugin-commonjs');
|
||||
const {getBabelOutputPlugin} = require('@rollup/plugin-babel');
|
||||
|
||||
const RELEASE_CHANNEL = process.env.RELEASE_CHANNEL;
|
||||
|
||||
@@ -418,12 +417,6 @@ function getPlugins(
|
||||
bundle
|
||||
)
|
||||
),
|
||||
// For Meta internal requirements this package needs to be built targeting ES5.
|
||||
bundle.name === 'eslint-plugin-react-hooks'
|
||||
? getBabelOutputPlugin({
|
||||
presets: ['@babel/preset-env'],
|
||||
})
|
||||
: false,
|
||||
// Remove 'use strict' from individual source files. We skip eslint-plugin-react-hooks because
|
||||
// it bundles compiler-type code that may examine "use strict" used outside of a directive
|
||||
// context, e.g. as a StringLiteral.
|
||||
@@ -503,7 +496,7 @@ function getPlugins(
|
||||
// takes care of it.
|
||||
renaming: false,
|
||||
}),
|
||||
(needsMinifiedByClosure || bundle.name === 'eslint-plugin-react-hooks') &&
|
||||
needsMinifiedByClosure &&
|
||||
// Add the whitespace back
|
||||
prettier({
|
||||
parser: 'flow',
|
||||
|
||||
@@ -1182,24 +1182,6 @@ const bundles = [
|
||||
externals: ['react', 'scheduler', 'scheduler/unstable_mock'],
|
||||
},
|
||||
|
||||
/******* ESLint Plugin for Hooks *******/
|
||||
{
|
||||
// TODO: we're building this from typescript source now, but there's really
|
||||
// no reason to have both dev and prod for this package. It's
|
||||
// currently required in order for the package to be copied over correctly.
|
||||
// So, it would be worth improving that flow.
|
||||
name: 'eslint-plugin-react-hooks',
|
||||
bundleTypes: [NODE_DEV, NODE_PROD, CJS_DTS],
|
||||
moduleType: ISOMORPHIC,
|
||||
entry: 'eslint-plugin-react-hooks/src/index.ts',
|
||||
global: 'ESLintPluginReactHooks',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: [],
|
||||
tsconfig: './packages/eslint-plugin-react-hooks/tsconfig.json',
|
||||
prebuild: `mkdir -p ./compiler/packages/babel-plugin-react-compiler/dist && echo "module.exports = require('../src/index.ts');" > ./compiler/packages/babel-plugin-react-compiler/dist/index.js`,
|
||||
},
|
||||
|
||||
/******* React Fresh *******/
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
|
||||
Reference in New Issue
Block a user