Compare commits
8 Commits
fabric-cle
...
old-majors
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71aff3c23f | ||
|
|
4337c1c006 | ||
|
|
170a8b21b6 | ||
|
|
6948842cf8 | ||
|
|
056ac56b52 | ||
|
|
1fb5c123b8 | ||
|
|
790e2446ab | ||
|
|
037717685a |
@@ -1,8 +1,8 @@
|
||||
version: 2.1
|
||||
version: 2
|
||||
|
||||
aliases:
|
||||
- &docker
|
||||
- image: cimg/openjdk:17.0.0-node
|
||||
- image: circleci/openjdk:8-jdk-node-browsers
|
||||
|
||||
- &environment
|
||||
TZ: /usr/share/zoneinfo/America/Los_Angeles
|
||||
@@ -16,28 +16,42 @@ aliases:
|
||||
restore_cache:
|
||||
name: Restore node_modules cache
|
||||
keys:
|
||||
- v2-node-{{ arch }}-{{ .Branch }}-{{ checksum "yarn.lock" }}-{{ checksum "workspace_info.txt" }}-node-modules
|
||||
- v2-node-{{ arch }}-{{ .Branch }}-{{ checksum "yarn.lock" }}-node-modules
|
||||
|
||||
- &TEST_PARALLELISM 20
|
||||
|
||||
- &attach_workspace
|
||||
at: build
|
||||
|
||||
# The CircleCI API doesn't yet support triggering a specific workflow, but it
|
||||
# does support triggering a pipeline. So as a workaround you can triggger the
|
||||
# entire pipeline and use parameters to disable everything except the workflow
|
||||
# you want. CircleCI recommends this workaround here:
|
||||
# https://support.circleci.com/hc/en-us/articles/360050351292-How-to-trigger-a-workflow-via-CircleCI-API-v2-
|
||||
parameters:
|
||||
# This is only set when triggering the CI pipeline via an API request.
|
||||
prerelease_commit_sha:
|
||||
type: string
|
||||
default: ''
|
||||
- &process_artifacts
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace: *attach_workspace
|
||||
- *restore_node_modules
|
||||
- run: node ./scripts/rollup/consolidateBundleSizes.js
|
||||
- run: ./scripts/circleci/pack_and_store_artifact.sh
|
||||
- store_artifacts:
|
||||
path: ./node_modules.tgz
|
||||
- store_artifacts:
|
||||
path: ./build.tgz
|
||||
- store_artifacts:
|
||||
path: ./build/bundle-sizes.json
|
||||
- store_artifacts:
|
||||
# TODO: Update release script to use local file instead of pulling
|
||||
# from artifacts.
|
||||
path: ./scripts/error-codes/codes.json
|
||||
- persist_to_workspace:
|
||||
root: build
|
||||
paths:
|
||||
- bundle-sizes.json
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
@@ -47,7 +61,6 @@ jobs:
|
||||
- run:
|
||||
name: Install Packages
|
||||
command: yarn --frozen-lockfile --cache-folder ~/.cache/yarn
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- save_cache:
|
||||
# Store the yarn cache globally for all lock files with this same
|
||||
# checksum. This will speed up the setup job for all PRs where the
|
||||
@@ -62,7 +75,7 @@ jobs:
|
||||
# all jobs run on this branch with the same lockfile.
|
||||
name: Save node_modules cache
|
||||
# This cache key is per branch, a yarn install in setup is required.
|
||||
key: v2-node-{{ arch }}-{{ .Branch }}-{{ checksum "yarn.lock" }}-{{ checksum "workspace_info.txt" }}-node-modules
|
||||
key: v2-node-{{ arch }}-{{ .Branch }}-{{ checksum "yarn.lock" }}-node-modules
|
||||
paths:
|
||||
- node_modules
|
||||
|
||||
@@ -72,7 +85,6 @@ jobs:
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- *restore_node_modules
|
||||
- run: node ./scripts/prettier/index
|
||||
- run: node ./scripts/tasks/eslint
|
||||
@@ -83,112 +95,191 @@ jobs:
|
||||
yarn_flow:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: 5
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- *restore_node_modules
|
||||
- run: node ./scripts/tasks/flow-ci
|
||||
|
||||
scrape_warning_messages:
|
||||
yarn_test-stable:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- *restore_node_modules
|
||||
- run: yarn test-stable --ci
|
||||
|
||||
yarn_test:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
steps:
|
||||
- checkout
|
||||
- *restore_node_modules
|
||||
- run: yarn test --ci
|
||||
|
||||
yarn_test-classic:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
steps:
|
||||
- checkout
|
||||
- *restore_node_modules
|
||||
- run: yarn test-classic --ci
|
||||
|
||||
yarn_test-classic_variant:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
steps:
|
||||
- checkout
|
||||
- *restore_node_modules
|
||||
- run: yarn test-classic --variant --ci
|
||||
|
||||
yarn_test-classic_prod:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
steps:
|
||||
- checkout
|
||||
- *restore_node_modules
|
||||
- run: yarn test-classic --prod --ci
|
||||
|
||||
yarn_test-classic_prod_variant:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
steps:
|
||||
- checkout
|
||||
- *restore_node_modules
|
||||
- run: yarn test-classic --prod --variant --ci
|
||||
|
||||
yarn_test-www:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
steps:
|
||||
- checkout
|
||||
- *restore_node_modules
|
||||
- run: yarn test-www --ci
|
||||
|
||||
yarn_test-www_variant:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
steps:
|
||||
- checkout
|
||||
- *restore_node_modules
|
||||
- run: yarn test-www --variant --ci
|
||||
|
||||
yarn_test-www_prod:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
steps:
|
||||
- checkout
|
||||
- *restore_node_modules
|
||||
- run: yarn test-www --prod --ci
|
||||
|
||||
yarn_test-www_prod_variant:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
steps:
|
||||
- checkout
|
||||
- *restore_node_modules
|
||||
- run: yarn test-www --prod --variant --ci
|
||||
|
||||
yarn_test-stable_persistent:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- *restore_node_modules
|
||||
- run: yarn test-stable --persistent --ci
|
||||
|
||||
yarn_test-stable_prod:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- *restore_node_modules
|
||||
- run: yarn test-stable --prod --ci
|
||||
|
||||
yarn_test_prod:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
steps:
|
||||
- checkout
|
||||
- *restore_node_modules
|
||||
- run: yarn test --prod --ci
|
||||
|
||||
RELEASE_CHANNEL_stable_yarn_build:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
steps:
|
||||
- checkout
|
||||
- *restore_node_modules
|
||||
- run:
|
||||
environment:
|
||||
RELEASE_CHANNEL: stable
|
||||
command: |
|
||||
mkdir -p ./build
|
||||
node ./scripts/print-warnings/print-warnings.js > build/WARNINGS
|
||||
./scripts/circleci/add_build_info_json.sh
|
||||
./scripts/circleci/update_package_versions.sh
|
||||
yarn build
|
||||
- run: echo "stable" >> build/RELEASE_CHANNEL
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
root: build
|
||||
paths:
|
||||
- build
|
||||
- RELEASE_CHANNEL
|
||||
- facebook-www
|
||||
- facebook-react-native
|
||||
- node_modules
|
||||
- react-native
|
||||
- dist
|
||||
- sizes/*.json
|
||||
|
||||
yarn_build_combined:
|
||||
yarn_build:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: 40
|
||||
parallelism: 20
|
||||
steps:
|
||||
- checkout
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- *restore_node_modules
|
||||
- run: yarn build-combined
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- build
|
||||
|
||||
get_base_build:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- *restore_node_modules
|
||||
- run:
|
||||
name: Download artifacts for base revision
|
||||
environment:
|
||||
RELEASE_CHANNEL: experimental
|
||||
command: |
|
||||
git fetch origin main
|
||||
cd ./scripts/release && yarn && cd ../../
|
||||
scripts/release/download-experimental-build.js --commit=$(git merge-base HEAD origin/main)
|
||||
mv ./build ./base-build
|
||||
- run:
|
||||
# TODO: The `download-experimental-build` script copies the npm
|
||||
# packages into the `node_modules` directory. This is a historical
|
||||
# quirk of how the release script works. Let's pretend they
|
||||
# don't exist.
|
||||
name: Delete extraneous files
|
||||
command: rm -rf ./base-build/node_modules
|
||||
|
||||
./scripts/circleci/add_build_info_json.sh
|
||||
./scripts/circleci/update_package_versions.sh
|
||||
yarn build
|
||||
- run: echo "experimental" >> build/RELEASE_CHANNEL
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
root: build
|
||||
paths:
|
||||
- base-build
|
||||
|
||||
process_artifacts_combined:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- *restore_node_modules
|
||||
- run: echo "<< pipeline.git.revision >>" >> build/COMMIT_SHA
|
||||
# Compress build directory into a single tarball for easy download
|
||||
- run: tar -zcvf ./build.tgz ./build
|
||||
# TODO: Migrate scripts to use `build` directory instead of `build2`
|
||||
- run: cp ./build.tgz ./build2.tgz
|
||||
- store_artifacts:
|
||||
path: ./build2.tgz
|
||||
- store_artifacts:
|
||||
path: ./build.tgz
|
||||
|
||||
sizebot:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run: echo "<< pipeline.git.revision >>" >> build/COMMIT_SHA
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- *restore_node_modules
|
||||
- run:
|
||||
command: node ./scripts/tasks/danger
|
||||
- RELEASE_CHANNEL
|
||||
- facebook-www
|
||||
- facebook-react-native
|
||||
- node_modules
|
||||
- react-native
|
||||
- dist
|
||||
- sizes/*.json
|
||||
|
||||
build_devtools_and_process_artifacts:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- attach_workspace: *attach_workspace
|
||||
- *restore_yarn_cache
|
||||
- *restore_node_modules
|
||||
- run:
|
||||
name: Install Packages
|
||||
@@ -200,69 +291,139 @@ jobs:
|
||||
- store_artifacts:
|
||||
path: ./build/devtools.tgz
|
||||
|
||||
build_devtools_scheduling_profiler:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace: *attach_workspace
|
||||
- *restore_yarn_cache
|
||||
- *restore_node_modules
|
||||
- run:
|
||||
name: Install Packages
|
||||
command: yarn --frozen-lockfile --cache-folder ~/.cache/yarn
|
||||
- run:
|
||||
name: Build and archive
|
||||
command: |
|
||||
mkdir -p build/devtools
|
||||
cd packages/react-devtools-scheduling-profiler
|
||||
yarn build
|
||||
cd dist
|
||||
tar -zcvf ../../../build/devtools-scheduling-profiler.tgz .
|
||||
- store_artifacts:
|
||||
path: ./build/devtools-scheduling-profiler.tgz
|
||||
- persist_to_workspace:
|
||||
root: packages/react-devtools-scheduling-profiler
|
||||
paths:
|
||||
- dist
|
||||
|
||||
deploy_devtools_scheduling_profiler:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: packages/react-devtools-scheduling-profiler
|
||||
- *restore_node_modules
|
||||
- run:
|
||||
name: Deploy
|
||||
command: |
|
||||
cd packages/react-devtools-scheduling-profiler
|
||||
yarn vercel deploy dist --prod --confirm --token $SCHEDULING_PROFILER_DEPLOY_VERCEL_TOKEN
|
||||
|
||||
# These jobs are named differently so we can distinguish the stable and
|
||||
# and experimental artifacts
|
||||
process_artifacts: *process_artifacts
|
||||
process_artifacts_experimental: *process_artifacts
|
||||
|
||||
sizebot_stable:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace: *attach_workspace
|
||||
- *restore_node_modules
|
||||
# This runs in the process_artifacts job, too, but it's faster to run
|
||||
# this step in both jobs instead of running the jobs sequentially
|
||||
- run: node ./scripts/rollup/consolidateBundleSizes.js
|
||||
- run:
|
||||
environment:
|
||||
RELEASE_CHANNEL: stable
|
||||
command: node ./scripts/tasks/danger
|
||||
|
||||
sizebot_experimental:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace: *attach_workspace
|
||||
- *restore_node_modules
|
||||
# This runs in the process_artifacts job, too, but it's faster to run
|
||||
# this step in both jobs instead of running the jobs sequentially
|
||||
- run: node ./scripts/rollup/consolidateBundleSizes.js
|
||||
- run:
|
||||
environment:
|
||||
RELEASE_CHANNEL: experimental
|
||||
command: node ./scripts/tasks/danger
|
||||
|
||||
yarn_lint_build:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace: *attach_workspace
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- *restore_node_modules
|
||||
- run: yarn lint-build
|
||||
- run: scripts/circleci/check_minified_errors.sh
|
||||
|
||||
check_error_codes:
|
||||
RELEASE_CHANNEL_stable_yarn_lint_build:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace: *attach_workspace
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- *restore_node_modules
|
||||
- run:
|
||||
name: Search build artifacts for unminified errors
|
||||
command: |
|
||||
yarn extract-errors
|
||||
git diff || (echo "Found unminified errors. Either update the error codes map or disable error minification for the affected build, if appropriate." && false)
|
||||
environment:
|
||||
RELEASE_CHANNEL: stable
|
||||
command: yarn lint-build
|
||||
- run: scripts/circleci/check_minified_errors.sh
|
||||
|
||||
yarn_test:
|
||||
yarn_test-stable_build:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
parameters:
|
||||
args:
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- attach_workspace: *attach_workspace
|
||||
- *restore_node_modules
|
||||
- run: yarn test <<parameters.args>> --ci
|
||||
- run: yarn test-stable --build --ci
|
||||
|
||||
yarn_test_build:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
parameters:
|
||||
args:
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- attach_workspace: *attach_workspace
|
||||
- *restore_node_modules
|
||||
- run:
|
||||
name: Install nested packages from Yarn cache
|
||||
command: yarn --frozen-lockfile --cache-folder ~/.cache/yarn
|
||||
- run: yarn test --build <<parameters.args>> --ci
|
||||
- run: yarn test --build --ci
|
||||
|
||||
yarn_test_build_devtools:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace: *attach_workspace
|
||||
- *restore_node_modules
|
||||
- run: yarn test --project=devtools --build --ci
|
||||
|
||||
RELEASE_CHANNEL_stable_yarn_test_dom_fixtures:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- attach_workspace: *attach_workspace
|
||||
- *restore_node_modules
|
||||
- run:
|
||||
name: Run DOM fixture tests
|
||||
@@ -279,7 +440,6 @@ jobs:
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- *restore_node_modules
|
||||
- run:
|
||||
name: Run fuzz tests
|
||||
@@ -287,158 +447,137 @@ jobs:
|
||||
FUZZ_TEST_SEED=$RANDOM yarn test fuzz --ci
|
||||
FUZZ_TEST_SEED=$RANDOM yarn test --prod fuzz --ci
|
||||
|
||||
publish_prerelease:
|
||||
parameters:
|
||||
commit_sha:
|
||||
type: string
|
||||
release_channel:
|
||||
type: string
|
||||
dist_tag:
|
||||
type: string
|
||||
yarn_test-stable_build_prod:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
steps:
|
||||
- checkout
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- attach_workspace: *attach_workspace
|
||||
- *restore_node_modules
|
||||
- run:
|
||||
name: Run publish script
|
||||
command: |
|
||||
git fetch origin main
|
||||
cd ./scripts/release && yarn && cd ../../
|
||||
scripts/release/prepare-release-from-ci.js --skipTests -r << parameters.release_channel >> --commit=<< parameters.commit_sha >>
|
||||
cp ./scripts/release/ci-npmrc ~/.npmrc
|
||||
scripts/release/publish.js --ci --tags << parameters.dist_tag >>
|
||||
- run: yarn test-stable --build --prod --ci
|
||||
|
||||
# We don't always keep the reconciler forks in sync (otherwise it we wouldn't
|
||||
# have forked it) but during periods when they are meant to be in sync, we
|
||||
# use this job to confirm there are no differences.
|
||||
sync_reconciler_forks:
|
||||
yarn_test_build_prod:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
steps:
|
||||
- checkout
|
||||
- run: yarn workspaces info | head -n -1 > workspace_info.txt
|
||||
- attach_workspace: *attach_workspace
|
||||
- *restore_node_modules
|
||||
- run:
|
||||
name: Confirm reconciler forks are the same
|
||||
command: |
|
||||
yarn replace-fork
|
||||
git diff --quiet || (echo "Reconciler forks are not the same! Run yarn replace-fork. Or, if this was intentional, disable this CI check." && false)
|
||||
|
||||
- run: yarn test --build --prod --ci
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
# New workflow that will replace "stable" and "experimental"
|
||||
build_and_test:
|
||||
unless: << pipeline.parameters.prerelease_commit_sha >>
|
||||
stable:
|
||||
jobs:
|
||||
- setup
|
||||
- yarn_flow:
|
||||
requires:
|
||||
- setup
|
||||
# NOTE: This job is only enabled when we want the forks to be in sync.
|
||||
# When the forks intentionally diverge, comment out the job to disable it.
|
||||
- sync_reconciler_forks:
|
||||
requires:
|
||||
- setup
|
||||
- yarn_lint:
|
||||
requires:
|
||||
- setup
|
||||
- yarn_flow:
|
||||
requires:
|
||||
- setup
|
||||
- yarn_test-stable:
|
||||
requires:
|
||||
- setup
|
||||
- yarn_test-stable_prod:
|
||||
requires:
|
||||
- setup
|
||||
- yarn_test-stable_persistent:
|
||||
requires:
|
||||
- setup
|
||||
- yarn_test-classic:
|
||||
requires:
|
||||
- setup
|
||||
- yarn_test-classic_variant:
|
||||
requires:
|
||||
- setup
|
||||
- yarn_test-classic_prod:
|
||||
requires:
|
||||
- setup
|
||||
- yarn_test-classic_prod_variant:
|
||||
requires:
|
||||
- setup
|
||||
- RELEASE_CHANNEL_stable_yarn_build:
|
||||
requires:
|
||||
- setup
|
||||
- process_artifacts:
|
||||
requires:
|
||||
- RELEASE_CHANNEL_stable_yarn_build
|
||||
- sizebot_stable:
|
||||
requires:
|
||||
- RELEASE_CHANNEL_stable_yarn_build
|
||||
- RELEASE_CHANNEL_stable_yarn_lint_build:
|
||||
requires:
|
||||
- RELEASE_CHANNEL_stable_yarn_build
|
||||
- yarn_test-stable_build:
|
||||
requires:
|
||||
- RELEASE_CHANNEL_stable_yarn_build
|
||||
- yarn_test-stable_build_prod:
|
||||
requires:
|
||||
- RELEASE_CHANNEL_stable_yarn_build
|
||||
- RELEASE_CHANNEL_stable_yarn_test_dom_fixtures:
|
||||
requires:
|
||||
- RELEASE_CHANNEL_stable_yarn_build
|
||||
|
||||
experimental:
|
||||
jobs:
|
||||
- setup
|
||||
- yarn_test:
|
||||
requires:
|
||||
- setup
|
||||
matrix:
|
||||
parameters:
|
||||
args:
|
||||
# Intentionally passing these as strings instead of creating a
|
||||
# separate parameter per CLI argument, since it's easier to
|
||||
# control/see which combinations we want to run.
|
||||
- "-r=stable --env=development"
|
||||
- "-r=stable --env=production"
|
||||
- "-r=experimental --env=development"
|
||||
- "-r=experimental --env=production"
|
||||
- "-r=www-classic --env=development --variant=false"
|
||||
- "-r=www-classic --env=production --variant=false"
|
||||
- "-r=www-classic --env=development --variant=true"
|
||||
- "-r=www-classic --env=production --variant=true"
|
||||
- "-r=www-modern --env=development --variant=false"
|
||||
- "-r=www-modern --env=production --variant=false"
|
||||
- "-r=www-modern --env=development --variant=true"
|
||||
- "-r=www-modern --env=production --variant=true"
|
||||
|
||||
# TODO: Test more persistent configurations?
|
||||
- '-r=stable --env=development --persistent'
|
||||
- '-r=experimental --env=development --persistent'
|
||||
- yarn_build_combined:
|
||||
- yarn_test_prod:
|
||||
requires:
|
||||
- setup
|
||||
- scrape_warning_messages:
|
||||
- yarn_test-www:
|
||||
requires:
|
||||
- setup
|
||||
- process_artifacts_combined:
|
||||
- yarn_test-www_variant:
|
||||
requires:
|
||||
- scrape_warning_messages
|
||||
- yarn_build_combined
|
||||
- setup
|
||||
- yarn_test-www_prod:
|
||||
requires:
|
||||
- setup
|
||||
- yarn_test-www_prod_variant:
|
||||
requires:
|
||||
- setup
|
||||
- yarn_build:
|
||||
requires:
|
||||
- setup
|
||||
- process_artifacts_experimental:
|
||||
requires:
|
||||
- yarn_build
|
||||
- sizebot_experimental:
|
||||
requires:
|
||||
- yarn_build
|
||||
- yarn_test_build:
|
||||
requires:
|
||||
- yarn_build_combined
|
||||
matrix:
|
||||
parameters:
|
||||
args:
|
||||
# Intentionally passing these as strings instead of creating a
|
||||
# separate parameter per CLI argument, since it's easier to
|
||||
# control/see which combinations we want to run.
|
||||
- "-r=stable --env=development"
|
||||
- "-r=stable --env=production"
|
||||
- "-r=experimental --env=development"
|
||||
- "-r=experimental --env=production"
|
||||
|
||||
# Dev Tools
|
||||
- "--project=devtools -r=experimental"
|
||||
|
||||
# TODO: Update test config to support www build tests
|
||||
# - "-r=www-classic --env=development --variant=false"
|
||||
# - "-r=www-classic --env=production --variant=false"
|
||||
# - "-r=www-classic --env=development --variant=true"
|
||||
# - "-r=www-classic --env=production --variant=true"
|
||||
# - "-r=www-modern --env=development --variant=false"
|
||||
# - "-r=www-modern --env=production --variant=false"
|
||||
# - "-r=www-modern --env=development --variant=true"
|
||||
# - "-r=www-modern --env=production --variant=true"
|
||||
|
||||
# TODO: Test more persistent configurations?
|
||||
- get_base_build:
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- main
|
||||
- yarn_build
|
||||
- yarn_test_build_prod:
|
||||
requires:
|
||||
- setup
|
||||
- sizebot:
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- main
|
||||
requires:
|
||||
- get_base_build
|
||||
- yarn_build_combined
|
||||
- yarn_build
|
||||
- yarn_lint_build:
|
||||
requires:
|
||||
- yarn_build_combined
|
||||
- check_error_codes:
|
||||
- yarn_build
|
||||
- yarn_test_build_devtools:
|
||||
requires:
|
||||
- yarn_build_combined
|
||||
- RELEASE_CHANNEL_stable_yarn_test_dom_fixtures:
|
||||
requires:
|
||||
- yarn_build_combined
|
||||
- yarn_build
|
||||
- build_devtools_and_process_artifacts:
|
||||
requires:
|
||||
- yarn_build_combined
|
||||
- yarn_build
|
||||
- build_devtools_scheduling_profiler:
|
||||
requires:
|
||||
- yarn_build
|
||||
- deploy_devtools_scheduling_profiler:
|
||||
requires:
|
||||
- build_devtools_scheduling_profiler
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
fuzz_tests:
|
||||
unless: << pipeline.parameters.prerelease_commit_sha >>
|
||||
triggers:
|
||||
- schedule:
|
||||
# Fuzz tests run hourly
|
||||
@@ -446,66 +585,9 @@ workflows:
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
- master
|
||||
jobs:
|
||||
- setup
|
||||
- test_fuzz:
|
||||
requires:
|
||||
- setup
|
||||
|
||||
# Used to publish a prerelease manually via the command line
|
||||
publish_preleases:
|
||||
when: << pipeline.parameters.prerelease_commit_sha >>
|
||||
jobs:
|
||||
- setup
|
||||
- publish_prerelease:
|
||||
name: Publish to Next channel
|
||||
requires:
|
||||
- setup
|
||||
commit_sha: << pipeline.parameters.prerelease_commit_sha >>
|
||||
release_channel: stable
|
||||
dist_tag: "next,alpha"
|
||||
- publish_prerelease:
|
||||
name: Publish to Experimental channel
|
||||
requires:
|
||||
# NOTE: Intentionally running these jobs sequentially because npm
|
||||
# will sometimes fail if you try to concurrently publish two
|
||||
# different versions of the same package, even if they use different
|
||||
# dist tags.
|
||||
- Publish to Next channel
|
||||
commit_sha: << pipeline.parameters.prerelease_commit_sha >>
|
||||
release_channel: experimental
|
||||
dist_tag: experimental
|
||||
|
||||
# Publishes on a cron schedule
|
||||
publish_preleases_nightly:
|
||||
unless: << pipeline.parameters.prerelease_commit_sha >>
|
||||
triggers:
|
||||
- schedule:
|
||||
# At 10 minutes past 16:00 on Mon, Tue, Wed, Thu, and Fri
|
||||
cron: "10 16 * * 1,2,3,4,5"
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
jobs:
|
||||
- setup
|
||||
- publish_prerelease:
|
||||
name: Publish to Next channel
|
||||
requires:
|
||||
- setup
|
||||
commit_sha: << pipeline.git.revision >>
|
||||
release_channel: stable
|
||||
dist_tag: "next,alpha"
|
||||
- publish_prerelease:
|
||||
name: Publish to Experimental channel
|
||||
requires:
|
||||
# NOTE: Intentionally running these jobs sequentially because npm
|
||||
# will sometimes fail if you try to concurrently publish two
|
||||
# different versions of the same package, even if they use different
|
||||
# dist tags.
|
||||
- Publish to Next channel
|
||||
commit_sha: << pipeline.git.revision >>
|
||||
release_channel: experimental
|
||||
dist_tag: experimental
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
{
|
||||
"packages": ["packages/react", "packages/react-dom", "packages/scheduler"],
|
||||
"buildCommand": "download-build-in-codesandbox-ci",
|
||||
"node": "14",
|
||||
"buildCommand": "build --type=NODE react/index,react-dom/index,react-dom/server,react-dom/test-utils,scheduler/index,scheduler/tracing",
|
||||
"publishDirectory": {
|
||||
"react": "build/oss-experimental/react",
|
||||
"react-dom": "build/oss-experimental/react-dom",
|
||||
"scheduler": "build/oss-experimental/scheduler"
|
||||
"react": "build/node_modules/react",
|
||||
"react-dom": "build/node_modules/react-dom",
|
||||
"scheduler": "build/node_modules/scheduler"
|
||||
},
|
||||
"sandboxes": ["new"],
|
||||
"silent": true
|
||||
"sandboxes": ["new"]
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ build/
|
||||
coverage/
|
||||
fixtures/
|
||||
scripts/bench/benchmarks/**/*.js
|
||||
old_major_packages/
|
||||
|
||||
# React repository clone
|
||||
scripts/bench/remote-repo/
|
||||
@@ -17,10 +18,7 @@ packages/react-devtools-core/dist
|
||||
packages/react-devtools-extensions/chrome/build
|
||||
packages/react-devtools-extensions/firefox/build
|
||||
packages/react-devtools-extensions/shared/build
|
||||
packages/react-devtools-extensions/src/ErrorTesterCompiled.js
|
||||
packages/react-devtools-inline/dist
|
||||
packages/react-devtools-shared/src/hooks/__tests__/__source__/__compiled__/
|
||||
packages/react-devtools-shared/src/hooks/__tests__/__source__/__untransformed__/
|
||||
packages/react-devtools-shell/dist
|
||||
packages/react-devtools-timeline/dist
|
||||
packages/react-devtools-timeline/static
|
||||
packages/react-devtools-scheduling-profiler/dist
|
||||
packages/react-devtools-scheduling-profiler/static
|
||||
|
||||
93
.eslintrc.js
93
.eslintrc.js
@@ -48,7 +48,6 @@ module.exports = {
|
||||
'jsx-quotes': [ERROR, 'prefer-double'],
|
||||
'keyword-spacing': [ERROR, {after: true, before: true}],
|
||||
'no-bitwise': OFF,
|
||||
'no-console': OFF,
|
||||
'no-inner-declarations': [ERROR, 'functions'],
|
||||
'no-multi-spaces': ERROR,
|
||||
'no-restricted-globals': [ERROR].concat(restrictedGlobals),
|
||||
@@ -62,8 +61,6 @@ module.exports = {
|
||||
'space-before-blocks': ERROR,
|
||||
'space-before-function-paren': OFF,
|
||||
'valid-typeof': [ERROR, {requireStringLiterals: true}],
|
||||
// Flow fails with with non-string literal keys
|
||||
'no-useless-computed-key': OFF,
|
||||
|
||||
// We apply these settings to files that should run on Node.
|
||||
// They can't use JSX or ES6 modules, and must be in strict mode.
|
||||
@@ -77,8 +74,6 @@ module.exports = {
|
||||
// deal. But I turned it off because loading the plugin causes some obscure
|
||||
// syntax error and it didn't seem worth investigating.
|
||||
'max-len': OFF,
|
||||
// Prettier forces semicolons in a few places
|
||||
'flowtype/object-type-delimiter': OFF,
|
||||
|
||||
// React & JSX
|
||||
// Our transforms set this automatically
|
||||
@@ -108,10 +103,6 @@ module.exports = {
|
||||
// CUSTOM RULES
|
||||
// the second argument of warning/invariant should be a literal string
|
||||
'react-internal/no-primitive-constructors': ERROR,
|
||||
'react-internal/safe-string-coercion': [
|
||||
ERROR,
|
||||
{isProductionUserAppCode: true},
|
||||
],
|
||||
'react-internal/no-to-warn-dev-within-to-throw': ERROR,
|
||||
'react-internal/invariant-args': ERROR,
|
||||
'react-internal/warning-args': ERROR,
|
||||
@@ -120,48 +111,18 @@ module.exports = {
|
||||
'react-internal/no-cross-fork-types': [
|
||||
ERROR,
|
||||
{
|
||||
old: [],
|
||||
new: [],
|
||||
old: [
|
||||
'firstEffect',
|
||||
'nextEffect',
|
||||
// Disabled because it's also used by the Hook type.
|
||||
// 'lastEffect',
|
||||
],
|
||||
new: ['subtreeFlags'],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
overrides: [
|
||||
{
|
||||
// By default, anything error message that appears the packages directory
|
||||
// must have a corresponding error code. The exceptions are defined
|
||||
// in the next override entry.
|
||||
files: ['packages/**/*.js'],
|
||||
rules: {
|
||||
'react-internal/prod-error-codes': ERROR,
|
||||
},
|
||||
},
|
||||
{
|
||||
// These are files where it's OK to have unminified error messages. These
|
||||
// are environments where bundle size isn't a concern, like tests
|
||||
// or Node.
|
||||
files: [
|
||||
'packages/react-dom/src/test-utils/**/*.js',
|
||||
'packages/react-devtools-shared/**/*.js',
|
||||
'packages/react-noop-renderer/**/*.js',
|
||||
'packages/react-pg/**/*.js',
|
||||
'packages/react-fs/**/*.js',
|
||||
'packages/react-refresh/**/*.js',
|
||||
'packages/react-server-dom-webpack/**/*.js',
|
||||
'packages/react-test-renderer/**/*.js',
|
||||
'packages/react-debug-tools/**/*.js',
|
||||
'packages/react-devtools-extensions/**/*.js',
|
||||
'packages/react-devtools-timeline/**/*.js',
|
||||
'packages/react-native-renderer/**/*.js',
|
||||
'packages/eslint-plugin-react-hooks/**/*.js',
|
||||
'packages/jest-react/**/*.js',
|
||||
'packages/**/__tests__/*.js',
|
||||
'packages/**/npm/*.js',
|
||||
],
|
||||
rules: {
|
||||
'react-internal/prod-error-codes': OFF,
|
||||
},
|
||||
},
|
||||
{
|
||||
// We apply these settings to files that we ship through npm.
|
||||
// They must be ES5.
|
||||
@@ -207,53 +168,20 @@ module.exports = {
|
||||
'packages/*/npm/**/*.js',
|
||||
'packages/dom-event-testing-library/**/*.js',
|
||||
'packages/react-devtools*/**/*.js',
|
||||
'dangerfile.js',
|
||||
'fixtures',
|
||||
'packages/react-dom/src/test-utils/*.js',
|
||||
],
|
||||
rules: {
|
||||
'react-internal/no-production-logging': OFF,
|
||||
'react-internal/warning-args': OFF,
|
||||
'react-internal/safe-string-coercion': [
|
||||
ERROR,
|
||||
{isProductionUserAppCode: false},
|
||||
],
|
||||
|
||||
// Disable accessibility checks
|
||||
'jsx-a11y/aria-role': OFF,
|
||||
'jsx-a11y/no-noninteractive-element-interactions': OFF,
|
||||
'jsx-a11y/no-static-element-interactions': OFF,
|
||||
'jsx-a11y/role-has-required-aria-props': OFF,
|
||||
'jsx-a11y/no-noninteractive-tabindex': OFF,
|
||||
'jsx-a11y/tabindex-no-positive': OFF,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'scripts/eslint-rules/*.js',
|
||||
'packages/eslint-plugin-react-hooks/src/*.js',
|
||||
],
|
||||
plugins: ['eslint-plugin'],
|
||||
rules: {
|
||||
'eslint-plugin/prefer-object-rule': ERROR,
|
||||
'eslint-plugin/require-meta-fixable': [
|
||||
ERROR,
|
||||
{catchNoFixerButFixableProperty: true},
|
||||
],
|
||||
'eslint-plugin/require-meta-has-suggestions': ERROR,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'packages/react-native-renderer/**/*.js',
|
||||
'packages/react-server-native-relay/**/*.js',
|
||||
],
|
||||
files: ['packages/react-native-renderer/**/*.js'],
|
||||
globals: {
|
||||
nativeFabricUIManager: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/react-server-dom-webpack/**/*.js'],
|
||||
files: ['packages/react-transport-dom-webpack/**/*.js'],
|
||||
globals: {
|
||||
__webpack_chunk_load__: true,
|
||||
__webpack_require__: true,
|
||||
@@ -268,6 +196,8 @@ module.exports = {
|
||||
],
|
||||
|
||||
globals: {
|
||||
SharedArrayBuffer: true,
|
||||
|
||||
spyOnDev: true,
|
||||
spyOnDevAndProd: true,
|
||||
spyOnProd: true,
|
||||
@@ -279,6 +209,5 @@ module.exports = {
|
||||
__VARIANT__: true,
|
||||
gate: true,
|
||||
trustedTypes: true,
|
||||
IS_REACT_ACT_ENVIRONMENT: true,
|
||||
},
|
||||
};
|
||||
|
||||
81
.github/ISSUE_TEMPLATE/devtools_bug_report.yml
vendored
81
.github/ISSUE_TEMPLATE/devtools_bug_report.yml
vendored
@@ -1,81 +0,0 @@
|
||||
name: "⚛️ 🛠 DevTools bug report"
|
||||
description: "Report a problem with React DevTools. Please provide enough information that we can reproduce the problem."
|
||||
title: "[DevTools Bug]: "
|
||||
labels: ["Component: Developer Tools", "Type: Bug", "Status: Unconfirmed"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Website or app
|
||||
description: |
|
||||
Which website or app were you using when the bug happened?
|
||||
|
||||
This should be a public URL, GitHub repo, or Code Sandbox app so the React team can reproduce the error being reported. (Please no localhost URLs.)
|
||||
placeholder: |
|
||||
e.g. website URL, public GitHub repo, or Code Sandbox app
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Repro steps
|
||||
description: |
|
||||
What were you doing on the website or app when the bug happened? Detailed information helps maintainers reproduce and fix bugs.
|
||||
|
||||
Issues filed without repro steps will be closed.
|
||||
placeholder: |
|
||||
Example bug report:
|
||||
1. Log in with username/password
|
||||
2. Click "Messages" on the left menu
|
||||
3. Open any message in the list
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: How often does this bug happen?
|
||||
description: |
|
||||
Following the repro steps above, how easily are you able to reproduce this bug?
|
||||
options:
|
||||
- Every time
|
||||
- Often
|
||||
- Sometimes
|
||||
- Only once
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: automated_package
|
||||
attributes:
|
||||
label: DevTools package (automated)
|
||||
description: |
|
||||
Please do not edit this field.
|
||||
- type: input
|
||||
id: automated_version
|
||||
attributes:
|
||||
label: DevTools version (automated)
|
||||
description: |
|
||||
Please do not edit this field.
|
||||
- type: input
|
||||
id: automated_error_message
|
||||
attributes:
|
||||
label: Error message (automated)
|
||||
description: |
|
||||
Please do not edit this field.
|
||||
- type: textarea
|
||||
id: automated_call_stack
|
||||
attributes:
|
||||
label: Error call stack (automated)
|
||||
description: |
|
||||
Please do not edit this field.
|
||||
render: text
|
||||
- type: textarea
|
||||
id: automated_component_stack
|
||||
attributes:
|
||||
label: Error component stack (automated)
|
||||
description: |
|
||||
Please do not edit this field.
|
||||
render: text
|
||||
- type: textarea
|
||||
id: automated_github_query_string
|
||||
attributes:
|
||||
label: GitHub query string (automated)
|
||||
description: |
|
||||
Please do not edit this field.
|
||||
render: text
|
||||
11
.github/ISSUE_TEMPLATE/react_18.md
vendored
11
.github/ISSUE_TEMPLATE/react_18.md
vendored
@@ -1,11 +0,0 @@
|
||||
---
|
||||
name: "💬 React 18"
|
||||
about: Bug reports, questions, and general feedback about React 18
|
||||
title: 'React 18 '
|
||||
labels: 'Type: Discussion, React 18'
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Ask a question or share feedback about the React 18 release here.
|
||||
-->
|
||||
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -4,7 +4,7 @@
|
||||
|
||||
Before submitting a pull request, please make sure the following is done:
|
||||
|
||||
1. Fork [the repository](https://github.com/facebook/react) and create your branch from `main`.
|
||||
1. Fork [the repository](https://github.com/facebook/react) and create your branch from `master`.
|
||||
2. Run `yarn` in the repository root.
|
||||
3. If you've fixed a bug or added code that should be tested, add tests!
|
||||
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch TestName` is helpful in development.
|
||||
@@ -20,14 +20,8 @@
|
||||
|
||||
## Summary
|
||||
|
||||
<!--
|
||||
Explain the **motivation** for making this change. What existing problem does the pull request solve?
|
||||
-->
|
||||
<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->
|
||||
|
||||
## How did you test this change?
|
||||
## Test Plan
|
||||
|
||||
<!--
|
||||
Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface.
|
||||
How exactly did you verify that your PR solves the issue you wanted to solve?
|
||||
If you leave this empty, your PR will very likely be closed.
|
||||
-->
|
||||
<!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface. -->
|
||||
|
||||
205
.github/workflows/devtools_check_repro.yml
vendored
205
.github/workflows/devtools_check_repro.yml
vendored
@@ -1,205 +0,0 @@
|
||||
name: DevTools Check for bug repro
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited]
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
|
||||
jobs:
|
||||
check-repro:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/github-script@v3
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const URL_REGEXP = /### Website or app[\r\n]+([^#]+)###/m;
|
||||
const REPRO_STEPS_REGEXP = /### Repro steps[\r\n]+([^#]+)###/m;
|
||||
const LABEL_NEEDS_MORE_INFORMATION = "Resolution: Needs More Information";
|
||||
const LABEL_UNCONFIRMED = "Status: Unconfirmed";
|
||||
|
||||
function debug(...args) {
|
||||
core.info(args.map(JSON.stringify).join(' '));
|
||||
}
|
||||
|
||||
if (context.payload.comment) {
|
||||
debug('Ignoring comment update.');
|
||||
return;
|
||||
}
|
||||
|
||||
const user = context.payload.sender.login;
|
||||
const issue = context.payload.issue;
|
||||
const body = issue.body;
|
||||
|
||||
const urlMatch = body.match(URL_REGEXP);
|
||||
const reproStepsMatch = body.match(REPRO_STEPS_REGEXP);
|
||||
|
||||
const url = urlMatch !== null ? urlMatch[1].trim() : null;
|
||||
const reproSteps = reproStepsMatch !== null ? reproStepsMatch[1].trim() : null;
|
||||
|
||||
if (!url || !reproSteps) {
|
||||
debug('This issue is not a DevTools bug report.');
|
||||
return;
|
||||
}
|
||||
|
||||
debug(`found URL "${url}"`);
|
||||
debug(`found repro steps "${reproSteps}"`);
|
||||
|
||||
async function createComment(comment) {
|
||||
// Format
|
||||
comment = comment
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.join("\n")
|
||||
.trim();
|
||||
|
||||
await github.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: comment,
|
||||
});
|
||||
}
|
||||
|
||||
async function getGitHubActionComments() {
|
||||
debug(`Loading existing comments...`);
|
||||
|
||||
const comments = await github.issues.listComments({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
});
|
||||
|
||||
return comments.data.filter(comment => {
|
||||
debug(`comment by user: "${comment.user.login}"`);
|
||||
return comment.user.login === 'github-actions[bot]';
|
||||
});
|
||||
}
|
||||
|
||||
async function getIssueLabels() {
|
||||
const issues = await github.issues.listLabelsOnIssue({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
});
|
||||
|
||||
return issues.data;
|
||||
}
|
||||
|
||||
async function updateIssue(state, assignees = []) {
|
||||
await github.issues.update({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state,
|
||||
assignees,
|
||||
});
|
||||
}
|
||||
|
||||
async function closeWithComment(comment) {
|
||||
if (issue.state !== 'open') {
|
||||
debug(`Issue is not open`);
|
||||
return;
|
||||
}
|
||||
|
||||
const labels = await getIssueLabels();
|
||||
const label = labels.find(label => label.name === LABEL_UNCONFIRMED);
|
||||
if (!label) {
|
||||
debug(`Issue was not opened via DevTools bug report template`);
|
||||
return;
|
||||
}
|
||||
|
||||
const comments = await getGitHubActionComments();
|
||||
if (comments.length > 0) {
|
||||
debug(`Already commented on issue; won't comment again`);
|
||||
return;
|
||||
}
|
||||
|
||||
debug(`Missing required information`);
|
||||
|
||||
await github.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: [LABEL_NEEDS_MORE_INFORMATION],
|
||||
});
|
||||
|
||||
await createComment(comment);
|
||||
await updateIssue('closed', [user]);
|
||||
}
|
||||
|
||||
async function openWithComment(comment) {
|
||||
if (issue.state !== 'closed') {
|
||||
debug(`Issue is already open`);
|
||||
return;
|
||||
}
|
||||
|
||||
const labels = await getIssueLabels();
|
||||
const label = labels.find(label => label.name === LABEL_NEEDS_MORE_INFORMATION);
|
||||
if (!label) {
|
||||
debug(`Issue was not tagged as needs information`);
|
||||
return;
|
||||
}
|
||||
|
||||
const comments = await getGitHubActionComments();
|
||||
if (comments.length === 0) {
|
||||
debug(`Issue was closed by someone else; won't reopen`);
|
||||
return;
|
||||
}
|
||||
|
||||
debug(`Re-opening closed issue`);
|
||||
|
||||
await github.issues.removeLabel({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: LABEL_NEEDS_MORE_INFORMATION,
|
||||
});
|
||||
|
||||
await createComment(comment);
|
||||
await updateIssue('open');
|
||||
}
|
||||
|
||||
const PROBABLY_NOT_A_URL_REGEX = /(^Chrome$|^Firefox$| Website)/i;
|
||||
|
||||
const COMMENT_HEADER = `
|
||||
@${user}: We're sorry you've seen this error. ❤️
|
||||
`.trim();
|
||||
|
||||
const COMMENT_FOOTER = `
|
||||
Please help us by providing a link to a CodeSandbox (https://codesandbox.io/s/new), a repository on GitHub, or a minimal code example that reproduces the problem. (Screenshots or videos can also be helpful if they help provide context on how to repro the bug.)
|
||||
|
||||
Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve
|
||||
|
||||
Issues without repros are automatically closed but we will re-open if you update with repro info.
|
||||
`.trim();
|
||||
|
||||
if (url.includes("localhost")) {
|
||||
closeWithComment(`
|
||||
${COMMENT_HEADER}
|
||||
|
||||
Unfortunately the URL you provided ("localhost") is not publicly accessible. (This means that we will not be able to reproduce the problem you're reporting.)
|
||||
|
||||
${COMMENT_FOOTER}
|
||||
`);
|
||||
} else if (url.length < 10 || url.match(PROBABLY_NOT_A_URL_REGEX)) {
|
||||
closeWithComment(`
|
||||
${COMMENT_HEADER}
|
||||
|
||||
It looks like you forgot to specify a valid URL. (This means that we will not be able to reproduce the problem you're reporting.)
|
||||
|
||||
${COMMENT_FOOTER}
|
||||
`);
|
||||
} else if (reproSteps.length < 25) {
|
||||
closeWithComment(`
|
||||
${COMMENT_HEADER}
|
||||
|
||||
Unfortunately, it doesn't look like this issue has enough info for one of us to reproduce and fix it though.
|
||||
|
||||
${COMMENT_FOOTER}
|
||||
`);
|
||||
} else {
|
||||
openWithComment(`
|
||||
Thank you for providing repro steps! Re-opening issue now for triage.
|
||||
`);
|
||||
}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -35,4 +35,4 @@ packages/react-devtools-extensions/shared/build
|
||||
packages/react-devtools-extensions/.tempUserDataDir
|
||||
packages/react-devtools-inline/dist
|
||||
packages/react-devtools-shell/dist
|
||||
packages/react-devtools-timeline/dist
|
||||
packages/react-devtools-scheduling-profiler/dist
|
||||
@@ -2,10 +2,7 @@ packages/react-devtools-core/dist
|
||||
packages/react-devtools-extensions/chrome/build
|
||||
packages/react-devtools-extensions/firefox/build
|
||||
packages/react-devtools-extensions/shared/build
|
||||
packages/react-devtools-extensions/src/ErrorTesterCompiled.js
|
||||
packages/react-devtools-inline/dist
|
||||
packages/react-devtools-shared/src/hooks/__tests__/__source__/__compiled__/
|
||||
packages/react-devtools-shared/src/hooks/__tests__/__source__/__untransformed__/
|
||||
packages/react-devtools-shell/dist
|
||||
packages/react-devtools-timeline/dist
|
||||
packages/react-devtools-timeline/static
|
||||
packages/react-devtools-scheduling-profiler/dist
|
||||
packages/react-devtools-scheduling-profiler/static
|
||||
3
AUTHORS
3
AUTHORS
@@ -608,6 +608,7 @@ Ludovico Fischer <livrerie@gmail.com>
|
||||
Luigy Leon <luichi.19@gmail.com>
|
||||
Luke Belliveau <luke.belliveau@gmail.com>
|
||||
Luke Horvat <lukehorvat@gmail.com>
|
||||
Lutz Rosema <terabaud@gmail.com>
|
||||
MICHAEL JACKSON <mjijackson@gmail.com>
|
||||
MIKAMI Yoshiyuki <yoshuki@saikyoline.jp>
|
||||
Maciej Kasprzyk <kapustka.maciek@gmail.com>
|
||||
@@ -670,7 +671,7 @@ Matthew Shotton <matthew.shotton@bbc.co.uk>
|
||||
Matthias Le Brun <mlbli@me.com>
|
||||
Matti Nelimarkka <matti.nelimarkka@hiit.fi>
|
||||
Mattijs Kneppers <mattijs@arttech.nl>
|
||||
Max Donchenko <maxx.donchenko@gmail.com>
|
||||
Max Donchenko <wereya2@yandex.ru>
|
||||
Max F. Albrecht <1@178.is>
|
||||
Max Heiber <max.heiber@gmail.com>
|
||||
Max Stoiber <contact@mstoiber.com>
|
||||
|
||||
126
CHANGELOG.md
126
CHANGELOG.md
@@ -1,110 +1,3 @@
|
||||
## 17.0.2 (March 22, 2021)
|
||||
|
||||
### React DOM
|
||||
|
||||
* Remove an unused dependency to address the [`SharedArrayBuffer` cross-origin isolation warning](https://developer.chrome.com/blog/enabling-shared-array-buffer/). ([@koba04](https://github.com/koba04) and [@bvaughn](https://github.com/bvaughn) in [#20831](https://github.com/facebook/react/pull/20831), [#20832](https://github.com/facebook/react/pull/20832), and [#20840](https://github.com/facebook/react/pull/20840))
|
||||
|
||||
## 17.0.1 (October 22, 2020)
|
||||
|
||||
### React DOM
|
||||
|
||||
* Fix a crash in IE11. ([@gaearon](https://github.com/gaearon) in [#20071](https://github.com/facebook/react/pull/20071))
|
||||
|
||||
## 17.0.0 (October 20, 2020)
|
||||
|
||||
Today, we are releasing React 17!
|
||||
|
||||
**[Learn more about React 17 and how to update to it on the official React blog.](https://reactjs.org/blog/2020/10/20/react-v17.html)**
|
||||
|
||||
### React
|
||||
|
||||
* Add `react/jsx-runtime` and `react/jsx-dev-runtime` for the [new JSX transform](https://babeljs.io/blog/2020/03/16/7.9.0#a-new-jsx-transform-11154-https-githubcom-babel-babel-pull-11154). ([@lunaruan](https://github.com/lunaruan) in [#18299](https://github.com/facebook/react/pull/18299))
|
||||
* Build component stacks from native error frames. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18561](https://github.com/facebook/react/pull/18561))
|
||||
* Allow to specify `displayName` on context for improved stacks. ([@eps1lon](https://github.com/eps1lon) in [#18224](https://github.com/facebook/react/pull/18224))
|
||||
* Prevent `'use strict'` from leaking in the UMD bundles. ([@koba04](https://github.com/koba04) in [#19614](https://github.com/facebook/react/pull/19614))
|
||||
* Stop using `fb.me` for redirects. ([@cylim](https://github.com/cylim) in [#19598](https://github.com/facebook/react/pull/19598))
|
||||
|
||||
### React DOM
|
||||
|
||||
* Delegate events to roots instead of `document`. ([@trueadm](https://github.com/trueadm) in [#18195](https://github.com/facebook/react/pull/18195) and [others](https://github.com/facebook/react/pulls?q=is%3Apr+author%3Atrueadm+modern+event+is%3Amerged))
|
||||
* Clean up all effects before running any next effects. ([@bvaughn](https://github.com/bvaughn) in [#17947](https://github.com/facebook/react/pull/17947))
|
||||
* Run `useEffect` cleanup functions asynchronously. ([@bvaughn](https://github.com/bvaughn) in [#17925](https://github.com/facebook/react/pull/17925))
|
||||
* Use browser `focusin` and `focusout` for `onFocus` and `onBlur`. ([@trueadm](https://github.com/trueadm) in [#19186](https://github.com/facebook/react/pull/19186))
|
||||
* Make all `Capture` events use the browser capture phase. ([@trueadm](https://github.com/trueadm) in [#19221](https://github.com/facebook/react/pull/19221))
|
||||
* Don't emulate bubbling of the `onScroll` event. ([@gaearon](https://github.com/gaearon) in [#19464](https://github.com/facebook/react/pull/19464))
|
||||
* Throw if `forwardRef` or `memo` component returns `undefined`. ([@gaearon](https://github.com/gaearon) in [#19550](https://github.com/facebook/react/pull/19550))
|
||||
* Remove event pooling. ([@trueadm](https://github.com/trueadm) in [#18969](https://github.com/facebook/react/pull/18969))
|
||||
* Stop exposing internals that won’t be needed by React Native Web. ([@necolas](https://github.com/necolas) in [#18483](https://github.com/facebook/react/pull/18483))
|
||||
* Attach all known event listeners when the root mounts. ([@gaearon](https://github.com/gaearon) in [#19659](https://github.com/facebook/react/pull/19659))
|
||||
* Disable `console` in the second render pass of DEV mode double render. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18547](https://github.com/facebook/react/pull/18547))
|
||||
* Deprecate the undocumented and misleading `ReactTestUtils.SimulateNative` API. ([@gaearon](https://github.com/gaearon) in [#13407](https://github.com/facebook/react/pull/13407))
|
||||
* Rename private field names used in the internals. ([@gaearon](https://github.com/gaearon) in [#18377](https://github.com/facebook/react/pull/18377))
|
||||
* Don't call User Timing API in development. ([@gaearon](https://github.com/gaearon) in [#18417](https://github.com/facebook/react/pull/18417))
|
||||
* Disable console during the repeated render in Strict Mode. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18547](https://github.com/facebook/react/pull/18547))
|
||||
* In Strict Mode, double-render components without Hooks too. ([@eps1lon](https://github.com/eps1lon) in [#18430](https://github.com/facebook/react/pull/18430))
|
||||
* Allow calling `ReactDOM.flushSync` during lifecycle methods (but warn). ([@sebmarkbage](https://github.com/sebmarkbage) in [#18759](https://github.com/facebook/react/pull/18759))
|
||||
* Add the `code` property to the keyboard event objects. ([@bl00mber](https://github.com/bl00mber) in [#18287](https://github.com/facebook/react/pull/18287))
|
||||
* Add the `disableRemotePlayback` property for `video` elements. ([@tombrowndev](https://github.com/tombrowndev) in [#18619](https://github.com/facebook/react/pull/18619))
|
||||
* Add the `enterKeyHint` property for `input` elements. ([@eps1lon](https://github.com/eps1lon) in [#18634](https://github.com/facebook/react/pull/18634))
|
||||
* Warn when no `value` is provided to `<Context.Provider>`. ([@charlie1404](https://github.com/charlie1404) in [#19054](https://github.com/facebook/react/pull/19054))
|
||||
* Warn when `memo` or `forwardRef` components return `undefined`. ([@bvaughn](https://github.com/bvaughn) in [#19550](https://github.com/facebook/react/pull/19550))
|
||||
* Improve the error message for invalid updates. ([@JoviDeCroock](https://github.com/JoviDeCroock) in [#18316](https://github.com/facebook/react/pull/18316))
|
||||
* Exclude forwardRef and memo from stack frames. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18559](https://github.com/facebook/react/pull/18559))
|
||||
* Improve the error message when switching between controlled and uncontrolled inputs. ([@vcarl](https://github.com/vcarl) in [#17070](https://github.com/facebook/react/pull/17070))
|
||||
* Keep `onTouchStart`, `onTouchMove`, and `onWheel` passive. ([@gaearon](https://github.com/gaearon) in [#19654](https://github.com/facebook/react/pull/19654))
|
||||
* Fix `setState` hanging in development inside a closed iframe. ([@gaearon](https://github.com/gaearon) in [#19220](https://github.com/facebook/react/pull/19220))
|
||||
* Fix rendering bailout for lazy components with `defaultProps`. ([@jddxf](https://github.com/jddxf) in [#18539](https://github.com/facebook/react/pull/18539))
|
||||
* Fix a false positive warning when `dangerouslySetInnerHTML` is `undefined`. ([@eps1lon](https://github.com/eps1lon) in [#18676](https://github.com/facebook/react/pull/18676))
|
||||
* Fix Test Utils with non-standard `require` implementation. ([@just-boris](https://github.com/just-boris) in [#18632](https://github.com/facebook/react/pull/18632))
|
||||
* Fix `onBeforeInput` reporting an incorrect `event.type`. ([@eps1lon](https://github.com/eps1lon) in [#19561](https://github.com/facebook/react/pull/19561))
|
||||
* Fix `event.relatedTarget` reported as `undefined` in Firefox. ([@claytercek](https://github.com/claytercek) in [#19607](https://github.com/facebook/react/pull/19607))
|
||||
* Fix "unspecified error" in IE11. ([@hemakshis](https://github.com/hemakshis) in [#19664](https://github.com/facebook/react/pull/19664))
|
||||
* Fix rendering into a shadow root. ([@Jack-Works](https://github.com/Jack-Works) in [#15894](https://github.com/facebook/react/pull/15894))
|
||||
* Fix `movementX/Y` polyfill with capture events. ([@gaearon](https://github.com/gaearon) in [#19672](https://github.com/facebook/react/pull/19672))
|
||||
* Use delegation for `onSubmit` and `onReset` events. ([@gaearon](https://github.com/gaearon) in [#19333](https://github.com/facebook/react/pull/19333))
|
||||
* Improve memory usage. ([@trueadm](https://github.com/trueadm) in [#18970](https://github.com/facebook/react/pull/18970))
|
||||
|
||||
### React DOM Server
|
||||
|
||||
* Make `useCallback` behavior consistent with `useMemo` for the server renderer. ([@alexmckenley](https://github.com/alexmckenley) in [#18783](https://github.com/facebook/react/pull/18783))
|
||||
* Fix state leaking when a function component throws. ([@pmaccart](https://github.com/pmaccart) in [#19212](https://github.com/facebook/react/pull/19212))
|
||||
|
||||
### React Test Renderer
|
||||
|
||||
* Improve `findByType` error message. ([@henryqdineen](https://github.com/henryqdineen) in [#17439](https://github.com/facebook/react/pull/17439))
|
||||
|
||||
### Concurrent Mode (Experimental)
|
||||
|
||||
* Revamp the priority batching heuristics. ([@acdlite](https://github.com/acdlite) in [#18796](https://github.com/facebook/react/pull/18796))
|
||||
* Add the `unstable_` prefix before the experimental APIs. ([@acdlite](https://github.com/acdlite) in [#18825](https://github.com/facebook/react/pull/18825))
|
||||
* Remove `unstable_discreteUpdates` and `unstable_flushDiscreteUpdates`. ([@trueadm](https://github.com/trueadm) in [#18825](https://github.com/facebook/react/pull/18825))
|
||||
* Remove the `timeoutMs` argument. ([@acdlite](https://github.com/acdlite) in [#19703](https://github.com/facebook/react/pull/19703))
|
||||
* Disable `<div hidden />` prerendering in favor of a different future API. ([@acdlite](https://github.com/acdlite) in [#18917](https://github.com/facebook/react/pull/18917))
|
||||
* Add `unstable_expectedLoadTime` to Suspense for CPU-bound trees. ([@acdlite](https://github.com/acdlite) in [#19936](https://github.com/facebook/react/pull/19936))
|
||||
* Add an experimental `unstable_useOpaqueIdentifier` Hook. ([@lunaruan](https://github.com/lunaruan) in [#17322](https://github.com/facebook/react/pull/17322))
|
||||
* Add an experimental `unstable_startTransition` API. ([@rickhanlonii](https://github.com/rickhanlonii) in [#19696](https://github.com/facebook/react/pull/19696))
|
||||
* Using `act` in the test renderer no longer flushes Suspense fallbacks. ([@acdlite](https://github.com/acdlite) in [#18596](https://github.com/facebook/react/pull/18596))
|
||||
* Use global render timeout for CPU Suspense. ([@sebmarkbage](https://github.com/sebmarkbage) in [#19643](https://github.com/facebook/react/pull/19643))
|
||||
* Clear the existing root content before mounting. ([@bvaughn](https://github.com/bvaughn) in [#18730](https://github.com/facebook/react/pull/18730))
|
||||
* Fix a bug with error boundaries. ([@acdlite](https://github.com/acdlite) in [#18265](https://github.com/facebook/react/pull/18265))
|
||||
* Fix a bug causing dropped updates in a suspended tree. ([@acdlite](https://github.com/acdlite) in [#18384](https://github.com/facebook/react/pull/18384) and [#18457](https://github.com/facebook/react/pull/18457))
|
||||
* Fix a bug causing dropped render phase updates. ([@acdlite](https://github.com/acdlite) in [#18537](https://github.com/facebook/react/pull/18537))
|
||||
* Fix a bug in SuspenseList. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18412](https://github.com/facebook/react/pull/18412))
|
||||
* Fix a bug causing Suspense fallback to show too early. ([@acdlite](https://github.com/acdlite) in [#18411](https://github.com/facebook/react/pull/18411))
|
||||
* Fix a bug with class components inside SuspenseList. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18448](https://github.com/facebook/react/pull/18448))
|
||||
* Fix a bug with inputs that may cause updates to be dropped. ([@jddxf](https://github.com/jddxf) in [#18515](https://github.com/facebook/react/pull/18515) and [@acdlite](https://github.com/acdlite) in [#18535](https://github.com/facebook/react/pull/18535))
|
||||
* Fix a bug causing Suspense fallback to get stuck. ([@acdlite](https://github.com/acdlite) in [#18663](https://github.com/facebook/react/pull/18663))
|
||||
* Don't cut off the tail of a SuspenseList if hydrating. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18854](https://github.com/facebook/react/pull/18854))
|
||||
* Fix a bug in `useMutableSource` that may happen when `getSnapshot` changes. ([@bvaughn](https://github.com/bvaughn) in [#18297](https://github.com/facebook/react/pull/18297))
|
||||
* Fix a tearing bug in `useMutableSource`. ([@bvaughn](https://github.com/bvaughn) in [#18912](https://github.com/facebook/react/pull/18912))
|
||||
* Warn if calling setState outside of render but before commit. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18838](https://github.com/facebook/react/pull/18838))
|
||||
|
||||
## 16.14.0 (October 14, 2020)
|
||||
|
||||
### React
|
||||
|
||||
* Add support for the [new JSX transform](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html). ([@lunaruan](https://github.com/lunaruan) in [#18299](https://github.com/facebook/react/pull/18299))
|
||||
|
||||
## 16.13.1 (March 19, 2020)
|
||||
|
||||
### React DOM
|
||||
@@ -679,7 +572,7 @@ This release was published in a broken state and should be skipped.
|
||||
|
||||
### React Is (New)
|
||||
|
||||
* First release of the [new package](https://github.com/facebook/react/tree/main/packages/react-is) that libraries can use to detect different React node types. ([@bvaughn](https://github.com/bvaughn) in [#12199](https://github.com/facebook/react/pull/12199))
|
||||
* First release of the [new package](https://github.com/facebook/react/tree/master/packages/react-is) that libraries can use to detect different React node types. ([@bvaughn](https://github.com/bvaughn) in [#12199](https://github.com/facebook/react/pull/12199))
|
||||
* Add `ReactIs.isValidElementType()` to help higher-order components validate their inputs. ([@jamesreggio](https://github.com/jamesreggio) in [#12483](https://github.com/facebook/react/pull/12483))
|
||||
|
||||
### React Lifecycles Compat (New)
|
||||
@@ -688,7 +581,7 @@ This release was published in a broken state and should be skipped.
|
||||
|
||||
### Create Subscription (New)
|
||||
|
||||
* First release of the [new package](https://github.com/facebook/react/tree/main/packages/create-subscription) to subscribe to external data sources safely for async rendering. ([@bvaughn](https://github.com/bvaughn) in [#12325](https://github.com/facebook/react/pull/12325))
|
||||
* First release of the [new package](https://github.com/facebook/react/tree/master/packages/create-subscription) to subscribe to external data sources safely for async rendering. ([@bvaughn](https://github.com/bvaughn) in [#12325](https://github.com/facebook/react/pull/12325))
|
||||
|
||||
### React Reconciler (Experimental)
|
||||
|
||||
@@ -819,12 +712,12 @@ Starting with 16.1.0, we will no longer be publishing new releases on Bower. You
|
||||
|
||||
### React Reconciler (Experimental)
|
||||
|
||||
* First release of the [new experimental package](https://github.com/facebook/react/blob/main/packages/react-reconciler/README.md) for creating custom renderers. ([@iamdustan](https://github.com/iamdustan) in [#10758](https://github.com/facebook/react/pull/10758))
|
||||
* First release of the [new experimental package](https://github.com/facebook/react/blob/master/packages/react-reconciler/README.md) for creating custom renderers. ([@iamdustan](https://github.com/iamdustan) in [#10758](https://github.com/facebook/react/pull/10758))
|
||||
* Add support for React DevTools. ([@gaearon](https://github.com/gaearon) in [#11463](https://github.com/facebook/react/pull/11463))
|
||||
|
||||
### React Call Return (Experimental)
|
||||
|
||||
* First release of the [new experimental package](https://github.com/facebook/react/tree/main/packages/react-call-return) for parent-child communication. ([@gaearon](https://github.com/gaearon) in [#11364](https://github.com/facebook/react/pull/11364))
|
||||
* First release of the [new experimental package](https://github.com/facebook/react/tree/master/packages/react-call-return) for parent-child communication. ([@gaearon](https://github.com/gaearon) in [#11364](https://github.com/facebook/react/pull/11364))
|
||||
|
||||
## 16.0.1 (August 1, 2018)
|
||||
|
||||
@@ -877,12 +770,6 @@ Starting with 16.1.0, we will no longer be publishing new releases on Bower. You
|
||||
- There is no `react-with-addons.js` build anymore. All compatible addons are published separately on npm, and have single-file browser versions if you need them.
|
||||
- The deprecations introduced in 15.x have been removed from the core package. `React.createClass` is now available as create-react-class, `React.PropTypes` as prop-types, `React.DOM` as react-dom-factories, react-addons-test-utils as react-dom/test-utils, and shallow renderer as react-test-renderer/shallow. See [15.5.0](https://reactjs.org/blog/2017/04/07/react-v15.5.0.html) and [15.6.0](https://reactjs.org/blog/2017/06/13/react-v15.6.0.html) blog posts for instructions on migrating code and automated codemods.
|
||||
|
||||
## 15.7.0 (October 14, 2020)
|
||||
|
||||
### React
|
||||
|
||||
* Backport support for the [new JSX transform](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html) to 15.x. ([@lunaruan](https://github.com/lunaruan) in [#18299](https://github.com/facebook/react/pull/18299) and [@gaearon](https://github.com/gaearon) in [#20024](https://github.com/facebook/react/pull/20024))
|
||||
|
||||
## 15.6.2 (September 25, 2017)
|
||||
|
||||
### All Packages
|
||||
@@ -1334,11 +1221,6 @@ Each of these changes will continue to work as before with a new warning until t
|
||||
- React DOM adds a new `suppressContentEditableWarning` prop for components like [Draft.js](https://draftjs.org/) that intentionally manage `contentEditable` children with React. ([@mxstbr](https://github.com/mxstbr) in [#6112](https://github.com/facebook/react/pull/6112))
|
||||
- React improves the performance for `createClass()` on complex specs. ([@sophiebits](https://github.com/sophiebits) in [#5550](https://github.com/facebook/react/pull/5550))
|
||||
|
||||
## 0.14.10 (October 14, 2020)
|
||||
|
||||
### React
|
||||
|
||||
* Backport support for the [new JSX transform](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html) to 0.14.x. ([@lunaruan](https://github.com/lunaruan) in [#18299](https://github.com/facebook/react/pull/18299) and [@gaearon](https://github.com/gaearon) in [#20024](https://github.com/facebook/react/pull/20024))
|
||||
|
||||
## 0.14.8 (March 29, 2016)
|
||||
|
||||
|
||||
@@ -52,10 +52,6 @@ project e-mail address, posting via an official social media account, or acting
|
||||
as an appointed representative at an online or offline event. Representation of
|
||||
a project may be further defined and clarified by project maintainers.
|
||||
|
||||
This Code of Conduct also applies outside the project spaces when there is a
|
||||
reasonable belief that an individual's behavior may have a negative impact on
|
||||
the project or its community.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# [React](https://reactjs.org/) · [](https://github.com/facebook/react/blob/main/LICENSE) [](https://www.npmjs.com/package/react) [](https://circleci.com/gh/facebook/react) [](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
|
||||
# [React](https://reactjs.org/) · [](https://github.com/facebook/react/blob/master/LICENSE) [](https://www.npmjs.com/package/react) [](https://circleci.com/gh/facebook/react) [](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
|
||||
|
||||
React is a JavaScript library for building user interfaces.
|
||||
|
||||
@@ -20,7 +20,7 @@ You can use React as a `<script>` tag from a [CDN](https://reactjs.org/docs/cdn-
|
||||
|
||||
## Documentation
|
||||
|
||||
You can find the React documentation [on the website](https://reactjs.org/).
|
||||
You can find the React documentation [on the website](https://reactjs.org/docs).
|
||||
|
||||
Check out the [Getting Started](https://reactjs.org/docs/getting-started.html) page for a quick overview.
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// This module is the single source of truth for versioning packages that we
|
||||
// publish to npm.
|
||||
//
|
||||
// Packages will not be published unless they are added here.
|
||||
//
|
||||
// The @latest channel uses the version as-is, e.g.:
|
||||
//
|
||||
// 18.0.0
|
||||
//
|
||||
// The @next channel appends additional information, with the scheme
|
||||
// <version>-<label>-<commit_sha>, e.g.:
|
||||
//
|
||||
// 18.0.0-alpha-a1c2d3e4
|
||||
//
|
||||
// The @experimental channel doesn't include a version, only a date and a sha, e.g.:
|
||||
//
|
||||
// 0.0.0-experimental-241c4467e-20200129
|
||||
|
||||
const ReactVersion = '18.0.0';
|
||||
|
||||
// The label used by the @next channel. Represents the upcoming release's
|
||||
// stability. Could be "alpha", "beta", "rc", etc.
|
||||
const nextChannelLabel = 'alpha';
|
||||
|
||||
const stablePackages = {
|
||||
'create-subscription': ReactVersion,
|
||||
'eslint-plugin-react-hooks': '4.2.1',
|
||||
'jest-react': '0.12.1',
|
||||
react: ReactVersion,
|
||||
'react-art': ReactVersion,
|
||||
'react-dom': ReactVersion,
|
||||
'react-is': ReactVersion,
|
||||
'react-reconciler': '0.27.0',
|
||||
'react-refresh': '0.11.0',
|
||||
'react-test-renderer': ReactVersion,
|
||||
'use-subscription': '1.6.0',
|
||||
'use-sync-external-store': '1.0.0',
|
||||
scheduler: '0.21.0',
|
||||
};
|
||||
|
||||
// These packages do not exist in the @next or @latest channel, only
|
||||
// @experimental. We don't use semver, just the commit sha, so this is just a
|
||||
// list of package names instead of a map.
|
||||
const experimentalPackages = [
|
||||
'react-fetch',
|
||||
'react-fs',
|
||||
'react-pg',
|
||||
'react-server-dom-webpack',
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
ReactVersion,
|
||||
nextChannelLabel,
|
||||
stablePackages,
|
||||
experimentalPackages,
|
||||
};
|
||||
@@ -18,7 +18,7 @@ platform:
|
||||
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
- master
|
||||
|
||||
# Disable Visual Studio build and deploy
|
||||
build: off
|
||||
|
||||
429
dangerfile.js
429
dangerfile.js
@@ -7,8 +7,6 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
/* eslint-disable no-for-of-loops/no-for-of-loops */
|
||||
|
||||
// Hi, if this is your first time editing/reading a Dangerfile, here's a summary:
|
||||
// It's a JS runtime which helps you provide continuous feedback inside GitHub.
|
||||
//
|
||||
@@ -28,231 +26,270 @@
|
||||
// `DANGER_GITHUB_API_TOKEN=[ENV_ABOVE] yarn danger pr https://github.com/facebook/react/pull/11865
|
||||
|
||||
const {markdown, danger, warn} = require('danger');
|
||||
const {promisify} = require('util');
|
||||
const glob = promisify(require('glob'));
|
||||
const gzipSize = require('gzip-size');
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
const {readFileSync, statSync} = require('fs');
|
||||
const {generateResultsArray} = require('./scripts/rollup/stats');
|
||||
const {existsSync, readFileSync} = require('fs');
|
||||
const {exec} = require('child_process');
|
||||
|
||||
const BASE_DIR = 'base-build';
|
||||
const HEAD_DIR = 'build';
|
||||
// This must match the name of the CI job that creates the build artifacts
|
||||
const RELEASE_CHANNEL =
|
||||
process.env.RELEASE_CHANNEL === 'experimental' ? 'experimental' : 'stable';
|
||||
const artifactsJobName =
|
||||
process.env.RELEASE_CHANNEL === 'experimental'
|
||||
? 'process_artifacts_experimental'
|
||||
: 'process_artifacts';
|
||||
|
||||
const CRITICAL_THRESHOLD = 0.02;
|
||||
const SIGNIFICANCE_THRESHOLD = 0.002;
|
||||
const CRITICAL_ARTIFACT_PATHS = new Set([
|
||||
// We always report changes to these bundles, even if the change is
|
||||
// insiginificant or non-existent.
|
||||
'oss-stable/react-dom/cjs/react-dom.production.min.js',
|
||||
'oss-experimental/react-dom/cjs/react-dom.production.min.js',
|
||||
'facebook-www/ReactDOM-prod.classic.js',
|
||||
'facebook-www/ReactDOM-prod.modern.js',
|
||||
'facebook-www/ReactDOMForked-prod.classic.js',
|
||||
]);
|
||||
|
||||
const kilobyteFormatter = new Intl.NumberFormat('en', {
|
||||
style: 'unit',
|
||||
unit: 'kilobyte',
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
});
|
||||
|
||||
function kbs(bytes) {
|
||||
return kilobyteFormatter.format(bytes / 1000);
|
||||
if (!existsSync('./build/bundle-sizes.json')) {
|
||||
// This indicates the build failed previously.
|
||||
// In that case, there's nothing for the Dangerfile to do.
|
||||
// Exit early to avoid leaving a redundant (and potentially confusing) PR comment.
|
||||
warn(
|
||||
'No bundle size information found. This indicates the build ' +
|
||||
'job failed.'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const percentFormatter = new Intl.NumberFormat('en', {
|
||||
style: 'percent',
|
||||
signDisplay: 'exceptZero',
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
});
|
||||
const currentBuildResults = JSON.parse(
|
||||
readFileSync('./build/bundle-sizes.json')
|
||||
);
|
||||
|
||||
function change(decimal) {
|
||||
if (Number === Infinity) {
|
||||
return 'New file';
|
||||
}
|
||||
if (decimal === -1) {
|
||||
return 'Deleted';
|
||||
}
|
||||
if (decimal < 0.0001) {
|
||||
return '=';
|
||||
}
|
||||
return percentFormatter.format(decimal);
|
||||
/**
|
||||
* Generates a Markdown table
|
||||
* @param {string[]} headers
|
||||
* @param {string[][]} body
|
||||
*/
|
||||
function generateMDTable(headers, body) {
|
||||
const tableHeaders = [
|
||||
headers.join(' | '),
|
||||
headers.map(() => ' --- ').join(' | '),
|
||||
];
|
||||
|
||||
const tablebody = body.map(r => r.join(' | '));
|
||||
return tableHeaders.join('\n') + '\n' + tablebody.join('\n');
|
||||
}
|
||||
|
||||
const header = `
|
||||
| Name | +/- | Base | Current | +/- gzip | Base gzip | Current gzip |
|
||||
| ---- | --- | ---- | ------- | -------- | --------- | ------------ |`;
|
||||
/**
|
||||
* Generates a user-readable string from a percentage change
|
||||
* @param {number} change
|
||||
* @param {boolean} includeEmoji
|
||||
*/
|
||||
function addPercent(change, includeEmoji) {
|
||||
if (!isFinite(change)) {
|
||||
// When a new package is created
|
||||
return 'n/a';
|
||||
}
|
||||
const formatted = (change * 100).toFixed(1);
|
||||
if (/^-|^0(?:\.0+)$/.test(formatted)) {
|
||||
return `${formatted}%`;
|
||||
} else {
|
||||
if (includeEmoji) {
|
||||
return `:small_red_triangle:+${formatted}%`;
|
||||
} else {
|
||||
return `+${formatted}%`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function row(result) {
|
||||
// prettier-ignore
|
||||
return `| ${result.path} | **${change(result.change)}** | ${kbs(result.baseSize)} | ${kbs(result.headSize)} | ${change(result.changeGzip)} | ${kbs(result.baseSizeGzip)} | ${kbs(result.headSizeGzip)}`;
|
||||
function setBoldness(row, isBold) {
|
||||
if (isBold) {
|
||||
return row.map(element => `**${element}**`);
|
||||
} else {
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the commit that represents the merge between the current branch
|
||||
* and master.
|
||||
*/
|
||||
function git(args) {
|
||||
return new Promise(res => {
|
||||
exec('git ' + args, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
res(stdout.trim());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
(async function() {
|
||||
// Use git locally to grab the commit which represents the place
|
||||
// where the branches differ
|
||||
|
||||
const upstreamRepo = danger.github.pr.base.repo.full_name;
|
||||
if (upstreamRepo !== 'facebook/react') {
|
||||
// Exit unless we're running in the main repo
|
||||
return;
|
||||
}
|
||||
|
||||
let headSha;
|
||||
let baseSha;
|
||||
markdown(`## Size changes (${RELEASE_CHANNEL})`);
|
||||
|
||||
const upstreamRef = danger.github.pr.base.ref;
|
||||
await git(`remote add upstream https://github.com/facebook/react.git`);
|
||||
await git('fetch upstream');
|
||||
const baseCommit = await git(`merge-base HEAD upstream/${upstreamRef}`);
|
||||
|
||||
let previousBuildResults = null;
|
||||
try {
|
||||
headSha = String(readFileSync(HEAD_DIR + '/COMMIT_SHA')).trim();
|
||||
baseSha = String(readFileSync(BASE_DIR + '/COMMIT_SHA')).trim();
|
||||
} catch {
|
||||
warn(
|
||||
"Failed to read build artifacts. It's possible a build configuration " +
|
||||
'has changed upstream. Try pulling the latest changes from the ' +
|
||||
'main branch.'
|
||||
let baseCIBuildId = null;
|
||||
const statusesResponse = await fetch(
|
||||
`https://api.github.com/repos/facebook/react/commits/${baseCommit}/status`
|
||||
);
|
||||
const {statuses, state} = await statusesResponse.json();
|
||||
if (state === 'failure') {
|
||||
warn(`Base commit is broken: ${baseCommit}`);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < statuses.length; i++) {
|
||||
const status = statuses[i];
|
||||
if (status.context === `ci/circleci: ${artifactsJobName}`) {
|
||||
if (status.state === 'success') {
|
||||
baseCIBuildId = /\/facebook\/react\/([0-9]+)/.exec(
|
||||
status.target_url
|
||||
)[1];
|
||||
break;
|
||||
}
|
||||
if (status.state === 'pending') {
|
||||
warn(`Build job for base commit is still pending: ${baseCommit}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (baseCIBuildId === null) {
|
||||
warn(`Could not find build artifacts for base commit: ${baseCommit}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const baseArtifactsInfoResponse = await fetch(
|
||||
`https://circleci.com/api/v1.1/project/github/facebook/react/${baseCIBuildId}/artifacts`
|
||||
);
|
||||
const baseArtifactsInfo = await baseArtifactsInfoResponse.json();
|
||||
|
||||
for (let i = 0; i < baseArtifactsInfo.length; i++) {
|
||||
const info = baseArtifactsInfo[i];
|
||||
if (info.path.endsWith('bundle-sizes.json')) {
|
||||
const resultsResponse = await fetch(info.url);
|
||||
previousBuildResults = await resultsResponse.json();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
warn(`Failed to fetch build artifacts for base commit: ${baseCommit}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable sizeBot in a Devtools Pull Request. Because that doesn't affect production bundle size.
|
||||
const commitFiles = [
|
||||
...danger.git.created_files,
|
||||
...danger.git.deleted_files,
|
||||
...danger.git.modified_files,
|
||||
];
|
||||
if (
|
||||
commitFiles.every(filename => filename.includes('packages/react-devtools'))
|
||||
)
|
||||
if (previousBuildResults === null) {
|
||||
warn(`Could not find build artifacts for base commit: ${baseCommit}`);
|
||||
return;
|
||||
|
||||
const resultsMap = new Map();
|
||||
|
||||
// Find all the head (current) artifacts paths.
|
||||
const headArtifactPaths = await glob('**/*.js', {cwd: 'build'});
|
||||
for (const artifactPath of headArtifactPaths) {
|
||||
try {
|
||||
// This will throw if there's no matching base artifact
|
||||
const baseSize = statSync(BASE_DIR + '/' + artifactPath).size;
|
||||
const baseSizeGzip = gzipSize.fileSync(BASE_DIR + '/' + artifactPath);
|
||||
|
||||
const headSize = statSync(HEAD_DIR + '/' + artifactPath).size;
|
||||
const headSizeGzip = gzipSize.fileSync(HEAD_DIR + '/' + artifactPath);
|
||||
resultsMap.set(artifactPath, {
|
||||
path: artifactPath,
|
||||
headSize,
|
||||
headSizeGzip,
|
||||
baseSize,
|
||||
baseSizeGzip,
|
||||
change: (headSize - baseSize) / baseSize,
|
||||
changeGzip: (headSizeGzip - baseSizeGzip) / baseSizeGzip,
|
||||
});
|
||||
} catch {
|
||||
// There's no matching base artifact. This is a new file.
|
||||
const baseSize = 0;
|
||||
const baseSizeGzip = 0;
|
||||
const headSize = statSync(HEAD_DIR + '/' + artifactPath).size;
|
||||
const headSizeGzip = gzipSize.fileSync(HEAD_DIR + '/' + artifactPath);
|
||||
resultsMap.set(artifactPath, {
|
||||
path: artifactPath,
|
||||
headSize,
|
||||
headSizeGzip,
|
||||
baseSize,
|
||||
baseSizeGzip,
|
||||
change: Infinity,
|
||||
changeGzip: Infinity,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check for base artifacts that were deleted in the head.
|
||||
const baseArtifactPaths = await glob('**/*.js', {cwd: 'base-build'});
|
||||
for (const artifactPath of baseArtifactPaths) {
|
||||
if (!resultsMap.has(artifactPath)) {
|
||||
const baseSize = statSync(BASE_DIR + '/' + artifactPath).size;
|
||||
const baseSizeGzip = gzipSize.fileSync(BASE_DIR + '/' + artifactPath);
|
||||
const headSize = 0;
|
||||
const headSizeGzip = 0;
|
||||
resultsMap.set(artifactPath, {
|
||||
path: artifactPath,
|
||||
headSize,
|
||||
headSizeGzip,
|
||||
baseSize,
|
||||
baseSizeGzip,
|
||||
change: -1,
|
||||
changeGzip: -1,
|
||||
});
|
||||
}
|
||||
}
|
||||
// Take the JSON of the build response and
|
||||
// make an array comparing the results for printing
|
||||
const results = generateResultsArray(
|
||||
currentBuildResults,
|
||||
previousBuildResults
|
||||
);
|
||||
|
||||
const results = Array.from(resultsMap.values());
|
||||
results.sort((a, b) => b.change - a.change);
|
||||
const packagesToShow = results
|
||||
.filter(
|
||||
r =>
|
||||
Math.abs(r.prevFileSizeAbsoluteChange) >= 300 || // bytes
|
||||
Math.abs(r.prevGzipSizeAbsoluteChange) >= 100 // bytes
|
||||
)
|
||||
.map(r => r.packageName);
|
||||
|
||||
let criticalResults = [];
|
||||
for (const artifactPath of CRITICAL_ARTIFACT_PATHS) {
|
||||
const result = resultsMap.get(artifactPath);
|
||||
if (result === undefined) {
|
||||
throw new Error(
|
||||
'Missing expected bundle. If this was an intentional change to the ' +
|
||||
'build configuration, update Dangerfile.js accordingly: ' +
|
||||
artifactPath
|
||||
if (packagesToShow.length) {
|
||||
let allTables = [];
|
||||
|
||||
// Highlight React and React DOM changes inline
|
||||
// e.g. react: `react.production.min.js`: -3%, `react.development.js`: +4%
|
||||
|
||||
if (packagesToShow.includes('react')) {
|
||||
const reactProd = results.find(
|
||||
r => r.bundleType === 'UMD_PROD' && r.packageName === 'react'
|
||||
);
|
||||
if (
|
||||
reactProd.prevFileSizeChange !== 0 ||
|
||||
reactProd.prevGzipSizeChange !== 0
|
||||
) {
|
||||
const changeSize = addPercent(reactProd.prevFileSizeChange, true);
|
||||
const changeGzip = addPercent(reactProd.prevGzipSizeChange, true);
|
||||
markdown(`React: size: ${changeSize}, gzip: ${changeGzip}`);
|
||||
}
|
||||
}
|
||||
criticalResults.push(row(result));
|
||||
|
||||
if (packagesToShow.includes('react-dom')) {
|
||||
const reactDOMProd = results.find(
|
||||
r => r.bundleType === 'UMD_PROD' && r.packageName === 'react-dom'
|
||||
);
|
||||
if (
|
||||
reactDOMProd.prevFileSizeChange !== 0 ||
|
||||
reactDOMProd.prevGzipSizeChange !== 0
|
||||
) {
|
||||
const changeSize = addPercent(reactDOMProd.prevFileSizeChange, true);
|
||||
const changeGzip = addPercent(reactDOMProd.prevGzipSizeChange, true);
|
||||
markdown(`ReactDOM: size: ${changeSize}, gzip: ${changeGzip}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Show a hidden summary table for all diffs
|
||||
|
||||
// eslint-disable-next-line no-var,no-for-of-loops/no-for-of-loops
|
||||
for (var name of new Set(packagesToShow)) {
|
||||
const thisBundleResults = results.filter(r => r.packageName === name);
|
||||
const changedFiles = thisBundleResults.filter(
|
||||
r => r.prevFileSizeChange !== 0 || r.prevGzipSizeChange !== 0
|
||||
);
|
||||
|
||||
const mdHeaders = [
|
||||
'File',
|
||||
'Filesize Diff',
|
||||
'Gzip Diff',
|
||||
'Prev Size',
|
||||
'Current Size',
|
||||
'Prev Gzip',
|
||||
'Current Gzip',
|
||||
'ENV',
|
||||
];
|
||||
|
||||
const mdRows = changedFiles.map(r => {
|
||||
const isProd = r.bundleType.includes('PROD');
|
||||
return setBoldness(
|
||||
[
|
||||
r.filename,
|
||||
addPercent(r.prevFileSizeChange, isProd),
|
||||
addPercent(r.prevGzipSizeChange, isProd),
|
||||
r.prevSize,
|
||||
r.prevFileSize,
|
||||
r.prevGzip,
|
||||
r.prevGzipSize,
|
||||
r.bundleType,
|
||||
],
|
||||
isProd
|
||||
);
|
||||
});
|
||||
|
||||
allTables.push(`\n## ${name}`);
|
||||
allTables.push(generateMDTable(mdHeaders, mdRows));
|
||||
}
|
||||
|
||||
const summary = `
|
||||
<details>
|
||||
<summary>Details of bundled changes.</summary>
|
||||
|
||||
<p>Comparing: ${baseCommit}...${danger.github.pr.head.sha}</p>
|
||||
|
||||
|
||||
${allTables.join('\n')}
|
||||
|
||||
</details>
|
||||
`;
|
||||
markdown(summary);
|
||||
} else {
|
||||
markdown('No significant bundle size changes to report.');
|
||||
}
|
||||
|
||||
let significantResults = [];
|
||||
for (const result of results) {
|
||||
// If result exceeds critical threshold, add to top section.
|
||||
if (
|
||||
(result.change > CRITICAL_THRESHOLD ||
|
||||
0 - result.change > CRITICAL_THRESHOLD ||
|
||||
// New file
|
||||
result.change === Infinity ||
|
||||
// Deleted file
|
||||
result.change === -1) &&
|
||||
// Skip critical artifacts. We added those earlier, in a fixed order.
|
||||
!CRITICAL_ARTIFACT_PATHS.has(result.path)
|
||||
) {
|
||||
criticalResults.push(row(result));
|
||||
}
|
||||
|
||||
// Do the same for results that exceed the significant threshold. These
|
||||
// will go into the bottom, collapsed section. Intentionally including
|
||||
// critical artifacts in this section, too.
|
||||
if (
|
||||
result.change > SIGNIFICANCE_THRESHOLD ||
|
||||
0 - result.change > SIGNIFICANCE_THRESHOLD ||
|
||||
result.change === Infinity ||
|
||||
result.change === -1
|
||||
) {
|
||||
significantResults.push(row(result));
|
||||
}
|
||||
}
|
||||
|
||||
markdown(`
|
||||
Comparing: ${baseSha}...${headSha}
|
||||
|
||||
## Critical size changes
|
||||
|
||||
Includes critical production bundles, as well as any change greater than ${CRITICAL_THRESHOLD *
|
||||
100}%:
|
||||
|
||||
${header}
|
||||
${criticalResults.join('\n')}
|
||||
|
||||
## Significant size changes
|
||||
|
||||
Includes any change greater than ${SIGNIFICANCE_THRESHOLD * 100}%:
|
||||
|
||||
${
|
||||
significantResults.length > 0
|
||||
? `
|
||||
<details>
|
||||
<summary>Expand to show</summary>
|
||||
${header}
|
||||
${significantResults.join('\n')}
|
||||
</details>
|
||||
`
|
||||
: '(No significant changes)'
|
||||
}
|
||||
`);
|
||||
})();
|
||||
|
||||
@@ -28,4 +28,4 @@ Right now, we use a purple outline to call out cases where the assigned property
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
You can find the guide for how to do things in a CRA [here](https://github.com/facebook/create-react-app/blob/main/packages/cra-template/template/README.md).
|
||||
You can find the guide for how to do things in a CRA [here](https://github.com/facebook/create-react-app/blob/master/packages/cra-template/template/README.md).
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {createRoot} from 'react-dom';
|
||||
import {unstable_createRoot as createRoot} from 'react-dom';
|
||||
import './index.css';
|
||||
import Router from './Router';
|
||||
|
||||
|
||||
@@ -10,22 +10,10 @@ It depends on a local build of React and enables us to easily test async "time s
|
||||
|
||||
No. The APIs being tested here are unstable and some of them have still not been released to NPM. For now, this fixture is only a test harness.
|
||||
|
||||
There are also known bugs and inefficiencies in main so **don't use this fixture for demonstration purposes either yet**. Until they are fixed, this fixture is **not** indicative of React async rendering performance.
|
||||
There are also known bugs and inefficiencies in master so **don't use this fixture for demonstration purposes either yet**. Until they are fixed, this fixture is **not** indicative of React async rendering performance.
|
||||
|
||||
## How do I run this fixture?
|
||||
|
||||
### From npm version
|
||||
|
||||
```
|
||||
# 1: Install fixture dependencies
|
||||
cd fixtures/unstable-async/time-slicing/
|
||||
yarn
|
||||
|
||||
# 2: Run the app
|
||||
yarn start
|
||||
```
|
||||
|
||||
### From React source code
|
||||
```shell
|
||||
# 1: Build react from source
|
||||
cd /path/to/react
|
||||
@@ -36,9 +24,6 @@ yarn build react-dom/index,react/index,react-cache,scheduler --type=NODE
|
||||
cd fixtures/unstable-async/time-slicing/
|
||||
yarn
|
||||
|
||||
# 3: Copy React source code over
|
||||
yarn copy-source
|
||||
|
||||
# 3: Run the app
|
||||
yarn start
|
||||
```
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"glamor": "^2.20.40",
|
||||
"react": "0.0.0-experimental-269dd6ec5",
|
||||
"react-dom": "0.0.0-experimental-269dd6ec5",
|
||||
"react-markdown": "^3.2.0",
|
||||
"react-scripts": "^1.1.4",
|
||||
"victory": "^0.25.6"
|
||||
},
|
||||
"scripts": {
|
||||
"copy-source": "cp -r ../../../build/node_modules/* ./node_modules/",
|
||||
"prestart": "cp -r ../../../build/node_modules/* ./node_modules/",
|
||||
"prebuild": "cp -r ../../../build/node_modules/* ./node_modules/",
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
|
||||
@@ -32,18 +32,16 @@ body {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
zoom: 1.8;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
label {
|
||||
zoom: 1;
|
||||
margin-right: 50px;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
label.selected {
|
||||
font-weight: bold;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
label:nth-child(1).selected {
|
||||
@@ -58,10 +56,6 @@ label:nth-child(3).selected {
|
||||
color: #61dafb;
|
||||
}
|
||||
|
||||
input[type="radio" i]:nth-child(1) {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, {PureComponent, unstable_startTransition} from 'react';
|
||||
import {createRoot} from 'react-dom';
|
||||
import React, {PureComponent} from 'react';
|
||||
import {flushSync, createRoot} from 'react-dom';
|
||||
import Scheduler from 'scheduler';
|
||||
import _ from 'lodash';
|
||||
import Charts from './Charts';
|
||||
import Clock from './Clock';
|
||||
@@ -54,9 +55,11 @@ class App extends PureComponent {
|
||||
return;
|
||||
}
|
||||
if (this.state.strategy !== 'async') {
|
||||
this.setState(state => ({
|
||||
showDemo: !state.showDemo,
|
||||
}));
|
||||
flushSync(() => {
|
||||
this.setState(state => ({
|
||||
showDemo: !state.showDemo,
|
||||
}));
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this._ignoreClick) {
|
||||
@@ -64,7 +67,7 @@ class App extends PureComponent {
|
||||
}
|
||||
this._ignoreClick = true;
|
||||
|
||||
unstable_startTransition(() => {
|
||||
Scheduler.unstable_next(() => {
|
||||
this.setState({showDemo: true}, () => {
|
||||
this._ignoreClick = false;
|
||||
});
|
||||
@@ -73,7 +76,9 @@ class App extends PureComponent {
|
||||
|
||||
debouncedHandleChange = _.debounce(value => {
|
||||
if (this.state.strategy === 'debounced') {
|
||||
this.setState({value: value});
|
||||
flushSync(() => {
|
||||
this.setState({value: value});
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
@@ -103,9 +108,9 @@ class App extends PureComponent {
|
||||
break;
|
||||
case 'async':
|
||||
// TODO: useTransition hook instead.
|
||||
unstable_startTransition(() => {
|
||||
setTimeout(() => {
|
||||
this.setState({value});
|
||||
});
|
||||
}, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,7 @@ if (typeof SchedulerTracing !== 'undefined') {
|
||||
trace = (_, __, callback) => callback();
|
||||
}
|
||||
|
||||
// https://github.com/facebook/react/blob/main/CHANGELOG.md
|
||||
// https://github.com/facebook/react/blob/master/CHANGELOG.md
|
||||
switch (major) {
|
||||
case 16:
|
||||
switch (minor) {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
dependencies
|
||||
@@ -1,15 +0,0 @@
|
||||
# Test fixture for `packages/react-devtools-scheduling-profiler`
|
||||
|
||||
1. First, run the fixture:
|
||||
```sh
|
||||
# In the root directory
|
||||
# Download the latest *experimental* React build
|
||||
scripts/release/download-experimental-build.js
|
||||
|
||||
# Run this fixtures
|
||||
fixtures/devtools/scheduling-profiler/run.js
|
||||
```
|
||||
|
||||
2. Then open [localhost:8000/](http://localhost:8000/) and use the Performance tab in Chrome to reload-and-profile.
|
||||
3. Now stop profiling and export JSON.
|
||||
4. Lastly, open [react-scheduling-profiler.vercel.app](https://react-scheduling-profiler.vercel.app/) and upload the performance JSON data you just recorded.
|
||||
@@ -1,14 +0,0 @@
|
||||
const {createElement, useLayoutEffect, useState} = React;
|
||||
const {createRoot} = ReactDOM;
|
||||
|
||||
function App() {
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
useLayoutEffect(() => {
|
||||
setIsMounted(true);
|
||||
}, []);
|
||||
return createElement('div', null, `isMounted? ${isMounted}`);
|
||||
}
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const root = createRoot(container);
|
||||
root.render(createElement(App));
|
||||
@@ -1,14 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Scheduling Profiler Fixture</title>
|
||||
|
||||
<script src="./scheduler.js"></script>
|
||||
<script src="./react.js"></script>
|
||||
<script src="./react-dom.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script src="./app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,78 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
copyFileSync,
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
readFileSync,
|
||||
rmdirSync,
|
||||
} = require('fs');
|
||||
const {join} = require('path');
|
||||
const http = require('http');
|
||||
|
||||
const DEPENDENCIES = [
|
||||
['scheduler/umd/scheduler.development.js', 'scheduler.js'],
|
||||
['react/umd/react.development.js', 'react.js'],
|
||||
['react-dom/umd/react-dom.development.js', 'react-dom.js'],
|
||||
];
|
||||
|
||||
const BUILD_DIRECTORY = '../../../build/node_modules/';
|
||||
const DEPENDENCIES_DIRECTORY = 'dependencies';
|
||||
|
||||
function initDependencies() {
|
||||
if (existsSync(DEPENDENCIES_DIRECTORY)) {
|
||||
rmdirSync(DEPENDENCIES_DIRECTORY, {recursive: true});
|
||||
}
|
||||
mkdirSync(DEPENDENCIES_DIRECTORY);
|
||||
|
||||
DEPENDENCIES.forEach(([from, to]) => {
|
||||
const fromPath = join(__dirname, BUILD_DIRECTORY, from);
|
||||
const toPath = join(__dirname, DEPENDENCIES_DIRECTORY, to);
|
||||
console.log(`Copying ${fromPath} => ${toPath}`);
|
||||
copyFileSync(fromPath, toPath);
|
||||
});
|
||||
}
|
||||
|
||||
function initServer() {
|
||||
const host = 'localhost';
|
||||
const port = 8000;
|
||||
|
||||
const requestListener = function(request, response) {
|
||||
let contents;
|
||||
switch (request.url) {
|
||||
case '/react.js':
|
||||
case '/react-dom.js':
|
||||
case '/scheduler.js':
|
||||
response.setHeader('Content-Type', 'text/javascript');
|
||||
response.writeHead(200);
|
||||
contents = readFileSync(
|
||||
join(__dirname, DEPENDENCIES_DIRECTORY, request.url)
|
||||
);
|
||||
response.end(contents);
|
||||
break;
|
||||
case '/app.js':
|
||||
response.setHeader('Content-Type', 'text/javascript');
|
||||
response.writeHead(200);
|
||||
contents = readFileSync(join(__dirname, 'app.js'));
|
||||
response.end(contents);
|
||||
break;
|
||||
case '/index.html':
|
||||
default:
|
||||
response.setHeader('Content-Type', 'text/html');
|
||||
response.writeHead(200);
|
||||
contents = readFileSync(join(__dirname, 'index.html'));
|
||||
response.end(contents);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const server = http.createServer(requestListener);
|
||||
server.listen(port, host, () => {
|
||||
console.log(`Server is running on http://${host}:${port}`);
|
||||
});
|
||||
}
|
||||
|
||||
initDependencies();
|
||||
initServer();
|
||||
@@ -20,7 +20,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"prestart": "cp ../../build/oss-stable/scheduler/umd/scheduler-unstable_mock.development.js ../../build/oss-stable/scheduler/umd/scheduler-unstable_mock.production.min.js ../../build/oss-stable/react/umd/react.development.js ../../build/oss-stable/react-dom/umd/react-dom.development.js ../../build/oss-stable/react/umd/react.production.min.js ../../build/oss-stable/react-dom/umd/react-dom.production.min.js ../../build/oss-stable/react-dom/umd/react-dom-server.browser.development.js ../../build/oss-stable/react-dom/umd/react-dom-server.browser.production.min.js ../../build/oss-stable/react-dom/umd/react-dom-test-utils.development.js ../../build/oss-stable/react-dom/umd/react-dom-test-utils.production.min.js public/ && cp -a ../../build/oss-stable/. node_modules",
|
||||
"prestart": "cp ../../build/node_modules/scheduler/umd/scheduler-unstable_mock.development.js ../../build/node_modules/scheduler/umd/scheduler-unstable_mock.production.min.js ../../build/node_modules/react/umd/react.development.js ../../build/node_modules/react-dom/umd/react-dom.development.js ../../build/node_modules/react/umd/react.production.min.js ../../build/node_modules/react-dom/umd/react-dom.production.min.js ../../build/node_modules/react-dom/umd/react-dom-server.browser.development.js ../../build/node_modules/react-dom/umd/react-dom-server.browser.production.min.js ../../build/node_modules/react-dom/umd/react-dom-test-utils.development.js ../../build/node_modules/react-dom/umd/react-dom-test-utils.production.min.js public/ && cp -a ../../build/node_modules/. node_modules",
|
||||
"build": "react-scripts build && cp build/index.html build/200.html",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject"
|
||||
|
||||
@@ -48,7 +48,7 @@ describe('unmocked scheduler', () => {
|
||||
TestAct(() => {
|
||||
TestRenderer.create(<Effecty />);
|
||||
});
|
||||
expect(log).toEqual([]);
|
||||
expect(log).toEqual(['called']);
|
||||
});
|
||||
expect(log).toEqual(['called']);
|
||||
});
|
||||
|
||||
201
fixtures/dom/src/__tests__/wrong-act-test.js
Normal file
201
fixtures/dom/src/__tests__/wrong-act-test.js
Normal file
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactART;
|
||||
let TestUtils;
|
||||
let ARTSVGMode;
|
||||
let ARTCurrentMode;
|
||||
let TestRenderer;
|
||||
let ARTTest;
|
||||
|
||||
global.__DEV__ = process.env.NODE_ENV !== 'production';
|
||||
global.__EXPERIMENTAL__ = process.env.RELEASE_CHANNEL === 'experimental';
|
||||
|
||||
expect.extend(require('../toWarnDev'));
|
||||
|
||||
function App(props) {
|
||||
return 'hello world';
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
ReactART = require('react-art');
|
||||
ARTSVGMode = require('art/modes/svg');
|
||||
ARTCurrentMode = require('art/modes/current');
|
||||
TestRenderer = require('react-test-renderer');
|
||||
|
||||
ARTCurrentMode.setCurrent(ARTSVGMode);
|
||||
|
||||
ARTTest = function ARTTestComponent(props) {
|
||||
return (
|
||||
<ReactART.Surface width={150} height={200}>
|
||||
<ReactART.Group>
|
||||
<ReactART.Shape
|
||||
d="M0,0l50,0l0,50l-50,0z"
|
||||
fill={new ReactART.LinearGradient(['black', 'white'])}
|
||||
key="a"
|
||||
width={50}
|
||||
height={50}
|
||||
x={50}
|
||||
y={50}
|
||||
opacity={0.1}
|
||||
/>
|
||||
<ReactART.Shape
|
||||
fill="#3C5A99"
|
||||
key="b"
|
||||
scale={0.5}
|
||||
x={50}
|
||||
y={50}
|
||||
title="This is an F"
|
||||
cursor="pointer">
|
||||
M64.564,38.583H54l0.008-5.834c0-3.035,0.293-4.666,4.657-4.666
|
||||
h5.833V16.429h-9.33c-11.213,0-15.159,5.654-15.159,15.16v6.994
|
||||
h-6.99v11.652h6.99v33.815H54V50.235h9.331L64.564,38.583z
|
||||
</ReactART.Shape>
|
||||
</ReactART.Group>
|
||||
</ReactART.Surface>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
it("doesn't warn when you use the right act + renderer: dom", () => {
|
||||
TestUtils.act(() => {
|
||||
ReactDOM.render(<App />, document.createElement('div'));
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't warn when you use the right act + renderer: test", () => {
|
||||
TestRenderer.act(() => {
|
||||
TestRenderer.create(<App />);
|
||||
});
|
||||
});
|
||||
|
||||
it('resets correctly across renderers', () => {
|
||||
function Effecty() {
|
||||
React.useEffect(() => {}, []);
|
||||
return null;
|
||||
}
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.act(() => {});
|
||||
expect(() => {
|
||||
TestRenderer.create(<Effecty />);
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - test + dom: render', () => {
|
||||
expect(() => {
|
||||
TestRenderer.act(() => {
|
||||
ReactDOM.render(<App />, document.createElement('div'));
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - test + dom: updates', () => {
|
||||
let setCtr;
|
||||
function Counter(props) {
|
||||
const [ctr, _setCtr] = React.useState(0);
|
||||
setCtr = _setCtr;
|
||||
return ctr;
|
||||
}
|
||||
ReactDOM.render(<Counter />, document.createElement('div'));
|
||||
expect(() => {
|
||||
TestRenderer.act(() => {
|
||||
setCtr(1);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - dom + test: .create()', () => {
|
||||
expect(() => {
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.create(<App />);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - dom + test: .update()', () => {
|
||||
const root = TestRenderer.create(<App key="one" />);
|
||||
expect(() => {
|
||||
TestUtils.act(() => {
|
||||
root.update(<App key="two" />);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - dom + test: updates', () => {
|
||||
let setCtr;
|
||||
function Counter(props) {
|
||||
const [ctr, _setCtr] = React.useState(0);
|
||||
setCtr = _setCtr;
|
||||
return ctr;
|
||||
}
|
||||
TestRenderer.create(<Counter />);
|
||||
expect(() => {
|
||||
TestUtils.act(() => {
|
||||
setCtr(1);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('does not warn when nesting react-act inside react-dom', () => {
|
||||
TestUtils.act(() => {
|
||||
ReactDOM.render(<ARTTest />, document.createElement('div'));
|
||||
});
|
||||
});
|
||||
|
||||
it('does not warn when nesting react-act inside react-test-renderer', () => {
|
||||
TestRenderer.act(() => {
|
||||
TestRenderer.create(<ARTTest />);
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't warn if you use nested acts from different renderers", () => {
|
||||
TestRenderer.act(() => {
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.create(<App />);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (__EXPERIMENTAL__) {
|
||||
it('warns when using createRoot() + .render', () => {
|
||||
const root = ReactDOM.unstable_createRoot(document.createElement('div'));
|
||||
expect(() => {
|
||||
TestRenderer.act(() => {
|
||||
root.render(<App />);
|
||||
});
|
||||
}).toWarnDev(
|
||||
[
|
||||
'In Concurrent or Sync modes, the "scheduler" module needs to be mocked',
|
||||
"It looks like you're using the wrong act()",
|
||||
],
|
||||
{
|
||||
withoutStack: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -41,7 +41,7 @@ class ErrorBoundary extends React.Component {
|
||||
if (this.state.error) {
|
||||
return <p>Captured an error: {this.state.error.message}</p>;
|
||||
} else {
|
||||
return <p>Captured an error: {String(this.state.error)}</p>;
|
||||
return <p>Captured an error: {'' + this.state.error}</p>;
|
||||
}
|
||||
}
|
||||
if (this.state.shouldThrow) {
|
||||
@@ -241,20 +241,6 @@ class TrySilenceFatalError extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
function naiveMemoize(fn) {
|
||||
let memoizedEntry;
|
||||
return function() {
|
||||
if (!memoizedEntry) {
|
||||
memoizedEntry = {result: null};
|
||||
memoizedEntry.result = fn();
|
||||
}
|
||||
return memoizedEntry.result;
|
||||
};
|
||||
}
|
||||
let memoizedFunction = naiveMemoize(function() {
|
||||
throw new Error('Passed');
|
||||
});
|
||||
|
||||
export default class ErrorHandlingTestCases extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
@@ -302,21 +288,6 @@ export default class ErrorHandlingTestCases extends React.Component {
|
||||
}}
|
||||
/>
|
||||
</TestCase>
|
||||
<TestCase title="Throwing memoized result" description="">
|
||||
<TestCase.Steps>
|
||||
<li>Click the "Trigger error" button</li>
|
||||
<li>Click the reset button</li>
|
||||
</TestCase.Steps>
|
||||
<TestCase.ExpectedResult>
|
||||
The "Trigger error" button should be replaced with "Captured an
|
||||
error: Passed". Clicking reset should reset the test case.
|
||||
</TestCase.ExpectedResult>
|
||||
<Example
|
||||
doThrow={() => {
|
||||
memoizedFunction().value;
|
||||
}}
|
||||
/>
|
||||
</TestCase>
|
||||
<TestCase
|
||||
title="Cross-origin errors (development mode only)"
|
||||
description="">
|
||||
|
||||
@@ -21,7 +21,6 @@ export default class MediaEvents extends React.Component {
|
||||
onPlaying: false,
|
||||
onProgress: false,
|
||||
onRateChange: false,
|
||||
onResize: false,
|
||||
onSeeked: false,
|
||||
onSeeking: false,
|
||||
onSuspend: false,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,18 +17,10 @@
|
||||
</p>
|
||||
</div>
|
||||
<script src="../../build/node_modules/react/umd/react.development.js"></script>
|
||||
<script src="../../build/node_modules/react-dom/umd/react-dom-server.browser.development.js"></script>
|
||||
<script src="../../build/node_modules/react-dom/umd/react-dom-unstable-fizz.browser.development.js"></script>
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
|
||||
<script type="text/babel">
|
||||
let controller = new AbortController();
|
||||
let stream = ReactDOMServer.renderToReadableStream(
|
||||
<html>
|
||||
<body>Success</body>
|
||||
</html>,
|
||||
{
|
||||
signal: controller.signal,
|
||||
}
|
||||
);
|
||||
let stream = ReactDOMFizzServer.renderToReadableStream(<body>Success</body>);
|
||||
let response = new Response(stream, {
|
||||
headers: {'Content-Type': 'text/html'},
|
||||
});
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
<script src="../../build/node_modules/react/umd/react.development.js"></script>
|
||||
<script src="../../build/node_modules/react-dom/umd/react-dom.development.js"></script>
|
||||
<script src="../../build/node_modules/react-dom/umd/react-dom-server.browser.development.js"></script>
|
||||
<script src="../../build/node_modules/react-server-dom-webpack/umd/react-server-dom-webpack-writer.browser.development.server.js"></script>
|
||||
<script src="../../build/node_modules/react-server-dom-webpack/umd/react-server-dom-webpack.development.js"></script>
|
||||
<script src="../../build/node_modules/react-transport-dom-webpack/umd/react-transport-dom-webpack-server.browser.development.js"></script>
|
||||
<script src="../../build/node_modules/react-transport-dom-webpack/umd/react-transport-dom-webpack.development.js"></script>
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
|
||||
<script type="text/babel">
|
||||
let Suspense = React.Suspense;
|
||||
@@ -60,7 +60,7 @@
|
||||
content: <HTML />,
|
||||
};
|
||||
|
||||
let stream = ReactServerDOMWriter.renderToReadableStream(model);
|
||||
let stream = ReactTransportDOMServer.renderToReadableStream(model);
|
||||
let response = new Response(stream, {
|
||||
headers: {'Content-Type': 'text/html'},
|
||||
});
|
||||
@@ -70,13 +70,13 @@
|
||||
let blob = await responseToDisplay.blob();
|
||||
let url = URL.createObjectURL(blob);
|
||||
|
||||
let data = ReactServerDOMReader.createFromFetch(
|
||||
let data = ReactTransportDOMClient.createFromFetch(
|
||||
fetch(url)
|
||||
);
|
||||
// The client also supports XHR streaming.
|
||||
// var xhr = new XMLHttpRequest();
|
||||
// xhr.open('GET', url);
|
||||
// let data = ReactServerDOMReader.createFromXHR(xhr);
|
||||
// let data = ReactTransportDOMClient.createFromXHR(xhr);
|
||||
// xhr.send();
|
||||
|
||||
renderResult(data);
|
||||
|
||||
2
fixtures/flight/.gitignore
vendored
2
fixtures/flight/.gitignore
vendored
@@ -10,8 +10,6 @@
|
||||
|
||||
# production
|
||||
/build
|
||||
/dist
|
||||
.eslintcache
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
@@ -17,11 +17,11 @@ if (!NODE_ENV) {
|
||||
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
|
||||
const dotenvFiles = [
|
||||
`${paths.dotenv}.${NODE_ENV}.local`,
|
||||
`${paths.dotenv}.${NODE_ENV}`,
|
||||
// Don't include `.env.local` for `test` environment
|
||||
// since normally you expect tests to produce the same
|
||||
// results for everyone
|
||||
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
|
||||
`${paths.dotenv}.${NODE_ENV}`,
|
||||
paths.dotenv,
|
||||
].filter(Boolean);
|
||||
|
||||
@@ -46,7 +46,7 @@ dotenvFiles.forEach(dotenvFile => {
|
||||
// It works similar to `NODE_PATH` in Node itself:
|
||||
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
|
||||
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
|
||||
// Otherwise, we risk importing Node.js core modules into an app instead of webpack shims.
|
||||
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
|
||||
// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
|
||||
// We also resolve them to make sure all tools using them work consistently.
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
@@ -57,7 +57,7 @@ process.env.NODE_PATH = (process.env.NODE_PATH || '')
|
||||
.join(path.delimiter);
|
||||
|
||||
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
|
||||
// injected into the application via DefinePlugin in webpack configuration.
|
||||
// injected into the application via DefinePlugin in Webpack configuration.
|
||||
const REACT_APP = /^REACT_APP_/i;
|
||||
|
||||
function getClientEnvironment(publicUrl) {
|
||||
@@ -77,22 +77,9 @@ function getClientEnvironment(publicUrl) {
|
||||
// This should only be used as an escape hatch. Normally you would put
|
||||
// images into the `src` and `import` them in code to get their paths.
|
||||
PUBLIC_URL: publicUrl,
|
||||
// We support configuring the sockjs pathname during development.
|
||||
// These settings let a developer run multiple simultaneous projects.
|
||||
// They are used as the connection `hostname`, `pathname` and `port`
|
||||
// in webpackHotDevClient. They are used as the `sockHost`, `sockPath`
|
||||
// and `sockPort` options in webpack-dev-server.
|
||||
WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,
|
||||
WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
|
||||
WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
|
||||
// Whether or not react-refresh is enabled.
|
||||
// react-refresh is not 100% stable at this time,
|
||||
// which is why it's disabled by default.
|
||||
// It is defined here so it is available in the webpackHotDevClient.
|
||||
FAST_REFRESH: process.env.FAST_REFRESH !== 'false',
|
||||
}
|
||||
);
|
||||
// Stringify all values so we can feed into webpack DefinePlugin
|
||||
// Stringify all values so we can feed into Webpack DefinePlugin
|
||||
const stringified = {
|
||||
'process.env': Object.keys(raw).reduce((env, key) => {
|
||||
env[key] = JSON.stringify(raw[key]);
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const paths = require('./paths');
|
||||
|
||||
// Ensure the certificate and key provided are valid and if not
|
||||
// throw an easy to debug error
|
||||
function validateKeyAndCerts({cert, key, keyFile, crtFile}) {
|
||||
let encrypted;
|
||||
try {
|
||||
// publicEncrypt will throw an error with an invalid cert
|
||||
encrypted = crypto.publicEncrypt(cert, Buffer.from('test'));
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// privateDecrypt will throw an error with an invalid key
|
||||
crypto.privateDecrypt(key, encrypted);
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${
|
||||
err.message
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Read file and throw an error if it doesn't exist
|
||||
function readEnvFile(file, type) {
|
||||
if (!fs.existsSync(file)) {
|
||||
throw new Error(
|
||||
`You specified ${chalk.cyan(
|
||||
type
|
||||
)} in your env, but the file "${chalk.yellow(file)}" can't be found.`
|
||||
);
|
||||
}
|
||||
return fs.readFileSync(file);
|
||||
}
|
||||
|
||||
// Get the https config
|
||||
// Return cert files if provided in env, otherwise just true or false
|
||||
function getHttpsConfig() {
|
||||
const {SSL_CRT_FILE, SSL_KEY_FILE, HTTPS} = process.env;
|
||||
const isHttps = HTTPS === 'true';
|
||||
|
||||
if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) {
|
||||
const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE);
|
||||
const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE);
|
||||
const config = {
|
||||
cert: readEnvFile(crtFile, 'SSL_CRT_FILE'),
|
||||
key: readEnvFile(keyFile, 'SSL_KEY_FILE'),
|
||||
};
|
||||
|
||||
validateKeyAndCerts({...config, keyFile, crtFile});
|
||||
return config;
|
||||
}
|
||||
return isHttps;
|
||||
}
|
||||
|
||||
module.exports = getHttpsConfig;
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
// This is a custom Jest transformer turning style imports into empty objects.
|
||||
// http://facebook.github.io/jest/docs/en/webpack.html
|
||||
// https://jestjs.io/docs/en/webpack.html
|
||||
|
||||
module.exports = {
|
||||
process() {
|
||||
|
||||
@@ -4,7 +4,7 @@ const path = require('path');
|
||||
const camelcase = require('camelcase');
|
||||
|
||||
// This is a custom Jest transformer turning file imports into filenames.
|
||||
// http://facebook.github.io/jest/docs/en/webpack.html
|
||||
// https://jestjs.io/docs/en/webpack.html
|
||||
|
||||
module.exports = {
|
||||
process(src, filename) {
|
||||
|
||||
@@ -14,8 +14,15 @@ const resolve = require('resolve');
|
||||
function getAdditionalModulePaths(options = {}) {
|
||||
const baseUrl = options.baseUrl;
|
||||
|
||||
if (!baseUrl) {
|
||||
return '';
|
||||
// We need to explicitly check for null and undefined (and not a falsy value) because
|
||||
// TypeScript treats an empty string as `.`.
|
||||
if (baseUrl == null) {
|
||||
// If there's no baseUrl set we respect NODE_PATH
|
||||
// Note that NODE_PATH is deprecated and will be removed
|
||||
// in the next major release of create-react-app.
|
||||
|
||||
const nodePath = process.env.NODE_PATH || '';
|
||||
return nodePath.split(path.delimiter).filter(Boolean);
|
||||
}
|
||||
|
||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||
@@ -86,7 +93,7 @@ function getJestAliases(options = {}) {
|
||||
|
||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
||||
return {
|
||||
'^src/(.*)$': '<rootDir>/src/$1',
|
||||
'src/(.*)$': '<rootDir>/src/$1',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"type": "commonjs"
|
||||
}
|
||||
@@ -2,24 +2,41 @@
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');
|
||||
const url = require('url');
|
||||
|
||||
// Make sure any symlinks in the project folder are resolved:
|
||||
// https://github.com/facebook/create-react-app/issues/637
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
|
||||
|
||||
const envPublicUrl = process.env.PUBLIC_URL;
|
||||
|
||||
function ensureSlash(inputPath, needsSlash) {
|
||||
const hasSlash = inputPath.endsWith('/');
|
||||
if (hasSlash && !needsSlash) {
|
||||
return inputPath.substr(0, inputPath.length - 1);
|
||||
} else if (!hasSlash && needsSlash) {
|
||||
return `${inputPath}/`;
|
||||
} else {
|
||||
return inputPath;
|
||||
}
|
||||
}
|
||||
|
||||
const getPublicUrl = appPackageJson =>
|
||||
envPublicUrl || require(appPackageJson).homepage;
|
||||
|
||||
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
|
||||
// "public path" at which the app is served.
|
||||
// webpack needs to know it to put the right <script> hrefs into HTML even in
|
||||
// Webpack needs to know it to put the right <script> hrefs into HTML even in
|
||||
// single-page apps that may serve index.html for nested URLs like /todos/42.
|
||||
// We can't use a relative path in HTML because we don't want to load something
|
||||
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
|
||||
const publicUrlOrPath = getPublicUrlOrPath(
|
||||
process.env.NODE_ENV === 'development',
|
||||
require(resolveApp('package.json')).homepage,
|
||||
process.env.PUBLIC_URL
|
||||
);
|
||||
function getServedPath(appPackageJson) {
|
||||
const publicUrl = getPublicUrl(appPackageJson);
|
||||
const servedUrl =
|
||||
envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : '/');
|
||||
return ensureSlash(servedUrl, true);
|
||||
}
|
||||
|
||||
const moduleFileExtensions = [
|
||||
'web.mjs',
|
||||
@@ -64,8 +81,8 @@ module.exports = {
|
||||
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
|
||||
proxySetup: resolveApp('src/setupProxy.js'),
|
||||
appNodeModules: resolveApp('node_modules'),
|
||||
swSrc: resolveModule(resolveApp, 'src/service-worker'),
|
||||
publicUrlOrPath,
|
||||
publicUrl: getPublicUrl(resolveApp('package.json')),
|
||||
servedPath: getServedPath(resolveApp('package.json')),
|
||||
};
|
||||
|
||||
module.exports.moduleFileExtensions = moduleFileExtensions;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
// Fork Start
|
||||
const ReactFlightWebpackPlugin = require('react-server-dom-webpack/plugin');
|
||||
const ReactFlightWebpackPlugin = require('react-transport-dom-webpack/plugin');
|
||||
// Fork End
|
||||
|
||||
const fs = require('fs');
|
||||
const isWsl = require('is-wsl');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const resolve = require('resolve');
|
||||
@@ -22,14 +23,13 @@ const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
|
||||
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
|
||||
const ESLintPlugin = require('eslint-webpack-plugin');
|
||||
const paths = require('./paths');
|
||||
const modules = require('./modules');
|
||||
const getClientEnvironment = require('./env');
|
||||
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
|
||||
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
|
||||
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
|
||||
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
||||
const eslint = require('eslint');
|
||||
|
||||
const postcssNormalize = require('postcss-normalize');
|
||||
|
||||
@@ -37,14 +37,6 @@ const appPackageJson = require(paths.appPackageJson);
|
||||
|
||||
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
|
||||
|
||||
const webpackDevClientEntry = require.resolve(
|
||||
'react-dev-utils/webpackHotDevClient'
|
||||
);
|
||||
const reactRefreshOverlayEntry = require.resolve(
|
||||
'react-dev-utils/refreshOverlayInterop'
|
||||
);
|
||||
|
||||
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
|
||||
// makes for a smoother build process.
|
||||
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
|
||||
@@ -56,28 +48,12 @@ const imageInlineSizeLimit = parseInt(
|
||||
// Check if TypeScript is setup
|
||||
const useTypeScript = fs.existsSync(paths.appTsConfig);
|
||||
|
||||
// Get the path to the uncompiled service worker (if it exists).
|
||||
const swSrc = paths.swSrc;
|
||||
|
||||
// style files regexes
|
||||
const cssRegex = /\.css$/;
|
||||
const cssModuleRegex = /\.module\.css$/;
|
||||
const sassRegex = /\.(scss|sass)$/;
|
||||
const sassModuleRegex = /\.module\.(scss|sass)$/;
|
||||
|
||||
const hasJsxRuntime = (() => {
|
||||
if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
require.resolve('react/jsx-runtime');
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
|
||||
// This is the production and development configuration.
|
||||
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
|
||||
module.exports = function(webpackEnv) {
|
||||
@@ -89,13 +65,24 @@ module.exports = function(webpackEnv) {
|
||||
const isEnvProductionProfile =
|
||||
isEnvProduction && process.argv.includes('--profile');
|
||||
|
||||
// We will provide `paths.publicUrlOrPath` to our app
|
||||
// Webpack uses `publicPath` to determine where the app is being served from.
|
||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
||||
// In development, we always serve from the root. This makes config easier.
|
||||
const publicPath = isEnvProduction
|
||||
? paths.servedPath
|
||||
: isEnvDevelopment && '/';
|
||||
// Some apps do not use client-side routing with pushState.
|
||||
// For these, "homepage" can be set to "." to enable relative asset paths.
|
||||
const shouldUseRelativeAssetPaths = publicPath === './';
|
||||
|
||||
// `publicUrl` is just like `publicPath`, but we will provide it to our app
|
||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
|
||||
const publicUrl = isEnvProduction
|
||||
? publicPath.slice(0, -1)
|
||||
: isEnvDevelopment && '';
|
||||
// Get environment variables to inject into our app.
|
||||
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
|
||||
|
||||
const shouldUseReactRefresh = env.raw.FAST_REFRESH;
|
||||
const env = getClientEnvironment(publicUrl);
|
||||
|
||||
// common function to get style loaders
|
||||
const getStyleLoaders = (cssOptions, preProcessor) => {
|
||||
@@ -103,11 +90,7 @@ module.exports = function(webpackEnv) {
|
||||
isEnvDevelopment && require.resolve('style-loader'),
|
||||
isEnvProduction && {
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
// css is located in `static/css`, use '../../' to locate index.html folder
|
||||
// in production `paths.publicUrlOrPath` can be a relative path
|
||||
options: paths.publicUrlOrPath.startsWith('.')
|
||||
? {publicPath: '../../'}
|
||||
: {},
|
||||
options: shouldUseRelativeAssetPaths ? {publicPath: '../../'} : {},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
@@ -135,7 +118,7 @@ module.exports = function(webpackEnv) {
|
||||
// which in turn let's users customize the target behavior as per their needs.
|
||||
postcssNormalize(),
|
||||
],
|
||||
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
].filter(Boolean);
|
||||
@@ -144,8 +127,7 @@ module.exports = function(webpackEnv) {
|
||||
{
|
||||
loader: require.resolve('resolve-url-loader'),
|
||||
options: {
|
||||
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
|
||||
root: paths.appSrc,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -170,31 +152,25 @@ module.exports = function(webpackEnv) {
|
||||
: isEnvDevelopment && 'cheap-module-source-map',
|
||||
// These are the "entry points" to our application.
|
||||
// This means they will be the "root" imports that are included in JS bundle.
|
||||
entry:
|
||||
isEnvDevelopment && !shouldUseReactRefresh
|
||||
? [
|
||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
||||
// When you save a file, the client will either apply hot updates (in case
|
||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
||||
// make a syntax error, this client will display a syntax error overlay.
|
||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
||||
// to bring better experience for Create React App users. You can replace
|
||||
// the line below with these two lines if you prefer the stock client:
|
||||
//
|
||||
// require.resolve('webpack-dev-server/client') + '?/',
|
||||
// require.resolve('webpack/hot/dev-server'),
|
||||
//
|
||||
// When using the experimental react-refresh integration,
|
||||
// the webpack plugin takes care of injecting the dev client for us.
|
||||
webpackDevClientEntry,
|
||||
// Finally, this is your app's code:
|
||||
paths.appIndexJs,
|
||||
// We include the app code last so that if there is a runtime error during
|
||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
||||
// changing JS code would still trigger a refresh.
|
||||
]
|
||||
: paths.appIndexJs,
|
||||
entry: [
|
||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
||||
// When you save a file, the client will either apply hot updates (in case
|
||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
||||
// make a syntax error, this client will display a syntax error overlay.
|
||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
||||
// to bring better experience for Create React App users. You can replace
|
||||
// the line below with these two lines if you prefer the stock client:
|
||||
// require.resolve('webpack-dev-server/client') + '?/',
|
||||
// require.resolve('webpack/hot/dev-server'),
|
||||
isEnvDevelopment &&
|
||||
require.resolve('react-dev-utils/webpackHotDevClient'),
|
||||
// Finally, this is your app's code:
|
||||
paths.appIndexJs,
|
||||
// We include the app code last so that if there is a runtime error during
|
||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
||||
// changing JS code would still trigger a refresh.
|
||||
].filter(Boolean),
|
||||
output: {
|
||||
// The build folder.
|
||||
path: isEnvProduction ? paths.appBuild : undefined,
|
||||
@@ -211,10 +187,9 @@ module.exports = function(webpackEnv) {
|
||||
chunkFilename: isEnvProduction
|
||||
? 'static/js/[name].[contenthash:8].chunk.js'
|
||||
: isEnvDevelopment && 'static/js/[name].chunk.js',
|
||||
// webpack uses `publicPath` to determine where the app is being served from.
|
||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
||||
// We inferred the "public path" (such as / or /my-project) from homepage.
|
||||
publicPath: paths.publicUrlOrPath,
|
||||
// We use "/" in development.
|
||||
publicPath: publicPath,
|
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: isEnvProduction
|
||||
? info =>
|
||||
@@ -223,7 +198,7 @@ module.exports = function(webpackEnv) {
|
||||
.replace(/\\/g, '/')
|
||||
: isEnvDevelopment &&
|
||||
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
|
||||
// Prevents conflicts when multiple webpack runtimes (from different apps)
|
||||
// Prevents conflicts when multiple Webpack runtimes (from different apps)
|
||||
// are used on the same page.
|
||||
jsonpFunction: `webpackJsonp${appPackageJson.name}`,
|
||||
// this defaults to 'window', but by setting it to 'this' then
|
||||
@@ -272,6 +247,13 @@ module.exports = function(webpackEnv) {
|
||||
ascii_only: true,
|
||||
},
|
||||
},
|
||||
// Use multi-process parallel running to improve the build speed
|
||||
// Default number of concurrent runs: os.cpus().length - 1
|
||||
// Disabled on WSL (Windows Subsystem for Linux) due to an issue with Terser
|
||||
// https://github.com/webpack-contrib/terser-webpack-plugin/issues/21
|
||||
parallel: !isWsl,
|
||||
// Enable file caching
|
||||
cache: true,
|
||||
sourceMap: shouldUseSourceMap,
|
||||
}),
|
||||
// This is only used in production mode
|
||||
@@ -289,9 +271,6 @@ module.exports = function(webpackEnv) {
|
||||
}
|
||||
: false,
|
||||
},
|
||||
cssProcessorPluginOptions: {
|
||||
preset: ['default', {minifyFontValues: {removeQuotes: false}}],
|
||||
},
|
||||
}),
|
||||
],
|
||||
// Automatically split vendor and commons
|
||||
@@ -309,7 +288,7 @@ module.exports = function(webpackEnv) {
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
// This allows you to set a fallback for where webpack should look for modules.
|
||||
// This allows you to set a fallback for where Webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebook/create-react-app/issues/253
|
||||
@@ -345,15 +324,12 @@ module.exports = function(webpackEnv) {
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
new ModuleScopePlugin(paths.appSrc, [
|
||||
paths.appPackageJson,
|
||||
reactRefreshOverlayEntry,
|
||||
]),
|
||||
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
|
||||
],
|
||||
},
|
||||
resolveLoader: {
|
||||
plugins: [
|
||||
// Also related to Plug'n'Play, but this time it tells webpack to load its loaders
|
||||
// Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
|
||||
// from the current package.
|
||||
PnpWebpackPlugin.moduleLoader(module),
|
||||
],
|
||||
@@ -363,22 +339,30 @@ module.exports = function(webpackEnv) {
|
||||
rules: [
|
||||
// Disable require.ensure as it's not a standard language feature.
|
||||
{parser: {requireEnsure: false}},
|
||||
|
||||
// First, run the linter.
|
||||
// It's important to do this before Babel processes the JS.
|
||||
{
|
||||
test: /\.(js|mjs|jsx|ts|tsx)$/,
|
||||
enforce: 'pre',
|
||||
use: [
|
||||
{
|
||||
options: {
|
||||
cache: true,
|
||||
formatter: require.resolve('react-dev-utils/eslintFormatter'),
|
||||
eslintPath: require.resolve('eslint'),
|
||||
resolvePluginsRelativeTo: __dirname,
|
||||
},
|
||||
loader: require.resolve('eslint-loader'),
|
||||
},
|
||||
],
|
||||
include: paths.appSrc,
|
||||
},
|
||||
{
|
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [
|
||||
// TODO: Merge this config once `image/avif` is in the mime-db
|
||||
// https://github.com/jshttp/mime-db
|
||||
{
|
||||
test: [/\.avif$/],
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: imageInlineSizeLimit,
|
||||
mimetype: 'image/avif',
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// "url" loader works like "file" loader except that it embeds assets
|
||||
// smaller than specified limit in bytes as data URLs to avoid requests.
|
||||
// A missing `test` is equivalent to a match.
|
||||
@@ -400,14 +384,6 @@ module.exports = function(webpackEnv) {
|
||||
customize: require.resolve(
|
||||
'babel-preset-react-app/webpack-overrides'
|
||||
),
|
||||
presets: [
|
||||
[
|
||||
require.resolve('babel-preset-react-app'),
|
||||
{
|
||||
runtime: hasJsxRuntime ? 'automatic' : 'classic',
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
plugins: [
|
||||
[
|
||||
@@ -421,10 +397,7 @@ module.exports = function(webpackEnv) {
|
||||
},
|
||||
},
|
||||
],
|
||||
isEnvDevelopment &&
|
||||
shouldUseReactRefresh &&
|
||||
require.resolve('react-refresh/babel'),
|
||||
].filter(Boolean),
|
||||
],
|
||||
// This is a feature of `babel-loader` for webpack (not Babel itself).
|
||||
// It enables caching results in ./node_modules/.cache/babel-loader/
|
||||
// directory for faster rebuilds.
|
||||
@@ -454,11 +427,11 @@ module.exports = function(webpackEnv) {
|
||||
// See #6846 for context on why cacheCompression is disabled
|
||||
cacheCompression: false,
|
||||
|
||||
// Babel sourcemaps are needed for debugging into node_modules
|
||||
// code. Without the options below, debuggers like VSCode
|
||||
// show incorrect code and set breakpoints on the wrong lines.
|
||||
sourceMaps: shouldUseSourceMap,
|
||||
inputSourceMap: shouldUseSourceMap,
|
||||
// If an error happens in a package, it's possible to be
|
||||
// because it was compiled. Thus, we don't want the browser
|
||||
// debugger to show the original code. Instead, the code
|
||||
// being evaluated would be much more helpful.
|
||||
sourceMaps: false,
|
||||
},
|
||||
},
|
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
@@ -473,9 +446,7 @@ module.exports = function(webpackEnv) {
|
||||
exclude: cssModuleRegex,
|
||||
use: getStyleLoaders({
|
||||
importLoaders: 1,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
}),
|
||||
// Don't consider CSS imports dead code even if the
|
||||
// containing package claims to have no side effects.
|
||||
@@ -489,12 +460,9 @@ module.exports = function(webpackEnv) {
|
||||
test: cssModuleRegex,
|
||||
use: getStyleLoaders({
|
||||
importLoaders: 1,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
modules: {
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
},
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
modules: true,
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
}),
|
||||
},
|
||||
// Opt-in support for SASS (using .scss or .sass extensions).
|
||||
@@ -505,10 +473,8 @@ module.exports = function(webpackEnv) {
|
||||
exclude: sassModuleRegex,
|
||||
use: getStyleLoaders(
|
||||
{
|
||||
importLoaders: 3,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
importLoaders: 2,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
},
|
||||
'sass-loader'
|
||||
),
|
||||
@@ -524,13 +490,10 @@ module.exports = function(webpackEnv) {
|
||||
test: sassModuleRegex,
|
||||
use: getStyleLoaders(
|
||||
{
|
||||
importLoaders: 3,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
modules: {
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
},
|
||||
importLoaders: 2,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
modules: true,
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
},
|
||||
'sass-loader'
|
||||
),
|
||||
@@ -593,8 +556,9 @@ module.exports = function(webpackEnv) {
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// It will be an empty string unless you specify "homepage"
|
||||
// In production, it will be an empty string unless you specify "homepage"
|
||||
// in `package.json`, in which case it will be the pathname of that URL.
|
||||
// In development, this will be an empty string.
|
||||
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
|
||||
// This gives some necessary context to module not found errors, such as
|
||||
// the requesting resource.
|
||||
@@ -605,29 +569,14 @@ module.exports = function(webpackEnv) {
|
||||
// during a production build.
|
||||
// Otherwise React will be compiled in the very slow development mode.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
// This is necessary to emit hot updates (CSS and Fast Refresh):
|
||||
// This is necessary to emit hot updates (currently CSS only):
|
||||
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
|
||||
// Experimental hot reloading for React .
|
||||
// https://github.com/facebook/react/tree/main/packages/react-refresh
|
||||
isEnvDevelopment &&
|
||||
shouldUseReactRefresh &&
|
||||
new ReactRefreshWebpackPlugin({
|
||||
overlay: {
|
||||
entry: webpackDevClientEntry,
|
||||
// The expected exports are slightly different from what the overlay exports,
|
||||
// so an interop is included here to enable feedback on module-level errors.
|
||||
module: reactRefreshOverlayEntry,
|
||||
// Since we ship a custom dev client and overlay integration,
|
||||
// the bundled socket handling logic can be eliminated.
|
||||
sockIntegration: false,
|
||||
},
|
||||
}),
|
||||
// Watcher doesn't work well if you mistype casing in a path so we use
|
||||
// a plugin that prints an error when you attempt to do this.
|
||||
// See https://github.com/facebook/create-react-app/issues/240
|
||||
isEnvDevelopment && new CaseSensitivePathsPlugin(),
|
||||
// If you require a missing module and then `npm install` it, you still have
|
||||
// to restart the development server for webpack to discover it. This plugin
|
||||
// to restart the development server for Webpack to discover it. This plugin
|
||||
// makes the discovery automatic so you don't have to restart.
|
||||
// See https://github.com/facebook/create-react-app/issues/186
|
||||
isEnvDevelopment &&
|
||||
@@ -647,7 +596,7 @@ module.exports = function(webpackEnv) {
|
||||
// can be used to reconstruct the HTML if necessary
|
||||
new ManifestPlugin({
|
||||
fileName: 'asset-manifest.json',
|
||||
publicPath: paths.publicUrlOrPath,
|
||||
publicPath: publicPath,
|
||||
generate: (seed, files, entrypoints) => {
|
||||
const manifestFiles = files.reduce((manifest, file) => {
|
||||
manifest[file.name] = file.path;
|
||||
@@ -664,23 +613,28 @@ module.exports = function(webpackEnv) {
|
||||
},
|
||||
}),
|
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how webpack interprets its code. This is a practical
|
||||
// by default due to how Webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
// Generate a service worker script that will precache, and keep up to date,
|
||||
// the HTML & assets that are part of the webpack build.
|
||||
// the HTML & assets that are part of the Webpack build.
|
||||
isEnvProduction &&
|
||||
fs.existsSync(swSrc) &&
|
||||
new WorkboxWebpackPlugin.InjectManifest({
|
||||
swSrc,
|
||||
dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
|
||||
exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/],
|
||||
// Bump up the default maximum size (2mb) that's precached,
|
||||
// to make lazy-loading failure scenarios less likely.
|
||||
// See https://github.com/cra-template/pwa/issues/13#issuecomment-722667270
|
||||
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
|
||||
new WorkboxWebpackPlugin.GenerateSW({
|
||||
clientsClaim: true,
|
||||
exclude: [/\.map$/, /asset-manifest\.json$/],
|
||||
importWorkboxFrom: 'cdn',
|
||||
navigateFallback: publicUrl + '/index.html',
|
||||
navigateFallbackBlacklist: [
|
||||
// Exclude URLs starting with /_, as they're likely an API call
|
||||
new RegExp('^/_'),
|
||||
// Exclude any URLs whose last part seems to be a file extension
|
||||
// as they're likely a resource and not a SPA route.
|
||||
// URLs containing a "?" character won't be blacklisted as they're likely
|
||||
// a route with query params (e.g. auth callbacks).
|
||||
new RegExp('/[^/?]+\\.[^/]+$'),
|
||||
],
|
||||
}),
|
||||
// TypeScript type checking
|
||||
useTypeScript &&
|
||||
@@ -689,6 +643,7 @@ module.exports = function(webpackEnv) {
|
||||
basedir: paths.appNodeModules,
|
||||
}),
|
||||
async: isEnvDevelopment,
|
||||
useTypescriptIncrementalApi: true,
|
||||
checkSyntacticErrors: true,
|
||||
resolveModuleNameModule: process.versions.pnp
|
||||
? `${__dirname}/pnpTs.js`
|
||||
@@ -698,14 +653,9 @@ module.exports = function(webpackEnv) {
|
||||
: undefined,
|
||||
tsconfig: paths.appTsConfig,
|
||||
reportFiles: [
|
||||
// This one is specifically to match during CI tests,
|
||||
// as micromatch doesn't match
|
||||
// '../cra-template-typescript/template/src/App.tsx'
|
||||
// otherwise.
|
||||
'../**/src/**/*.{ts,tsx}',
|
||||
'**/src/**/*.{ts,tsx}',
|
||||
'!**/src/**/__tests__/**',
|
||||
'!**/src/**/?(*.)(spec|test).*',
|
||||
'**',
|
||||
'!**/__tests__/**',
|
||||
'!**/?(*.)(spec|test).*',
|
||||
'!**/src/setupProxy.*',
|
||||
'!**/src/setupTests.*',
|
||||
],
|
||||
@@ -713,31 +663,12 @@ module.exports = function(webpackEnv) {
|
||||
// The formatter is invoked directly in WebpackDevServerUtils during development
|
||||
formatter: isEnvProduction ? typescriptFormatter : undefined,
|
||||
}),
|
||||
new ESLintPlugin({
|
||||
// Plugin options
|
||||
extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
|
||||
formatter: require.resolve('react-dev-utils/eslintFormatter'),
|
||||
eslintPath: require.resolve('eslint'),
|
||||
context: paths.appSrc,
|
||||
cache: true,
|
||||
// ESLint class options
|
||||
cwd: paths.appPath,
|
||||
resolvePluginsRelativeTo: __dirname,
|
||||
baseConfig: {
|
||||
extends: [require.resolve('eslint-config-react-app/base')],
|
||||
rules: {
|
||||
...(!hasJsxRuntime && {
|
||||
'react/react-in-jsx-scope': 'error',
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
// Fork Start
|
||||
new ReactFlightWebpackPlugin({isServer: false}),
|
||||
// Fork End
|
||||
].filter(Boolean),
|
||||
// Some libraries import Node modules but don't use them in the browser.
|
||||
// Tell webpack to provide empty mocks for them so importing them works.
|
||||
// Tell Webpack to provide empty mocks for them so importing them works.
|
||||
node: {
|
||||
module: 'empty',
|
||||
dgram: 'empty',
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
|
||||
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
|
||||
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
|
||||
const ignoredFiles = require('react-dev-utils/ignoredFiles');
|
||||
const redirectServedPath = require('react-dev-utils/redirectServedPathMiddleware');
|
||||
const paths = require('./paths');
|
||||
const getHttpsConfig = require('./getHttpsConfig');
|
||||
const fs = require('fs');
|
||||
|
||||
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
|
||||
const host = process.env.HOST || '0.0.0.0';
|
||||
const sockHost = process.env.WDS_SOCKET_HOST;
|
||||
const sockPath = process.env.WDS_SOCKET_PATH; // default: '/sockjs-node'
|
||||
const sockPort = process.env.WDS_SOCKET_PORT;
|
||||
|
||||
module.exports = function(proxy, allowedHost) {
|
||||
return {
|
||||
@@ -51,35 +47,20 @@ module.exports = function(proxy, allowedHost) {
|
||||
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
|
||||
// Note that we only recommend to use `public` folder as an escape hatch
|
||||
// for files like `favicon.ico`, `manifest.json`, and libraries that are
|
||||
// for some reason broken when imported through webpack. If you just want to
|
||||
// for some reason broken when imported through Webpack. If you just want to
|
||||
// use an image, put it in `src` and `import` it from JavaScript instead.
|
||||
contentBase: paths.appPublic,
|
||||
contentBasePublicPath: paths.publicUrlOrPath,
|
||||
// By default files from `contentBase` will not trigger a page reload.
|
||||
watchContentBase: true,
|
||||
// Enable hot reloading server. It will provide WDS_SOCKET_PATH endpoint
|
||||
// Enable hot reloading server. It will provide /sockjs-node/ endpoint
|
||||
// for the WebpackDevServer client so it can learn when the files were
|
||||
// updated. The WebpackDevServer client is included as an entry point
|
||||
// in the webpack development configuration. Note that only changes
|
||||
// in the Webpack development configuration. Note that only changes
|
||||
// to CSS are currently hot reloaded. JS changes will refresh the browser.
|
||||
hot: true,
|
||||
// Use 'ws' instead of 'sockjs-node' on server since we're using native
|
||||
// websockets in `webpackHotDevClient`.
|
||||
transportMode: 'ws',
|
||||
// Prevent a WS client from getting injected as we're already including
|
||||
// `webpackHotDevClient`.
|
||||
injectClient: false,
|
||||
// Enable custom sockjs pathname for websocket connection to hot reloading server.
|
||||
// Enable custom sockjs hostname, pathname and port for websocket connection
|
||||
// to hot reloading server.
|
||||
sockHost,
|
||||
sockPath,
|
||||
sockPort,
|
||||
// It is important to tell WebpackDevServer to use the same "publicPath" path as
|
||||
// we specified in the webpack config. When homepage is '.', default to serving
|
||||
// from the root.
|
||||
// remove last slash so user can land on `/test` instead of `/test/`
|
||||
publicPath: paths.publicUrlOrPath.slice(0, -1),
|
||||
// It is important to tell WebpackDevServer to use the same "root" path
|
||||
// as we specified in the config. In development, we always serve from /.
|
||||
publicPath: '/',
|
||||
// WebpackDevServer is noisy by default so we emit custom message instead
|
||||
// by listening to the compiler events with `compiler.hooks[...].tap` calls above.
|
||||
quiet: true,
|
||||
@@ -90,44 +71,34 @@ module.exports = function(proxy, allowedHost) {
|
||||
watchOptions: {
|
||||
ignored: ignoredFiles(paths.appSrc),
|
||||
},
|
||||
writeToDisk: filePath => {
|
||||
return /react-client-manifest\.json$/.test(filePath);
|
||||
},
|
||||
https: getHttpsConfig(),
|
||||
// Enable HTTPS if the HTTPS environment variable is set to 'true'
|
||||
https: protocol === 'https',
|
||||
host,
|
||||
overlay: false,
|
||||
historyApiFallback: {
|
||||
// Paths with dots should still use the history fallback.
|
||||
// See https://github.com/facebook/create-react-app/issues/387.
|
||||
disableDotRule: true,
|
||||
index: paths.publicUrlOrPath,
|
||||
},
|
||||
public: allowedHost,
|
||||
// `proxy` is run between `before` and `after` `webpack-dev-server` hooks
|
||||
proxy,
|
||||
before(app, server) {
|
||||
// Keep `evalSourceMapMiddleware` and `errorOverlayMiddleware`
|
||||
// middlewares before `redirectServedPath` otherwise will not have any effect
|
||||
// This lets us fetch source contents from webpack for the error overlay
|
||||
app.use(evalSourceMapMiddleware(server));
|
||||
// This lets us open files from the runtime error overlay.
|
||||
app.use(errorOverlayMiddleware());
|
||||
|
||||
if (fs.existsSync(paths.proxySetup)) {
|
||||
// This registers user provided middleware for proxy reasons
|
||||
require(paths.proxySetup)(app);
|
||||
}
|
||||
},
|
||||
after(app) {
|
||||
// Redirect to `PUBLIC_URL` or `homepage` from `package.json` if url not match
|
||||
app.use(redirectServedPath(paths.publicUrlOrPath));
|
||||
|
||||
// This lets us fetch source contents from webpack for the error overlay
|
||||
app.use(evalSourceMapMiddleware(server));
|
||||
// This lets us open files from the runtime error overlay.
|
||||
app.use(errorOverlayMiddleware());
|
||||
|
||||
// This service worker file is effectively a 'no-op' that will reset any
|
||||
// previous service worker registered for the same host:port combination.
|
||||
// We do this in development to avoid hitting the production cache if
|
||||
// it used the same host and port.
|
||||
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
|
||||
app.use(noopServiceWorkerMiddleware(paths.publicUrlOrPath));
|
||||
app.use(noopServiceWorkerMiddleware());
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import {
|
||||
resolve,
|
||||
getSource,
|
||||
transformSource as reactTransformSource,
|
||||
} from 'react-server-dom-webpack/node-loader';
|
||||
|
||||
export {resolve, getSource};
|
||||
|
||||
import babel from '@babel/core';
|
||||
|
||||
const babelOptions = {
|
||||
babelrc: false,
|
||||
ignore: [/\/(build|node_modules)\//],
|
||||
plugins: [
|
||||
'@babel/plugin-syntax-import-meta',
|
||||
'@babel/plugin-transform-react-jsx',
|
||||
],
|
||||
};
|
||||
|
||||
async function babelTransformSource(source, context, defaultTransformSource) {
|
||||
const {format} = context;
|
||||
if (format === 'module') {
|
||||
const opt = Object.assign({filename: context.url}, babelOptions);
|
||||
const {code} = await babel.transformAsync(source, opt);
|
||||
return {source: code};
|
||||
}
|
||||
return defaultTransformSource(source, context, defaultTransformSource);
|
||||
}
|
||||
|
||||
export async function transformSource(source, context, defaultTransformSource) {
|
||||
return reactTransformSource(source, context, (s, c) => {
|
||||
return babelTransformSource(s, c, defaultTransformSource);
|
||||
});
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
@@ -1,82 +1,72 @@
|
||||
{
|
||||
"name": "flight",
|
||||
"type": "module",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "7.12.3",
|
||||
"@babel/plugin-syntax-import-meta": "^7.10.4",
|
||||
"@babel/core": "7.6.0",
|
||||
"@babel/register": "^7.7.0",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "0.4.2",
|
||||
"@svgr/webpack": "5.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.5.0",
|
||||
"@typescript-eslint/parser": "^4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-jest": "^26.6.0",
|
||||
"babel-loader": "8.1.0",
|
||||
"babel-plugin-named-asset-import": "^0.3.7",
|
||||
"babel-preset-react-app": "^10.0.0",
|
||||
"bfj": "^7.0.2",
|
||||
"camelcase": "^6.1.0",
|
||||
"case-sensitive-paths-webpack-plugin": "2.3.0",
|
||||
"@svgr/webpack": "4.3.2",
|
||||
"@typescript-eslint/eslint-plugin": "^2.2.0",
|
||||
"@typescript-eslint/parser": "^2.2.0",
|
||||
"babel-eslint": "10.0.3",
|
||||
"babel-jest": "^24.9.0",
|
||||
"babel-loader": "8.0.6",
|
||||
"babel-plugin-named-asset-import": "^0.3.4",
|
||||
"babel-preset-react-app": "^9.0.2",
|
||||
"camelcase": "^5.2.0",
|
||||
"case-sensitive-paths-webpack-plugin": "2.2.0",
|
||||
"concurrently": "^5.0.0",
|
||||
"css-loader": "4.3.0",
|
||||
"dotenv": "8.2.0",
|
||||
"css-loader": "2.1.1",
|
||||
"dotenv": "6.2.0",
|
||||
"dotenv-expand": "5.1.0",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-config-react-app": "^6.0.0",
|
||||
"eslint-plugin-flowtype": "^5.2.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-jest": "^24.1.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.3.1",
|
||||
"eslint-plugin-react": "^7.21.5",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-testing-library": "^3.9.2",
|
||||
"eslint-webpack-plugin": "^2.1.0",
|
||||
"eslint": "^6.1.0",
|
||||
"eslint-config-react-app": "^5.0.2",
|
||||
"eslint-loader": "3.0.2",
|
||||
"eslint-plugin-flowtype": "3.13.0",
|
||||
"eslint-plugin-import": "2.18.2",
|
||||
"eslint-plugin-jsx-a11y": "6.2.3",
|
||||
"eslint-plugin-react": "7.14.3",
|
||||
"eslint-plugin-react-hooks": "^1.6.1",
|
||||
"express": "^4.17.1",
|
||||
"file-loader": "6.1.1",
|
||||
"fs-extra": "^9.0.1",
|
||||
"html-webpack-plugin": "4.5.0",
|
||||
"file-loader": "3.0.1",
|
||||
"fs-extra": "7.0.1",
|
||||
"html-webpack-plugin": "4.0.0-beta.5",
|
||||
"identity-obj-proxy": "3.0.0",
|
||||
"jest": "26.6.0",
|
||||
"jest-circus": "26.6.0",
|
||||
"jest-resolve": "26.6.0",
|
||||
"jest-watch-typeahead": "0.6.1",
|
||||
"mini-css-extract-plugin": "0.11.3",
|
||||
"nodemon": "^2.0.6",
|
||||
"optimize-css-assets-webpack-plugin": "5.0.4",
|
||||
"pnp-webpack-plugin": "1.6.4",
|
||||
"postcss-flexbugs-fixes": "4.2.1",
|
||||
"is-wsl": "^1.1.0",
|
||||
"jest": "24.9.0",
|
||||
"jest-environment-jsdom-fourteen": "0.1.0",
|
||||
"jest-resolve": "24.9.0",
|
||||
"jest-watch-typeahead": "0.4.0",
|
||||
"mini-css-extract-plugin": "0.8.0",
|
||||
"optimize-css-assets-webpack-plugin": "5.0.3",
|
||||
"pnp-webpack-plugin": "1.5.0",
|
||||
"postcss-flexbugs-fixes": "4.1.0",
|
||||
"postcss-loader": "3.0.0",
|
||||
"postcss-normalize": "8.0.1",
|
||||
"postcss-normalize": "7.0.1",
|
||||
"postcss-preset-env": "6.7.0",
|
||||
"postcss-safe-parser": "5.0.2",
|
||||
"prompts": "2.4.0",
|
||||
"react-app-polyfill": "^2.0.0",
|
||||
"react-dev-utils": "^11.0.1",
|
||||
"react-refresh": "^0.8.3",
|
||||
"resolve": "1.18.1",
|
||||
"resolve-url-loader": "^3.1.2",
|
||||
"sass-loader": "8.0.2",
|
||||
"semver": "7.3.2",
|
||||
"style-loader": "1.3.0",
|
||||
"terser-webpack-plugin": "4.2.3",
|
||||
"ts-pnp": "1.2.0",
|
||||
"url-loader": "4.1.1",
|
||||
"webpack": "4.44.2",
|
||||
"webpack-dev-server": "3.11.0",
|
||||
"webpack-manifest-plugin": "2.2.0",
|
||||
"workbox-webpack-plugin": "5.1.4"
|
||||
"postcss-safe-parser": "4.0.1",
|
||||
"react-app-polyfill": "^1.0.4",
|
||||
"react-dev-utils": "^9.1.0",
|
||||
"resolve": "1.12.0",
|
||||
"resolve-url-loader": "3.1.0",
|
||||
"sass-loader": "7.2.0",
|
||||
"semver": "6.3.0",
|
||||
"style-loader": "1.0.0",
|
||||
"terser-webpack-plugin": "1.4.1",
|
||||
"ts-pnp": "1.1.4",
|
||||
"url-loader": "2.1.0",
|
||||
"webpack": "4.41.0",
|
||||
"webpack-dev-server": "3.2.1",
|
||||
"webpack-manifest-plugin": "2.1.1",
|
||||
"workbox-webpack-plugin": "4.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
"prestart": "cp -r ../../build/node_modules/* ./node_modules/",
|
||||
"prebuild": "cp -r ../../build/node_modules/* ./node_modules/",
|
||||
"start": "concurrently \"npm run start:server\" \"npm run start:client\"",
|
||||
"start:client": "node scripts/start.js",
|
||||
"start:server": "NODE_ENV=development nodemon -- --experimental-loader ./loader/index.js --conditions=react-server server",
|
||||
"start:prod": "node scripts/build.js && concurrently \"npm run start:prod-server\" \"npm run start:prod-client\"",
|
||||
"start:prod-client": "cd ./build && python -m SimpleHTTPServer 3000",
|
||||
"start:prod-server": "NODE_ENV=production node --experimental-loader ./loader/index.js --conditions=react-server server",
|
||||
"start:server": "NODE_ENV=development node server",
|
||||
"start:prod": "node scripts/build.js && NODE_ENV=production node server",
|
||||
"build": "node scripts/build.js",
|
||||
"test": "node scripts/test.js --env=jsdom"
|
||||
},
|
||||
@@ -95,11 +85,6 @@
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"react-app"
|
||||
]
|
||||
},
|
||||
"jest": {
|
||||
"roots": [
|
||||
"<rootDir>/src"
|
||||
@@ -116,15 +101,14 @@
|
||||
"<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
|
||||
"<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"
|
||||
],
|
||||
"testEnvironment": "jsdom",
|
||||
"testRunner": "<rootDir>/node_modules/jest-circus/runner.js",
|
||||
"testEnvironment": "jest-environment-jsdom-fourteen",
|
||||
"transform": {
|
||||
"^.+\\.(js|jsx|mjs|cjs|ts|tsx)$": "<rootDir>/node_modules/babel-jest",
|
||||
"^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest",
|
||||
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
|
||||
"^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
|
||||
"^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
|
||||
},
|
||||
"transformIgnorePatterns": [
|
||||
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$",
|
||||
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$",
|
||||
"^.+\\.module\\.(css|sass|scss)$"
|
||||
],
|
||||
"modulePaths": [],
|
||||
@@ -147,7 +131,11 @@
|
||||
"watchPlugins": [
|
||||
"jest-watch-typeahead/filename",
|
||||
"jest-watch-typeahead/testname"
|
||||
],
|
||||
"resetMocks": true
|
||||
]
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"react-app"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ require('../config/env');
|
||||
const path = require('path');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const fs = require('fs-extra');
|
||||
const bfj = require('bfj');
|
||||
const webpack = require('webpack');
|
||||
const configFactory = require('../config/webpack.config');
|
||||
const paths = require('../config/paths');
|
||||
@@ -43,9 +42,6 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const argv = process.argv.slice(2);
|
||||
const writeStatsJson = argv.indexOf('--stats') !== -1;
|
||||
|
||||
// Generate configuration
|
||||
const config = configFactory('production');
|
||||
|
||||
@@ -97,7 +93,7 @@ checkBrowsers(paths.appPath, isInteractive)
|
||||
console.log();
|
||||
|
||||
const appPackage = require(paths.appPackageJson);
|
||||
const publicUrl = paths.publicUrlOrPath;
|
||||
const publicUrl = paths.publicUrl;
|
||||
const publicPath = config.output.publicPath;
|
||||
const buildFolder = path.relative(process.cwd(), paths.appBuild);
|
||||
printHostingInstructions(
|
||||
@@ -133,6 +129,18 @@ checkBrowsers(paths.appPath, isInteractive)
|
||||
|
||||
// Create the production build and print the deployment instructions.
|
||||
function build(previousFileSizes) {
|
||||
// We used to support resolving modules according to `NODE_PATH`.
|
||||
// This now has been deprecated in favor of jsconfig/tsconfig.json
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
if (process.env.NODE_PATH) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
|
||||
)
|
||||
);
|
||||
console.log();
|
||||
}
|
||||
|
||||
console.log('Creating an optimized production build...');
|
||||
|
||||
const compiler = webpack(config);
|
||||
@@ -143,18 +151,8 @@ function build(previousFileSizes) {
|
||||
if (!err.message) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
let errMessage = err.message;
|
||||
|
||||
// Add additional information for postcss errors
|
||||
if (Object.prototype.hasOwnProperty.call(err, 'postcssNode')) {
|
||||
errMessage +=
|
||||
'\nCompileError: Begins at CSS selector ' +
|
||||
err['postcssNode'].selector;
|
||||
}
|
||||
|
||||
messages = formatWebpackMessages({
|
||||
errors: [errMessage],
|
||||
errors: [err.message],
|
||||
warnings: [],
|
||||
});
|
||||
} else {
|
||||
@@ -185,20 +183,11 @@ function build(previousFileSizes) {
|
||||
return reject(new Error(messages.warnings.join('\n\n')));
|
||||
}
|
||||
|
||||
const resolveArgs = {
|
||||
return resolve({
|
||||
stats,
|
||||
previousFileSizes,
|
||||
warnings: messages.warnings,
|
||||
};
|
||||
|
||||
if (writeStatsJson) {
|
||||
return bfj
|
||||
.write(paths.appBuild + '/bundle-stats.json', stats.toJson())
|
||||
.then(() => resolve(resolveArgs))
|
||||
.catch(error => reject(new Error(error)));
|
||||
}
|
||||
|
||||
return resolve(resolveArgs);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"type": "commonjs"
|
||||
}
|
||||
@@ -27,14 +27,10 @@ const {
|
||||
prepareUrls,
|
||||
} = require('react-dev-utils/WebpackDevServerUtils');
|
||||
const openBrowser = require('react-dev-utils/openBrowser');
|
||||
const semver = require('semver');
|
||||
const paths = require('../config/paths');
|
||||
const configFactory = require('../config/webpack.config');
|
||||
const createDevServerConfig = require('../config/webpackDevServer.config');
|
||||
const getClientEnvironment = require('../config/env');
|
||||
const react = require(require.resolve('react', {paths: [paths.appPath]}));
|
||||
|
||||
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
|
||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
||||
const isInteractive = process.stdout.isTTY;
|
||||
|
||||
@@ -59,7 +55,7 @@ if (process.env.HOST) {
|
||||
`If this was unintentional, check that you haven't mistakenly set it in your shell.`
|
||||
);
|
||||
console.log(
|
||||
`Learn more here: ${chalk.yellow('https://cra.link/advanced-config')}`
|
||||
`Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}`
|
||||
);
|
||||
console.log();
|
||||
}
|
||||
@@ -78,19 +74,12 @@ checkBrowsers(paths.appPath, isInteractive)
|
||||
// We have not found a port.
|
||||
return;
|
||||
}
|
||||
|
||||
const config = configFactory('development');
|
||||
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
|
||||
const appName = require(paths.appPackageJson).name;
|
||||
|
||||
const useTypeScript = fs.existsSync(paths.appTsConfig);
|
||||
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
|
||||
const urls = prepareUrls(
|
||||
protocol,
|
||||
HOST,
|
||||
port,
|
||||
paths.publicUrlOrPath.slice(0, -1)
|
||||
);
|
||||
const urls = prepareUrls(protocol, HOST, port);
|
||||
const devSocket = {
|
||||
warnings: warnings =>
|
||||
devServer.sockWrite(devServer.sockets, 'warnings', warnings),
|
||||
@@ -110,11 +99,7 @@ checkBrowsers(paths.appPath, isInteractive)
|
||||
});
|
||||
// Load proxy config
|
||||
const proxySetting = require(paths.appPackageJson).proxy;
|
||||
const proxyConfig = prepareProxy(
|
||||
proxySetting,
|
||||
paths.appPublic,
|
||||
paths.publicUrlOrPath
|
||||
);
|
||||
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
|
||||
// Serve webpack assets generated by the compiler over a web server.
|
||||
const serverConfig = createDevServerConfig(
|
||||
proxyConfig,
|
||||
@@ -130,12 +115,16 @@ checkBrowsers(paths.appPath, isInteractive)
|
||||
clearConsole();
|
||||
}
|
||||
|
||||
if (env.raw.FAST_REFRESH && semver.lt(react.version, '16.10.0')) {
|
||||
// We used to support resolving modules according to `NODE_PATH`.
|
||||
// This now has been deprecated in favor of jsconfig/tsconfig.json
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
if (process.env.NODE_PATH) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`Fast Refresh requires React 16.10 or higher. You are using React ${react.version}.`
|
||||
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
|
||||
)
|
||||
);
|
||||
console.log();
|
||||
}
|
||||
|
||||
console.log(chalk.cyan('Starting the development server...\n'));
|
||||
@@ -148,14 +137,6 @@ checkBrowsers(paths.appPath, isInteractive)
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
|
||||
if (process.env.CI !== 'true') {
|
||||
// Gracefully exit when stdin ends
|
||||
process.stdin.on('end', function() {
|
||||
devServer.close();
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
if (err && err.message) {
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const register = require('react-server-dom-webpack/node-register');
|
||||
register();
|
||||
|
||||
const babelRegister = require('@babel/register');
|
||||
const path = require('path');
|
||||
|
||||
babelRegister({
|
||||
babelrc: false,
|
||||
ignore: [
|
||||
/\/(build|node_modules)\//,
|
||||
function(file) {
|
||||
if ((path.dirname(file) + '/').startsWith(__dirname + '/')) {
|
||||
// Ignore everything in this folder
|
||||
// because it's a mix of CJS and ESM
|
||||
// and working with raw code is easier.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
],
|
||||
presets: ['react-app'],
|
||||
plugins: ['@babel/transform-modules-commonjs'],
|
||||
});
|
||||
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
// Application
|
||||
app.get('/', function(req, res) {
|
||||
require('./handler.server.js')(req, res);
|
||||
});
|
||||
|
||||
app.get('/todos', function(req, res) {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.json([
|
||||
{
|
||||
id: 1,
|
||||
text: 'Shave yaks',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
text: 'Eat kale',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
app.listen(3001, () => {
|
||||
console.log('Flight Server listening on port 3001...');
|
||||
});
|
||||
|
||||
app.on('error', function(error) {
|
||||
if (error.syscall !== 'listen') {
|
||||
throw error;
|
||||
}
|
||||
|
||||
var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
|
||||
|
||||
switch (error.code) {
|
||||
case 'EACCES':
|
||||
console.error(bind + ' requires elevated privileges');
|
||||
process.exit(1);
|
||||
break;
|
||||
case 'EADDRINUSE':
|
||||
console.error(bind + ' is already in use');
|
||||
process.exit(1);
|
||||
break;
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
26
fixtures/flight/server/handler.js
Normal file
26
fixtures/flight/server/handler.js
Normal file
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
const ReactTransportDOMServer = require('react-transport-dom-webpack/server');
|
||||
const React = require('react');
|
||||
const Stream = require('stream');
|
||||
|
||||
function Text({children}) {
|
||||
return <span>{children}</span>;
|
||||
}
|
||||
|
||||
function HTML() {
|
||||
return (
|
||||
<div>
|
||||
<Text>Hello</Text>
|
||||
<Text>world</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = function(req, res) {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
let model = {
|
||||
content: <HTML />,
|
||||
};
|
||||
ReactTransportDOMServer.pipeToNodeWritable(model, res);
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const {renderToPipeableStream} = require('react-server-dom-webpack/writer');
|
||||
const {readFile} = require('fs');
|
||||
const {resolve} = require('path');
|
||||
const React = require('react');
|
||||
|
||||
module.exports = function(req, res) {
|
||||
// const m = require('../src/App.server.js');
|
||||
import('../src/App.server.js').then(m => {
|
||||
const dist = process.env.NODE_ENV === 'development' ? 'dist' : 'build';
|
||||
readFile(
|
||||
resolve(__dirname, `../${dist}/react-client-manifest.json`),
|
||||
'utf8',
|
||||
(err, data) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const App = m.default.default || m.default;
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
const moduleMap = JSON.parse(data);
|
||||
const {pipe} = renderToPipeableStream(
|
||||
React.createElement(App),
|
||||
moduleMap
|
||||
);
|
||||
pipe(res);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
46
fixtures/flight/server/index.js
Normal file
46
fixtures/flight/server/index.js
Normal file
@@ -0,0 +1,46 @@
|
||||
'use strict';
|
||||
|
||||
const babelRegister = require('@babel/register');
|
||||
|
||||
babelRegister({
|
||||
ignore: [/\/(build|node_modules)\//],
|
||||
presets: ['react-app'],
|
||||
});
|
||||
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
// Application
|
||||
app.get('/', function(req, res) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
for (var key in require.cache) {
|
||||
delete require.cache[key];
|
||||
}
|
||||
}
|
||||
require('./handler')(req, res);
|
||||
});
|
||||
|
||||
app.listen(3001, () => {
|
||||
console.log('Flight Server listening on port 3001...');
|
||||
});
|
||||
|
||||
app.on('error', function(error) {
|
||||
if (error.syscall !== 'listen') {
|
||||
throw error;
|
||||
}
|
||||
|
||||
var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
|
||||
|
||||
switch (error.code) {
|
||||
case 'EACCES':
|
||||
console.error(bind + ' requires elevated privileges');
|
||||
process.exit(1);
|
||||
break;
|
||||
case 'EADDRINUSE':
|
||||
console.error(bind + ' is already in use');
|
||||
process.exit(1);
|
||||
break;
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"type": "commonjs",
|
||||
"main": "./cli.server.js"
|
||||
}
|
||||
15
fixtures/flight/src/App.js
Normal file
15
fixtures/flight/src/App.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import React, {Suspense} from 'react';
|
||||
|
||||
function Content({data}) {
|
||||
return data.readRoot().content;
|
||||
}
|
||||
|
||||
function App({data}) {
|
||||
return (
|
||||
<Suspense fallback={<h1>Loading...</h1>}>
|
||||
<Content data={data} />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,28 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import {fetch} from 'react-fetch';
|
||||
|
||||
import Container from './Container.js';
|
||||
|
||||
import {Counter} from './Counter.client.js';
|
||||
import {Counter as Counter2} from './Counter2.client.js';
|
||||
|
||||
import ShowMore from './ShowMore.client.js';
|
||||
|
||||
export default function App() {
|
||||
const todos = fetch('http://localhost:3001/todos').json();
|
||||
return (
|
||||
<Container>
|
||||
<h1>Hello, world</h1>
|
||||
<Counter />
|
||||
<Counter2 />
|
||||
<ul>
|
||||
{todos.map(todo => (
|
||||
<li key={todo.id}>{todo.text}</li>
|
||||
))}
|
||||
</ul>
|
||||
<ShowMore>
|
||||
<p>Lorem ipsum</p>
|
||||
</ShowMore>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export default function Container({children}) {
|
||||
return <div>{children}</div>;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import Container from './Container.js';
|
||||
|
||||
export function Counter() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
return (
|
||||
<Container>
|
||||
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export * from './Counter.client.js';
|
||||
@@ -1,11 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import Container from './Container.js';
|
||||
|
||||
export default function ShowMore({children}) {
|
||||
const [show, setShow] = React.useState(false);
|
||||
if (!show) {
|
||||
return <button onClick={() => setShow(true)}>Show More</button>;
|
||||
}
|
||||
return <Container>{children}</Container>;
|
||||
}
|
||||
@@ -1,17 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import {Suspense} from 'react';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactServerDOMReader from 'react-server-dom-webpack';
|
||||
import ReactTransportDOMClient from 'react-transport-dom-webpack';
|
||||
import App from './App';
|
||||
|
||||
let data = ReactServerDOMReader.createFromFetch(fetch('http://localhost:3001'));
|
||||
|
||||
function Content() {
|
||||
return data.readRoot();
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<Suspense fallback={<h1>Loading...</h1>}>
|
||||
<Content />
|
||||
</Suspense>,
|
||||
document.getElementById('root')
|
||||
let data = ReactTransportDOMClient.createFromFetch(
|
||||
fetch('http://localhost:3001')
|
||||
);
|
||||
|
||||
ReactDOM.render(<App data={data} />, document.getElementById('root'));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -318,9 +318,9 @@ it('should give context for PropType errors in nested components.', () => {
|
||||
}
|
||||
expect(() => ReactTestUtils.renderIntoDocument(<ParentComp />)).toErrorDev(
|
||||
'Warning: Failed prop type: ' +
|
||||
'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
|
||||
'expected `string`.\n' +
|
||||
' in color (at **)\n' +
|
||||
'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
|
||||
'expected `string`.\n' +
|
||||
' in fn (at **)\n' + // Jest/Node issue
|
||||
' in ParentComp (at **)'
|
||||
);
|
||||
});
|
||||
@@ -377,7 +377,7 @@ it('should check default prop values', () => {
|
||||
).toErrorDev(
|
||||
'Warning: Failed prop type: The prop `prop` is marked as required in ' +
|
||||
'`RequiredPropComponent`, but its value is `null`.\n' +
|
||||
' in RequiredPropComponent (at **)'
|
||||
' in construct (at **)' // Jest/Node issue
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -33,6 +33,16 @@ function shouldIgnoreConsoleError(format, args) {
|
||||
// They are noisy too so we'll try to ignore them.
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
format.indexOf(
|
||||
'act(...) is not supported in production builds of React'
|
||||
) === 0
|
||||
) {
|
||||
// We don't yet support act() for prod builds, and warn for it.
|
||||
// But we'd like to use act() ourselves for prod builds.
|
||||
// Let's ignore the warning and #yolo.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Looks legit
|
||||
return false;
|
||||
|
||||
@@ -3247,9 +3247,9 @@ source-map-resolve@^0.5.0:
|
||||
urix "^0.1.0"
|
||||
|
||||
source-map-support@^0.5.6:
|
||||
version "0.5.13"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
|
||||
integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
|
||||
version "0.5.19"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
|
||||
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
source-map "^0.6.0"
|
||||
|
||||
@@ -21,12 +21,12 @@ if (process.env.NODE_ENV === 'development') {
|
||||
delete require.cache[key];
|
||||
}
|
||||
const render = require('./render').default;
|
||||
render(req.url, res);
|
||||
res.send(render(req.url));
|
||||
});
|
||||
} else {
|
||||
const render = require('./render').default;
|
||||
app.get('/', function(req, res) {
|
||||
render(req.url, res);
|
||||
res.send(render(req.url));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {renderToPipeableStream} from 'react-dom/server';
|
||||
import {renderToString} from 'react-dom/server';
|
||||
|
||||
import App from '../src/components/App';
|
||||
|
||||
@@ -14,26 +14,9 @@ if (process.env.NODE_ENV === 'development') {
|
||||
assets = require('../build/asset-manifest.json');
|
||||
}
|
||||
|
||||
export default function render(url, res) {
|
||||
res.socket.on('error', error => {
|
||||
// Log fatal errors
|
||||
console.error('Fatal', error);
|
||||
});
|
||||
let didError = false;
|
||||
const {pipe, abort} = renderToPipeableStream(<App assets={assets} />, {
|
||||
bootstrapScripts: [assets['main.js']],
|
||||
onCompleteShell() {
|
||||
// If something errored before we started streaming, we set the error code appropriately.
|
||||
res.statusCode = didError ? 500 : 200;
|
||||
res.setHeader('Content-type', 'text/html');
|
||||
pipe(res);
|
||||
},
|
||||
onError(x) {
|
||||
didError = true;
|
||||
console.error(x);
|
||||
},
|
||||
});
|
||||
// Abandon and switch to client rendering after 5 seconds.
|
||||
// Try lowering this to see the client recover.
|
||||
setTimeout(abort, 5000);
|
||||
export default function render() {
|
||||
var html = renderToString(<App assets={assets} />);
|
||||
// There's no way to render a doctype in React so prepend manually.
|
||||
// Also append a bootstrap script tag.
|
||||
return '<!DOCTYPE html>' + html;
|
||||
}
|
||||
|
||||
@@ -10,29 +10,23 @@ function LoadingIndicator() {
|
||||
return <div className={theme + '-loading'}>Loading...</div>;
|
||||
}
|
||||
|
||||
function Content() {
|
||||
export default function App({assets}) {
|
||||
let [CurrentPage, switchPage] = useState(() => Page);
|
||||
return (
|
||||
<div>
|
||||
<h1>Hello World</h1>
|
||||
<a className="link" onClick={() => switchPage(() => Page)}>
|
||||
Page 1
|
||||
</a>
|
||||
{' | '}
|
||||
<a className="link" onClick={() => switchPage(() => Page2)}>
|
||||
Page 2
|
||||
</a>
|
||||
<Suspense fallback={<LoadingIndicator />}>
|
||||
<CurrentPage />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App({assets}) {
|
||||
return (
|
||||
<Chrome title="Hello World" assets={assets}>
|
||||
<Content />
|
||||
<div>
|
||||
<h1>Hello World</h1>
|
||||
<a className="link" onClick={() => switchPage(() => Page)}>
|
||||
Page 1
|
||||
</a>
|
||||
{' | '}
|
||||
<a className="link" onClick={() => switchPage(() => Page2)}>
|
||||
Page 2
|
||||
</a>
|
||||
<Suspense fallback={<LoadingIndicator />}>
|
||||
<CurrentPage />
|
||||
</Suspense>
|
||||
</div>
|
||||
</Chrome>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import React, {
|
||||
Component,
|
||||
Suspense,
|
||||
unstable_startTransition as startTransition,
|
||||
} from 'react';
|
||||
import React, {Component} from 'react';
|
||||
|
||||
import Theme, {ThemeToggleButton} from './Theme';
|
||||
|
||||
@@ -27,25 +23,24 @@ export default class Chrome extends Component {
|
||||
__html: `<b>Enable JavaScript to run this app.</b>`,
|
||||
}}
|
||||
/>
|
||||
<Suspense fallback="Loading...">
|
||||
<Theme.Provider value={this.state.theme}>
|
||||
{this.props.children}
|
||||
<div>
|
||||
<ThemeToggleButton
|
||||
onChange={theme => {
|
||||
startTransition(() => {
|
||||
this.setState({theme});
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Theme.Provider>
|
||||
</Suspense>
|
||||
<Theme.Provider value={this.state.theme}>
|
||||
{this.props.children}
|
||||
<div>
|
||||
<ThemeToggleButton
|
||||
onChange={theme => {
|
||||
React.startTransition(() => {
|
||||
this.setState({theme});
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Theme.Provider>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `assetManifest = ${JSON.stringify(assets)};`,
|
||||
}}
|
||||
/>
|
||||
<script src={assets['main.js']} />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -4,19 +4,18 @@ let isResolved = false;
|
||||
export default function Suspend({children}) {
|
||||
// This will suspend the content from rendering but only on the client.
|
||||
// This is used to demo a slow loading app.
|
||||
if (!isResolved) {
|
||||
if (promise === null) {
|
||||
promise = new Promise(resolve => {
|
||||
setTimeout(
|
||||
() => {
|
||||
if (typeof window === 'object') {
|
||||
if (!isResolved) {
|
||||
if (promise === null) {
|
||||
promise = new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
isResolved = true;
|
||||
resolve();
|
||||
},
|
||||
typeof window === 'object' ? 6000 : 1000
|
||||
);
|
||||
});
|
||||
}, 6000);
|
||||
});
|
||||
}
|
||||
throw promise;
|
||||
}
|
||||
throw promise;
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
12684
fixtures/ssr2/package-lock.json
generated
12684
fixtures/ssr2/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,53 +0,0 @@
|
||||
{
|
||||
"name": "react-ssr",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=14.9.0"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "7.14.3",
|
||||
"@babel/register": "7.13.16",
|
||||
"babel-loader": "8.1.0",
|
||||
"babel-preset-react-app": "10.0.0",
|
||||
"compression": "^1.7.4",
|
||||
"concurrently": "^5.3.0",
|
||||
"express": "^4.17.1",
|
||||
"nodemon": "^2.0.6",
|
||||
"react": "link:../../build/node_modules/react",
|
||||
"react-dom": "link:../../build/node_modules/react-dom",
|
||||
"react-error-boundary": "^3.1.3",
|
||||
"resolve": "1.12.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"webpack": "4.44.2",
|
||||
"webpack-cli": "^4.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
"prettier": "1.19.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "concurrently \"npm run server:dev\" \"npm run bundler:dev\"",
|
||||
"start:prod": "concurrently \"npm run server:prod\" \"npm run bundler:prod\"",
|
||||
"server:dev": "cross-env NODE_ENV=development nodemon -- server/server.js",
|
||||
"server:prod": "cross-env NODE_ENV=production nodemon -- server/server.js",
|
||||
"bundler:dev": "cross-env NODE_ENV=development nodemon -- scripts/build.js",
|
||||
"bundler:prod": "cross-env NODE_ENV=production nodemon -- scripts/build.js"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
[
|
||||
"react-app",
|
||||
{
|
||||
"runtime": "automatic"
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"ignore": [
|
||||
"build/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
body {
|
||||
font-family: system-ui, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
nav {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
padding: 10px;
|
||||
height: 500px;
|
||||
float: left;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.post {
|
||||
padding: 20px;
|
||||
float: left;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul, li {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.post p {
|
||||
font-size: larger;
|
||||
font-family: Georgia, serif;
|
||||
}
|
||||
|
||||
.comments {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.comment {
|
||||
border: 2px solid #aaa;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* https://codepen.io/mandelid/pen/vwKoe */
|
||||
.spinner {
|
||||
display: inline-block;
|
||||
transition: opacity linear 0.1s;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid rgba(80, 80, 80, 0.5);
|
||||
border-radius: 50%;
|
||||
border-top-color: #fff;
|
||||
animation: spin 1s ease-in-out infinite;
|
||||
opacity: 0;
|
||||
}
|
||||
.spinner--active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const rimraf = require('rimraf');
|
||||
const webpack = require('webpack');
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
rimraf.sync(path.resolve(__dirname, '../build'));
|
||||
webpack(
|
||||
{
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
devtool: isProduction ? 'source-map' : 'cheap-module-source-map',
|
||||
entry: [path.resolve(__dirname, '../src/index.js')],
|
||||
output: {
|
||||
path: path.resolve(__dirname, '../build'),
|
||||
filename: 'main.js',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: 'babel-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
(err, stats) => {
|
||||
if (err) {
|
||||
console.error(err.stack || err);
|
||||
if (err.details) {
|
||||
console.error(err.details);
|
||||
}
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
const info = stats.toJson();
|
||||
if (stats.hasErrors()) {
|
||||
console.log('Finished running webpack with errors.');
|
||||
info.errors.forEach(e => console.error(e));
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log('Finished running webpack.');
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
// Tweak these to play with different kinds of latency.
|
||||
|
||||
// How long the data fetches on the server.
|
||||
exports.API_DELAY = 2000;
|
||||
|
||||
// How long the server waits for data before giving up.
|
||||
exports.ABORT_DELAY = 10000;
|
||||
|
||||
// How long serving the JS bundles is delayed.
|
||||
exports.JS_BUNDLE_DELAY = 4000;
|
||||
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
// import {renderToString} from 'react-dom/server';
|
||||
import {renderToPipeableStream} from 'react-dom/server';
|
||||
import App from '../src/App';
|
||||
import {DataProvider} from '../src/data';
|
||||
import {API_DELAY, ABORT_DELAY} from './delays';
|
||||
|
||||
// In a real setup, you'd read it from webpack build stats.
|
||||
let assets = {
|
||||
'main.js': '/main.js',
|
||||
'main.css': '/main.css',
|
||||
};
|
||||
|
||||
module.exports = function render(url, res) {
|
||||
// This is how you would wire it up previously:
|
||||
//
|
||||
// res.send(
|
||||
// '<!DOCTYPE html>' +
|
||||
// renderToString(
|
||||
// <DataProvider data={data}>
|
||||
// <App assets={assets} />
|
||||
// </DataProvider>,
|
||||
// )
|
||||
// );
|
||||
|
||||
// The new wiring is a bit more involved.
|
||||
res.socket.on('error', error => {
|
||||
console.error('Fatal', error);
|
||||
});
|
||||
let didError = false;
|
||||
const data = createServerData();
|
||||
const {pipe, abort} = renderToPipeableStream(
|
||||
<DataProvider data={data}>
|
||||
<App assets={assets} />
|
||||
</DataProvider>,
|
||||
{
|
||||
bootstrapScripts: [assets['main.js']],
|
||||
onCompleteShell() {
|
||||
// If something errored before we started streaming, we set the error code appropriately.
|
||||
res.statusCode = didError ? 500 : 200;
|
||||
res.setHeader('Content-type', 'text/html');
|
||||
pipe(res);
|
||||
},
|
||||
onError(x) {
|
||||
didError = true;
|
||||
console.error(x);
|
||||
},
|
||||
}
|
||||
);
|
||||
// Abandon and switch to client rendering if enough time passes.
|
||||
// Try lowering this to see the client recover.
|
||||
setTimeout(abort, ABORT_DELAY);
|
||||
};
|
||||
|
||||
// Simulate a delay caused by data fetching.
|
||||
// We fake this because the streaming HTML renderer
|
||||
// is not yet integrated with real data fetching strategies.
|
||||
function createServerData() {
|
||||
let done = false;
|
||||
let promise = null;
|
||||
return {
|
||||
read() {
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
if (promise) {
|
||||
throw promise;
|
||||
}
|
||||
promise = new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
done = true;
|
||||
promise = null;
|
||||
resolve();
|
||||
}, API_DELAY);
|
||||
});
|
||||
throw promise;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const babelRegister = require('@babel/register');
|
||||
babelRegister({
|
||||
ignore: [/[\\\/](build|server\/server|node_modules)[\\\/]/],
|
||||
presets: [['react-app', {runtime: 'automatic'}]],
|
||||
plugins: ['@babel/transform-modules-commonjs'],
|
||||
});
|
||||
|
||||
const express = require('express');
|
||||
const compress = require('compression');
|
||||
const {readFileSync} = require('fs');
|
||||
const path = require('path');
|
||||
const render = require('./render');
|
||||
const {JS_BUNDLE_DELAY} = require('./delays');
|
||||
|
||||
const PORT = process.env.PORT || 4000;
|
||||
const app = express();
|
||||
|
||||
app.use((req, res, next) => {
|
||||
if (req.url.endsWith('.js')) {
|
||||
// Artificially delay serving JS
|
||||
// to demonstrate streaming HTML.
|
||||
setTimeout(next, JS_BUNDLE_DELAY);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
app.use(compress());
|
||||
app.get(
|
||||
'/',
|
||||
handleErrors(async function(req, res) {
|
||||
await waitForWebpack();
|
||||
render(req.url, res);
|
||||
})
|
||||
);
|
||||
app.use(express.static('build'));
|
||||
app.use(express.static('public'));
|
||||
|
||||
app
|
||||
.listen(PORT, () => {
|
||||
console.log(`Listening at ${PORT}...`);
|
||||
})
|
||||
.on('error', function(error) {
|
||||
if (error.syscall !== 'listen') {
|
||||
throw error;
|
||||
}
|
||||
const isPipe = portOrPipe => Number.isNaN(portOrPipe);
|
||||
const bind = isPipe(PORT) ? 'Pipe ' + PORT : 'Port ' + PORT;
|
||||
switch (error.code) {
|
||||
case 'EACCES':
|
||||
console.error(bind + ' requires elevated privileges');
|
||||
process.exit(1);
|
||||
break;
|
||||
case 'EADDRINUSE':
|
||||
console.error(bind + ' is already in use');
|
||||
process.exit(1);
|
||||
break;
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
function handleErrors(fn) {
|
||||
return async function(req, res, next) {
|
||||
try {
|
||||
return await fn(req, res);
|
||||
} catch (x) {
|
||||
next(x);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function waitForWebpack() {
|
||||
while (true) {
|
||||
try {
|
||||
readFileSync(path.resolve(__dirname, '../build/main.js'));
|
||||
return;
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'Could not find webpack build output. Will retry in a second...'
|
||||
);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
import {Suspense, lazy} from 'react';
|
||||
import {ErrorBoundary} from 'react-error-boundary';
|
||||
import Html from './Html';
|
||||
import Spinner from './Spinner';
|
||||
import Layout from './Layout';
|
||||
import NavBar from './NavBar';
|
||||
|
||||
const Comments = lazy(() => import('./Comments' /* webpackPrefetch: true */));
|
||||
const Sidebar = lazy(() => import('./Sidebar' /* webpackPrefetch: true */));
|
||||
const Post = lazy(() => import('./Post' /* webpackPrefetch: true */));
|
||||
|
||||
export default function App({assets}) {
|
||||
return (
|
||||
<Html assets={assets} title="Hello">
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<ErrorBoundary FallbackComponent={Error}>
|
||||
<Content />
|
||||
</ErrorBoundary>
|
||||
</Suspense>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
|
||||
function Content() {
|
||||
return (
|
||||
<Layout>
|
||||
<NavBar />
|
||||
<aside className="sidebar">
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<Sidebar />
|
||||
</Suspense>
|
||||
</aside>
|
||||
<article className="post">
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<Post />
|
||||
</Suspense>
|
||||
<section className="comments">
|
||||
<h2>Comments</h2>
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<Comments />
|
||||
</Suspense>
|
||||
</section>
|
||||
<h2>Thanks for reading!</h2>
|
||||
</article>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
function Error({error}) {
|
||||
return (
|
||||
<div>
|
||||
<h1>Application Error</h1>
|
||||
<pre style={{whiteSpace: 'pre-wrap'}}>{error.stack}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
import {useData} from './data';
|
||||
|
||||
export default function Comments() {
|
||||
const comments = useData();
|
||||
return (
|
||||
<>
|
||||
{comments.map((comment, i) => (
|
||||
<p className="comment" key={i}>
|
||||
{comment}
|
||||
</p>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
export default function Html({assets, children, title}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="shortcut icon" href="favicon.ico" />
|
||||
<link rel="stylesheet" href={assets['main.css']} />
|
||||
<title>{title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `<b>Enable JavaScript to run this app.</b>`,
|
||||
}}
|
||||
/>
|
||||
{children}
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `assetManifest = ${JSON.stringify(assets)};`,
|
||||
}}
|
||||
/>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
export default function Layout({children}) {
|
||||
return <main>{children}</main>;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
export default function NavBar() {
|
||||
return (
|
||||
<nav>
|
||||
<a href="/">Home</a>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
export default function Post() {
|
||||
return (
|
||||
<>
|
||||
<h1>Hello world</h1>
|
||||
<p>
|
||||
This demo is <b>artificially slowed down</b>. Open{' '}
|
||||
<code>server/delays.js</code> to adjust how much different things are
|
||||
slowed down.
|
||||
</p>
|
||||
<p>
|
||||
Notice how HTML for comments "streams in" before the JS (or React) has
|
||||
loaded on the page.
|
||||
</p>
|
||||
<p>
|
||||
Also notice that the JS for comments and sidebar has been code-split,
|
||||
but HTML for it is still included in the server output.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
export default function Sidebar() {
|
||||
return (
|
||||
<>
|
||||
<h1>Archive</h1>
|
||||
<ul>
|
||||
<li>May 2021</li>
|
||||
<li>April 2021</li>
|
||||
<li>March 2021</li>
|
||||
<li>February 2021</li>
|
||||
<li>January 2021</li>
|
||||
<li>December 2020</li>
|
||||
<li>November 2020</li>
|
||||
<li>October 2020</li>
|
||||
<li>September 2020</li>
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
export default function Spinner({active = true}) {
|
||||
return (
|
||||
<div
|
||||
className={['spinner', active && 'spinner--active'].join(' ')}
|
||||
role="progressbar"
|
||||
aria-busy={active ? 'true' : 'false'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
import {createContext, useContext} from 'react';
|
||||
|
||||
// Note: this file does not demonstrate a real data fetching strategy.
|
||||
// We only use this to simulate data fetching happening on the server
|
||||
// while the cache is populated on the client. In a real app, you would
|
||||
// instead use a data fetching library or Server Components for this.
|
||||
|
||||
const DataContext = createContext(null);
|
||||
|
||||
export function DataProvider({children, data}) {
|
||||
return <DataContext.Provider value={data}>{children}</DataContext.Provider>;
|
||||
}
|
||||
|
||||
// In a real implementation the data would be streamed with the HTML.
|
||||
// We haven't integrated this part yet, so we'll just use fake data.
|
||||
const fakeData = [
|
||||
"Wait, it doesn't wait for React to load?",
|
||||
'How does this even work?',
|
||||
'I like marshmallows',
|
||||
];
|
||||
|
||||
export function useData() {
|
||||
const ctx = useContext(DataContext);
|
||||
if (ctx !== null) {
|
||||
// This context is only provided on the server.
|
||||
// It is here to simulate a suspending data fetch.
|
||||
ctx.read();
|
||||
}
|
||||
return fakeData;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
import {hydrateRoot} from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
hydrateRoot(document, <App assets={window.assetManifest} />);
|
||||
File diff suppressed because it is too large
Load Diff
83
fixtures/tracing/index.html
Normal file
83
fixtures/tracing/index.html
Normal file
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html style="width: 100%; height: 100%;">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test tracing UMD</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
ol {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
li {
|
||||
background-color: #F7F7F7;
|
||||
border: solid #CCC 0.125rem;
|
||||
margin-bottom: 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
li:after {
|
||||
content: attr(data-value);
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
.correct {
|
||||
border-color: #0C0;
|
||||
border-style: solid;
|
||||
background: #EFE;
|
||||
}
|
||||
.incorrect {
|
||||
border-color: #F00;
|
||||
border-style: dashed;
|
||||
background-color: #FEE;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test tracing UMD</h1>
|
||||
<p>
|
||||
This fixture tests that the new tracing API is accessible via UMD build using the UMD shim.
|
||||
It does not exhaustively test API functionality, only that the forwarded methods can be called.
|
||||
</p>
|
||||
<p>
|
||||
Before running the tests below, check the console to make sure there are no errors.
|
||||
</p>
|
||||
<h3>
|
||||
Tests
|
||||
<button id="run-test-button" onClick="runAllTests()">Run all tests</button>
|
||||
</h3>
|
||||
<ol>
|
||||
<li id="checkSchedulerAPI" data-value="...">
|
||||
<strong>Test scheduler API</strong>
|
||||
</li>
|
||||
<li id="checkSchedulerTracingAPI" data-value="...">
|
||||
<strong>Test tracing API</strong>
|
||||
</li>
|
||||
<li id="checkSchedulerTracingSubscriptionsAPI" data-value="...">
|
||||
<strong>Test tracing subscriptions API</strong>
|
||||
</li>
|
||||
<li id="checkEndToEndIntegration" data-value="...">
|
||||
<strong>Test end-to-end integration</strong>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<script>
|
||||
if (window.location.search.includes('puppeteer=true')) {
|
||||
// Collocated calls to performance.now() often yield different values in Puppeteer.
|
||||
// This causes the Scheduler API test to fail.
|
||||
// For the purposes of our automated release scripts,
|
||||
// Coerce tests to use Date.now() instead to reduce the chances of a false positive.
|
||||
window.performance = {now: Date.now};
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Load the tracing API before react to test that it's lazily evaluated -->
|
||||
<script src="../../build/node_modules/scheduler/umd/scheduler.development.js"></script>
|
||||
<script src="../../build/node_modules/scheduler/umd/scheduler-tracing.development.js"></script>
|
||||
<script src="../../build/node_modules/react/umd/react.development.js"></script>
|
||||
<script src="../../build/node_modules/react-dom/umd/react-dom.development.js"></script>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
206
fixtures/tracing/script.js
Normal file
206
fixtures/tracing/script.js
Normal file
@@ -0,0 +1,206 @@
|
||||
function runTest(listItem, callback) {
|
||||
try {
|
||||
callback();
|
||||
listItem.className = 'correct';
|
||||
listItem.setAttribute('data-value', 'All checks pass');
|
||||
} catch (error) {
|
||||
listItem.className = 'incorrect';
|
||||
listItem.setAttribute('data-value', error);
|
||||
}
|
||||
}
|
||||
|
||||
function runAllTests() {
|
||||
try {
|
||||
checkSchedulerAPI();
|
||||
} finally {
|
||||
try {
|
||||
checkSchedulerTracingAPI();
|
||||
} finally {
|
||||
try {
|
||||
checkSchedulerTracingSubscriptionsAPI();
|
||||
} finally {
|
||||
checkEndToEndIntegration();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkSchedulerAPI() {
|
||||
runTest(document.getElementById('checkSchedulerAPI'), () => {
|
||||
if (
|
||||
typeof Scheduler === 'undefined' ||
|
||||
typeof Scheduler.unstable_now !== 'function' ||
|
||||
typeof Scheduler.unstable_scheduleCallback !== 'function' ||
|
||||
typeof Scheduler.unstable_cancelCallback !== 'function'
|
||||
) {
|
||||
throw 'API is not defined';
|
||||
}
|
||||
|
||||
const abs = Math.abs(Scheduler.unstable_now() - performance.now());
|
||||
if (typeof abs !== 'number' || Number.isNaN(abs) || abs > 5) {
|
||||
throw 'API does not work';
|
||||
}
|
||||
|
||||
// There is no real way to verify that the two APIs are connected.
|
||||
});
|
||||
}
|
||||
|
||||
function checkSchedulerTracingAPI() {
|
||||
runTest(document.getElementById('checkSchedulerTracingAPI'), () => {
|
||||
if (
|
||||
typeof SchedulerTracing === 'undefined' ||
|
||||
typeof SchedulerTracing.unstable_clear !== 'function' ||
|
||||
typeof SchedulerTracing.unstable_getCurrent !== 'function' ||
|
||||
typeof SchedulerTracing.unstable_getThreadID !== 'function' ||
|
||||
typeof SchedulerTracing.unstable_trace !== 'function' ||
|
||||
typeof SchedulerTracing.unstable_wrap !== 'function'
|
||||
) {
|
||||
throw 'API is not defined';
|
||||
}
|
||||
|
||||
try {
|
||||
let interactionsSet;
|
||||
SchedulerTracing.unstable_trace('test', 123, () => {
|
||||
interactionsSet = SchedulerTracing.unstable_getCurrent();
|
||||
});
|
||||
if (interactionsSet.size !== 1) {
|
||||
throw null;
|
||||
}
|
||||
const interaction = Array.from(interactionsSet)[0];
|
||||
if (interaction.name !== 'test' || interaction.timestamp !== 123) {
|
||||
throw null;
|
||||
}
|
||||
} catch (error) {
|
||||
throw 'API does not work';
|
||||
}
|
||||
|
||||
const ForwardedSchedulerTracing =
|
||||
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing;
|
||||
|
||||
if (
|
||||
SchedulerTracing.unstable_getThreadID() ===
|
||||
ForwardedSchedulerTracing.unstable_getThreadID()
|
||||
) {
|
||||
throw 'API forwarding is broken';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkSchedulerTracingSubscriptionsAPI() {
|
||||
runTest(
|
||||
document.getElementById('checkSchedulerTracingSubscriptionsAPI'),
|
||||
() => {
|
||||
if (
|
||||
typeof SchedulerTracing === 'undefined' ||
|
||||
typeof SchedulerTracing.unstable_subscribe !== 'function' ||
|
||||
typeof SchedulerTracing.unstable_unsubscribe !== 'function'
|
||||
) {
|
||||
throw 'API is not defined';
|
||||
}
|
||||
|
||||
const onInteractionScheduledWorkCompletedCalls = [];
|
||||
const onInteractionTracedCalls = [];
|
||||
const onWorkCanceledCalls = [];
|
||||
const onWorkScheduledCalls = [];
|
||||
const onWorkStartedCalls = [];
|
||||
const onWorkStoppedCalls = [];
|
||||
const subscriber = {
|
||||
onInteractionScheduledWorkCompleted: (...args) =>
|
||||
onInteractionScheduledWorkCompletedCalls.push(args),
|
||||
onInteractionTraced: (...args) => onInteractionTracedCalls.push(args),
|
||||
onWorkCanceled: (...args) => onWorkCanceledCalls.push(args),
|
||||
onWorkScheduled: (...args) => onWorkScheduledCalls.push(args),
|
||||
onWorkStarted: (...args) => onWorkStartedCalls.push(args),
|
||||
onWorkStopped: (...args) => onWorkStoppedCalls.push(args),
|
||||
};
|
||||
|
||||
try {
|
||||
SchedulerTracing.unstable_subscribe(subscriber);
|
||||
SchedulerTracing.unstable_trace('foo', 123, () => {});
|
||||
SchedulerTracing.unstable_unsubscribe(subscriber);
|
||||
if (onInteractionTracedCalls.length !== 1) {
|
||||
throw null;
|
||||
}
|
||||
const interaction = onInteractionTracedCalls[0][0];
|
||||
if (interaction.name !== 'foo' || interaction.timestamp !== 123) {
|
||||
throw null;
|
||||
}
|
||||
SchedulerTracing.unstable_trace('bar', 456, () => {});
|
||||
if (onInteractionTracedCalls.length !== 1) {
|
||||
throw null;
|
||||
}
|
||||
} catch (error) {
|
||||
throw 'API does not forward methods';
|
||||
}
|
||||
|
||||
const ForwardedSchedulerTracing =
|
||||
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.SchedulerTracing;
|
||||
|
||||
try {
|
||||
ForwardedSchedulerTracing.unstable_subscribe(subscriber);
|
||||
SchedulerTracing.unstable_trace('foo', 123, () => {});
|
||||
ForwardedSchedulerTracing.unstable_trace('bar', 456, () => {});
|
||||
SchedulerTracing.unstable_unsubscribe(subscriber);
|
||||
if (onInteractionTracedCalls.length !== 3) {
|
||||
throw null;
|
||||
}
|
||||
const interactionFoo = onInteractionTracedCalls[1][0];
|
||||
const interactionBar = onInteractionTracedCalls[2][0];
|
||||
if (
|
||||
interactionFoo.name !== 'foo' ||
|
||||
interactionFoo.timestamp !== 123 ||
|
||||
interactionBar.name !== 'bar' ||
|
||||
interactionBar.timestamp !== 456
|
||||
) {
|
||||
throw null;
|
||||
}
|
||||
ForwardedSchedulerTracing.unstable_trace('baz', 789, () => {});
|
||||
if (onInteractionTracedCalls.length !== 3) {
|
||||
throw null;
|
||||
}
|
||||
} catch (error) {
|
||||
throw 'API forwarding is broken';
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function checkEndToEndIntegration() {
|
||||
runTest(document.getElementById('checkEndToEndIntegration'), () => {
|
||||
try {
|
||||
const onRenderCalls = [];
|
||||
const onRender = (...args) => onRenderCalls.push(args);
|
||||
const container = document.createElement('div');
|
||||
|
||||
SchedulerTracing.unstable_trace('render', 123, () => {
|
||||
ReactDOM.render(
|
||||
React.createElement(
|
||||
React.Profiler,
|
||||
{id: 'profiler', onRender},
|
||||
React.createElement('div', null, 'hi')
|
||||
),
|
||||
container
|
||||
);
|
||||
});
|
||||
|
||||
if (container.textContent !== 'hi') {
|
||||
throw null;
|
||||
}
|
||||
|
||||
if (onRenderCalls.length !== 1) {
|
||||
throw null;
|
||||
}
|
||||
const call = onRenderCalls[0];
|
||||
if (call.length !== 7) {
|
||||
throw null;
|
||||
}
|
||||
const interaction = Array.from(call[6])[0];
|
||||
if (interaction.name !== 'render' || interaction.timestamp !== 123) {
|
||||
throw null;
|
||||
}
|
||||
} catch (error) {
|
||||
throw 'End to end integration is broken';
|
||||
}
|
||||
});
|
||||
}
|
||||
21
fixtures/tracing/test.html
Normal file
21
fixtures/tracing/test.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html style="width: 100%; height: 100%;">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test tracing UMD</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<!-- Load the tracing API before react to test that it's lazily evaluated -->
|
||||
<script src="../../build/node_modules/scheduler/umd/scheduler.development.js"></script>
|
||||
<script src="../../build/node_modules/scheduler/umd/scheduler-tracing.development.js"></script>
|
||||
<script src="../../build/node_modules/react/umd/react.development.js"></script>
|
||||
<script src="../../build/node_modules/react-dom/umd/react-dom.development.js"></script>
|
||||
<script src="./test.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
101
fixtures/tracing/test.js
Normal file
101
fixtures/tracing/test.js
Normal file
@@ -0,0 +1,101 @@
|
||||
const {createElement, Component, Suspense} = React;
|
||||
const {createRoot} = ReactDOM;
|
||||
const {
|
||||
unstable_subscribe: subscribe,
|
||||
unstable_trace: trace,
|
||||
unstable_wrap: wrap,
|
||||
} = SchedulerTracing;
|
||||
|
||||
const createLogger = (backgroundColor, color, enabled) => (
|
||||
message,
|
||||
...args
|
||||
) => {
|
||||
if (enabled === false) return;
|
||||
console.groupCollapsed(
|
||||
`%c${message}`,
|
||||
`background-color: ${backgroundColor}; color: ${color}; padding: 2px 4px;`,
|
||||
...args
|
||||
);
|
||||
console.log(
|
||||
new Error('stack').stack
|
||||
.split('\n')
|
||||
.slice(2)
|
||||
.join('\n')
|
||||
);
|
||||
console.groupEnd();
|
||||
};
|
||||
|
||||
window.log = {
|
||||
app: createLogger('#37474f', '#fff'),
|
||||
interaction: createLogger('#6a1b9a', '#fff'),
|
||||
react: createLogger('#ff5722', '#fff'),
|
||||
tracing: createLogger('#2962ff', '#fff'),
|
||||
work: createLogger('#e1bee7', '#000'),
|
||||
};
|
||||
|
||||
// Fake suspense
|
||||
const resolvedValues = {};
|
||||
const read = key => {
|
||||
if (!resolvedValues[key]) {
|
||||
log.app(`Suspending for "${key}" ...`);
|
||||
throw new Promise(
|
||||
wrap(resolve => {
|
||||
setTimeout(
|
||||
wrap(() => {
|
||||
log.app(`Loaded "${key}" ...`);
|
||||
resolvedValues[key] = true;
|
||||
resolve(key);
|
||||
}),
|
||||
1000
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
return key;
|
||||
};
|
||||
|
||||
const TestApp = () =>
|
||||
createElement(
|
||||
Suspense,
|
||||
{fallback: createElement(PlaceholderText)},
|
||||
createElement(SuspendingChild, {text: 'foo'}),
|
||||
createElement(SuspendingChild, {text: 'bar'}),
|
||||
createElement(SuspendingChild, {text: 'baz'})
|
||||
);
|
||||
|
||||
const PlaceholderText = () => 'Loading ...';
|
||||
|
||||
const SuspendingChild = ({text}) => {
|
||||
const resolvedValue = read(text);
|
||||
return resolvedValue;
|
||||
};
|
||||
|
||||
subscribe({
|
||||
onInteractionScheduledWorkCompleted: interaction =>
|
||||
log.interaction(
|
||||
'onInteractionScheduledWorkCompleted',
|
||||
JSON.stringify(interaction)
|
||||
),
|
||||
onInteractionTraced: interaction =>
|
||||
log.interaction('onInteractionTraced', JSON.stringify(interaction)),
|
||||
onWorkCanceled: interactions =>
|
||||
log.work('onWorkCanceled', JSON.stringify(Array.from(interactions))),
|
||||
onWorkScheduled: interactions =>
|
||||
log.work('onWorkScheduled', JSON.stringify(Array.from(interactions))),
|
||||
onWorkStarted: interactions =>
|
||||
log.work('onWorkStarted', JSON.stringify(Array.from(interactions))),
|
||||
onWorkStopped: interactions =>
|
||||
log.work('onWorkStopped', JSON.stringify(Array.from(interactions))),
|
||||
});
|
||||
|
||||
const element = document.getElementById('root');
|
||||
trace('initial_render', performance.now(), () => {
|
||||
const root = createRoot(element);
|
||||
log.app('render()');
|
||||
root.render(
|
||||
createElement(TestApp),
|
||||
wrap(() => {
|
||||
log.app('committed');
|
||||
})
|
||||
);
|
||||
});
|
||||
31
old_major_packages/14/react-dom/LICENSE
Normal file
31
old_major_packages/14/react-dom/LICENSE
Normal file
@@ -0,0 +1,31 @@
|
||||
BSD License
|
||||
|
||||
For React software
|
||||
|
||||
Copyright (c) 2013-2015, Facebook, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name Facebook nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user