Compare commits

..

1 Commits

Author SHA1 Message Date
Dan Abramov
7b90545420 wip 2025-03-19 04:42:31 +09:00
91 changed files with 528 additions and 3023 deletions

View File

@@ -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: ['compiler/packages/eslint-plugin-react-hooks/src/**/*'],
files: ['packages/eslint-plugin-react-hooks/src/**/*'],
extends: ['plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'eslint-plugin'],

View File

@@ -38,7 +38,7 @@ jobs:
with:
path: |
**/node_modules
key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
key: compiler-node_modules-v5-${{ 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

View File

@@ -48,7 +48,7 @@ jobs:
with:
path: |
**/node_modules
key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
- run: yarn install --frozen-lockfile
- name: Publish packages to npm
run: |

View File

@@ -48,7 +48,7 @@ jobs:
with:
path: |
**/node_modules
key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
key: compiler-node_modules-v5-${{ 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-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
key: compiler-node_modules-v5-${{ 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-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
key: compiler-node_modules-v5-${{ 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'

View File

@@ -31,7 +31,7 @@ jobs:
with:
path: |
**/node_modules
key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
key: runtime-release-node_modules-v5-${{ 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-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
key: runtime-node_modules-v5-${{ 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-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
key: runtime-node_modules-v5-${{ 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-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
- run: yarn install --frozen-lockfile
- name: Restore all archived build artifacts
uses: actions/download-artifact@v4

View File

@@ -17,75 +17,6 @@ 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
@@ -105,7 +36,7 @@ jobs:
flow:
name: Flow check ${{ matrix.flow_inline_config_shortname }}
needs: [discover_flow_inline_configs, runtime_node_modules_cache]
needs: discover_flow_inline_configs
runs-on: ubuntu-latest
strategy:
fail-fast: false
@@ -121,22 +52,20 @@ jobs:
cache: yarn
cache-dependency-path: yarn.lock
- name: Restore cached node_modules
uses: actions/cache/restore@v4
uses: actions/cache@v4
id: node_modules
with:
path: |
**/node_modules
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
key: runtime-node_modules-v5-${{ 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
@@ -148,16 +77,15 @@ jobs:
cache: yarn
cache-dependency-path: yarn.lock
- name: Restore cached node_modules
uses: actions/cache/restore@v4
uses: actions/cache@v4
id: node_modules
with:
path: |
**/node_modules
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
key: runtime-node_modules-v5-${{ 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)
@@ -165,7 +93,6 @@ jobs:
# ----- FEATURE FLAGS -----
flags:
name: Check flags
needs: [runtime_node_modules_cache]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -177,22 +104,20 @@ jobs:
cache: yarn
cache-dependency-path: yarn.lock
- name: Restore cached node_modules
uses: actions/cache/restore@v4
uses: actions/cache@v4
id: node_modules
with:
path: |
**/node_modules
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
key: runtime-node_modules-v5-${{ 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
@@ -235,30 +160,27 @@ jobs:
yarn.lock
compiler/yarn.lock
- name: Restore cached node_modules
uses: actions/cache/restore@v4
uses: actions/cache@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') }}
key: runtime-and-compiler-node_modules-v5-${{ 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,20,21,22,23,24]
worker_id: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
release_channel: [stable, experimental]
steps:
- uses: actions/checkout@v4
@@ -276,19 +198,17 @@ jobs:
distribution: temurin
java-version: 11.0.22
- name: Restore cached node_modules
uses: actions/cache/restore@v4
uses: actions/cache@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') }}
key: runtime-and-compiler-node_modules-v5-${{ 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 build --index=${{ matrix.worker_id }} --total=25 --r=${{ matrix.release_channel }} --ci
- run: yarn build --index=${{ matrix.worker_id }} --total=20 --r=${{ matrix.release_channel }} --ci
env:
CI: github
RELEASE_CHANNEL: ${{ matrix.release_channel }}
@@ -305,7 +225,7 @@ jobs:
test_build:
name: yarn test-build
needs: [build_and_lint, runtime_compiler_node_modules_cache]
needs: build_and_lint
strategy:
fail-fast: false
matrix:
@@ -356,18 +276,16 @@ jobs:
yarn.lock
compiler/yarn.lock
- name: Restore cached node_modules
uses: actions/cache/restore@v4
uses: actions/cache@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') }}
key: runtime-and-compiler-node_modules-v5-${{ 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:
@@ -380,7 +298,7 @@ jobs:
process_artifacts_combined:
name: Process artifacts combined
needs: [build_and_lint, runtime_node_modules_cache]
needs: build_and_lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -392,16 +310,15 @@ jobs:
cache: yarn
cache-dependency-path: yarn.lock
- name: Restore cached node_modules
uses: actions/cache/restore@v4
uses: actions/cache@v4
id: node_modules
with:
path: |
**/node_modules
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
key: runtime-node_modules-v5-${{ 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:
@@ -429,7 +346,7 @@ jobs:
check_error_codes:
name: Search build artifacts for unminified errors
needs: [build_and_lint, runtime_node_modules_cache]
needs: build_and_lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -441,16 +358,15 @@ jobs:
cache: yarn
cache-dependency-path: yarn.lock
- name: Restore cached node_modules
uses: actions/cache/restore@v4
uses: actions/cache@v4
id: node_modules
with:
path: |
**/node_modules
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
key: runtime-node_modules-v5-${{ 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:
@@ -466,7 +382,7 @@ jobs:
check_release_dependencies:
name: Check release dependencies
needs: [build_and_lint, runtime_node_modules_cache]
needs: build_and_lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -478,16 +394,15 @@ jobs:
cache: yarn
cache-dependency-path: yarn.lock
- name: Restore cached node_modules
uses: actions/cache/restore@v4
uses: actions/cache@v4
id: node_modules
with:
path: |
**/node_modules
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
key: runtime-node_modules-v5-${{ 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:
@@ -512,16 +427,15 @@ jobs:
cache: yarn
cache-dependency-path: yarn.lock
- name: Restore cached node_modules
uses: actions/cache@v4 # note: this does not reuse centralized cache since it has unique cache key
uses: actions/cache@v4
id: node_modules
with:
path: |
**/node_modules
key: fixtures_dom-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
key: fixtures_dom-node_modules-v5-${{ 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:
@@ -556,16 +470,15 @@ 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 # note: this does not reuse centralized cache since it has unique cache key
uses: actions/cache@v4
id: node_modules
with:
path: |
**/node_modules
key: fixtures_flight-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
key: fixtures_flight-node_modules-v5-${{ 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:
@@ -604,7 +517,7 @@ jobs:
# ----- DEVTOOLS -----
build_devtools_and_process_artifacts:
name: Build DevTools and process artifacts
needs: [build_and_lint, runtime_node_modules_cache]
needs: build_and_lint
runs-on: ubuntu-latest
strategy:
fail-fast: false
@@ -620,16 +533,15 @@ jobs:
cache: yarn
cache-dependency-path: yarn.lock
- name: Restore cached node_modules
uses: actions/cache/restore@v4
uses: actions/cache@v4
id: node_modules
with:
path: |
**/node_modules
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
key: runtime-node_modules-v5-${{ 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:
@@ -661,7 +573,7 @@ jobs:
run_devtools_e2e_tests:
name: Run DevTools e2e tests
needs: [build_and_lint, runtime_node_modules_cache]
needs: build_and_lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -673,16 +585,15 @@ jobs:
cache: yarn
cache-dependency-path: yarn.lock
- name: Restore cached node_modules
uses: actions/cache/restore@v4
uses: actions/cache@v4
id: node_modules
with:
path: |
**/node_modules
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
key: runtime-node_modules-v5-${{ 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:
@@ -700,7 +611,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, runtime_node_modules_cache]
needs: [build_and_lint]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -712,18 +623,15 @@ jobs:
cache: yarn
cache-dependency-path: yarn.lock
- name: Restore cached node_modules
uses: actions/cache/restore@v4
uses: actions/cache@v4
id: node_modules
with:
path: |
**/node_modules
key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
key: runtime-node_modules-v5-${{ 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 --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 }})
@@ -739,7 +647,6 @@ 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:

View File

@@ -80,7 +80,7 @@ jobs:
with:
path: |
**/node_modules
key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
key: runtime-release-node_modules-v5-${{ 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,19 +246,15 @@ 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 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"
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"
- name: Push changes to branch
if: inputs.dry_run == false && (inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true')
run: git push
@@ -417,19 +413,15 @@ 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 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"
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"
- name: Push changes to branch
if: inputs.dry_run == false && (inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true')
run: git push

View File

@@ -44,7 +44,7 @@ jobs:
with:
path: |
**/node_modules
key: runtime-and-compiler-eslint_e2e-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
key: runtime-and-compiler-eslint_e2e-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
- name: Ensure clean build directory
run: rm -rf build
- run: yarn install --frozen-lockfile

View File

@@ -41,7 +41,7 @@ jobs:
with:
path: |
**/node_modules
key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
key: runtime-release-node_modules-v5-${{ 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

View File

@@ -73,7 +73,7 @@ jobs:
with:
path: |
**/node_modules
key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
key: runtime-release-node_modules-v5-${{ 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

View File

@@ -1,35 +0,0 @@
# 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

View File

@@ -30,7 +30,7 @@ jobs:
with:
path: |
**/node_modules
key: shared-lint-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
key: shared-lint-node_modules-v5-${{ 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-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
key: shared-lint-node_modules-v5-${{ 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-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
key: shared-lint-node_modules-v5-${{ 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-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
- name: Ensure clean build directory
run: rm -rf build
- run: yarn install --frozen-lockfile

View File

@@ -33,6 +33,7 @@ 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,

View File

@@ -0,0 +1,19 @@
'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,
};

View File

@@ -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": "../../node_modules/eslint-v8/bin/eslint.js src",
"lint": "yarn eslint 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-v8": "npm:eslint@^8.57.1",
"eslint": "^8.57.1",
"invariant": "^2.2.4",
"jest": "^29.0.3",
"jest-environment-jsdom": "^29.0.3",

View File

@@ -13,13 +13,11 @@ import {
BlockId,
DependencyPathEntry,
GeneratedSource,
getHookKind,
HIRFunction,
Identifier,
IdentifierId,
InstructionId,
InstructionValue,
LoweredFunction,
PropertyLiteral,
ReactiveScopeDependency,
ScopeId,
@@ -114,9 +112,6 @@ export function collectHoistablePropertyLoads(
hoistableFromOptionals,
registry,
nestedFnImmutableContext: null,
assumedInvokedFns: fn.env.config.enableTreatFunctionDepsAsConditional
? new Set()
: getAssumedInvokedFunctions(fn),
});
}
@@ -132,11 +127,6 @@ 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,
@@ -348,13 +338,7 @@ function collectNonNullsInBlocks(
context.registry.getOrCreateIdentifier(identifier),
);
}
const nodes = new Map<
BlockId,
{
block: BasicBlock;
assumedNonNullObjects: Set<PropertyPathNode>;
}
>();
const nodes = new Map<BlockId, BlockInfo>();
for (const [_, block] of fn.body.blocks) {
const assumedNonNullObjects = new Set<PropertyPathNode>(
knownNonNullIdentifiers,
@@ -374,30 +358,32 @@ function collectNonNullsInBlocks(
) {
assumedNonNullObjects.add(maybeNonNull);
}
if (instr.value.kind === 'FunctionExpression') {
if (
(instr.value.kind === 'FunctionExpression' ||
instr.value.kind === 'ObjectMethod') &&
!fn.env.config.enableTreatFunctionDepsAsConditional
) {
const innerFn = instr.value.loweredFunc;
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);
}
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);
}
}
}
@@ -605,130 +591,3 @@ 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;
}

View File

@@ -23,11 +23,11 @@ import { c as _c } from "react/compiler-runtime";
function Component(props) {
const $ = _c(4);
let t0;
if ($[0] !== props) {
if ($[0] !== props.name) {
t0 = function () {
return <div>{props.name}</div>;
};
$[0] = props;
$[0] = props.name;
$[1] = t0;
} else {
t0 = $[1];

View File

@@ -1,133 +0,0 @@
## 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>

View File

@@ -1,35 +0,0 @@
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}]},
],
};

View File

@@ -1,108 +0,0 @@
## 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]]

View File

@@ -1,23 +0,0 @@
/**
* 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}]},
],
};

View File

@@ -1,130 +0,0 @@
## 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]]

View File

@@ -1,26 +0,0 @@
/**
* 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}]},
],
};

View File

@@ -1,111 +0,0 @@
## 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]]

View File

@@ -1,25 +0,0 @@
/**
* 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}]},
],
};

View File

@@ -1,112 +0,0 @@
## 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]

View File

@@ -1,29 +0,0 @@
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}},
],
};

View File

@@ -1,85 +0,0 @@
## 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 ]]"}

View File

@@ -1,23 +0,0 @@
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}}],
};

View File

@@ -1,87 +0,0 @@
## 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>

View File

@@ -1,32 +0,0 @@
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},
],
};

View File

@@ -1,74 +0,0 @@
## 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 ]]"]

View File

@@ -1,17 +0,0 @@
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}}],
};

View File

@@ -1,130 +0,0 @@
## 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>

View File

@@ -1,51 +0,0 @@
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},
],
};

View File

@@ -1,80 +0,0 @@
## 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>

View File

@@ -1,29 +0,0 @@
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},
],
};

View File

@@ -1,80 +0,0 @@
## 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>

View File

@@ -1,17 +0,0 @@
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}]},
],
};

View File

@@ -1,75 +0,0 @@
## 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>

View File

@@ -1,29 +0,0 @@
// @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},
],
};

View File

@@ -1,78 +0,0 @@
## 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>

View File

@@ -1,28 +0,0 @@
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},
],
};

View File

@@ -1,82 +0,0 @@
## 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 ]]"

View File

@@ -1,28 +0,0 @@
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}}],
};

View File

@@ -1,68 +0,0 @@
## 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 }] },
],
};
```

View File

@@ -1,18 +0,0 @@
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}]},
],
};

View File

@@ -1,57 +0,0 @@
## 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 ]]"}

View File

@@ -1,17 +0,0 @@
/**
* 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: []}],
};

View File

@@ -41,9 +41,9 @@ function useFoo(t0) {
local = $[1];
}
let t1;
if ($[2] !== local) {
if ($[2] !== local.b.c) {
t1 = () => [() => local.b.c];
$[2] = local;
$[2] = local.b.c;
$[3] = t1;
} else {
t1 = $[3];

View File

@@ -34,13 +34,13 @@ function useFoo(t0) {
const $ = _c(4);
const { a } = t0;
let t1;
if ($[0] !== a) {
if ($[0] !== a.b.c) {
t1 = {
fn() {
return identity(a.b.c);
},
};
$[0] = a;
$[0] = a.b.c;
$[1] = t1;
} else {
t1 = $[1];

View File

@@ -51,7 +51,7 @@ import { identity } from "shared-runtime";
function Component(props) {
const $ = _c(4);
let x;
if ($[0] !== props) {
if ($[0] !== props.cond) {
const f = () => {
if (props.cond) {
x = 1;
@@ -62,7 +62,7 @@ function Component(props) {
const f2 = identity(f);
f2();
$[0] = props;
$[0] = props.cond;
$[1] = x;
} else {
x = $[1];

View File

@@ -82,9 +82,9 @@ function Component(t0) {
contextVar = $[2];
}
let t1;
if ($[3] !== contextVar) {
if ($[3] !== contextVar.val) {
t1 = { cb: () => contextVar.val * 4 };
$[3] = contextVar;
$[3] = contextVar.val;
$[4] = t1;
} else {
t1 = $[4];

View File

@@ -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;
const c_00 = $0[0] !== props.value;
let t1;
if (c_00) {
const a = {
@@ -61,7 +61,7 @@ function useFoo(props) {
};
t1 = a.foo().bar();
$0[0] = props;
$0[0] = props.value;
$0[1] = t1;
} else {
t1 = $0[1];

View File

@@ -35,7 +35,7 @@ function Component(props) {
import { c as _c } from "react/compiler-runtime"; // @enableTransitivelyFreezeFunctionExpressions:false
function Component(props) {
const $ = _c(7);
const $ = _c(9);
const item = useMutable(props.itemId);
const dispatch = useDispatch();
useFreeze(dispatch);
@@ -51,8 +51,7 @@ function Component(props) {
}
const exit = t0;
let t1;
let t2;
if ($[2] !== exit || $[3] !== item) {
if ($[2] !== exit || $[3] !== item.value) {
t1 = () => {
const cleanup = GlobalEventEmitter.addListener("onInput", () => {
if (item.value) {
@@ -61,24 +60,30 @@ function Component(props) {
});
return () => cleanup.remove();
};
t2 = [exit, item];
$[2] = exit;
$[3] = item;
$[3] = item.value;
$[4] = t1;
$[5] = t2;
} else {
t1 = $[4];
t2 = $[5];
}
let t2;
if ($[5] !== exit || $[6] !== item) {
t2 = [exit, item];
$[5] = exit;
$[6] = item;
$[7] = t2;
} else {
t2 = $[7];
}
useEffect(t1, t2);
maybeMutate(item);
let t3;
if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
t3 = <div />;
$[6] = t3;
$[8] = t3;
} else {
t3 = $[6];
t3 = $[8];
}
return t3;
}

View File

@@ -26,7 +26,7 @@
"@types/eslint": "^8.56.12",
"@types/node": "^20.2.5",
"babel-jest": "^29.0.3",
"eslint-v8": "npm:eslint@^8.57.1",
"eslint": "8.57.0",
"hermes-eslint": "^0.25.1",
"jest": "^29.5.0"
},

View File

@@ -25,7 +25,7 @@ const configs = {
},
},
rules: {
'react-compiler/react-compiler': 'error' as const,
'react-compiler/react-compiler': 'error',
},
},
};

View File

@@ -1,15 +0,0 @@
/**
* 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', '@babel/preset-typescript'],
plugins: [
['@babel/plugin-transform-private-property-in-object', {loose: true}],
['@babel/plugin-transform-class-properties', {loose: true}],
['@babel/plugin-transform-private-methods', {loose: true}],
],
};

View File

@@ -1,36 +0,0 @@
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";`,
},
});

View File

@@ -450,7 +450,6 @@ 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',

View File

@@ -1,7 +1,6 @@
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

View File

@@ -7,10 +7,3 @@ body {
padding: 0;
font-family: sans-serif;
}
::view-transition-group(*),
::view-transition-image-pair(*),
::view-transition-old(*),
::view-transition-new(*) {
pointer-events: none;
}

View File

@@ -92,6 +92,7 @@ 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.

View File

@@ -0,0 +1,8 @@
/**
* 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',
};

View File

@@ -0,0 +1,8 @@
'use strict';
process.env.NODE_ENV = 'development';
module.exports = {
setupFiles: [require.resolve('../../scripts/jest/setupEnvironment.js')],
moduleFileExtensions: ['ts', 'js', 'json'],
};

View File

@@ -5,7 +5,7 @@
"repository": {
"type": "git",
"url": "https://github.com/facebook/react.git",
"directory": "compiler/packages/eslint-plugin-react-hooks"
"directory": "packages/eslint-plugin-react-hooks"
},
"files": [
"LICENSE",
@@ -21,11 +21,9 @@
"react"
],
"scripts": {
"build:compiler": "yarn workspace babel-plugin-react-compiler build",
"build": "rimraf dist && tsup",
"build:compiler": "cd ../../compiler && yarn workspace babel-plugin-react-compiler build",
"test": "yarn build:compiler && jest",
"typecheck": "tsc --noEmit",
"watch": "yarn build --watch"
"typecheck": "tsc --noEmit"
},
"license": "MIT",
"bugs": {
@@ -50,7 +48,6 @@
},
"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",

View File

@@ -9,7 +9,7 @@
"types": ["estree-jsx", "node"],
"downlevelIteration": true,
"paths": {
"babel-plugin-react-compiler": ["../babel-plugin-react-compiler/src"]
"babel-plugin-react-compiler": ["../../compiler/packages/babel-plugin-react-compiler/src"]
},
"jsx": "react-jsxdev",
"rootDir": "../..",

View File

@@ -261,7 +261,7 @@ function scheduleFibersWithFamiliesRecursively(
staleFamilies: Set<Family>,
): void {
if (__DEV__) {
const {alternate, child, sibling, tag, type} = fiber;
const {alternate, child, sibling, tag, type, elementType} = fiber;
let candidateType = null;
switch (tag) {
@@ -283,6 +283,11 @@ 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) {

View File

@@ -146,6 +146,12 @@ 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;
}
@@ -215,6 +221,7 @@ export function performReactRefresh(): RefreshUpdate | null {
if (canPreserveStateBetween(prevType, nextType)) {
updatedFamilies.add(family);
} else {
console.log('mark family as stale', family);
staleFamilies.add(family);
}
});

View File

@@ -699,6 +699,244 @@ 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(() => {

View File

@@ -7,6 +7,12 @@ 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')},

View File

@@ -26,6 +26,7 @@ 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;
@@ -417,6 +418,12 @@ 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.
@@ -496,7 +503,7 @@ function getPlugins(
// takes care of it.
renaming: false,
}),
needsMinifiedByClosure &&
(needsMinifiedByClosure || bundle.name === 'eslint-plugin-react-hooks') &&
// Add the whitespace back
prettier({
parser: 'flow',

View File

@@ -1182,6 +1182,24 @@ 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],