Compare commits
5 Commits
repro-bug-
...
pr32595
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c19f052b2d | ||
|
|
d2d5350e65 | ||
|
|
8ba22587b5 | ||
|
|
7606b29048 | ||
|
|
54e602d891 |
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Discord Webhook Action
|
||||
uses: tsickert/discord-webhook@86dc739f3f165f16dadc5666051c367efa1692f4
|
||||
uses: tsickert/discord-webhook@v6.0.0
|
||||
with:
|
||||
webhook-url: ${{ secrets.COMPILER_DISCORD_WEBHOOK_URL }}
|
||||
embed-author-name: ${{ github.event.pull_request.user.login }}
|
||||
|
||||
5
.github/workflows/compiler_playground.yml
vendored
5
.github/workflows/compiler_playground.yml
vendored
@@ -36,9 +36,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: compiler-node_modules-${{ 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
|
||||
|
||||
5
.github/workflows/compiler_prereleases.yml
vendored
5
.github/workflows/compiler_prereleases.yml
vendored
@@ -46,9 +46,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- name: Publish packages to npm
|
||||
run: |
|
||||
|
||||
15
.github/workflows/compiler_typescript.yml
vendored
15
.github/workflows/compiler_typescript.yml
vendored
@@ -46,9 +46,8 @@ jobs:
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn workspace babel-plugin-react-compiler lint
|
||||
|
||||
@@ -67,9 +66,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn workspace babel-plugin-react-compiler jest
|
||||
|
||||
@@ -92,9 +90,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: compiler-node_modules-${{ 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'
|
||||
|
||||
23
.github/workflows/devtools_regression_tests.yml
vendored
23
.github/workflows/devtools_regression_tests.yml
vendored
@@ -29,13 +29,13 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-release-node_modules-${{ 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
|
||||
- run: yarn --cwd scripts/release install --frozen-lockfile
|
||||
- run: yarn install --frozen-lockfile
|
||||
working-directory: scripts/release
|
||||
- name: Download react-devtools artifacts for base revision
|
||||
run: |
|
||||
git fetch origin main
|
||||
@@ -63,9 +63,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -121,9 +120,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- name: Restore all archived build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -156,9 +154,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- name: Restore all archived build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
95
.github/workflows/runtime_build_and_test.yml
vendored
95
.github/workflows/runtime_build_and_test.yml
vendored
@@ -55,9 +55,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -80,9 +79,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -107,9 +105,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -156,20 +153,16 @@ jobs:
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: yarn
|
||||
cache-dependency-path: |
|
||||
yarn.lock
|
||||
compiler/yarn.lock
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@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') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn --cwd compiler install --frozen-lockfile
|
||||
- run: yarn test ${{ matrix.params }} --ci --shard=${{ matrix.shard }}
|
||||
|
||||
# ----- BUILD -----
|
||||
@@ -190,9 +183,7 @@ jobs:
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: yarn
|
||||
cache-dependency-path: |
|
||||
yarn.lock
|
||||
compiler/yarn.lock
|
||||
cache-dependency-path: yarn.lock
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
@@ -201,13 +192,11 @@ jobs:
|
||||
uses: actions/cache@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') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn --cwd compiler install --frozen-lockfile
|
||||
- run: yarn build --index=${{ matrix.worker_id }} --total=20 --r=${{ matrix.release_channel }} --ci
|
||||
env:
|
||||
CI: github
|
||||
@@ -272,20 +261,16 @@ jobs:
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: yarn
|
||||
cache-dependency-path: |
|
||||
yarn.lock
|
||||
compiler/yarn.lock
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@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') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn --cwd compiler install --frozen-lockfile
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -313,9 +298,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -327,7 +311,7 @@ jobs:
|
||||
merge-multiple: true
|
||||
- name: Display structure of build
|
||||
run: ls -R build
|
||||
- run: echo ${{ github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA
|
||||
- run: echo ${{ github.sha }} >> build/COMMIT_SHA
|
||||
- name: Scrape warning messages
|
||||
run: |
|
||||
mkdir -p ./build/__test_utils__
|
||||
@@ -361,9 +345,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -397,9 +380,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -430,12 +412,12 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: fixtures_dom-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: fixtures_dom-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn --cwd fixtures/dom install --frozen-lockfile
|
||||
- run: yarn install --frozen-lockfile
|
||||
working-directory: fixtures/dom
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -473,9 +455,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: fixtures_flight-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: fixtures_flight-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -536,9 +517,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -588,9 +568,8 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -626,12 +605,12 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn --cwd scripts/release install --frozen-lockfile
|
||||
- run: yarn install --frozen-lockfile
|
||||
working-directory: scripts/release
|
||||
- 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 }})
|
||||
@@ -659,7 +638,7 @@ jobs:
|
||||
node ./scripts/print-warnings/print-warnings.js > build/__test_utils__/ReactAllWarnings.js
|
||||
- name: Display structure of build for PR
|
||||
run: ls -R build
|
||||
- run: echo ${{ github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA
|
||||
- run: echo ${{ github.sha }} >> build/COMMIT_SHA
|
||||
- run: node ./scripts/tasks/danger
|
||||
- name: Archive sizebot results
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
56
.github/workflows/runtime_commit_artifacts.yml
vendored
56
.github/workflows/runtime_commit_artifacts.yml
vendored
@@ -16,11 +16,6 @@ on:
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
dry_run:
|
||||
description: Perform a dry run (run everything except push)
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
TZ: /usr/share/zoneinfo/America/Los_Angeles
|
||||
@@ -78,13 +73,15 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-release-node_modules-${{ 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
|
||||
- run: yarn --cwd scripts/release install --frozen-lockfile
|
||||
name: yarn install (react)
|
||||
- run: yarn install --frozen-lockfile
|
||||
name: yarn install (scripts/release)
|
||||
working-directory: scripts/release
|
||||
- name: Download artifacts for base revision
|
||||
run: |
|
||||
GH_TOKEN=${{ github.token }} scripts/release/download-experimental-build.js --commit=${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }}
|
||||
@@ -110,10 +107,9 @@ jobs:
|
||||
mkdir ./compiled/facebook-www/__test_utils__
|
||||
mv build/__test_utils__/ReactAllWarnings.js ./compiled/facebook-www/__test_utils__/ReactAllWarnings.js
|
||||
|
||||
# Move eslint-plugin-react-hooks into eslint-plugin-react-hooks
|
||||
mkdir ./compiled/eslint-plugin-react-hooks
|
||||
# Move eslint-plugin-react-hooks into facebook-www
|
||||
mv build/oss-experimental/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js \
|
||||
./compiled/eslint-plugin-react-hooks/index.js
|
||||
./compiled/facebook-www/eslint-plugin-react-hooks.js
|
||||
|
||||
# Move unstable_server-external-runtime.js into facebook-www
|
||||
mv build/oss-experimental/react-dom/unstable_server-external-runtime.js \
|
||||
@@ -248,16 +244,16 @@ jobs:
|
||||
git status -u
|
||||
- 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 }}"
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: |
|
||||
${{ github.event.workflow_run.head_commit.message || format('Manual build of {0}', github.event.workflow_run.head_sha || github.sha) }}
|
||||
|
||||
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
|
||||
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 }})
|
||||
branch: builds/facebook-www
|
||||
commit_user_name: ${{ github.triggering_actor }}
|
||||
commit_user_email: ${{ format('{0}@users.noreply.github.com', github.triggering_actor) }}
|
||||
create_branch: true
|
||||
|
||||
commit_fbsource_artifacts:
|
||||
needs: download_artifacts
|
||||
@@ -415,13 +411,13 @@ jobs:
|
||||
git status
|
||||
- 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 }}"
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: |
|
||||
${{ github.event.workflow_run.head_commit.message || format('Manual build of {0}', github.event.workflow_run.head_sha || github.sha) }}
|
||||
|
||||
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
|
||||
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 }})
|
||||
branch: builds/facebook-fbsource
|
||||
commit_user_name: ${{ github.triggering_actor }}
|
||||
commit_user_email: ${{ format('{0}@users.noreply.github.com', github.triggering_actor) }}
|
||||
create_branch: true
|
||||
|
||||
2
.github/workflows/runtime_discord_notify.yml
vendored
2
.github/workflows/runtime_discord_notify.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Discord Webhook Action
|
||||
uses: tsickert/discord-webhook@86dc739f3f165f16dadc5666051c367efa1692f4
|
||||
uses: tsickert/discord-webhook@v6.0.0
|
||||
with:
|
||||
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
embed-author-name: ${{ github.event.pull_request.user.login }}
|
||||
|
||||
10
.github/workflows/runtime_eslint_plugin_e2e.yml
vendored
10
.github/workflows/runtime_eslint_plugin_e2e.yml
vendored
@@ -35,20 +35,16 @@ jobs:
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: yarn
|
||||
cache-dependency-path: |
|
||||
yarn.lock
|
||||
compiler/yarn.lock
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-eslint_e2e-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
path: "node_modules"
|
||||
key: runtime-eslint_e2e-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn --cwd compiler install --frozen-lockfile
|
||||
- name: Build plugin
|
||||
working-directory: fixtures/eslint-v${{ matrix.eslint_major }}
|
||||
run: node build.mjs
|
||||
|
||||
8
.github/workflows/runtime_prereleases.yml
vendored
8
.github/workflows/runtime_prereleases.yml
vendored
@@ -39,13 +39,13 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-release-node_modules-${{ 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
|
||||
- run: yarn --cwd scripts/release install --frozen-lockfile
|
||||
- run: yarn install --frozen-lockfile
|
||||
working-directory: scripts/release
|
||||
- run: |
|
||||
scripts/release/prepare-release-from-ci.js --skipTests -r ${{ inputs.release_channel }} --commit=${{ inputs.commit_sha }}
|
||||
cp ./scripts/release/ci-npmrc ~/.npmrc
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Discord Webhook Action
|
||||
uses: tsickert/discord-webhook@86dc739f3f165f16dadc5666051c367efa1692f4
|
||||
uses: tsickert/discord-webhook@v6.0.0
|
||||
with:
|
||||
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
embed-author-name: ${{ github.event.sender.login }}
|
||||
@@ -71,13 +71,13 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: runtime-release-node_modules-${{ 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
|
||||
- run: yarn --cwd scripts/release install --frozen-lockfile
|
||||
- run: yarn install --frozen-lockfile
|
||||
working-directory: scripts/release
|
||||
- run: cp ./scripts/release/ci-npmrc ~/.npmrc
|
||||
- if: '${{ inputs.only_packages }}'
|
||||
name: 'Prepare ${{ inputs.only_packages }} from NPM'
|
||||
|
||||
20
.github/workflows/shared_lint.yml
vendored
20
.github/workflows/shared_lint.yml
vendored
@@ -28,9 +28,8 @@ jobs:
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -49,9 +48,8 @@ jobs:
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -70,9 +68,8 @@ jobs:
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -91,9 +88,8 @@ jobs:
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
path: "**/node_modules"
|
||||
key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
|
||||
@@ -4,7 +4,8 @@ module.exports = {
|
||||
plugins: [
|
||||
'@babel/plugin-syntax-jsx',
|
||||
'@babel/plugin-transform-flow-strip-types',
|
||||
['@babel/plugin-proposal-class-properties', {loose: true}],
|
||||
['@babel/plugin-transform-class-properties', {loose: true}],
|
||||
'@babel/plugin-transform-classes',
|
||||
'syntax-trailing-function-commas',
|
||||
[
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
|
||||
@@ -181,7 +181,6 @@ function ReactForgetFunctionTransform() {
|
||||
fn,
|
||||
forgetOptions,
|
||||
'Other',
|
||||
'all_features',
|
||||
'_c',
|
||||
null,
|
||||
null,
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
injectReanimatedFlag,
|
||||
pipelineUsesReanimatedPlugin,
|
||||
} from '../Entrypoint/Reanimated';
|
||||
import validateNoUntransformedReferences from '../Entrypoint/ValidateNoUntransformedReferences';
|
||||
|
||||
const ENABLE_REACT_COMPILER_TIMINGS =
|
||||
process.env['ENABLE_REACT_COMPILER_TIMINGS'] === '1';
|
||||
@@ -62,19 +61,12 @@ export default function BabelPluginReactCompiler(
|
||||
},
|
||||
};
|
||||
}
|
||||
const result = compileProgram(prog, {
|
||||
compileProgram(prog, {
|
||||
opts,
|
||||
filename: pass.filename ?? null,
|
||||
comments: pass.file.ast.comments ?? [],
|
||||
code: pass.file.code,
|
||||
});
|
||||
validateNoUntransformedReferences(
|
||||
prog,
|
||||
pass.filename ?? null,
|
||||
opts.logger,
|
||||
opts.environment,
|
||||
result?.retryErrors ?? [],
|
||||
);
|
||||
if (ENABLE_REACT_COMPILER_TIMINGS === true) {
|
||||
performance.mark(`${filename}:end`, {
|
||||
detail: 'BabelPlugin:Program:end',
|
||||
|
||||
@@ -8,119 +8,7 @@
|
||||
import {NodePath} from '@babel/core';
|
||||
import * as t from '@babel/types';
|
||||
import {PluginOptions} from './Options';
|
||||
import {CompilerError} from '../CompilerError';
|
||||
|
||||
/**
|
||||
* Gating rewrite for function declarations which are referenced before their
|
||||
* declaration site.
|
||||
*
|
||||
* ```js
|
||||
* // original
|
||||
* export default React.memo(Foo);
|
||||
* function Foo() { ... }
|
||||
*
|
||||
* // React compiler optimized + gated
|
||||
* import {gating} from 'myGating';
|
||||
* export default React.memo(Foo);
|
||||
* const gating_result = gating(); <- inserted
|
||||
* function Foo_optimized() {} <- inserted
|
||||
* function Foo_unoptimized() {} <- renamed from Foo
|
||||
* function Foo() { <- inserted function, which can be hoisted by JS engines
|
||||
* if (gating_result) return Foo_optimized();
|
||||
* else return Foo_unoptimized();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
function insertAdditionalFunctionDeclaration(
|
||||
fnPath: NodePath<t.FunctionDeclaration>,
|
||||
compiled: t.FunctionDeclaration,
|
||||
gating: NonNullable<PluginOptions['gating']>,
|
||||
): void {
|
||||
const originalFnName = fnPath.node.id;
|
||||
const originalFnParams = fnPath.node.params;
|
||||
const compiledParams = fnPath.node.params;
|
||||
/**
|
||||
* Note that other than `export default function() {}`, all other function
|
||||
* declarations must have a binding identifier. Since default exports cannot
|
||||
* be referenced, it's safe to assume that all function declarations passed
|
||||
* here will have an identifier.
|
||||
* https://tc39.es/ecma262/multipage/ecmascript-language-functions-and-classes.html#sec-function-definitions
|
||||
*/
|
||||
CompilerError.invariant(originalFnName != null && compiled.id != null, {
|
||||
reason:
|
||||
'Expected function declarations that are referenced elsewhere to have a named identifier',
|
||||
loc: fnPath.node.loc ?? null,
|
||||
});
|
||||
CompilerError.invariant(originalFnParams.length === compiledParams.length, {
|
||||
reason:
|
||||
'Expected React Compiler optimized function declarations to have the same number of parameters as source',
|
||||
loc: fnPath.node.loc ?? null,
|
||||
});
|
||||
|
||||
const gatingCondition = fnPath.scope.generateUidIdentifier(
|
||||
`${gating.importSpecifierName}_result`,
|
||||
);
|
||||
const unoptimizedFnName = fnPath.scope.generateUidIdentifier(
|
||||
`${originalFnName.name}_unoptimized`,
|
||||
);
|
||||
const optimizedFnName = fnPath.scope.generateUidIdentifier(
|
||||
`${originalFnName.name}_optimized`,
|
||||
);
|
||||
/**
|
||||
* Step 1: rename existing functions
|
||||
*/
|
||||
compiled.id.name = optimizedFnName.name;
|
||||
fnPath.get('id').replaceInline(unoptimizedFnName);
|
||||
|
||||
/**
|
||||
* Step 2: insert new function declaration
|
||||
*/
|
||||
const newParams: Array<t.Identifier | t.RestElement> = [];
|
||||
const genNewArgs: Array<() => t.Identifier | t.SpreadElement> = [];
|
||||
for (let i = 0; i < originalFnParams.length; i++) {
|
||||
const argName = `arg${i}`;
|
||||
if (originalFnParams[i].type === 'RestElement') {
|
||||
newParams.push(t.restElement(t.identifier(argName)));
|
||||
genNewArgs.push(() => t.spreadElement(t.identifier(argName)));
|
||||
} else {
|
||||
newParams.push(t.identifier(argName));
|
||||
genNewArgs.push(() => t.identifier(argName));
|
||||
}
|
||||
}
|
||||
// insertAfter called in reverse order of how nodes should appear in program
|
||||
fnPath.insertAfter(
|
||||
t.functionDeclaration(
|
||||
originalFnName,
|
||||
newParams,
|
||||
t.blockStatement([
|
||||
t.ifStatement(
|
||||
gatingCondition,
|
||||
t.returnStatement(
|
||||
t.callExpression(
|
||||
compiled.id,
|
||||
genNewArgs.map(fn => fn()),
|
||||
),
|
||||
),
|
||||
t.returnStatement(
|
||||
t.callExpression(
|
||||
unoptimizedFnName,
|
||||
genNewArgs.map(fn => fn()),
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
fnPath.insertBefore(
|
||||
t.variableDeclaration('const', [
|
||||
t.variableDeclarator(
|
||||
gatingCondition,
|
||||
t.callExpression(t.identifier(gating.importSpecifierName), []),
|
||||
),
|
||||
]),
|
||||
);
|
||||
fnPath.insertBefore(compiled);
|
||||
}
|
||||
export function insertGatedFunctionDeclaration(
|
||||
fnPath: NodePath<
|
||||
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
|
||||
@@ -130,57 +18,47 @@ export function insertGatedFunctionDeclaration(
|
||||
| t.ArrowFunctionExpression
|
||||
| t.FunctionExpression,
|
||||
gating: NonNullable<PluginOptions['gating']>,
|
||||
referencedBeforeDeclaration: boolean,
|
||||
): void {
|
||||
if (referencedBeforeDeclaration && fnPath.isFunctionDeclaration()) {
|
||||
CompilerError.invariant(compiled.type === 'FunctionDeclaration', {
|
||||
reason: 'Expected compiled node type to match input type',
|
||||
description: `Got ${compiled.type} but expected FunctionDeclaration`,
|
||||
loc: fnPath.node.loc ?? null,
|
||||
});
|
||||
insertAdditionalFunctionDeclaration(fnPath, compiled, gating);
|
||||
} else {
|
||||
const gatingExpression = t.conditionalExpression(
|
||||
t.callExpression(t.identifier(gating.importSpecifierName), []),
|
||||
buildFunctionExpression(compiled),
|
||||
buildFunctionExpression(fnPath.node),
|
||||
);
|
||||
const gatingExpression = t.conditionalExpression(
|
||||
t.callExpression(t.identifier(gating.importSpecifierName), []),
|
||||
buildFunctionExpression(compiled),
|
||||
buildFunctionExpression(fnPath.node),
|
||||
);
|
||||
|
||||
/*
|
||||
* Convert function declarations to named variables *unless* this is an
|
||||
* `export default function ...` since `export default const ...` is
|
||||
* not supported. For that case we fall through to replacing w the raw
|
||||
* conditional expression
|
||||
*/
|
||||
if (
|
||||
fnPath.parentPath.node.type !== 'ExportDefaultDeclaration' &&
|
||||
fnPath.node.type === 'FunctionDeclaration' &&
|
||||
fnPath.node.id != null
|
||||
) {
|
||||
fnPath.replaceWith(
|
||||
t.variableDeclaration('const', [
|
||||
t.variableDeclarator(fnPath.node.id, gatingExpression),
|
||||
]),
|
||||
);
|
||||
} else if (
|
||||
fnPath.parentPath.node.type === 'ExportDefaultDeclaration' &&
|
||||
fnPath.node.type !== 'ArrowFunctionExpression' &&
|
||||
fnPath.node.id != null
|
||||
) {
|
||||
fnPath.insertAfter(
|
||||
t.exportDefaultDeclaration(t.identifier(fnPath.node.id.name)),
|
||||
);
|
||||
fnPath.parentPath.replaceWith(
|
||||
t.variableDeclaration('const', [
|
||||
t.variableDeclarator(
|
||||
t.identifier(fnPath.node.id.name),
|
||||
gatingExpression,
|
||||
),
|
||||
]),
|
||||
);
|
||||
} else {
|
||||
fnPath.replaceWith(gatingExpression);
|
||||
}
|
||||
/*
|
||||
* Convert function declarations to named variables *unless* this is an
|
||||
* `export default function ...` since `export default const ...` is
|
||||
* not supported. For that case we fall through to replacing w the raw
|
||||
* conditional expression
|
||||
*/
|
||||
if (
|
||||
fnPath.parentPath.node.type !== 'ExportDefaultDeclaration' &&
|
||||
fnPath.node.type === 'FunctionDeclaration' &&
|
||||
fnPath.node.id != null
|
||||
) {
|
||||
fnPath.replaceWith(
|
||||
t.variableDeclaration('const', [
|
||||
t.variableDeclarator(fnPath.node.id, gatingExpression),
|
||||
]),
|
||||
);
|
||||
} else if (
|
||||
fnPath.parentPath.node.type === 'ExportDefaultDeclaration' &&
|
||||
fnPath.node.type !== 'ArrowFunctionExpression' &&
|
||||
fnPath.node.id != null
|
||||
) {
|
||||
fnPath.insertAfter(
|
||||
t.exportDefaultDeclaration(t.identifier(fnPath.node.id.name)),
|
||||
);
|
||||
fnPath.parentPath.replaceWith(
|
||||
t.variableDeclaration('const', [
|
||||
t.variableDeclarator(
|
||||
t.identifier(fnPath.node.id.name),
|
||||
gatingExpression,
|
||||
),
|
||||
]),
|
||||
);
|
||||
} else {
|
||||
fnPath.replaceWith(gatingExpression);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
EnvironmentConfig,
|
||||
ExternalFunction,
|
||||
parseEnvironmentConfig,
|
||||
tryParseExternalFunction,
|
||||
} from '../HIR/Environment';
|
||||
import {hasOwnProperty} from '../Utils/utils';
|
||||
import {fromZodError} from 'zod-validation-error';
|
||||
@@ -272,14 +271,6 @@ export function parsePluginOptions(obj: unknown): PluginOptions {
|
||||
parsedOptions[key] = parseTargetConfig(value);
|
||||
break;
|
||||
}
|
||||
case 'gating': {
|
||||
if (value == null) {
|
||||
parsedOptions[key] = null;
|
||||
} else {
|
||||
parsedOptions[key] = tryParseExternalFunction(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
parsedOptions[key] = value;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
pruneUnusedLabelsHIR,
|
||||
} from '../HIR';
|
||||
import {
|
||||
CompilerMode,
|
||||
Environment,
|
||||
EnvironmentConfig,
|
||||
ReactFunctionType,
|
||||
@@ -101,7 +100,6 @@ import {outlineJSX} from '../Optimization/OutlineJsx';
|
||||
import {optimizePropsMethodCalls} from '../Optimization/OptimizePropsMethodCalls';
|
||||
import {transformFire} from '../Transform';
|
||||
import {validateNoImpureFunctionsInRender} from '../Validation/ValiateNoImpureFunctionsInRender';
|
||||
import {CompilerError} from '..';
|
||||
|
||||
export type CompilerPipelineValue =
|
||||
| {kind: 'ast'; name: string; value: CodegenFunction}
|
||||
@@ -115,7 +113,6 @@ function run(
|
||||
>,
|
||||
config: EnvironmentConfig,
|
||||
fnType: ReactFunctionType,
|
||||
mode: CompilerMode,
|
||||
useMemoCacheIdentifier: string,
|
||||
logger: Logger | null,
|
||||
filename: string | null,
|
||||
@@ -125,7 +122,6 @@ function run(
|
||||
const env = new Environment(
|
||||
func.scope,
|
||||
fnType,
|
||||
mode,
|
||||
config,
|
||||
contextIdentifiers,
|
||||
logger,
|
||||
@@ -164,10 +160,10 @@ function runWithEnvironment(
|
||||
validateUseMemo(hir);
|
||||
|
||||
if (
|
||||
env.isInferredMemoEnabled &&
|
||||
!env.config.enablePreserveExistingManualUseMemo &&
|
||||
!env.config.disableMemoizationForDebugging &&
|
||||
!env.config.enableChangeDetectionForDebugging
|
||||
!env.config.enableChangeDetectionForDebugging &&
|
||||
!env.config.enableMinimalTransformsForRetry
|
||||
) {
|
||||
dropManualMemoization(hir);
|
||||
log({kind: 'hir', name: 'DropManualMemoization', value: hir});
|
||||
@@ -200,13 +196,8 @@ function runWithEnvironment(
|
||||
inferTypes(hir);
|
||||
log({kind: 'hir', name: 'InferTypes', value: hir});
|
||||
|
||||
if (env.isInferredMemoEnabled) {
|
||||
if (env.config.validateHooksUsage) {
|
||||
validateHooksUsage(hir);
|
||||
}
|
||||
if (env.config.validateNoCapitalizedCalls) {
|
||||
validateNoCapitalizedCalls(hir);
|
||||
}
|
||||
if (env.config.validateHooksUsage) {
|
||||
validateHooksUsage(hir);
|
||||
}
|
||||
|
||||
if (env.config.enableFire) {
|
||||
@@ -214,6 +205,10 @@ function runWithEnvironment(
|
||||
log({kind: 'hir', name: 'TransformFire', value: hir});
|
||||
}
|
||||
|
||||
if (env.config.validateNoCapitalizedCalls) {
|
||||
validateNoCapitalizedCalls(hir);
|
||||
}
|
||||
|
||||
if (env.config.lowerContextAccess) {
|
||||
lowerContextAccess(hir, env.config.lowerContextAccess);
|
||||
}
|
||||
@@ -224,12 +219,7 @@ function runWithEnvironment(
|
||||
analyseFunctions(hir);
|
||||
log({kind: 'hir', name: 'AnalyseFunctions', value: hir});
|
||||
|
||||
const fnEffectErrors = inferReferenceEffects(hir);
|
||||
if (env.isInferredMemoEnabled) {
|
||||
if (fnEffectErrors.length > 0) {
|
||||
CompilerError.throw(fnEffectErrors[0]);
|
||||
}
|
||||
}
|
||||
inferReferenceEffects(hir);
|
||||
log({kind: 'hir', name: 'InferReferenceEffects', value: hir});
|
||||
|
||||
validateLocalsNotReassignedAfterRender(hir);
|
||||
@@ -249,30 +239,28 @@ function runWithEnvironment(
|
||||
inferMutableRanges(hir);
|
||||
log({kind: 'hir', name: 'InferMutableRanges', value: hir});
|
||||
|
||||
if (env.isInferredMemoEnabled) {
|
||||
if (env.config.assertValidMutableRanges) {
|
||||
assertValidMutableRanges(hir);
|
||||
}
|
||||
if (env.config.assertValidMutableRanges) {
|
||||
assertValidMutableRanges(hir);
|
||||
}
|
||||
|
||||
if (env.config.validateRefAccessDuringRender) {
|
||||
validateNoRefAccessInRender(hir);
|
||||
}
|
||||
if (env.config.validateRefAccessDuringRender) {
|
||||
validateNoRefAccessInRender(hir);
|
||||
}
|
||||
|
||||
if (env.config.validateNoSetStateInRender) {
|
||||
validateNoSetStateInRender(hir);
|
||||
}
|
||||
if (env.config.validateNoSetStateInRender) {
|
||||
validateNoSetStateInRender(hir);
|
||||
}
|
||||
|
||||
if (env.config.validateNoSetStateInPassiveEffects) {
|
||||
validateNoSetStateInPassiveEffects(hir);
|
||||
}
|
||||
if (env.config.validateNoSetStateInPassiveEffects) {
|
||||
validateNoSetStateInPassiveEffects(hir);
|
||||
}
|
||||
|
||||
if (env.config.validateNoJSXInTryStatements) {
|
||||
validateNoJSXInTryStatement(hir);
|
||||
}
|
||||
if (env.config.validateNoJSXInTryStatements) {
|
||||
validateNoJSXInTryStatement(hir);
|
||||
}
|
||||
|
||||
if (env.config.validateNoImpureFunctionsInRender) {
|
||||
validateNoImpureFunctionsInRender(hir);
|
||||
}
|
||||
if (env.config.validateNoImpureFunctionsInRender) {
|
||||
validateNoImpureFunctionsInRender(hir);
|
||||
}
|
||||
|
||||
inferReactivePlaces(hir);
|
||||
@@ -292,12 +280,7 @@ function runWithEnvironment(
|
||||
value: hir,
|
||||
});
|
||||
|
||||
if (env.isInferredMemoEnabled) {
|
||||
/**
|
||||
* Only create reactive scopes (which directly map to generated memo blocks)
|
||||
* if inferred memoization is enabled. This makes all later passes which
|
||||
* transform reactive-scope labeled instructions no-ops.
|
||||
*/
|
||||
if (!env.config.enableMinimalTransformsForRetry) {
|
||||
inferReactiveScopeVariables(hir);
|
||||
log({kind: 'hir', name: 'InferReactiveScopeVariables', value: hir});
|
||||
}
|
||||
@@ -546,7 +529,6 @@ export function compileFn(
|
||||
>,
|
||||
config: EnvironmentConfig,
|
||||
fnType: ReactFunctionType,
|
||||
mode: CompilerMode,
|
||||
useMemoCacheIdentifier: string,
|
||||
logger: Logger | null,
|
||||
filename: string | null,
|
||||
@@ -556,7 +538,6 @@ export function compileFn(
|
||||
func,
|
||||
config,
|
||||
fnType,
|
||||
mode,
|
||||
useMemoCacheIdentifier,
|
||||
logger,
|
||||
filename,
|
||||
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
EnvironmentConfig,
|
||||
ExternalFunction,
|
||||
ReactFunctionType,
|
||||
MINIMAL_RETRY_CONFIG,
|
||||
tryParseExternalFunction,
|
||||
} from '../HIR/Environment';
|
||||
import {CodegenFunction} from '../ReactiveScopes';
|
||||
import {isComponentDeclaration} from '../Utils/ComponentDeclaration';
|
||||
@@ -271,9 +273,6 @@ function isFilePartOfSources(
|
||||
return false;
|
||||
}
|
||||
|
||||
type CompileProgramResult = {
|
||||
retryErrors: Array<{fn: BabelFn; error: CompilerError}>;
|
||||
};
|
||||
/**
|
||||
* `compileProgram` is directly invoked by the react-compiler babel plugin, so
|
||||
* exceptions thrown by this function will fail the babel build.
|
||||
@@ -288,16 +287,16 @@ type CompileProgramResult = {
|
||||
export function compileProgram(
|
||||
program: NodePath<t.Program>,
|
||||
pass: CompilerPass,
|
||||
): CompileProgramResult | null {
|
||||
): void {
|
||||
if (shouldSkipCompilation(program, pass)) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
const environment = pass.opts.environment;
|
||||
const restrictedImportsErr = validateRestrictedImports(program, environment);
|
||||
if (restrictedImportsErr) {
|
||||
handleError(restrictedImportsErr, pass, null);
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
const useMemoCacheIdentifier = program.scope.generateUidIdentifier('c');
|
||||
|
||||
@@ -368,7 +367,7 @@ export function compileProgram(
|
||||
filename: pass.filename ?? null,
|
||||
},
|
||||
);
|
||||
const retryErrors: Array<{fn: BabelFn; error: CompilerError}> = [];
|
||||
|
||||
const processFn = (
|
||||
fn: BabelFn,
|
||||
fnType: ReactFunctionType,
|
||||
@@ -409,7 +408,6 @@ export function compileProgram(
|
||||
fn,
|
||||
environment,
|
||||
fnType,
|
||||
'all_features',
|
||||
useMemoCacheIdentifier.name,
|
||||
pass.opts.logger,
|
||||
pass.filename,
|
||||
@@ -420,7 +418,28 @@ export function compileProgram(
|
||||
compileResult = {kind: 'error', error: err};
|
||||
}
|
||||
}
|
||||
|
||||
// If non-memoization features are enabled, retry regardless of error kind
|
||||
if (compileResult.kind === 'error' && environment.enableFire) {
|
||||
try {
|
||||
compileResult = {
|
||||
kind: 'compile',
|
||||
compiledFn: compileFn(
|
||||
fn,
|
||||
{
|
||||
...environment,
|
||||
...MINIMAL_RETRY_CONFIG,
|
||||
},
|
||||
fnType,
|
||||
useMemoCacheIdentifier.name,
|
||||
pass.opts.logger,
|
||||
pass.filename,
|
||||
pass.code,
|
||||
),
|
||||
};
|
||||
} catch (err) {
|
||||
compileResult = {kind: 'error', error: err};
|
||||
}
|
||||
}
|
||||
if (compileResult.kind === 'error') {
|
||||
/**
|
||||
* If an opt out directive is present, log only instead of throwing and don't mark as
|
||||
@@ -431,33 +450,7 @@ export function compileProgram(
|
||||
} else {
|
||||
handleError(compileResult.error, pass, fn.node.loc ?? null);
|
||||
}
|
||||
// If non-memoization features are enabled, retry regardless of error kind
|
||||
if (
|
||||
!(environment.enableFire || environment.inferEffectDependencies != null)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
compileResult = {
|
||||
kind: 'compile',
|
||||
compiledFn: compileFn(
|
||||
fn,
|
||||
environment,
|
||||
fnType,
|
||||
'no_inferred_memo',
|
||||
useMemoCacheIdentifier.name,
|
||||
pass.opts.logger,
|
||||
pass.filename,
|
||||
pass.code,
|
||||
),
|
||||
};
|
||||
} catch (err) {
|
||||
// TODO: we might want to log error here, but this will also result in duplicate logging
|
||||
if (err instanceof CompilerError) {
|
||||
retryErrors.push({fn, error: err});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pass.opts.logger?.logEvent(pass.filename, {
|
||||
@@ -546,28 +539,32 @@ export function compileProgram(
|
||||
program.node.directives,
|
||||
);
|
||||
if (moduleScopeOptOutDirectives.length > 0) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
let gating: null | {
|
||||
gatingFn: ExternalFunction;
|
||||
referencedBeforeDeclared: Set<CompileResult>;
|
||||
} = null;
|
||||
|
||||
if (pass.opts.gating != null) {
|
||||
gating = {
|
||||
gatingFn: pass.opts.gating,
|
||||
referencedBeforeDeclared:
|
||||
getFunctionReferencedBeforeDeclarationAtTopLevel(program, compiledFns),
|
||||
};
|
||||
const error = checkFunctionReferencedBeforeDeclarationAtTopLevel(
|
||||
program,
|
||||
compiledFns.map(result => {
|
||||
return result.originalFn;
|
||||
}),
|
||||
);
|
||||
if (error) {
|
||||
handleError(error, pass, null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const hasLoweredContextAccess = compiledFns.some(
|
||||
c => c.compiledFn.hasLoweredContextAccess,
|
||||
);
|
||||
const externalFunctions: Array<ExternalFunction> = [];
|
||||
let gating: null | ExternalFunction = null;
|
||||
try {
|
||||
// TODO: check for duplicate import specifiers
|
||||
if (gating != null) {
|
||||
externalFunctions.push(gating.gatingFn);
|
||||
if (pass.opts.gating != null) {
|
||||
gating = tryParseExternalFunction(pass.opts.gating);
|
||||
externalFunctions.push(gating);
|
||||
}
|
||||
|
||||
const lowerContextAccess = environment.lowerContextAccess;
|
||||
@@ -604,7 +601,7 @@ export function compileProgram(
|
||||
}
|
||||
} catch (err) {
|
||||
handleError(err, pass, null);
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -616,12 +613,7 @@ export function compileProgram(
|
||||
const transformedFn = createNewFunctionNode(originalFn, compiledFn);
|
||||
|
||||
if (gating != null && kind === 'original') {
|
||||
insertGatedFunctionDeclaration(
|
||||
originalFn,
|
||||
transformedFn,
|
||||
gating.gatingFn,
|
||||
gating.referencedBeforeDeclared.has(result),
|
||||
);
|
||||
insertGatedFunctionDeclaration(originalFn, transformedFn, gating);
|
||||
} else {
|
||||
originalFn.replaceWith(transformedFn);
|
||||
}
|
||||
@@ -646,7 +638,6 @@ export function compileProgram(
|
||||
}
|
||||
addImportsToProgram(program, externalFunctions);
|
||||
}
|
||||
return {retryErrors};
|
||||
}
|
||||
|
||||
function shouldSkipCompilation(
|
||||
@@ -1102,23 +1093,20 @@ function getFunctionName(
|
||||
}
|
||||
}
|
||||
|
||||
function getFunctionReferencedBeforeDeclarationAtTopLevel(
|
||||
function checkFunctionReferencedBeforeDeclarationAtTopLevel(
|
||||
program: NodePath<t.Program>,
|
||||
fns: Array<CompileResult>,
|
||||
): Set<CompileResult> {
|
||||
const fnNames = new Map<string, {id: t.Identifier; fn: CompileResult}>(
|
||||
fns: Array<BabelFn>,
|
||||
): CompilerError | null {
|
||||
const fnIds = new Set(
|
||||
fns
|
||||
.map<[NodePath<t.Expression> | null, CompileResult]>(fn => [
|
||||
getFunctionName(fn.originalFn),
|
||||
fn,
|
||||
])
|
||||
.map(fn => getFunctionName(fn))
|
||||
.filter(
|
||||
(entry): entry is [NodePath<t.Identifier>, CompileResult] =>
|
||||
!!entry[0] && entry[0].isIdentifier(),
|
||||
(name): name is NodePath<t.Identifier> => !!name && name.isIdentifier(),
|
||||
)
|
||||
.map(entry => [entry[0].node.name, {id: entry[0].node, fn: entry[1]}]),
|
||||
.map(name => name.node),
|
||||
);
|
||||
const referencedBeforeDeclaration = new Set<CompileResult>();
|
||||
const fnNames = new Map([...fnIds].map(id => [id.name, id]));
|
||||
const errors = new CompilerError();
|
||||
|
||||
program.traverse({
|
||||
TypeAnnotation(path) {
|
||||
@@ -1144,7 +1132,8 @@ function getFunctionReferencedBeforeDeclarationAtTopLevel(
|
||||
* We've reached the declaration, hoisting is no longer possible, stop
|
||||
* checking for this component name.
|
||||
*/
|
||||
if (id.node === fn.id) {
|
||||
if (fnIds.has(id.node)) {
|
||||
fnIds.delete(id.node);
|
||||
fnNames.delete(id.node.name);
|
||||
return;
|
||||
}
|
||||
@@ -1154,13 +1143,21 @@ function getFunctionReferencedBeforeDeclarationAtTopLevel(
|
||||
* A null scope means there's no function scope, which means we're at the
|
||||
* top level scope.
|
||||
*/
|
||||
if (scope === null && id.isReferencedIdentifier()) {
|
||||
referencedBeforeDeclaration.add(fn.fn);
|
||||
if (scope === null) {
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
reason: `Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting`,
|
||||
description: `Rewrite the reference to ${fn.name} to not rely on hoisting to fix this issue`,
|
||||
loc: fn.loc ?? null,
|
||||
suggestions: null,
|
||||
severity: ErrorSeverity.Invariant,
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return referencedBeforeDeclaration;
|
||||
return errors.details.length > 0 ? errors : null;
|
||||
}
|
||||
|
||||
function getReactCompilerRuntimeModule(opts: PluginOptions): string {
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
import {NodePath} from '@babel/core';
|
||||
import * as t from '@babel/types';
|
||||
|
||||
import {
|
||||
CompilerError,
|
||||
CompilerErrorDetailOptions,
|
||||
EnvironmentConfig,
|
||||
ErrorSeverity,
|
||||
Logger,
|
||||
} from '..';
|
||||
import {getOrInsertWith} from '../Utils/utils';
|
||||
import {Environment} from '../HIR';
|
||||
import {DEFAULT_EXPORT} from '../HIR/Environment';
|
||||
|
||||
function throwInvalidReact(
|
||||
options: Omit<CompilerErrorDetailOptions, 'severity'>,
|
||||
{logger, filename}: TraversalState,
|
||||
): never {
|
||||
const detail: CompilerErrorDetailOptions = {
|
||||
...options,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
};
|
||||
logger?.logEvent(filename, {
|
||||
kind: 'CompileError',
|
||||
fnLoc: null,
|
||||
detail,
|
||||
});
|
||||
CompilerError.throw(detail);
|
||||
}
|
||||
function assertValidEffectImportReference(
|
||||
numArgs: number,
|
||||
paths: Array<NodePath<t.Node>>,
|
||||
context: TraversalState,
|
||||
): void {
|
||||
for (const path of paths) {
|
||||
const parent = path.parentPath;
|
||||
if (parent != null && parent.isCallExpression()) {
|
||||
const args = parent.get('arguments');
|
||||
/**
|
||||
* Only error on untransformed references of the form `useMyEffect(...)`
|
||||
* or `moduleNamespace.useMyEffect(...)`, with matching argument counts.
|
||||
* TODO: do we also want a mode to also hard error on non-call references?
|
||||
*/
|
||||
if (args.length === numArgs) {
|
||||
const maybeErrorDiagnostic = matchCompilerDiagnostic(
|
||||
path,
|
||||
context.transformErrors,
|
||||
);
|
||||
/**
|
||||
* Note that we cannot easily check the type of the first argument here,
|
||||
* as it may have already been transformed by the compiler (and not
|
||||
* memoized).
|
||||
*/
|
||||
throwInvalidReact(
|
||||
{
|
||||
reason:
|
||||
'[InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. ' +
|
||||
'This will break your build! ' +
|
||||
'To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics.',
|
||||
description: maybeErrorDiagnostic
|
||||
? `(Bailout reason: ${maybeErrorDiagnostic})`
|
||||
: null,
|
||||
loc: parent.node.loc ?? null,
|
||||
},
|
||||
context,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assertValidFireImportReference(
|
||||
paths: Array<NodePath<t.Node>>,
|
||||
context: TraversalState,
|
||||
): void {
|
||||
if (paths.length > 0) {
|
||||
const maybeErrorDiagnostic = matchCompilerDiagnostic(
|
||||
paths[0],
|
||||
context.transformErrors,
|
||||
);
|
||||
throwInvalidReact(
|
||||
{
|
||||
reason:
|
||||
'[Fire] Untransformed reference to compiler-required feature. ' +
|
||||
'Either remove this `fire` call or ensure it is successfully transformed by the compiler',
|
||||
description: maybeErrorDiagnostic
|
||||
? `(Bailout reason: ${maybeErrorDiagnostic})`
|
||||
: null,
|
||||
loc: paths[0].node.loc ?? null,
|
||||
},
|
||||
context,
|
||||
);
|
||||
}
|
||||
}
|
||||
export default function validateNoUntransformedReferences(
|
||||
path: NodePath<t.Program>,
|
||||
filename: string | null,
|
||||
logger: Logger | null,
|
||||
env: EnvironmentConfig,
|
||||
transformErrors: Array<{fn: NodePath<t.Node>; error: CompilerError}>,
|
||||
): void {
|
||||
const moduleLoadChecks = new Map<
|
||||
string,
|
||||
Map<string, CheckInvalidReferenceFn>
|
||||
>();
|
||||
if (env.enableFire) {
|
||||
/**
|
||||
* Error on any untransformed references to `fire` (e.g. including non-call
|
||||
* expressions)
|
||||
*/
|
||||
for (const module of Environment.knownReactModules) {
|
||||
const react = getOrInsertWith(moduleLoadChecks, module, () => new Map());
|
||||
react.set('fire', assertValidFireImportReference);
|
||||
}
|
||||
}
|
||||
if (env.inferEffectDependencies) {
|
||||
for (const {
|
||||
function: {source, importSpecifierName},
|
||||
numRequiredArgs,
|
||||
} of env.inferEffectDependencies) {
|
||||
const module = getOrInsertWith(moduleLoadChecks, source, () => new Map());
|
||||
module.set(
|
||||
importSpecifierName,
|
||||
assertValidEffectImportReference.bind(null, numRequiredArgs),
|
||||
);
|
||||
}
|
||||
}
|
||||
if (moduleLoadChecks.size > 0) {
|
||||
transformProgram(path, moduleLoadChecks, filename, logger, transformErrors);
|
||||
}
|
||||
}
|
||||
|
||||
type TraversalState = {
|
||||
shouldInvalidateScopes: boolean;
|
||||
program: NodePath<t.Program>;
|
||||
logger: Logger | null;
|
||||
filename: string | null;
|
||||
transformErrors: Array<{fn: NodePath<t.Node>; error: CompilerError}>;
|
||||
};
|
||||
type CheckInvalidReferenceFn = (
|
||||
paths: Array<NodePath<t.Node>>,
|
||||
context: TraversalState,
|
||||
) => void;
|
||||
|
||||
function validateImportSpecifier(
|
||||
specifier: NodePath<t.ImportSpecifier>,
|
||||
importSpecifierChecks: Map<string, CheckInvalidReferenceFn>,
|
||||
state: TraversalState,
|
||||
): void {
|
||||
const imported = specifier.get('imported');
|
||||
const specifierName: string =
|
||||
imported.node.type === 'Identifier'
|
||||
? imported.node.name
|
||||
: imported.node.value;
|
||||
const checkFn = importSpecifierChecks.get(specifierName);
|
||||
if (checkFn == null) {
|
||||
return;
|
||||
}
|
||||
if (state.shouldInvalidateScopes) {
|
||||
state.shouldInvalidateScopes = false;
|
||||
state.program.scope.crawl();
|
||||
}
|
||||
|
||||
const local = specifier.get('local');
|
||||
const binding = local.scope.getBinding(local.node.name);
|
||||
CompilerError.invariant(binding != null, {
|
||||
reason: 'Expected binding to be found for import specifier',
|
||||
loc: local.node.loc ?? null,
|
||||
});
|
||||
checkFn(binding.referencePaths, state);
|
||||
}
|
||||
|
||||
function validateNamespacedImport(
|
||||
specifier: NodePath<t.ImportNamespaceSpecifier | t.ImportDefaultSpecifier>,
|
||||
importSpecifierChecks: Map<string, CheckInvalidReferenceFn>,
|
||||
state: TraversalState,
|
||||
): void {
|
||||
if (state.shouldInvalidateScopes) {
|
||||
state.shouldInvalidateScopes = false;
|
||||
state.program.scope.crawl();
|
||||
}
|
||||
const local = specifier.get('local');
|
||||
const binding = local.scope.getBinding(local.node.name);
|
||||
const defaultCheckFn = importSpecifierChecks.get(DEFAULT_EXPORT);
|
||||
|
||||
CompilerError.invariant(binding != null, {
|
||||
reason: 'Expected binding to be found for import specifier',
|
||||
loc: local.node.loc ?? null,
|
||||
});
|
||||
const filteredReferences = new Map<
|
||||
CheckInvalidReferenceFn,
|
||||
Array<NodePath<t.Node>>
|
||||
>();
|
||||
for (const reference of binding.referencePaths) {
|
||||
if (defaultCheckFn != null) {
|
||||
getOrInsertWith(filteredReferences, defaultCheckFn, () => []).push(
|
||||
reference,
|
||||
);
|
||||
}
|
||||
const parent = reference.parentPath;
|
||||
if (
|
||||
parent != null &&
|
||||
parent.isMemberExpression() &&
|
||||
parent.get('object') === reference
|
||||
) {
|
||||
if (parent.node.computed || parent.node.property.type !== 'Identifier') {
|
||||
continue;
|
||||
}
|
||||
const checkFn = importSpecifierChecks.get(parent.node.property.name);
|
||||
if (checkFn != null) {
|
||||
getOrInsertWith(filteredReferences, checkFn, () => []).push(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [checkFn, references] of filteredReferences) {
|
||||
checkFn(references, state);
|
||||
}
|
||||
}
|
||||
function transformProgram(
|
||||
path: NodePath<t.Program>,
|
||||
|
||||
moduleLoadChecks: Map<string, Map<string, CheckInvalidReferenceFn>>,
|
||||
filename: string | null,
|
||||
logger: Logger | null,
|
||||
transformErrors: Array<{fn: NodePath<t.Node>; error: CompilerError}>,
|
||||
): void {
|
||||
const traversalState: TraversalState = {
|
||||
shouldInvalidateScopes: true,
|
||||
program: path,
|
||||
filename,
|
||||
logger,
|
||||
transformErrors,
|
||||
};
|
||||
path.traverse({
|
||||
ImportDeclaration(path: NodePath<t.ImportDeclaration>) {
|
||||
const importSpecifierChecks = moduleLoadChecks.get(
|
||||
path.node.source.value,
|
||||
);
|
||||
if (importSpecifierChecks == null) {
|
||||
return;
|
||||
}
|
||||
const specifiers = path.get('specifiers');
|
||||
for (const specifier of specifiers) {
|
||||
if (specifier.isImportSpecifier()) {
|
||||
validateImportSpecifier(
|
||||
specifier,
|
||||
importSpecifierChecks,
|
||||
traversalState,
|
||||
);
|
||||
} else {
|
||||
validateNamespacedImport(
|
||||
specifier as NodePath<
|
||||
t.ImportNamespaceSpecifier | t.ImportDefaultSpecifier
|
||||
>,
|
||||
importSpecifierChecks,
|
||||
traversalState,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function matchCompilerDiagnostic(
|
||||
badReference: NodePath<t.Node>,
|
||||
transformErrors: Array<{fn: NodePath<t.Node>; error: CompilerError}>,
|
||||
): string | null {
|
||||
for (const {fn, error} of transformErrors) {
|
||||
if (fn.isAncestor(badReference)) {
|
||||
return error.toString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1455,11 +1455,6 @@ function lowerObjectPropertyKey(
|
||||
kind: 'identifier',
|
||||
name: key.node.name,
|
||||
};
|
||||
} else if (key.isNumericLiteral()) {
|
||||
return {
|
||||
kind: 'identifier',
|
||||
name: String(key.node.value),
|
||||
};
|
||||
}
|
||||
|
||||
builder.errors.push({
|
||||
|
||||
@@ -96,8 +96,6 @@ export const MacroSchema = z.union([
|
||||
z.tuple([z.string(), z.array(MacroMethodSchema)]),
|
||||
]);
|
||||
|
||||
export type CompilerMode = 'all_features' | 'no_inferred_memo';
|
||||
|
||||
export type Macro = z.infer<typeof MacroSchema>;
|
||||
export type MacroMethod = z.infer<typeof MacroMethodSchema>;
|
||||
|
||||
@@ -552,6 +550,8 @@ const EnvironmentConfigSchema = z.object({
|
||||
*/
|
||||
disableMemoizationForDebugging: z.boolean().default(false),
|
||||
|
||||
enableMinimalTransformsForRetry: z.boolean().default(false),
|
||||
|
||||
/**
|
||||
* When true, rather using memoized values, the compiler will always re-compute
|
||||
* values, and then use a heuristic to compare the memoized value to the newly
|
||||
@@ -626,6 +626,17 @@ const EnvironmentConfigSchema = z.object({
|
||||
|
||||
export type EnvironmentConfig = z.infer<typeof EnvironmentConfigSchema>;
|
||||
|
||||
export const MINIMAL_RETRY_CONFIG: PartialEnvironmentConfig = {
|
||||
validateHooksUsage: false,
|
||||
validateRefAccessDuringRender: false,
|
||||
validateNoSetStateInRender: false,
|
||||
validateNoSetStateInPassiveEffects: false,
|
||||
validateNoJSXInTryStatements: false,
|
||||
validateMemoizedEffectDependencies: false,
|
||||
validateNoCapitalizedCalls: null,
|
||||
validateBlocklistedImports: null,
|
||||
enableMinimalTransformsForRetry: true,
|
||||
};
|
||||
/**
|
||||
* For test fixtures and playground only.
|
||||
*
|
||||
@@ -840,7 +851,6 @@ export class Environment {
|
||||
code: string | null;
|
||||
config: EnvironmentConfig;
|
||||
fnType: ReactFunctionType;
|
||||
compilerMode: CompilerMode;
|
||||
useMemoCacheIdentifier: string;
|
||||
hasLoweredContextAccess: boolean;
|
||||
hasFireRewrite: boolean;
|
||||
@@ -851,7 +861,6 @@ export class Environment {
|
||||
constructor(
|
||||
scope: BabelScope,
|
||||
fnType: ReactFunctionType,
|
||||
compilerMode: CompilerMode,
|
||||
config: EnvironmentConfig,
|
||||
contextIdentifiers: Set<t.Identifier>,
|
||||
logger: Logger | null,
|
||||
@@ -861,7 +870,6 @@ export class Environment {
|
||||
) {
|
||||
this.#scope = scope;
|
||||
this.fnType = fnType;
|
||||
this.compilerMode = compilerMode;
|
||||
this.config = config;
|
||||
this.filename = filename;
|
||||
this.code = code;
|
||||
@@ -916,10 +924,6 @@ export class Environment {
|
||||
this.#hoistedIdentifiers = new Set();
|
||||
}
|
||||
|
||||
get isInferredMemoEnabled(): boolean {
|
||||
return this.compilerMode !== 'no_inferred_memo';
|
||||
}
|
||||
|
||||
get nextIdentifierId(): IdentifierId {
|
||||
return makeIdentifierId(this.#nextIdentifer++);
|
||||
}
|
||||
@@ -1121,7 +1125,6 @@ export class Environment {
|
||||
moduleName.toLowerCase() === 'react-dom'
|
||||
);
|
||||
}
|
||||
static knownReactModules: ReadonlyArray<string> = ['react', 'react-dom'];
|
||||
|
||||
getFallthroughPropertyType(
|
||||
receiver: Type,
|
||||
|
||||
@@ -703,10 +703,6 @@ export type ObjectPropertyKey =
|
||||
| {
|
||||
kind: 'computed';
|
||||
name: Place;
|
||||
}
|
||||
| {
|
||||
kind: 'number';
|
||||
name: number;
|
||||
};
|
||||
|
||||
export type ObjectProperty = {
|
||||
|
||||
@@ -330,9 +330,6 @@ function printObjectPropertyKey(key: ObjectPropertyKey): string {
|
||||
case 'computed': {
|
||||
return `[${printPlace(key.name)}]`;
|
||||
}
|
||||
case 'number': {
|
||||
return String(key.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
CompilerError,
|
||||
CompilerErrorDetailOptions,
|
||||
ErrorSeverity,
|
||||
ValueKind,
|
||||
} from '..';
|
||||
import {CompilerError, ErrorSeverity, ValueKind} from '..';
|
||||
import {
|
||||
AbstractValue,
|
||||
BasicBlock,
|
||||
@@ -295,21 +290,21 @@ export function inferTerminalFunctionEffects(
|
||||
return functionEffects;
|
||||
}
|
||||
|
||||
export function transformFunctionEffectErrors(
|
||||
export function raiseFunctionEffectErrors(
|
||||
functionEffects: Array<FunctionEffect>,
|
||||
): Array<CompilerErrorDetailOptions> {
|
||||
return functionEffects.map(eff => {
|
||||
): void {
|
||||
functionEffects.forEach(eff => {
|
||||
switch (eff.kind) {
|
||||
case 'ReactMutation':
|
||||
case 'GlobalMutation': {
|
||||
return eff.error;
|
||||
CompilerError.throw(eff.error);
|
||||
}
|
||||
case 'ContextMutation': {
|
||||
return {
|
||||
CompilerError.throw({
|
||||
severity: ErrorSeverity.Invariant,
|
||||
reason: `Unexpected ContextMutation in top-level function effects`,
|
||||
loc: eff.loc,
|
||||
};
|
||||
});
|
||||
}
|
||||
default:
|
||||
assertExhaustive(
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {CompilerError, CompilerErrorDetailOptions} from '../CompilerError';
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {Environment} from '../HIR';
|
||||
import {
|
||||
AbstractValue,
|
||||
@@ -49,7 +49,7 @@ import {assertExhaustive} from '../Utils/utils';
|
||||
import {
|
||||
inferTerminalFunctionEffects,
|
||||
inferInstructionFunctionEffects,
|
||||
transformFunctionEffectErrors,
|
||||
raiseFunctionEffectErrors,
|
||||
} from './InferFunctionEffects';
|
||||
|
||||
const UndefinedValue: InstructionValue = {
|
||||
@@ -103,7 +103,7 @@ const UndefinedValue: InstructionValue = {
|
||||
export default function inferReferenceEffects(
|
||||
fn: HIRFunction,
|
||||
options: {isFunctionExpression: boolean} = {isFunctionExpression: false},
|
||||
): Array<CompilerErrorDetailOptions> {
|
||||
): void {
|
||||
/*
|
||||
* Initial state contains function params
|
||||
* TODO: include module declarations here as well
|
||||
@@ -241,9 +241,8 @@ export default function inferReferenceEffects(
|
||||
|
||||
if (options.isFunctionExpression) {
|
||||
fn.effects = functionEffects;
|
||||
return [];
|
||||
} else {
|
||||
return transformFunctionEffectErrors(functionEffects);
|
||||
} else if (!fn.env.config.enableMinimalTransformsForRetry) {
|
||||
raiseFunctionEffectErrors(functionEffects);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2429,9 +2429,6 @@ function codegenObjectPropertyKey(
|
||||
});
|
||||
return expr;
|
||||
}
|
||||
case 'number': {
|
||||
return t.numericLiteral(key.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @flow @gating
|
||||
import {Stringify} from 'shared-runtime';
|
||||
import * as React from 'react';
|
||||
|
||||
component Foo(ref: React.RefSetter<Controls>) {
|
||||
return <Stringify ref={ref} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: eval('(...args) => React.createElement(Foo, args)'),
|
||||
params: [{ref: React.createRef()}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
import * as React from "react";
|
||||
|
||||
const Foo = React.forwardRef(Foo_withRef);
|
||||
const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures();
|
||||
function _Foo_withRef_optimized(_$$empty_props_placeholder$$, ref) {
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== ref) {
|
||||
t0 = <Stringify ref={ref} />;
|
||||
$[0] = ref;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _Foo_withRef_unoptimized(
|
||||
_$$empty_props_placeholder$$: $ReadOnly<{}>,
|
||||
ref: React.RefSetter<Controls>,
|
||||
): React.Node {
|
||||
return <Stringify ref={ref} />;
|
||||
}
|
||||
function Foo_withRef(arg0, arg1) {
|
||||
if (_isForgetEnabled_Fixtures_result)
|
||||
return _Foo_withRef_optimized(arg0, arg1);
|
||||
else return _Foo_withRef_unoptimized(arg0, arg1);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: eval("(...args) => React.createElement(Foo, args)"),
|
||||
params: [{ ref: React.createRef() }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"ref":null}</div>
|
||||
@@ -1,12 +0,0 @@
|
||||
// @flow @gating
|
||||
import {Stringify} from 'shared-runtime';
|
||||
import * as React from 'react';
|
||||
|
||||
component Foo(ref: React.RefSetter<Controls>) {
|
||||
return <Stringify ref={ref} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: eval('(...args) => React.createElement(Foo, args)'),
|
||||
params: [{ref: React.createRef()}],
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @flow @gating
|
||||
component Foo(ref: React.RefSetter<Controls>) {
|
||||
return <Bar ref={ref} />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
1 | // @flow @gating
|
||||
> 2 | component Foo(ref: React.RefSetter<Controls>) {
|
||||
| ^^^ Invariant: Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting. Rewrite the reference to Foo_withRef to not rely on hoisting to fix this issue (2:2)
|
||||
3 | return <Bar ref={ref} />;
|
||||
4 | }
|
||||
5 |
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
// @flow @gating
|
||||
component Foo(ref: React.RefSetter<Controls>) {
|
||||
return <Bar ref={ref} />;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @gating
|
||||
const Foo = React.forwardRef(Foo_withRef);
|
||||
function Foo_withRef(props, ref) {
|
||||
return <Bar ref={ref} {...props}></Bar>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
1 | // @gating
|
||||
2 | const Foo = React.forwardRef(Foo_withRef);
|
||||
> 3 | function Foo_withRef(props, ref) {
|
||||
| ^^^^^^^^^^^ Invariant: Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting. Rewrite the reference to Foo_withRef to not rely on hoisting to fix this issue (3:3)
|
||||
4 | return <Bar ref={ref} {...props}></Bar>;
|
||||
5 | }
|
||||
6 |
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// @gating
|
||||
const Foo = React.forwardRef(Foo_withRef);
|
||||
function Foo_withRef(props, ref) {
|
||||
return <Bar ref={ref} {...props}></Bar>;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @gating
|
||||
import {memo} from 'react';
|
||||
|
||||
export default memo(Foo);
|
||||
function Foo() {}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
3 |
|
||||
4 | export default memo(Foo);
|
||||
> 5 | function Foo() {}
|
||||
| ^^^ Invariant: Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting. Rewrite the reference to Foo to not rely on hoisting to fix this issue (5:5)
|
||||
6 |
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// @gating
|
||||
import {memo} from 'react';
|
||||
|
||||
export default memo(Foo);
|
||||
function Foo() {}
|
||||
@@ -1,60 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @gating
|
||||
import {identity, useHook as useRenamed} from 'shared-runtime';
|
||||
const _ = {
|
||||
useHook: () => {},
|
||||
};
|
||||
identity(_.useHook);
|
||||
|
||||
function useHook() {
|
||||
useRenamed();
|
||||
return <div>hello world!</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useHook,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
|
||||
import { c as _c } from "react/compiler-runtime"; // @gating
|
||||
import { identity, useHook as useRenamed } from "shared-runtime";
|
||||
const _ = {
|
||||
useHook: isForgetEnabled_Fixtures() ? () => {} : () => {},
|
||||
};
|
||||
identity(_.useHook);
|
||||
const useHook = isForgetEnabled_Fixtures()
|
||||
? function useHook() {
|
||||
const $ = _c(1);
|
||||
useRenamed();
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <div>hello world!</div>;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
: function useHook() {
|
||||
useRenamed();
|
||||
return <div>hello world!</div>;
|
||||
};
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useHook,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>hello world!</div>
|
||||
@@ -1,16 +0,0 @@
|
||||
// @gating
|
||||
import {identity, useHook as useRenamed} from 'shared-runtime';
|
||||
const _ = {
|
||||
useHook: () => {},
|
||||
};
|
||||
identity(_.useHook);
|
||||
|
||||
function useHook() {
|
||||
useRenamed();
|
||||
return <div>hello world!</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useHook,
|
||||
params: [{}],
|
||||
};
|
||||
@@ -1,61 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @gating
|
||||
import {createRef, forwardRef} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
const Foo = forwardRef(Foo_withRef);
|
||||
function Foo_withRef(props, ref) {
|
||||
return <Stringify ref={ref} {...props} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: eval('(...args) => React.createElement(Foo, args)'),
|
||||
params: [{prop1: 1, prop2: 2, ref: createRef()}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
|
||||
import { c as _c } from "react/compiler-runtime"; // @gating
|
||||
import { createRef, forwardRef } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
const Foo = forwardRef(Foo_withRef);
|
||||
const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures();
|
||||
function _Foo_withRef_optimized(props, ref) {
|
||||
const $ = _c(3);
|
||||
let t0;
|
||||
if ($[0] !== props || $[1] !== ref) {
|
||||
t0 = <Stringify ref={ref} {...props} />;
|
||||
$[0] = props;
|
||||
$[1] = ref;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _Foo_withRef_unoptimized(props, ref) {
|
||||
return <Stringify ref={ref} {...props} />;
|
||||
}
|
||||
function Foo_withRef(arg0, arg1) {
|
||||
if (_isForgetEnabled_Fixtures_result)
|
||||
return _Foo_withRef_optimized(arg0, arg1);
|
||||
else return _Foo_withRef_unoptimized(arg0, arg1);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: eval("(...args) => React.createElement(Foo, args)"),
|
||||
params: [{ prop1: 1, prop2: 2, ref: createRef() }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"0":{"prop1":1,"prop2":2,"ref":{"current":null}},"ref":"[[ cyclic ref *3 ]]"}</div>
|
||||
@@ -1,13 +0,0 @@
|
||||
// @gating
|
||||
import {createRef, forwardRef} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
const Foo = forwardRef(Foo_withRef);
|
||||
function Foo_withRef(props, ref) {
|
||||
return <Stringify ref={ref} {...props} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: eval('(...args) => React.createElement(Foo, args)'),
|
||||
params: [{prop1: 1, prop2: 2, ref: createRef()}],
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @gating
|
||||
import {memo} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
export default memo(Foo);
|
||||
function Foo({prop1, prop2}) {
|
||||
'use memo';
|
||||
return <Stringify prop1={prop1} prop2={prop2} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: eval('Foo'),
|
||||
params: [{prop1: 1, prop2: 2}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
|
||||
import { c as _c } from "react/compiler-runtime"; // @gating
|
||||
import { memo } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
export default memo(Foo);
|
||||
const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures();
|
||||
function _Foo_optimized(t0) {
|
||||
"use memo";
|
||||
const $ = _c(3);
|
||||
const { prop1, prop2 } = t0;
|
||||
let t1;
|
||||
if ($[0] !== prop1 || $[1] !== prop2) {
|
||||
t1 = <Stringify prop1={prop1} prop2={prop2} />;
|
||||
$[0] = prop1;
|
||||
$[1] = prop2;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
function _Foo_unoptimized({ prop1, prop2 }) {
|
||||
"use memo";
|
||||
return <Stringify prop1={prop1} prop2={prop2} />;
|
||||
}
|
||||
function Foo(arg0) {
|
||||
if (_isForgetEnabled_Fixtures_result) return _Foo_optimized(arg0);
|
||||
else return _Foo_unoptimized(arg0);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: eval("Foo"),
|
||||
params: [{ prop1: 1, prop2: 2 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"prop1":1,"prop2":2}</div>
|
||||
@@ -1,14 +0,0 @@
|
||||
// @gating
|
||||
import {memo} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
export default memo(Foo);
|
||||
function Foo({prop1, prop2}) {
|
||||
'use memo';
|
||||
return <Stringify prop1={prop1} prop2={prop2} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: eval('Foo'),
|
||||
params: [{prop1: 1, prop2: 2}],
|
||||
};
|
||||
@@ -1,59 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @gating
|
||||
import * as React from 'react';
|
||||
|
||||
let Foo;
|
||||
const MemoFoo = React.memo(Foo);
|
||||
Foo = () => <div>hello world!</div>;
|
||||
|
||||
/**
|
||||
* Evaluate this fixture module to assert that compiler + original have the same
|
||||
* runtime error message.
|
||||
*/
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: () => {},
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
|
||||
import { c as _c } from "react/compiler-runtime"; // @gating
|
||||
import * as React from "react";
|
||||
|
||||
let Foo;
|
||||
const MemoFoo = React.memo(Foo);
|
||||
Foo = isForgetEnabled_Fixtures()
|
||||
? () => {
|
||||
const $ = _c(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <div>hello world!</div>;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
: () => <div>hello world!</div>;
|
||||
|
||||
/**
|
||||
* Evaluate this fixture module to assert that compiler + original have the same
|
||||
* runtime error message.
|
||||
*/
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: isForgetEnabled_Fixtures() ? () => {} : () => {},
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok)
|
||||
logs: ['memo: The first argument must be a component. Instead received: %s','undefined']
|
||||
@@ -1,15 +0,0 @@
|
||||
// @gating
|
||||
import * as React from 'react';
|
||||
|
||||
let Foo;
|
||||
const MemoFoo = React.memo(Foo);
|
||||
Foo = () => <div>hello world!</div>;
|
||||
|
||||
/**
|
||||
* Evaluate this fixture module to assert that compiler + original have the same
|
||||
* runtime error message.
|
||||
*/
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: () => {},
|
||||
params: [],
|
||||
};
|
||||
@@ -1,86 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @gating
|
||||
import * as React from 'react';
|
||||
|
||||
/**
|
||||
* Test that the correct `Foo` is printed
|
||||
*/
|
||||
let Foo = () => <div>hello world 1!</div>;
|
||||
const MemoOne = React.memo(Foo);
|
||||
Foo = () => <div>hello world 2!</div>;
|
||||
const MemoTwo = React.memo(Foo);
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: () => {
|
||||
'use no memo';
|
||||
return (
|
||||
<>
|
||||
<MemoOne />
|
||||
<MemoTwo />
|
||||
</>
|
||||
);
|
||||
},
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
|
||||
import { c as _c } from "react/compiler-runtime"; // @gating
|
||||
import * as React from "react";
|
||||
|
||||
/**
|
||||
* Test that the correct `Foo` is printed
|
||||
*/
|
||||
let Foo = isForgetEnabled_Fixtures()
|
||||
? () => {
|
||||
const $ = _c(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <div>hello world 1!</div>;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
: () => <div>hello world 1!</div>;
|
||||
const MemoOne = React.memo(Foo);
|
||||
Foo = isForgetEnabled_Fixtures()
|
||||
? () => {
|
||||
const $ = _c(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <div>hello world 2!</div>;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
: () => <div>hello world 2!</div>;
|
||||
const MemoTwo = React.memo(Foo);
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: () => {
|
||||
"use no memo";
|
||||
return (
|
||||
<>
|
||||
<MemoOne />
|
||||
<MemoTwo />
|
||||
</>
|
||||
);
|
||||
},
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>hello world 1!</div><div>hello world 2!</div>
|
||||
@@ -1,23 +0,0 @@
|
||||
// @gating
|
||||
import * as React from 'react';
|
||||
|
||||
/**
|
||||
* Test that the correct `Foo` is printed
|
||||
*/
|
||||
let Foo = () => <div>hello world 1!</div>;
|
||||
const MemoOne = React.memo(Foo);
|
||||
Foo = () => <div>hello world 2!</div>;
|
||||
const MemoTwo = React.memo(Foo);
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: () => {
|
||||
'use no memo';
|
||||
return (
|
||||
<>
|
||||
<MemoOne />
|
||||
<MemoTwo />
|
||||
</>
|
||||
);
|
||||
},
|
||||
params: [],
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @compilationMode(infer) @panicThreshold(none)
|
||||
import useMyEffect from 'useEffectWrapper';
|
||||
|
||||
function nonReactFn(arg) {
|
||||
useMyEffect(() => [1, 2, arg]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
3 |
|
||||
4 | function nonReactFn(arg) {
|
||||
> 5 | useMyEffect(() => [1, 2, arg]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (5:5)
|
||||
6 | }
|
||||
7 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// @inferEffectDependencies @compilationMode(infer) @panicThreshold(none)
|
||||
import useMyEffect from 'useEffectWrapper';
|
||||
|
||||
function nonReactFn(arg) {
|
||||
useMyEffect(() => [1, 2, arg]);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @compilationMode(infer) @panicThreshold(none)
|
||||
import {useEffect} from 'react';
|
||||
|
||||
function nonReactFn(arg) {
|
||||
useEffect(() => [1, 2, arg]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
3 |
|
||||
4 | function nonReactFn(arg) {
|
||||
> 5 | useEffect(() => [1, 2, arg]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (5:5)
|
||||
6 | }
|
||||
7 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// @inferEffectDependencies @compilationMode(infer) @panicThreshold(none)
|
||||
import {useEffect} from 'react';
|
||||
|
||||
function nonReactFn(arg) {
|
||||
useEffect(() => [1, 2, arg]);
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold(none)
|
||||
import {useEffect} from 'react';
|
||||
|
||||
/**
|
||||
* Error on non-inlined effect functions:
|
||||
* 1. From the effect hook callee's perspective, it only makes sense
|
||||
* to either
|
||||
* (a) never hard error (i.e. failing to infer deps is acceptable) or
|
||||
* (b) always hard error,
|
||||
* regardless of whether the callback function is an inline fn.
|
||||
* 2. (Technical detail) it's harder to support detecting cases in which
|
||||
* function (pre-Forget transform) was inline but becomes memoized
|
||||
*/
|
||||
function Component({foo}) {
|
||||
function f() {
|
||||
console.log(foo);
|
||||
}
|
||||
|
||||
// No inferred dep array, the argument is not a lambda
|
||||
useEffect(f);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
18 |
|
||||
19 | // No inferred dep array, the argument is not a lambda
|
||||
> 20 | useEffect(f);
|
||||
| ^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (20:20)
|
||||
21 | }
|
||||
22 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// @inferEffectDependencies @panicThreshold(none)
|
||||
import {useEffect} from 'react';
|
||||
|
||||
/**
|
||||
* Error on non-inlined effect functions:
|
||||
* 1. From the effect hook callee's perspective, it only makes sense
|
||||
* to either
|
||||
* (a) never hard error (i.e. failing to infer deps is acceptable) or
|
||||
* (b) always hard error,
|
||||
* regardless of whether the callback function is an inline fn.
|
||||
* 2. (Technical detail) it's harder to support detecting cases in which
|
||||
* function (pre-Forget transform) was inline but becomes memoized
|
||||
*/
|
||||
function Component({foo}) {
|
||||
function f() {
|
||||
console.log(foo);
|
||||
}
|
||||
|
||||
// No inferred dep array, the argument is not a lambda
|
||||
useEffect(f);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold(none)
|
||||
import React from 'react';
|
||||
|
||||
function NonReactiveDepInEffect() {
|
||||
const obj = makeObject_Primitives();
|
||||
React.useEffect(() => print(obj));
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
4 | function NonReactiveDepInEffect() {
|
||||
5 | const obj = makeObject_Primitives();
|
||||
> 6 | React.useEffect(() => print(obj));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:6)
|
||||
7 | }
|
||||
8 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold(none)
|
||||
import {useSpecialEffect} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Note that a react compiler-based transform still has limitations on JS syntax.
|
||||
* We should surface these as actionable lint / build errors to devs.
|
||||
*/
|
||||
function Component({prop1}) {
|
||||
'use memo';
|
||||
useSpecialEffect(() => {
|
||||
try {
|
||||
console.log(prop1);
|
||||
} finally {
|
||||
console.log('exiting');
|
||||
}
|
||||
}, [prop1]);
|
||||
return <div>{prop1}</div>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
8 | function Component({prop1}) {
|
||||
9 | 'use memo';
|
||||
> 10 | useSpecialEffect(() => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 11 | try {
|
||||
| ^^^^^^^^^
|
||||
> 12 | console.log(prop1);
|
||||
| ^^^^^^^^^
|
||||
> 13 | } finally {
|
||||
| ^^^^^^^^^
|
||||
> 14 | console.log('exiting');
|
||||
| ^^^^^^^^^
|
||||
> 15 | }
|
||||
| ^^^^^^^^^
|
||||
> 16 | }, [prop1]);
|
||||
| ^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics.. (Bailout reason: Todo: (BuildHIR::lowerStatement) Handle TryStatement without a catch clause (11:15)) (10:16)
|
||||
17 | return <div>{prop1}</div>;
|
||||
18 | }
|
||||
19 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// @inferEffectDependencies @panicThreshold(none)
|
||||
import {useSpecialEffect} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Note that a react compiler-based transform still has limitations on JS syntax.
|
||||
* We should surface these as actionable lint / build errors to devs.
|
||||
*/
|
||||
function Component({prop1}) {
|
||||
'use memo';
|
||||
useSpecialEffect(() => {
|
||||
try {
|
||||
console.log(prop1);
|
||||
} finally {
|
||||
console.log('exiting');
|
||||
}
|
||||
}, [prop1]);
|
||||
return <div>{prop1}</div>;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold(none)
|
||||
import {useEffect} from 'react';
|
||||
|
||||
function Component({propVal}) {
|
||||
'use no memo';
|
||||
useEffect(() => [propVal]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
4 | function Component({propVal}) {
|
||||
5 | 'use no memo';
|
||||
> 6 | useEffect(() => [propVal]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:6)
|
||||
7 | }
|
||||
8 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// @inferEffectDependencies @panicThreshold(none)
|
||||
import {useEffect} from 'react';
|
||||
|
||||
function Component({propVal}) {
|
||||
'use no memo';
|
||||
useEffect(() => [propVal]);
|
||||
}
|
||||
@@ -34,6 +34,13 @@ function Component({foo, bar}) {
|
||||
console.log(bar.qux);
|
||||
});
|
||||
|
||||
function f() {
|
||||
console.log(foo);
|
||||
}
|
||||
|
||||
// No inferred dep array, the argument is not a lambda
|
||||
useEffect(f);
|
||||
|
||||
useEffectWrapper(() => {
|
||||
console.log(foo);
|
||||
});
|
||||
@@ -51,7 +58,7 @@ import useEffectWrapper from "useEffectWrapper";
|
||||
const moduleNonReactive = 0;
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(12);
|
||||
const $ = _c(14);
|
||||
const { foo, bar } = t0;
|
||||
|
||||
const ref = useRef(0);
|
||||
@@ -112,7 +119,7 @@ function Component(t0) {
|
||||
useEffect(t4, [bar.baz, bar.qux]);
|
||||
let t5;
|
||||
if ($[10] !== foo) {
|
||||
t5 = () => {
|
||||
t5 = function f() {
|
||||
console.log(foo);
|
||||
};
|
||||
$[10] = foo;
|
||||
@@ -120,7 +127,20 @@ function Component(t0) {
|
||||
} else {
|
||||
t5 = $[11];
|
||||
}
|
||||
useEffectWrapper(t5, [foo]);
|
||||
const f = t5;
|
||||
|
||||
useEffect(f);
|
||||
let t6;
|
||||
if ($[12] !== foo) {
|
||||
t6 = () => {
|
||||
console.log(foo);
|
||||
};
|
||||
$[12] = foo;
|
||||
$[13] = t6;
|
||||
} else {
|
||||
t6 = $[13];
|
||||
}
|
||||
useEffectWrapper(t6, [foo]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -30,6 +30,13 @@ function Component({foo, bar}) {
|
||||
console.log(bar.qux);
|
||||
});
|
||||
|
||||
function f() {
|
||||
console.log(foo);
|
||||
}
|
||||
|
||||
// No inferred dep array, the argument is not a lambda
|
||||
useEffect(f);
|
||||
|
||||
useEffectWrapper(() => {
|
||||
console.log(foo);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies
|
||||
import React from 'react';
|
||||
|
||||
function NonReactiveDepInEffect() {
|
||||
const obj = makeObject_Primitives();
|
||||
React.useEffect(() => print(obj));
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
|
||||
import React from "react";
|
||||
|
||||
function NonReactiveDepInEffect() {
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = makeObject_Primitives();
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const obj = t0;
|
||||
let t1;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = () => print(obj);
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
React.useEffect(t1);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -1,4 +1,4 @@
|
||||
// @inferEffectDependencies @panicThreshold(none)
|
||||
// @inferEffectDependencies
|
||||
import React from 'react';
|
||||
|
||||
function NonReactiveDepInEffect() {
|
||||
@@ -1,65 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Test() {
|
||||
const obj = {
|
||||
21: 'dimaMachina',
|
||||
};
|
||||
// Destructuring assignment
|
||||
const {21: myVar} = obj;
|
||||
return (
|
||||
<div>
|
||||
{obj[21]}
|
||||
{myVar}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Test,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Test() {
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = { 21: "dimaMachina" };
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const obj = t0;
|
||||
|
||||
const { 21: myVar } = obj;
|
||||
let t1;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = (
|
||||
<div>
|
||||
{obj[21]}
|
||||
{myVar}
|
||||
</div>
|
||||
);
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Test,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>dimaMachinadimaMachina</div>
|
||||
@@ -1,18 +0,0 @@
|
||||
function Test() {
|
||||
const obj = {
|
||||
21: 'dimaMachina',
|
||||
};
|
||||
// Destructuring assignment
|
||||
const {21: myVar} = obj;
|
||||
return (
|
||||
<div>
|
||||
{obj[21]}
|
||||
{myVar}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Test,
|
||||
params: [{}],
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoCapitalizedCalls @enableFire @panicThreshold(none)
|
||||
// @validateNoCapitalizedCalls @enableFire
|
||||
import {fire} from 'react';
|
||||
const CapitalizedCall = require('shared-runtime').sum;
|
||||
|
||||
@@ -24,7 +24,7 @@ function Component({prop1, bar}) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useFire } from "react/compiler-runtime"; // @validateNoCapitalizedCalls @enableFire @panicThreshold(none)
|
||||
import { useFire } from "react/compiler-runtime"; // @validateNoCapitalizedCalls @enableFire
|
||||
import { fire } from "react";
|
||||
const CapitalizedCall = require("shared-runtime").sum;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validateNoCapitalizedCalls @enableFire @panicThreshold(none)
|
||||
// @validateNoCapitalizedCalls @enableFire
|
||||
import {fire} from 'react';
|
||||
const CapitalizedCall = require('shared-runtime').sum;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableFire @panicThreshold(none)
|
||||
// @enableFire
|
||||
import {useRef} from 'react';
|
||||
|
||||
function Component({props, bar}) {
|
||||
@@ -26,7 +26,7 @@ function Component({props, bar}) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useFire } from "react/compiler-runtime"; // @enableFire @panicThreshold(none)
|
||||
import { useFire } from "react/compiler-runtime"; // @enableFire
|
||||
import { useRef } from "react";
|
||||
|
||||
function Component(t0) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @enableFire @panicThreshold(none)
|
||||
// @enableFire
|
||||
import {useRef} from 'react';
|
||||
|
||||
function Component({props, bar}) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees @enableFire @panicThreshold(none)
|
||||
// @validatePreserveExistingMemoizationGuarantees @enableFire
|
||||
import {fire} from 'react';
|
||||
import {sum} from 'shared-runtime';
|
||||
|
||||
@@ -24,7 +24,7 @@ function Component({prop1, bar}) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useFire } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees @enableFire @panicThreshold(none)
|
||||
import { useFire } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees @enableFire
|
||||
import { fire } from "react";
|
||||
import { sum } from "shared-runtime";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @validatePreserveExistingMemoizationGuarantees @enableFire @panicThreshold(none)
|
||||
// @validatePreserveExistingMemoizationGuarantees @enableFire
|
||||
import {fire} from 'react';
|
||||
import {sum} from 'shared-runtime';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableFire @panicThreshold(none)
|
||||
// @enableFire
|
||||
import {fire} from 'react';
|
||||
|
||||
function Component({prop1}) {
|
||||
@@ -20,7 +20,7 @@ function Component({prop1}) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useFire } from "react/compiler-runtime"; // @enableFire @panicThreshold(none)
|
||||
import { useFire } from "react/compiler-runtime"; // @enableFire
|
||||
import { fire } from "react";
|
||||
|
||||
function Component(t0) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @enableFire @panicThreshold(none)
|
||||
// @enableFire
|
||||
import {fire} from 'react';
|
||||
|
||||
function Component({prop1}) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @flow @enableFire @panicThreshold(none)
|
||||
// @flow @enableFire
|
||||
import {fire} from 'react';
|
||||
import {print} from 'shared-runtime';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @flow @enableFire @panicThreshold(none)
|
||||
// @flow @enableFire
|
||||
import {fire} from 'react';
|
||||
import {print} from 'shared-runtime';
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold(none)
|
||||
import {useRef} from 'react';
|
||||
import {useSpecialEffect} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* The retry pipeline disables memoization features, which means we need to
|
||||
* provide an alternate implementation of effect dependencies which does not
|
||||
* rely on memoization.
|
||||
*/
|
||||
function useFoo({cond}) {
|
||||
const ref = useRef();
|
||||
const derived = cond ? ref.current : makeObject();
|
||||
useSpecialEffect(() => {
|
||||
log(derived);
|
||||
}, [derived]);
|
||||
return ref;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
11 | const ref = useRef();
|
||||
12 | const derived = cond ? ref.current : makeObject();
|
||||
> 13 | useSpecialEffect(() => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 14 | log(derived);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 15 | }, [derived]);
|
||||
| ^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics.. (Bailout reason: Invariant: Expected function expression scope to exist (13:15)) (13:15)
|
||||
16 | return ref;
|
||||
17 | }
|
||||
18 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
// @inferEffectDependencies @panicThreshold(none)
|
||||
import {useRef} from 'react';
|
||||
import {useSpecialEffect} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* The retry pipeline disables memoization features, which means we need to
|
||||
* provide an alternate implementation of effect dependencies which does not
|
||||
* rely on memoization.
|
||||
*/
|
||||
function useFoo({cond}) {
|
||||
const ref = useRef();
|
||||
const derived = cond ? ref.current : makeObject();
|
||||
useSpecialEffect(() => {
|
||||
log(derived);
|
||||
}, [derived]);
|
||||
return ref;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableFire @panicThreshold(none)
|
||||
// @enableFire
|
||||
import {fire} from 'react';
|
||||
|
||||
/**
|
||||
@@ -29,13 +29,21 @@ function Component({prop1}) {
|
||||
## Error
|
||||
|
||||
```
|
||||
9 | function Component({prop1}) {
|
||||
10 | const foo = () => {
|
||||
> 11 | try {
|
||||
| ^^^^^
|
||||
> 12 | console.log(prop1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 13 | } finally {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 14 | console.log('jbrown215');
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 15 | }
|
||||
| ^^^^^^ Todo: (BuildHIR::lowerStatement) Handle TryStatement without a catch clause (11:15)
|
||||
16 | };
|
||||
17 | useEffect(() => {
|
||||
> 18 | fire(foo());
|
||||
| ^^^^ InvalidReact: [Fire] Untransformed reference to compiler-required feature. Either remove this `fire` call or ensure it is successfully transformed by the compiler. (Bailout reason: Todo: (BuildHIR::lowerStatement) Handle TryStatement without a catch clause (11:15)) (18:18)
|
||||
19 | });
|
||||
20 | }
|
||||
21 |
|
||||
18 | fire(foo());
|
||||
```
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @enableFire @panicThreshold(none)
|
||||
// @enableFire
|
||||
import {fire} from 'react';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableFire @panicThreshold(none)
|
||||
import {fire} from 'react';
|
||||
|
||||
console.log(fire == null);
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
2 | import {fire} from 'react';
|
||||
3 |
|
||||
> 4 | console.log(fire == null);
|
||||
| ^^^^ InvalidReact: [Fire] Untransformed reference to compiler-required feature. Either remove this `fire` call or ensure it is successfully transformed by the compiler (4:4)
|
||||
5 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
// @enableFire @panicThreshold(none)
|
||||
import {fire} from 'react';
|
||||
|
||||
console.log(fire == null);
|
||||
@@ -1,42 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableFire @panicThreshold(none)
|
||||
import {fire} from 'react';
|
||||
|
||||
/**
|
||||
* TODO: we should eventually distinguish between `use no memo` and `use no
|
||||
* compiler` directives. The former should be used to *only* disable memoization
|
||||
* features.
|
||||
*/
|
||||
function Component({props, bar}) {
|
||||
'use no memo';
|
||||
const foo = () => {
|
||||
console.log(props);
|
||||
};
|
||||
useEffect(() => {
|
||||
fire(foo(props));
|
||||
fire(foo());
|
||||
fire(bar());
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
13 | };
|
||||
14 | useEffect(() => {
|
||||
> 15 | fire(foo(props));
|
||||
| ^^^^ InvalidReact: [Fire] Untransformed reference to compiler-required feature. Either remove this `fire` call or ensure it is successfully transformed by the compiler (15:15)
|
||||
16 | fire(foo());
|
||||
17 | fire(bar());
|
||||
18 | });
|
||||
```
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableFire @panicThreshold(none)
|
||||
import {fire} from 'react';
|
||||
|
||||
/**
|
||||
* Compilation of this file should succeed.
|
||||
*/
|
||||
function NonFireComponent({prop1}) {
|
||||
/**
|
||||
* This component bails out but does not use fire
|
||||
*/
|
||||
const foo = () => {
|
||||
try {
|
||||
console.log(prop1);
|
||||
} finally {
|
||||
console.log('jbrown215');
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
foo();
|
||||
});
|
||||
}
|
||||
|
||||
function FireComponent(props) {
|
||||
/**
|
||||
* This component uses fire and compiles successfully
|
||||
*/
|
||||
const foo = props => {
|
||||
console.log(props);
|
||||
};
|
||||
useEffect(() => {
|
||||
fire(foo(props));
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useFire } from "react/compiler-runtime";
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableFire @panicThreshold(none)
|
||||
import { fire } from "react";
|
||||
|
||||
/**
|
||||
* Compilation of this file should succeed.
|
||||
*/
|
||||
function NonFireComponent({ prop1 }) {
|
||||
/**
|
||||
* This component bails out but does not use fire
|
||||
*/
|
||||
const foo = () => {
|
||||
try {
|
||||
console.log(prop1);
|
||||
} finally {
|
||||
console.log("jbrown215");
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
foo();
|
||||
});
|
||||
}
|
||||
|
||||
function FireComponent(props) {
|
||||
const $ = _c(3);
|
||||
|
||||
const foo = _temp;
|
||||
const t0 = useFire(foo);
|
||||
let t1;
|
||||
if ($[0] !== props || $[1] !== t0) {
|
||||
t1 = () => {
|
||||
t0(props);
|
||||
};
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
useEffect(t1);
|
||||
return null;
|
||||
}
|
||||
function _temp(props_0) {
|
||||
console.log(props_0);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -1,35 +0,0 @@
|
||||
// @enableFire @panicThreshold(none)
|
||||
import {fire} from 'react';
|
||||
|
||||
/**
|
||||
* Compilation of this file should succeed.
|
||||
*/
|
||||
function NonFireComponent({prop1}) {
|
||||
/**
|
||||
* This component bails out but does not use fire
|
||||
*/
|
||||
const foo = () => {
|
||||
try {
|
||||
console.log(prop1);
|
||||
} finally {
|
||||
console.log('jbrown215');
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
foo();
|
||||
});
|
||||
}
|
||||
|
||||
function FireComponent(props) {
|
||||
/**
|
||||
* This component uses fire and compiles successfully
|
||||
*/
|
||||
const foo = props => {
|
||||
console.log(props);
|
||||
};
|
||||
useEffect(() => {
|
||||
fire(foo(props));
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableFire
|
||||
import {fire} from 'react';
|
||||
|
||||
function Component({props, bar}) {
|
||||
'use no memo';
|
||||
const foo = () => {
|
||||
console.log(props);
|
||||
};
|
||||
useEffect(() => {
|
||||
fire(foo(props));
|
||||
fire(foo());
|
||||
fire(bar());
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @enableFire
|
||||
import { fire } from "react";
|
||||
|
||||
function Component({ props, bar }) {
|
||||
"use no memo";
|
||||
const foo = () => {
|
||||
console.log(props);
|
||||
};
|
||||
useEffect(() => {
|
||||
fire(foo(props));
|
||||
fire(foo());
|
||||
fire(bar());
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -1,11 +1,6 @@
|
||||
// @enableFire @panicThreshold(none)
|
||||
// @enableFire
|
||||
import {fire} from 'react';
|
||||
|
||||
/**
|
||||
* TODO: we should eventually distinguish between `use no memo` and `use no
|
||||
* compiler` directives. The former should be used to *only* disable memoization
|
||||
* features.
|
||||
*/
|
||||
function Component({props, bar}) {
|
||||
'use no memo';
|
||||
const foo = () => {
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableFire @panicThreshold(none)
|
||||
// @enableFire
|
||||
import {fire, useEffect} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
@@ -29,7 +29,7 @@ function Component(props) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useFire } from "react/compiler-runtime"; // @enableFire @panicThreshold(none)
|
||||
import { useFire } from "react/compiler-runtime"; // @enableFire
|
||||
import { fire, useEffect } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @enableFire @panicThreshold(none)
|
||||
// @enableFire
|
||||
import {fire, useEffect} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
|
||||
@@ -274,38 +274,6 @@ const tests: CompilerTestCases = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Pipeline errors are reported',
|
||||
code: normalizeIndent`
|
||||
import useMyEffect from 'useMyEffect';
|
||||
function Component({a}) {
|
||||
'use no memo';
|
||||
useMyEffect(() => console.log(a.b));
|
||||
return <div>Hello world</div>;
|
||||
}
|
||||
`,
|
||||
options: [
|
||||
{
|
||||
environment: {
|
||||
inferEffectDependencies: [
|
||||
{
|
||||
function: {
|
||||
source: 'useMyEffect',
|
||||
importSpecifierName: 'default',
|
||||
},
|
||||
numRequiredArgs: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
errors: [
|
||||
{
|
||||
message:
|
||||
'[InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -16,7 +16,3 @@ class Fixture extends React.Component {
|
||||
Fixture.propTypes = propTypes;
|
||||
|
||||
export default Fixture;
|
||||
|
||||
Fixture.Controls = function FixtureControls({children}) {
|
||||
return <div className="test-fixture__controls">{children}</div>;
|
||||
};
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
import TestCase from '../../TestCase';
|
||||
import Fixture from '../../Fixture';
|
||||
|
||||
const React = window.React;
|
||||
const {Fragment, useEffect, useRef, useState} = React;
|
||||
|
||||
function WrapperComponent(props) {
|
||||
return props.children;
|
||||
}
|
||||
|
||||
function handler(e) {
|
||||
const text = e.currentTarget.innerText;
|
||||
alert('You clicked: ' + text);
|
||||
}
|
||||
|
||||
export default function EventListenerCase() {
|
||||
const fragmentRef = useRef(null);
|
||||
const [extraChildCount, setExtraChildCount] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
fragmentRef.current.addEventListener('click', handler);
|
||||
|
||||
const lastFragmentRefValue = fragmentRef.current;
|
||||
return () => {
|
||||
lastFragmentRefValue.removeEventListener('click', handler);
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<TestCase title="Event Registration">
|
||||
<TestCase.Steps>
|
||||
<li>Click one of the children, observe the alert</li>
|
||||
<li>Add a new child, click it, observe the alert</li>
|
||||
<li>Remove the event listeners, click a child, observe no alert</li>
|
||||
<li>Add the event listeners back, click a child, observe the alert</li>
|
||||
</TestCase.Steps>
|
||||
|
||||
<TestCase.ExpectedResult>
|
||||
<p>
|
||||
Fragment refs can manage event listeners on the first level of host
|
||||
children. This page loads with an effect that sets up click event
|
||||
hanndlers on each child card. Clicking on a card will show an alert
|
||||
with the card's text.
|
||||
</p>
|
||||
<p>
|
||||
New child nodes will also have event listeners applied. Removed nodes
|
||||
will have their listeners cleaned up.
|
||||
</p>
|
||||
</TestCase.ExpectedResult>
|
||||
|
||||
<Fixture>
|
||||
<Fixture.Controls>
|
||||
<div>Target count: {extraChildCount + 3}</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
setExtraChildCount(prev => prev + 1);
|
||||
}}>
|
||||
Add Child
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
fragmentRef.current.addEventListener('click', handler);
|
||||
}}>
|
||||
Add click event listeners
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
fragmentRef.current.removeEventListener('click', handler);
|
||||
}}>
|
||||
Remove click event listeners
|
||||
</button>
|
||||
</Fixture.Controls>
|
||||
<div className="card-container">
|
||||
<Fragment ref={fragmentRef}>
|
||||
<div className="card" id="child-a">
|
||||
Child A
|
||||
</div>
|
||||
<div className="card" id="child-b">
|
||||
Child B
|
||||
</div>
|
||||
<WrapperComponent>
|
||||
<div className="card" id="child-c">
|
||||
Child C
|
||||
</div>
|
||||
{Array.from({length: extraChildCount}).map((_, index) => (
|
||||
<div className="card" id={'extra-child-' + index} key={index}>
|
||||
Extra Child {index}
|
||||
</div>
|
||||
))}
|
||||
</WrapperComponent>
|
||||
</Fragment>
|
||||
</div>
|
||||
</Fixture>
|
||||
</TestCase>
|
||||
);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import TestCase from '../../TestCase';
|
||||
import Fixture from '../../Fixture';
|
||||
|
||||
const React = window.React;
|
||||
|
||||
const {Fragment, useEffect, useRef, useState} = React;
|
||||
|
||||
export default function FocusCase() {
|
||||
const fragmentRef = useRef(null);
|
||||
|
||||
return (
|
||||
<TestCase title="Focus Management">
|
||||
<TestCase.Steps>
|
||||
<li>Click to focus the first child</li>
|
||||
<li>Click to focus the last child</li>
|
||||
<li>Click to blur any focus within the fragment</li>
|
||||
</TestCase.Steps>
|
||||
|
||||
<TestCase.ExpectedResult>
|
||||
<p>
|
||||
The focus method will focus the first focusable child within the
|
||||
fragment, skipping any unfocusable children.
|
||||
</p>
|
||||
<p>
|
||||
The focusLast method is the reverse, focusing the last focusable
|
||||
child.
|
||||
</p>
|
||||
<p>
|
||||
Blur will call blur on the document, only if one of the children
|
||||
within the fragment is the active element.
|
||||
</p>
|
||||
</TestCase.ExpectedResult>
|
||||
|
||||
<Fixture>
|
||||
<Fixture.Controls>
|
||||
<button onClick={() => fragmentRef.current.focus()}>
|
||||
Focus first child
|
||||
</button>
|
||||
<button onClick={() => fragmentRef.current.focusLast()}>
|
||||
Focus last child
|
||||
</button>
|
||||
<button onClick={() => fragmentRef.current.blur()}>Blur</button>
|
||||
</Fixture.Controls>
|
||||
<div className="highlight-focused-children" style={{display: 'flex'}}>
|
||||
<Fragment ref={fragmentRef}>
|
||||
<div style={{outline: '1px solid black'}}>Unfocusable div</div>
|
||||
<button>Button 1</button>
|
||||
<button>Button 2</button>
|
||||
<input type="text" placeholder="Input field" />
|
||||
<div style={{outline: '1px solid black'}}>Unfocusable div</div>
|
||||
</Fragment>
|
||||
</div>
|
||||
</Fixture>
|
||||
</TestCase>
|
||||
);
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
import TestCase from '../../TestCase';
|
||||
import Fixture from '../../Fixture';
|
||||
|
||||
const React = window.React;
|
||||
const {Fragment, useEffect, useRef, useState} = React;
|
||||
|
||||
export default function GetClientRectsCase() {
|
||||
const fragmentRef = useRef(null);
|
||||
const [rects, setRects] = useState([]);
|
||||
const getRects = () => {
|
||||
const rects = fragmentRef.current.getClientRects();
|
||||
setRects(rects);
|
||||
};
|
||||
|
||||
return (
|
||||
<TestCase title="getClientRects">
|
||||
<TestCase.Steps>
|
||||
<li>
|
||||
Click the "Print Rects" button to get the client rects of the
|
||||
elements.
|
||||
</li>
|
||||
</TestCase.Steps>
|
||||
<TestCase.ExpectedResult>
|
||||
Calling getClientRects on the fragment instance will return a list of a
|
||||
DOMRectList for each child node.
|
||||
</TestCase.ExpectedResult>
|
||||
<Fixture>
|
||||
<Fixture.Controls>
|
||||
<button onClick={getRects}>Print Rects</button>
|
||||
<div style={{display: 'flex'}}>
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
width: '30vw',
|
||||
height: '30vh',
|
||||
border: '1px solid black',
|
||||
}}>
|
||||
{rects.map(({x, y, width, height}, index) => {
|
||||
const scale = 0.3;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: y * scale,
|
||||
left: x * scale,
|
||||
width: width * scale,
|
||||
height: height * scale,
|
||||
border: '1px solid red',
|
||||
boxSizing: 'border-box',
|
||||
}}></div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
{rects.map(({x, y, width, height}, index) => {
|
||||
return (
|
||||
<div>
|
||||
{index} :: {`{`}x: {x}, y: {y}, width: {width}, height:{' '}
|
||||
{height}
|
||||
{`}`}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</Fixture.Controls>
|
||||
<Fragment ref={fragmentRef}>
|
||||
<span
|
||||
style={{
|
||||
width: '300px',
|
||||
height: '250px',
|
||||
backgroundColor: 'lightblue',
|
||||
fontSize: 20,
|
||||
border: '1px solid black',
|
||||
marginBottom: '10px',
|
||||
}}>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</span>
|
||||
<div
|
||||
style={{
|
||||
width: '150px',
|
||||
height: '100px',
|
||||
backgroundColor: 'lightgreen',
|
||||
border: '1px solid black',
|
||||
}}></div>
|
||||
<div
|
||||
style={{
|
||||
width: '500px',
|
||||
height: '50px',
|
||||
backgroundColor: 'lightpink',
|
||||
border: '1px solid black',
|
||||
}}></div>
|
||||
</Fragment>
|
||||
</Fixture>
|
||||
</TestCase>
|
||||
);
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
import TestCase from '../../TestCase';
|
||||
import Fixture from '../../Fixture';
|
||||
|
||||
const React = window.React;
|
||||
const {Fragment, useEffect, useRef, useState} = React;
|
||||
|
||||
function WrapperComponent(props) {
|
||||
return props.children;
|
||||
}
|
||||
|
||||
function ObservedChild({id}) {
|
||||
return (
|
||||
<div id={id} className="observable-card">
|
||||
{id}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const initialItems = [
|
||||
['A', false],
|
||||
['B', false],
|
||||
['C', false],
|
||||
];
|
||||
|
||||
export default function IntersectionObserverCase() {
|
||||
const fragmentRef = useRef(null);
|
||||
const [items, setItems] = useState(initialItems);
|
||||
const addedItems = items.slice(3);
|
||||
const anyOnScreen = items.some(([, onScreen]) => onScreen);
|
||||
const observerRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (observerRef.current === null) {
|
||||
observerRef.current = new IntersectionObserver(
|
||||
entries => {
|
||||
setItems(prev => {
|
||||
const newItems = [...prev];
|
||||
entries.forEach(entry => {
|
||||
const index = newItems.findIndex(
|
||||
([id]) => id === entry.target.id
|
||||
);
|
||||
newItems[index] = [entry.target.id, entry.isIntersecting];
|
||||
});
|
||||
return newItems;
|
||||
});
|
||||
},
|
||||
{
|
||||
threshold: [0.5],
|
||||
}
|
||||
);
|
||||
}
|
||||
fragmentRef.current.observeUsing(observerRef.current);
|
||||
|
||||
const lastFragmentRefValue = fragmentRef.current;
|
||||
return () => {
|
||||
lastFragmentRefValue.unobserveUsing(observerRef.current);
|
||||
observerRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<TestCase title="Intersection Observer">
|
||||
<TestCase.Steps>
|
||||
<li>
|
||||
Scroll the children into view, observe the sidebar appears and shows
|
||||
which children are in the viewport
|
||||
</li>
|
||||
<li>
|
||||
Add a new child and observe that the Intersection Observer is applied
|
||||
</li>
|
||||
<li>
|
||||
Click Unobserve and observe that the state of children in the viewport
|
||||
is no longer updated
|
||||
</li>
|
||||
<li>
|
||||
Click Observe and observe that the state of children in the viewport
|
||||
is updated again
|
||||
</li>
|
||||
</TestCase.Steps>
|
||||
|
||||
<TestCase.ExpectedResult>
|
||||
<p>
|
||||
Fragment refs manage Intersection Observers on the first level of host
|
||||
children. This page loads with an effect that sets up an Inersection
|
||||
Observer applied to each child card.
|
||||
</p>
|
||||
<p>
|
||||
New child nodes will also have the observer applied. Removed nodes
|
||||
will be unobserved.
|
||||
</p>
|
||||
</TestCase.ExpectedResult>
|
||||
<Fixture>
|
||||
<Fixture.Controls>
|
||||
<button
|
||||
onClick={() => {
|
||||
setItems(prev => [
|
||||
...prev,
|
||||
[`Extra child: ${prev.length + 1}`, false],
|
||||
]);
|
||||
}}>
|
||||
Add Child
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setItems(prev => {
|
||||
if (prev.length === 3) {
|
||||
return prev;
|
||||
}
|
||||
return prev.slice(0, prev.length - 1);
|
||||
});
|
||||
}}>
|
||||
Remove Child
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
fragmentRef.current.observeUsing(observerRef.current);
|
||||
}}>
|
||||
Observe
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
fragmentRef.current.unobserveUsing(observerRef.current);
|
||||
setItems(prev => {
|
||||
return prev.map(item => [item[0], false]);
|
||||
});
|
||||
}}>
|
||||
Unobserve
|
||||
</button>
|
||||
{anyOnScreen && (
|
||||
<div className="fixed-sidebar card-container">
|
||||
<p>
|
||||
<strong>Children on screen:</strong>
|
||||
</p>
|
||||
{items.map(item => (
|
||||
<div className={`card ${item[1] ? 'onscreen' : null}`}>
|
||||
{item[0]}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Fixture.Controls>
|
||||
<Fragment ref={fragmentRef}>
|
||||
<ObservedChild id="A" />
|
||||
<WrapperComponent>
|
||||
<ObservedChild id="B" />
|
||||
</WrapperComponent>
|
||||
<ObservedChild id="C" />
|
||||
{addedItems.map((_, index) => (
|
||||
<ObservedChild id={`Extra child: ${index + 4}`} />
|
||||
))}
|
||||
</Fragment>
|
||||
</Fixture>
|
||||
</TestCase>
|
||||
);
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import TestCase from '../../TestCase';
|
||||
import Fixture from '../../Fixture';
|
||||
|
||||
const React = window.React;
|
||||
const {Fragment, useEffect, useRef, useState} = React;
|
||||
|
||||
export default function ResizeObserverCase() {
|
||||
const fragmentRef = useRef(null);
|
||||
const [width, setWidth] = useState([0, 0, 0]);
|
||||
|
||||
useEffect(() => {
|
||||
const resizeObserver = new window.ResizeObserver(entries => {
|
||||
if (entries.length > 0) {
|
||||
setWidth(prev => {
|
||||
const newWidth = [...prev];
|
||||
entries.forEach(entry => {
|
||||
const index = parseInt(entry.target.id, 10);
|
||||
newWidth[index] = Math.round(entry.contentRect.width);
|
||||
});
|
||||
return newWidth;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
fragmentRef.current.observeUsing(resizeObserver);
|
||||
const lastFragmentRefValue = fragmentRef.current;
|
||||
return () => {
|
||||
lastFragmentRefValue.unobserveUsing(resizeObserver);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<TestCase title="Resize Observer">
|
||||
<TestCase.Steps>
|
||||
<li>Resize the viewport width until the children respond</li>
|
||||
<li>See that the width data updates as they elements resize</li>
|
||||
</TestCase.Steps>
|
||||
<TestCase.ExpectedResult>
|
||||
The Fragment Ref has a ResizeObserver attached which has a callback to
|
||||
update the width state of each child node.
|
||||
</TestCase.ExpectedResult>
|
||||
<Fixture>
|
||||
<Fragment ref={fragmentRef}>
|
||||
<div className="card" id="0" style={{width: '100%'}}>
|
||||
<p>
|
||||
Width: <b>{width[0]}px</b>
|
||||
</p>
|
||||
</div>
|
||||
<div className="card" id="1" style={{width: '80%'}}>
|
||||
<p>
|
||||
Width: <b>{width[1]}px</b>
|
||||
</p>
|
||||
</div>
|
||||
<div className="card" id="2" style={{width: '50%'}}>
|
||||
<p>
|
||||
Width: <b>{width[2]}px</b>
|
||||
</p>
|
||||
</div>
|
||||
</Fragment>
|
||||
</Fixture>
|
||||
</TestCase>
|
||||
);
|
||||
}
|
||||
@@ -1,20 +1,104 @@
|
||||
import Fixture from '../../Fixture';
|
||||
import FixtureSet from '../../FixtureSet';
|
||||
import EventListenerCase from './EventListenerCase';
|
||||
import IntersectionObserverCase from './IntersectionObserverCase';
|
||||
import ResizeObserverCase from './ResizeObserverCase';
|
||||
import FocusCase from './FocusCase';
|
||||
import GetClientRectsCase from './GetClientRectsCase';
|
||||
import TestCase from '../../TestCase';
|
||||
|
||||
const React = window.React;
|
||||
const {Fragment, useEffect, useRef, useState} = React;
|
||||
|
||||
function WrapperComponent(props) {
|
||||
return props.children;
|
||||
}
|
||||
|
||||
function handler(e) {
|
||||
const text = e.currentTarget.innerText;
|
||||
alert('You clicked: ' + text);
|
||||
}
|
||||
|
||||
export default function FragmentRefsPage() {
|
||||
const fragmentRef = useRef(null);
|
||||
const [extraChildCount, setExtraChildCount] = useState(0);
|
||||
|
||||
React.useEffect(() => {
|
||||
fragmentRef.current.addEventListener('click', handler);
|
||||
|
||||
const lastFragmentRefValue = fragmentRef.current;
|
||||
return () => {
|
||||
lastFragmentRefValue.removeEventListener('click', handler);
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<FixtureSet title="Fragment Refs">
|
||||
<EventListenerCase />
|
||||
<IntersectionObserverCase />
|
||||
<ResizeObserverCase />
|
||||
<FocusCase />
|
||||
<GetClientRectsCase />
|
||||
<TestCase title="Event registration">
|
||||
<TestCase.Steps>
|
||||
<li>Click one of the children, observe the alert</li>
|
||||
<li>Add a new child, click it, observe the alert</li>
|
||||
<li>Remove the event listeners, click a child, observe no alert</li>
|
||||
<li>
|
||||
Add the event listeners back, click a child, observe the alert
|
||||
</li>
|
||||
</TestCase.Steps>
|
||||
|
||||
<TestCase.ExpectedResult>
|
||||
<p>
|
||||
Fragment refs can manage event listeners on the first level of host
|
||||
children. This page loads with an effect that sets up click event
|
||||
hanndlers on each child card. Clicking on a card will show an alert
|
||||
with the card's text.
|
||||
</p>
|
||||
<p>
|
||||
New child nodes will also have event listeners applied. Removed
|
||||
nodes will have their listeners cleaned up.
|
||||
</p>
|
||||
</TestCase.ExpectedResult>
|
||||
|
||||
<Fixture>
|
||||
<div className="control-box" id="control-box">
|
||||
<div>Target count: {extraChildCount + 3}</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
setExtraChildCount(prev => prev + 1);
|
||||
}}>
|
||||
Add Child
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
fragmentRef.current.addEventListener('click', handler);
|
||||
}}>
|
||||
Add click event listeners
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
fragmentRef.current.removeEventListener('click', handler);
|
||||
}}>
|
||||
Remove click event listeners
|
||||
</button>
|
||||
<div class="card-container">
|
||||
<Fragment ref={fragmentRef}>
|
||||
<div className="card" id="child-a">
|
||||
Child A
|
||||
</div>
|
||||
<div className="card" id="child-b">
|
||||
Child B
|
||||
</div>
|
||||
<WrapperComponent>
|
||||
<div className="card" id="child-c">
|
||||
Child C
|
||||
</div>
|
||||
{Array.from({length: extraChildCount}).map((_, index) => (
|
||||
<div
|
||||
className="card"
|
||||
id={'extra-child-' + index}
|
||||
key={index}>
|
||||
Extra Child {index}
|
||||
</div>
|
||||
))}
|
||||
</WrapperComponent>
|
||||
</Fragment>
|
||||
</div>
|
||||
</div>
|
||||
</Fixture>
|
||||
</TestCase>
|
||||
</FixtureSet>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ p {
|
||||
}
|
||||
|
||||
.test-case__body {
|
||||
padding: 10px 10px 0 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.test-case__desc {
|
||||
@@ -280,17 +280,11 @@ p {
|
||||
|
||||
.test-fixture {
|
||||
padding: 20px;
|
||||
margin: 0 -10px; /* opposite of .test-case padding */
|
||||
margin: 0 -15px; /* opposite of .test-case padding */
|
||||
background-color: #f4f4f4;
|
||||
border-top: 1px solid #d9d9d9;
|
||||
}
|
||||
|
||||
.test-fixture__controls {
|
||||
margin: -20px -20px 20px -20px;
|
||||
padding: 20px;
|
||||
border: 1px solid #444;
|
||||
}
|
||||
|
||||
.field-group {
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -328,43 +322,3 @@ tbody tr:nth-child(even) {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.observable-card {
|
||||
height: 200px;
|
||||
border: 1px solid black;
|
||||
background: #e0e0e0;
|
||||
padding: 20px;
|
||||
font-size: 18px;
|
||||
overflow: auto;
|
||||
margin-bottom: 50px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.observable-card::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
border-top: 1px dotted red;
|
||||
}
|
||||
|
||||
.fixed-sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 200px;
|
||||
z-index: 1000;
|
||||
background-color: gray;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.onscreen {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.highlight-focused-children *:focus {
|
||||
outline: 2px solid green;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"prestart": "cp -r ../../build/oss-experimental/* ./node_modules/",
|
||||
"prebuild": "cp -r ../../build/oss-experimental/* ./node_modules/",
|
||||
"dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
|
||||
"dev:client": "BROWSER=none PORT=3001 react-scripts start",
|
||||
"dev:client": "PORT=3001 react-scripts start",
|
||||
"dev:server": "NODE_ENV=development node server",
|
||||
"start": "react-scripts build && NODE_ENV=production node server",
|
||||
"build": "react-scripts build",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"prestart": "cp -r ../../build/oss-experimental/* ./node_modules/",
|
||||
"prebuild": "cp -r ../../build/oss-experimental/* ./node_modules/",
|
||||
"dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
|
||||
"dev:client": "BROWSER=none PORT=3001 react-scripts start",
|
||||
"dev:client": "PORT=3001 react-scripts start",
|
||||
"dev:server": "NODE_ENV=development node server",
|
||||
"start": "react-scripts build && NODE_ENV=production node server",
|
||||
"build": "react-scripts build",
|
||||
|
||||
@@ -8,16 +8,7 @@
|
||||
}
|
||||
|
||||
.swipe-recognizer {
|
||||
width: 300px;
|
||||
background: #eee;
|
||||
width: 200px;
|
||||
border: 1px solid #333333;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.button {
|
||||
background: #000;
|
||||
color: #fff;
|
||||
border: 0px;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import React, {
|
||||
unstable_useSwipeTransition as useSwipeTransition,
|
||||
useEffect,
|
||||
useState,
|
||||
useId,
|
||||
} from 'react';
|
||||
|
||||
import SwipeRecognizer from './SwipeRecognizer';
|
||||
@@ -40,11 +39,6 @@ function Component() {
|
||||
);
|
||||
}
|
||||
|
||||
function Id() {
|
||||
// This is just testing that Id inside a ViewTransition can hydrate correctly.
|
||||
return <span id={useId()} />;
|
||||
}
|
||||
|
||||
export default function Page({url, navigate}) {
|
||||
const [renderedUrl, startGesture] = useSwipeTransition('/?a', url, '/?b');
|
||||
const show = renderedUrl === '/?b';
|
||||
@@ -74,80 +68,69 @@ export default function Page({url, navigate}) {
|
||||
</ViewTransition>
|
||||
);
|
||||
return (
|
||||
<div className="swipe-recognizer">
|
||||
<SwipeRecognizer
|
||||
action={swipeAction}
|
||||
gesture={startGesture}
|
||||
direction={show ? 'left' : 'right'}>
|
||||
<button
|
||||
className="button"
|
||||
onClick={() => {
|
||||
navigate(url === '/?b' ? '/?a' : '/?b');
|
||||
}}>
|
||||
{url === '/?b' ? 'Goto A' : 'Goto B'}
|
||||
</button>
|
||||
<ViewTransition className="none">
|
||||
<div>
|
||||
<ViewTransition>
|
||||
<div>
|
||||
<ViewTransition className={transitions['slide-on-nav']}>
|
||||
<h1>{!show ? 'A' : 'B' + counter}</h1>
|
||||
</ViewTransition>
|
||||
</div>
|
||||
</ViewTransition>
|
||||
<ViewTransition
|
||||
className={{
|
||||
'navigation-back': transitions['slide-right'],
|
||||
'navigation-forward': transitions['slide-left'],
|
||||
}}>
|
||||
<h1>{!show ? 'A' + counter : 'B'}</h1>
|
||||
</ViewTransition>
|
||||
{show ? (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{b}
|
||||
{a}
|
||||
</div>
|
||||
)}
|
||||
<ViewTransition>
|
||||
{show ? (
|
||||
<div>hello{exclamation}</div>
|
||||
) : (
|
||||
<section>Loading</section>
|
||||
)}
|
||||
</ViewTransition>
|
||||
<p>
|
||||
<Id />
|
||||
</p>
|
||||
{show ? null : (
|
||||
<ViewTransition>
|
||||
<div>world{exclamation}</div>
|
||||
</ViewTransition>
|
||||
)}
|
||||
<Activity mode={show ? 'visible' : 'hidden'}>
|
||||
<ViewTransition>
|
||||
<div>!!</div>
|
||||
</ViewTransition>
|
||||
</Activity>
|
||||
<p>these</p>
|
||||
<p>rows</p>
|
||||
<p>exist</p>
|
||||
<p>to</p>
|
||||
<p>test</p>
|
||||
<p>scrolling</p>
|
||||
<p>content</p>
|
||||
<p>out</p>
|
||||
<p>of</p>
|
||||
<p>the</p>
|
||||
<p>viewport</p>
|
||||
{show ? <Component /> : null}
|
||||
<div>
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate(show ? '/?a' : '/?b');
|
||||
}}>
|
||||
{show ? 'A' : 'B'}
|
||||
</button>
|
||||
<ViewTransition className="none">
|
||||
<div>
|
||||
<ViewTransition className={transitions['slide-on-nav']}>
|
||||
<h1>{!show ? 'A' : 'B'}</h1>
|
||||
</ViewTransition>
|
||||
<ViewTransition
|
||||
className={{
|
||||
'navigation-back': transitions['slide-right'],
|
||||
'navigation-forward': transitions['slide-left'],
|
||||
}}>
|
||||
<h1>{!show ? 'A' + counter : 'B' + counter}</h1>
|
||||
</ViewTransition>
|
||||
{show ? (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{b}
|
||||
{a}
|
||||
</div>
|
||||
)}
|
||||
<ViewTransition>
|
||||
{show ? <div>hello{exclamation}</div> : <section>Loading</section>}
|
||||
</ViewTransition>
|
||||
<p>scroll me</p>
|
||||
<p></p>
|
||||
<p></p>
|
||||
<p></p>
|
||||
<p></p>
|
||||
<p></p>
|
||||
<p></p>
|
||||
<div className="swipe-recognizer">
|
||||
<SwipeRecognizer
|
||||
action={swipeAction}
|
||||
gesture={startGesture}
|
||||
direction={show ? 'left' : 'right'}>
|
||||
Swipe me
|
||||
</SwipeRecognizer>
|
||||
</div>
|
||||
</ViewTransition>
|
||||
</SwipeRecognizer>
|
||||
<p></p>
|
||||
<p></p>
|
||||
{show ? null : (
|
||||
<ViewTransition>
|
||||
<div>world{exclamation}</div>
|
||||
</ViewTransition>
|
||||
)}
|
||||
<Activity mode={show ? 'visible' : 'hidden'}>
|
||||
<ViewTransition>
|
||||
<div>!!</div>
|
||||
</ViewTransition>
|
||||
</Activity>
|
||||
{show ? <Component /> : null}
|
||||
</div>
|
||||
</ViewTransition>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@babel/plugin-transform-block-scoped-functions": "^7.10.4",
|
||||
"@babel/plugin-transform-block-scoping": "^7.11.1",
|
||||
"@babel/plugin-transform-class-properties": "^7.25.9",
|
||||
"@babel/plugin-transform-classes": "^7.10.4",
|
||||
"@babel/plugin-transform-classes": "^7.25.9",
|
||||
"@babel/plugin-transform-computed-properties": "^7.10.4",
|
||||
"@babel/plugin-transform-destructuring": "^7.10.4",
|
||||
"@babel/plugin-transform-for-of": "^7.10.4",
|
||||
@@ -118,7 +118,7 @@
|
||||
"testRegex": "/scripts/jest/dont-run-jest-directly\\.js$"
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild": "./scripts/react-compiler/link-compiler.sh",
|
||||
"prebuild": "yarn --cwd compiler install --frozen-lockfile && ./scripts/react-compiler/link-compiler.sh",
|
||||
"build": "node ./scripts/rollup/build-all-release-channels.js",
|
||||
"build-for-devtools": "cross-env RELEASE_CHANNEL=experimental yarn build react/index,react/jsx,react/compiler-runtime,react-dom/index,react-dom/client,react-dom/unstable_testing,react-dom/test-utils,react-is,react-debug-tools,scheduler,react-test-renderer,react-refresh,react-art --type=NODE",
|
||||
"build-for-devtools-dev": "yarn build-for-devtools --type=NODE_DEV",
|
||||
|
||||
@@ -518,10 +518,6 @@ export function measureInstance(instance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function measureClonedInstance(instance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function wasInstanceInViewport(measurement): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user