Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3145b1f37 | ||
|
|
72f436d0ef | ||
|
|
cff62b6370 | ||
|
|
6568bd00f9 | ||
|
|
7adca9e3b9 | ||
|
|
ede3840137 | ||
|
|
0e2ae74309 | ||
|
|
6e79ecf60b | ||
|
|
02ef495809 | ||
|
|
3f178f55fc | ||
|
|
87e33ca2b7 | ||
|
|
52cf381c72 | ||
|
|
b793948e15 | ||
|
|
73e4ba42cd | ||
|
|
5a1eb6f61a | ||
|
|
01eae200bf | ||
|
|
0e6781a06b | ||
|
|
2cd3c424ea | ||
|
|
a24654e65b |
@@ -331,6 +331,7 @@ module.exports = {
|
||||
'packages/react-server-dom-turbopack/**/*.js',
|
||||
'packages/react-server-dom-parcel/**/*.js',
|
||||
'packages/react-server-dom-fb/**/*.js',
|
||||
'packages/react-server-dom-unbundled/**/*.js',
|
||||
'packages/react-test-renderer/**/*.js',
|
||||
'packages/react-debug-tools/**/*.js',
|
||||
'packages/react-devtools-extensions/**/*.js',
|
||||
|
||||
161
.github/workflows/runtime_build_and_test.yml
vendored
161
.github/workflows/runtime_build_and_test.yml
vendored
@@ -3,9 +3,19 @@ name: (Runtime) Build and Test
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags:
|
||||
# To get CI for backport releases.
|
||||
# This will duplicate CI for releases from main which is acceptable
|
||||
- "v*"
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- compiler/**
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
commit_sha:
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
permissions: {}
|
||||
|
||||
@@ -28,14 +38,14 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Check cache hit
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
lookup-only: true
|
||||
- uses: actions/setup-node@v4
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
@@ -49,10 +59,8 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
- run: yarn install --frozen-lockfile
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- name: Save cache
|
||||
@@ -61,7 +69,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
|
||||
runtime_compiler_node_modules_cache:
|
||||
name: Cache Runtime, Compiler node_modules
|
||||
@@ -69,14 +77,14 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Check cache hit
|
||||
uses: actions/cache/restore@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
lookup-only: true
|
||||
- uses: actions/setup-node@v4
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
@@ -92,10 +100,8 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-and-compiler-node_modules-v6-
|
||||
key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
- run: yarn install --frozen-lockfile
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- run: yarn --cwd compiler install --frozen-lockfile
|
||||
@@ -106,7 +112,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
|
||||
# ----- FLOW -----
|
||||
discover_flow_inline_configs:
|
||||
@@ -117,7 +123,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/github-script@v7
|
||||
id: set-matrix
|
||||
with:
|
||||
@@ -136,7 +142,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -148,10 +154,8 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -166,7 +170,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -178,10 +182,8 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -198,7 +200,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -210,7 +212,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -254,7 +256,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -268,10 +270,8 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-and-compiler-node_modules-v6-
|
||||
key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -280,6 +280,37 @@ jobs:
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- run: yarn test ${{ matrix.params }} --ci --shard=${{ matrix.shard }}
|
||||
|
||||
# Hardcoded to improve parallelism
|
||||
test-linter:
|
||||
name: Test eslint-plugin-react-hooks
|
||||
needs: [runtime_compiler_node_modules_cache]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: yarn
|
||||
cache-dependency-path: |
|
||||
yarn.lock
|
||||
compiler/yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
- name: Install runtime dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- name: Install compiler dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
working-directory: compiler
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- run: ./scripts/react-compiler/build-compiler.sh && ./scripts/react-compiler/link-compiler.sh
|
||||
- run: yarn workspace eslint-plugin-react-hooks test
|
||||
|
||||
# ----- BUILD -----
|
||||
build_and_lint:
|
||||
name: yarn build and lint
|
||||
@@ -294,7 +325,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -312,10 +343,8 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-and-compiler-node_modules-v6-
|
||||
key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -389,7 +418,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -403,10 +432,8 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-and-compiler-node_modules-v6-
|
||||
key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -430,7 +457,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -442,10 +469,8 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -458,7 +483,7 @@ jobs:
|
||||
merge-multiple: true
|
||||
- name: Display structure of build
|
||||
run: ls -R build
|
||||
- run: echo ${{ github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA
|
||||
- run: echo ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA
|
||||
- name: Scrape warning messages
|
||||
run: |
|
||||
mkdir -p ./build/__test_utils__
|
||||
@@ -483,7 +508,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -495,10 +520,8 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -523,7 +546,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -535,10 +558,8 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -560,7 +581,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -601,7 +622,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -675,7 +696,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -687,10 +708,8 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -732,7 +751,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -744,10 +763,8 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-
|
||||
runtime-node_modules-v6-
|
||||
key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
# Don't use restore-keys here. Otherwise the cache grows indefinitely.
|
||||
- name: Ensure clean build directory
|
||||
run: rm -rf build
|
||||
- run: yarn install --frozen-lockfile
|
||||
@@ -777,7 +794,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -822,7 +839,7 @@ jobs:
|
||||
node ./scripts/print-warnings/print-warnings.js > build/__test_utils__/ReactAllWarnings.js
|
||||
- name: Display structure of build for PR
|
||||
run: ls -R build
|
||||
- run: echo ${{ github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA
|
||||
- run: echo ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA
|
||||
- run: node ./scripts/tasks/danger
|
||||
- name: Archive sizebot results
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -7,18 +7,18 @@
|
||||
//
|
||||
// The @latest channel uses the version as-is, e.g.:
|
||||
//
|
||||
// 19.1.0
|
||||
// 19.1.4
|
||||
//
|
||||
// The @canary channel appends additional information, with the scheme
|
||||
// <version>-<label>-<commit_sha>, e.g.:
|
||||
//
|
||||
// 19.1.0-canary-a1c2d3e4
|
||||
// 19.1.4-canary-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 = '19.1.0';
|
||||
const ReactVersion = '19.1.4';
|
||||
|
||||
// The label used by the @canary channel. Represents the upcoming release's
|
||||
// stability. Most of the time, this will be "canary", but we may temporarily
|
||||
@@ -34,7 +34,7 @@ const rcNumber = 0;
|
||||
|
||||
const stablePackages = {
|
||||
'eslint-plugin-react-hooks': '5.2.0',
|
||||
'jest-react': '0.17.0',
|
||||
'jest-react': '0.16.0',
|
||||
react: ReactVersion,
|
||||
'react-art': ReactVersion,
|
||||
'react-dom': ReactVersion,
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
load as reactLoad,
|
||||
getSource as getSourceImpl,
|
||||
transformSource as reactTransformSource,
|
||||
} from 'react-server-dom-webpack/node-loader';
|
||||
} from 'react-server-dom-unbundled/node-loader';
|
||||
|
||||
export {resolve};
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ const http = require('http');
|
||||
const React = require('react');
|
||||
|
||||
const {renderToPipeableStream} = require('react-dom/server');
|
||||
const {createFromNodeStream} = require('react-server-dom-webpack/client');
|
||||
const {createFromNodeStream} = require('react-server-dom-unbundled/client');
|
||||
const {PassThrough} = require('stream');
|
||||
|
||||
const app = express();
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
const path = require('path');
|
||||
const url = require('url');
|
||||
|
||||
const register = require('react-server-dom-webpack/node-register');
|
||||
const register = require('react-server-dom-unbundled/node-register');
|
||||
// TODO: This seems to have no effect anymore. Remove?
|
||||
register();
|
||||
|
||||
const babelRegister = require('@babel/register');
|
||||
@@ -52,7 +53,7 @@ const React = require('react');
|
||||
|
||||
async function renderApp(res, returnValue, formState) {
|
||||
const {renderToPipeableStream} = await import(
|
||||
'react-server-dom-webpack/server'
|
||||
'react-server-dom-unbundled/server'
|
||||
);
|
||||
// const m = require('../src/App.js');
|
||||
const m = await import('../src/App.js');
|
||||
@@ -107,7 +108,7 @@ async function renderApp(res, returnValue, formState) {
|
||||
|
||||
async function prerenderApp(res, returnValue, formState) {
|
||||
const {unstable_prerenderToNodeStream: prerenderToNodeStream} = await import(
|
||||
'react-server-dom-webpack/static'
|
||||
'react-server-dom-unbundled/static'
|
||||
);
|
||||
// const m = require('../src/App.js');
|
||||
const m = await import('../src/App.js');
|
||||
@@ -170,7 +171,7 @@ app.get('/', async function (req, res) {
|
||||
|
||||
app.post('/', bodyParser.text(), async function (req, res) {
|
||||
const {decodeReply, decodeReplyFromBusboy, decodeAction, decodeFormState} =
|
||||
await import('react-server-dom-webpack/server');
|
||||
await import('react-server-dom-unbundled/server');
|
||||
const serverReference = req.get('rsc-action');
|
||||
if (serverReference) {
|
||||
// This is the client-side case
|
||||
|
||||
@@ -120,10 +120,10 @@
|
||||
"scripts": {
|
||||
"prebuild": "./scripts/react-compiler/link-compiler.sh",
|
||||
"build": "node ./scripts/rollup/build-all-release-channels.js",
|
||||
"build-for-devtools": "cross-env RELEASE_CHANNEL=experimental yarn build react/index,react/jsx,react/compiler-runtime,react-dom/index,react-dom/client,react-dom/unstable_testing,react-dom/test-utils,react-is,react-debug-tools,scheduler,react-test-renderer,react-refresh,react-art --type=NODE",
|
||||
"build-for-devtools": "cross-env yarn build react/index,react/jsx,react/compiler-runtime,react-dom/index,react-dom/client,react-dom/unstable_testing,react-dom/test-utils,react-is,react-debug-tools,scheduler,react-test-renderer,react-refresh,react-art --type=NODE --release-channel=experimental",
|
||||
"build-for-devtools-dev": "yarn build-for-devtools --type=NODE_DEV",
|
||||
"build-for-devtools-prod": "yarn build-for-devtools --type=NODE_PROD",
|
||||
"build-for-flight-dev": "cross-env RELEASE_CHANNEL=experimental node ./scripts/rollup/build.js react/index,react/jsx,react.react-server,react-dom/index,react-dom/client,react-dom/server,react-dom.react-server,react-dom-server.node,react-dom-server-legacy.node,scheduler,react-server-dom-webpack/ --type=NODE_DEV,ESM_PROD,NODE_ES2015 && mv ./build/node_modules ./build/oss-experimental",
|
||||
"build-for-flight-dev": "cross-env RELEASE_CHANNEL=experimental node ./scripts/rollup/build.js react/index,react/jsx,react.react-server,react-dom/index,react-dom/client,react-dom/server,react-dom.react-server,react-dom-server.node,react-dom-server-legacy.node,scheduler,react-server-dom-webpack/,react-server-dom-unbundled/ --type=NODE_DEV,ESM_PROD,NODE_ES2015 && mv ./build/node_modules ./build/oss-experimental",
|
||||
"build-for-vt-dev": "cross-env RELEASE_CHANNEL=experimental node ./scripts/rollup/build.js react/index,react/jsx,react-dom/index,react-dom/client,react-dom/server,react-dom-server.node,react-dom-server-legacy.node,scheduler --type=NODE_DEV && mv ./build/node_modules ./build/oss-experimental",
|
||||
"linc": "node ./scripts/tasks/linc.js",
|
||||
"lint": "node ./scripts/tasks/eslint.js",
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
"homepage": "https://react.dev/",
|
||||
"peerDependencies": {
|
||||
"jest": "^23.0.1 || ^24.0.0 || ^25.1.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0",
|
||||
"react": "^19.0.0",
|
||||
"react-test-renderer": "^19.0.0"
|
||||
"react": "^19.1.4",
|
||||
"react-test-renderer": "^19.1.4"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-art",
|
||||
"description": "React ART is a JavaScript library for drawing vector graphics using React. It provides declarative and reactive bindings to the ART library. Using the same declarative API you can render the output to either Canvas, SVG or VML (IE8).",
|
||||
"version": "19.1.0",
|
||||
"version": "19.1.4",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -24,10 +24,10 @@
|
||||
"dependencies": {
|
||||
"art": "^0.10.1",
|
||||
"create-react-class": "^15.6.2",
|
||||
"scheduler": "^0.25.0"
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
"react": "^19.1.4"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
@@ -38,4 +38,4 @@
|
||||
"Rectangle.js",
|
||||
"Wedge.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
33
packages/react-client/src/ReactFlightClient.js
vendored
33
packages/react-client/src/ReactFlightClient.js
vendored
@@ -555,6 +555,7 @@ function resolveModuleChunk<T>(
|
||||
const resolvedChunk: ResolvedModuleChunk<T> = (chunk: any);
|
||||
resolvedChunk.status = RESOLVED_MODULE;
|
||||
resolvedChunk.value = value;
|
||||
resolvedChunk.reason = null;
|
||||
if (resolveListeners !== null) {
|
||||
initializeModuleChunk(resolvedChunk);
|
||||
wakeChunkIfInitialized(chunk, resolveListeners, rejectListeners);
|
||||
@@ -616,6 +617,7 @@ function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void {
|
||||
const initializedChunk: InitializedChunk<T> = (chunk: any);
|
||||
initializedChunk.status = INITIALIZED;
|
||||
initializedChunk.value = value;
|
||||
initializedChunk.reason = null;
|
||||
} catch (error) {
|
||||
const erroredChunk: ErroredChunk<T> = (chunk: any);
|
||||
erroredChunk.status = ERRORED;
|
||||
@@ -634,6 +636,7 @@ function initializeModuleChunk<T>(chunk: ResolvedModuleChunk<T>): void {
|
||||
const initializedChunk: InitializedChunk<T> = (chunk: any);
|
||||
initializedChunk.status = INITIALIZED;
|
||||
initializedChunk.value = value;
|
||||
initializedChunk.reason = null;
|
||||
} catch (error) {
|
||||
const erroredChunk: ErroredChunk<T> = (chunk: any);
|
||||
erroredChunk.status = ERRORED;
|
||||
@@ -652,6 +655,8 @@ export function reportGlobalError(response: Response, error: Error): void {
|
||||
// because we won't be getting any new data to resolve it.
|
||||
if (chunk.status === PENDING) {
|
||||
triggerErrorOnChunk(chunk, error);
|
||||
} else if (chunk.status === INITIALIZED && chunk.reason !== null) {
|
||||
chunk.reason.error(error);
|
||||
}
|
||||
});
|
||||
if (enableProfilerTimer && enableComponentPerformanceTrack) {
|
||||
@@ -1008,6 +1013,7 @@ function waitForReference<T>(
|
||||
const initializedChunk: InitializedChunk<T> = (chunk: any);
|
||||
initializedChunk.status = INITIALIZED;
|
||||
initializedChunk.value = handler.value;
|
||||
initializedChunk.reason = null;
|
||||
if (resolveListeners !== null) {
|
||||
wakeChunk(resolveListeners, handler.value);
|
||||
}
|
||||
@@ -1186,6 +1192,7 @@ function loadServerReference<A: Iterable<any>, T>(
|
||||
const initializedChunk: InitializedChunk<T> = (chunk: any);
|
||||
initializedChunk.status = INITIALIZED;
|
||||
initializedChunk.value = handler.value;
|
||||
initializedChunk.reason = null;
|
||||
if (resolveListeners !== null) {
|
||||
wakeChunk(resolveListeners, handler.value);
|
||||
}
|
||||
@@ -1443,7 +1450,7 @@ function parseModelString(
|
||||
// Symbol
|
||||
return Symbol.for(value.slice(2));
|
||||
}
|
||||
case 'F': {
|
||||
case 'h': {
|
||||
// Server Reference
|
||||
const ref = value.slice(2);
|
||||
return getOutlinedModel(
|
||||
@@ -1854,6 +1861,7 @@ function startReadableStream<T>(
|
||||
type: void | 'bytes',
|
||||
): void {
|
||||
let controller: ReadableStreamController = (null: any);
|
||||
let closed = false;
|
||||
const stream = new ReadableStream({
|
||||
type: type,
|
||||
start(c) {
|
||||
@@ -1911,6 +1919,10 @@ function startReadableStream<T>(
|
||||
}
|
||||
},
|
||||
close(json: UninitializedModel): void {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
if (previousBlockedChunk === null) {
|
||||
controller.close();
|
||||
} else {
|
||||
@@ -1921,6 +1933,10 @@ function startReadableStream<T>(
|
||||
}
|
||||
},
|
||||
error(error: mixed): void {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
if (previousBlockedChunk === null) {
|
||||
// $FlowFixMe[incompatible-call]
|
||||
controller.error(error);
|
||||
@@ -1980,6 +1996,7 @@ function startAsyncIterable<T>(
|
||||
(chunk: any);
|
||||
initializedChunk.status = INITIALIZED;
|
||||
initializedChunk.value = {done: false, value: value};
|
||||
initializedChunk.reason = null;
|
||||
if (resolveListeners !== null) {
|
||||
wakeChunkIfInitialized(chunk, resolveListeners, rejectListeners);
|
||||
}
|
||||
@@ -1999,6 +2016,9 @@ function startAsyncIterable<T>(
|
||||
nextWriteIndex++;
|
||||
},
|
||||
close(value: UninitializedModel): void {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
if (nextWriteIndex === buffer.length) {
|
||||
buffer[nextWriteIndex] = createResolvedIteratorResultChunk(
|
||||
@@ -2020,6 +2040,9 @@ function startAsyncIterable<T>(
|
||||
}
|
||||
},
|
||||
error(error: Error): void {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
if (nextWriteIndex === buffer.length) {
|
||||
buffer[nextWriteIndex] =
|
||||
@@ -2438,7 +2461,7 @@ function buildFakeTask(
|
||||
}
|
||||
|
||||
const createFakeJSXCallStack = {
|
||||
'react-stack-bottom-frame': function (
|
||||
react_stack_bottom_frame: function (
|
||||
response: Response,
|
||||
stack: ReactStackTrace,
|
||||
environmentName: string,
|
||||
@@ -2459,7 +2482,7 @@ const createFakeJSXCallStackInDEV: (
|
||||
environmentName: string,
|
||||
) => Error = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(createFakeJSXCallStack['react-stack-bottom-frame'].bind(
|
||||
(createFakeJSXCallStack.react_stack_bottom_frame.bind(
|
||||
createFakeJSXCallStack,
|
||||
): any)
|
||||
: (null: any);
|
||||
@@ -2563,7 +2586,7 @@ function getCurrentStackInDEV(): string {
|
||||
}
|
||||
|
||||
const replayConsoleWithCallStack = {
|
||||
'react-stack-bottom-frame': function (
|
||||
react_stack_bottom_frame: function (
|
||||
response: Response,
|
||||
methodName: string,
|
||||
stackTrace: ReactStackTrace,
|
||||
@@ -2614,7 +2637,7 @@ const replayConsoleWithCallStackInDEV: (
|
||||
args: Array<mixed>,
|
||||
) => void = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(replayConsoleWithCallStack['react-stack-bottom-frame'].bind(
|
||||
(replayConsoleWithCallStack.react_stack_bottom_frame.bind(
|
||||
replayConsoleWithCallStack,
|
||||
): any)
|
||||
: (null: any);
|
||||
|
||||
@@ -107,7 +107,7 @@ function serializePromiseID(id: number): string {
|
||||
}
|
||||
|
||||
function serializeServerReferenceID(id: number): string {
|
||||
return '$F' + id.toString(16);
|
||||
return '$h' + id.toString(16);
|
||||
}
|
||||
|
||||
function serializeTemporaryReferenceMarker(): string {
|
||||
@@ -115,7 +115,6 @@ function serializeTemporaryReferenceMarker(): string {
|
||||
}
|
||||
|
||||
function serializeFormDataReference(id: number): string {
|
||||
// Why K? F is "Function". D is "Date". What else?
|
||||
return '$K' + id.toString(16);
|
||||
}
|
||||
|
||||
@@ -477,8 +476,22 @@ export function processReply(
|
||||
}
|
||||
}
|
||||
|
||||
const existingReference = writtenObjects.get(value);
|
||||
|
||||
// $FlowFixMe[method-unbinding]
|
||||
if (typeof value.then === 'function') {
|
||||
if (existingReference !== undefined) {
|
||||
if (modelRoot === value) {
|
||||
// This is the ID we're currently emitting so we need to write it
|
||||
// once but if we discover it again, we refer to it by id.
|
||||
modelRoot = null;
|
||||
} else {
|
||||
// We've already emitted this as an outlined object, so we can
|
||||
// just refer to that by its existing ID.
|
||||
return existingReference;
|
||||
}
|
||||
}
|
||||
|
||||
// We assume that any object with a .then property is a "Thenable" type,
|
||||
// or a Promise type. Either of which can be represented by a Promise.
|
||||
if (formData === null) {
|
||||
@@ -487,11 +500,19 @@ export function processReply(
|
||||
}
|
||||
pendingParts++;
|
||||
const promiseId = nextPartId++;
|
||||
const promiseReference = serializePromiseID(promiseId);
|
||||
writtenObjects.set(value, promiseReference);
|
||||
const thenable: Thenable<any> = (value: any);
|
||||
thenable.then(
|
||||
partValue => {
|
||||
try {
|
||||
const partJSON = serializeModel(partValue, promiseId);
|
||||
const previousReference = writtenObjects.get(partValue);
|
||||
let partJSON;
|
||||
if (previousReference !== undefined) {
|
||||
partJSON = JSON.stringify(previousReference);
|
||||
} else {
|
||||
partJSON = serializeModel(partValue, promiseId);
|
||||
}
|
||||
// $FlowFixMe[incompatible-type] We know it's not null because we assigned it above.
|
||||
const data: FormData = formData;
|
||||
data.append(formFieldPrefix + promiseId, partJSON);
|
||||
@@ -507,10 +528,9 @@ export function processReply(
|
||||
// that throws on the server instead.
|
||||
reject,
|
||||
);
|
||||
return serializePromiseID(promiseId);
|
||||
return promiseReference;
|
||||
}
|
||||
|
||||
const existingReference = writtenObjects.get(value);
|
||||
if (existingReference !== undefined) {
|
||||
if (modelRoot === value) {
|
||||
// This is the ID we're currently emitting so we need to write it
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export {default as rendererVersion} from 'shared/ReactVersion';
|
||||
export const rendererPackageName = 'react-server-dom-webpack';
|
||||
export const rendererPackageName = 'react-server-dom-unbundled';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackServer';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigTargetWebpackServer';
|
||||
export * from 'react-server-dom-unbundled/src/client/ReactFlightClientConfigBundlerNode';
|
||||
export * from 'react-server-dom-unbundled/src/client/ReactFlightClientConfigTargetNodeServer';
|
||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||
export const usedWithSSR = true;
|
||||
@@ -6,13 +6,13 @@
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export {default as rendererVersion} from 'shared/ReactVersion';
|
||||
export const rendererPackageName = 'react-server-dom-webpack';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerNode';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackServer';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigTargetWebpackServer';
|
||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||
export const usedWithSSR = true;
|
||||
|
||||
@@ -50,6 +50,7 @@ import {
|
||||
gt,
|
||||
gte,
|
||||
parseSourceFromComponentStack,
|
||||
parseSourceFromOwnerStack,
|
||||
serializeToString,
|
||||
} from 'react-devtools-shared/src/backend/utils';
|
||||
import {
|
||||
@@ -5758,15 +5759,13 @@ export function attach(
|
||||
function getSourceForFiberInstance(
|
||||
fiberInstance: FiberInstance,
|
||||
): Source | null {
|
||||
const unresolvedSource = fiberInstance.source;
|
||||
if (
|
||||
unresolvedSource !== null &&
|
||||
typeof unresolvedSource === 'object' &&
|
||||
!isError(unresolvedSource)
|
||||
) {
|
||||
// $FlowFixMe: isError should have refined it.
|
||||
return unresolvedSource;
|
||||
// Favor the owner source if we have one.
|
||||
const ownerSource = getSourceForInstance(fiberInstance);
|
||||
if (ownerSource !== null) {
|
||||
return ownerSource;
|
||||
}
|
||||
|
||||
// Otherwise fallback to the throwing trick.
|
||||
const dispatcherRef = getDispatcherRef(renderer);
|
||||
const stackFrame =
|
||||
dispatcherRef == null
|
||||
@@ -5777,10 +5776,7 @@ export function attach(
|
||||
dispatcherRef,
|
||||
);
|
||||
if (stackFrame === null) {
|
||||
// If we don't find a source location by throwing, try to get one
|
||||
// from an owned child if possible. This is the same branch as
|
||||
// for virtual instances.
|
||||
return getSourceForInstance(fiberInstance);
|
||||
return null;
|
||||
}
|
||||
const source = parseSourceFromComponentStack(stackFrame);
|
||||
fiberInstance.source = source;
|
||||
@@ -5788,7 +5784,7 @@ export function attach(
|
||||
}
|
||||
|
||||
function getSourceForInstance(instance: DevToolsInstance): Source | null {
|
||||
let unresolvedSource = instance.source;
|
||||
const unresolvedSource = instance.source;
|
||||
if (unresolvedSource === null) {
|
||||
// We don't have any source yet. We can try again later in case an owned child mounts later.
|
||||
// TODO: We won't have any information here if the child is filtered.
|
||||
@@ -5801,7 +5797,9 @@ export function attach(
|
||||
// any intermediate utility functions. This won't point to the top of the component function
|
||||
// but it's at least somewhere within it.
|
||||
if (isError(unresolvedSource)) {
|
||||
unresolvedSource = formatOwnerStack((unresolvedSource: any));
|
||||
return (instance.source = parseSourceFromOwnerStack(
|
||||
(unresolvedSource: any),
|
||||
));
|
||||
}
|
||||
if (typeof unresolvedSource === 'string') {
|
||||
const idx = unresolvedSource.lastIndexOf('\n');
|
||||
|
||||
@@ -13,8 +13,12 @@ export function formatOwnerStack(error: Error): string {
|
||||
const prevPrepareStackTrace = Error.prepareStackTrace;
|
||||
// $FlowFixMe[incompatible-type] It does accept undefined.
|
||||
Error.prepareStackTrace = undefined;
|
||||
let stack = error.stack;
|
||||
const stack = error.stack;
|
||||
Error.prepareStackTrace = prevPrepareStackTrace;
|
||||
return formatOwnerStackString(stack);
|
||||
}
|
||||
|
||||
export function formatOwnerStackString(stack: string): string {
|
||||
if (stack.startsWith('Error: react-stack-top-frame\n')) {
|
||||
// V8's default formatting prefixes with the error message which we
|
||||
// don't want/need.
|
||||
@@ -25,7 +29,10 @@ export function formatOwnerStack(error: Error): string {
|
||||
// Pop the JSX frame.
|
||||
stack = stack.slice(idx + 1);
|
||||
}
|
||||
idx = stack.indexOf('react-stack-bottom-frame');
|
||||
idx = stack.indexOf('react_stack_bottom_frame');
|
||||
if (idx === -1) {
|
||||
idx = stack.indexOf('react-stack-bottom-frame');
|
||||
}
|
||||
if (idx !== -1) {
|
||||
idx = stack.lastIndexOf('\n', idx);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ import type {DehydratedData} from 'react-devtools-shared/src/frontend/types';
|
||||
export {default as formatWithStyles} from './formatWithStyles';
|
||||
export {default as formatConsoleArguments} from './formatConsoleArguments';
|
||||
|
||||
import {formatOwnerStackString} from '../shared/DevToolsOwnerStack';
|
||||
|
||||
// TODO: update this to the first React version that has a corresponding DevTools backend
|
||||
const FIRST_DEVTOOLS_BACKEND_LOCKSTEP_VER = '999.9.9';
|
||||
export function hasAssignedBackend(version?: string): boolean {
|
||||
@@ -345,6 +347,81 @@ export function parseSourceFromComponentStack(
|
||||
return parseSourceFromFirefoxStack(componentStack);
|
||||
}
|
||||
|
||||
let collectedLocation: Source | null = null;
|
||||
|
||||
function collectStackTrace(
|
||||
error: Error,
|
||||
structuredStackTrace: CallSite[],
|
||||
): string {
|
||||
let result: null | Source = null;
|
||||
// Collect structured stack traces from the callsites.
|
||||
// We mirror how V8 serializes stack frames and how we later parse them.
|
||||
for (let i = 0; i < structuredStackTrace.length; i++) {
|
||||
const callSite = structuredStackTrace[i];
|
||||
const name = callSite.getFunctionName();
|
||||
if (
|
||||
name.includes('react_stack_bottom_frame') ||
|
||||
name.includes('react-stack-bottom-frame')
|
||||
) {
|
||||
// We pick the last frame that matches before the bottom frame since
|
||||
// that will be immediately inside the component as opposed to some helper.
|
||||
// If we don't find a bottom frame then we bail to string parsing.
|
||||
collectedLocation = result;
|
||||
// Skip everything after the bottom frame since it'll be internals.
|
||||
break;
|
||||
} else {
|
||||
const sourceURL = callSite.getScriptNameOrSourceURL();
|
||||
const line =
|
||||
// $FlowFixMe[prop-missing]
|
||||
typeof callSite.getEnclosingLineNumber === 'function'
|
||||
? (callSite: any).getEnclosingLineNumber()
|
||||
: callSite.getLineNumber();
|
||||
const col =
|
||||
// $FlowFixMe[prop-missing]
|
||||
typeof callSite.getEnclosingColumnNumber === 'function'
|
||||
? (callSite: any).getEnclosingColumnNumber()
|
||||
: callSite.getLineNumber();
|
||||
if (!sourceURL || !line || !col) {
|
||||
// Skip eval etc. without source url. They don't have location.
|
||||
continue;
|
||||
}
|
||||
result = {
|
||||
sourceURL,
|
||||
line: line,
|
||||
column: col,
|
||||
};
|
||||
}
|
||||
}
|
||||
// At the same time we generate a string stack trace just in case someone
|
||||
// else reads it.
|
||||
const name = error.name || 'Error';
|
||||
const message = error.message || '';
|
||||
let stack = name + ': ' + message;
|
||||
for (let i = 0; i < structuredStackTrace.length; i++) {
|
||||
stack += '\n at ' + structuredStackTrace[i].toString();
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
export function parseSourceFromOwnerStack(error: Error): Source | null {
|
||||
// First attempt to collected the structured data using prepareStackTrace.
|
||||
collectedLocation = null;
|
||||
const previousPrepare = Error.prepareStackTrace;
|
||||
Error.prepareStackTrace = collectStackTrace;
|
||||
let stack;
|
||||
try {
|
||||
stack = error.stack;
|
||||
} finally {
|
||||
Error.prepareStackTrace = previousPrepare;
|
||||
}
|
||||
if (collectedLocation !== null) {
|
||||
return collectedLocation;
|
||||
}
|
||||
// Fallback to parsing the string form.
|
||||
const componentStack = formatOwnerStackString(stack);
|
||||
return parseSourceFromComponentStack(componentStack);
|
||||
}
|
||||
|
||||
// 0.123456789 => 0.123
|
||||
// Expects high-resolution timestamp in milliseconds, like from performance.now()
|
||||
// Mainly used for optimizing the size of serialized profiling payload
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-dom-bindings",
|
||||
"description": "React implementation details for react-dom.",
|
||||
"version": "19.1.0",
|
||||
"version": "19.1.4",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -18,6 +18,6 @@
|
||||
},
|
||||
"homepage": "https://react.dev/",
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
"react": "^19.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-dom",
|
||||
"version": "19.1.0",
|
||||
"version": "19.1.4",
|
||||
"description": "React package for working with the DOM.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -17,10 +17,10 @@
|
||||
},
|
||||
"homepage": "https://react.dev/",
|
||||
"dependencies": {
|
||||
"scheduler": "^0.25.0"
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
"react": "^19.1.4"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
@@ -123,4 +123,4 @@
|
||||
"./server.js": "./server.browser.js",
|
||||
"./static.js": "./static.browser.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-is",
|
||||
"version": "19.1.0",
|
||||
"version": "19.1.4",
|
||||
"description": "Brand checking of React Elements.",
|
||||
"main": "index.js",
|
||||
"sideEffects": false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-markup",
|
||||
"version": "19.1.0",
|
||||
"version": "19.1.4",
|
||||
"description": "React package generating embedded markup such as e-mails with support for Server Components.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"homepage": "https://react.dev/",
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
"react": "^19.1.4"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
"directory": "packages/react-native-renderer"
|
||||
},
|
||||
"dependencies": {
|
||||
"scheduler": "^0.25.0"
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ import {
|
||||
} from './ReactNativeFiberInspector';
|
||||
|
||||
import {
|
||||
enableFabricCompleteRootInCommitPhase,
|
||||
passChildrenWhenCloningPersistedNodes,
|
||||
enableLazyPublicInstanceInFabric,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
@@ -534,19 +533,14 @@ export function finalizeContainerChildren(
|
||||
container: Container,
|
||||
newChildren: ChildSet,
|
||||
): void {
|
||||
if (!enableFabricCompleteRootInCommitPhase) {
|
||||
completeRoot(container.containerTag, newChildren);
|
||||
}
|
||||
// Noop - children will be replaced in replaceContainerChildren
|
||||
}
|
||||
|
||||
export function replaceContainerChildren(
|
||||
container: Container,
|
||||
newChildren: ChildSet,
|
||||
): void {
|
||||
// Noop - children will be replaced in finalizeContainerChildren
|
||||
if (enableFabricCompleteRootInCommitPhase) {
|
||||
completeRoot(container.containerTag, newChildren);
|
||||
}
|
||||
completeRoot(container.containerTag, newChildren);
|
||||
}
|
||||
|
||||
export {getClosestInstanceFromNode as getInstanceFromNode};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-reconciler",
|
||||
"description": "React package for creating custom renderers.",
|
||||
"version": "0.32.0",
|
||||
"version": "0.32.1",
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
@@ -26,9 +26,9 @@
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
"react": "^19.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"scheduler": "^0.25.0"
|
||||
"scheduler": "^0.26.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import {enableUseEffectCRUDOverload} from 'shared/ReactFeatureFlags';
|
||||
// TODO: Consider marking the whole bundle instead of these boundaries.
|
||||
|
||||
const callComponent = {
|
||||
'react-stack-bottom-frame': function <Props, Arg, R>(
|
||||
react_stack_bottom_frame: function <Props, Arg, R>(
|
||||
Component: (p: Props, arg: Arg) => R,
|
||||
props: Props,
|
||||
secondArg: Arg,
|
||||
@@ -46,7 +46,7 @@ export const callComponentInDEV: <Props, Arg, R>(
|
||||
secondArg: Arg,
|
||||
) => R = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callComponent['react-stack-bottom-frame'].bind(callComponent): any)
|
||||
(callComponent.react_stack_bottom_frame.bind(callComponent): any)
|
||||
: (null: any);
|
||||
|
||||
interface ClassInstance<R> {
|
||||
@@ -62,7 +62,7 @@ interface ClassInstance<R> {
|
||||
}
|
||||
|
||||
const callRender = {
|
||||
'react-stack-bottom-frame': function <R>(instance: ClassInstance<R>): R {
|
||||
react_stack_bottom_frame: function <R>(instance: ClassInstance<R>): R {
|
||||
const wasRendering = isRendering;
|
||||
setIsRendering(true);
|
||||
try {
|
||||
@@ -77,11 +77,11 @@ const callRender = {
|
||||
export const callRenderInDEV: <R>(instance: ClassInstance<R>) => R => R =
|
||||
__DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callRender['react-stack-bottom-frame'].bind(callRender): any)
|
||||
(callRender.react_stack_bottom_frame.bind(callRender): any)
|
||||
: (null: any);
|
||||
|
||||
const callComponentDidMount = {
|
||||
'react-stack-bottom-frame': function (
|
||||
react_stack_bottom_frame: function (
|
||||
finishedWork: Fiber,
|
||||
instance: ClassInstance<any>,
|
||||
): void {
|
||||
@@ -98,13 +98,13 @@ export const callComponentDidMountInDEV: (
|
||||
instance: ClassInstance<any>,
|
||||
) => void = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callComponentDidMount['react-stack-bottom-frame'].bind(
|
||||
(callComponentDidMount.react_stack_bottom_frame.bind(
|
||||
callComponentDidMount,
|
||||
): any)
|
||||
: (null: any);
|
||||
|
||||
const callComponentDidUpdate = {
|
||||
'react-stack-bottom-frame': function (
|
||||
react_stack_bottom_frame: function (
|
||||
finishedWork: Fiber,
|
||||
instance: ClassInstance<any>,
|
||||
prevProps: Object,
|
||||
@@ -127,13 +127,13 @@ export const callComponentDidUpdateInDEV: (
|
||||
snaphot: Object,
|
||||
) => void = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callComponentDidUpdate['react-stack-bottom-frame'].bind(
|
||||
(callComponentDidUpdate.react_stack_bottom_frame.bind(
|
||||
callComponentDidUpdate,
|
||||
): any)
|
||||
: (null: any);
|
||||
|
||||
const callComponentDidCatch = {
|
||||
'react-stack-bottom-frame': function (
|
||||
react_stack_bottom_frame: function (
|
||||
instance: ClassInstance<any>,
|
||||
errorInfo: CapturedValue<mixed>,
|
||||
): void {
|
||||
@@ -150,13 +150,13 @@ export const callComponentDidCatchInDEV: (
|
||||
errorInfo: CapturedValue<mixed>,
|
||||
) => void = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callComponentDidCatch['react-stack-bottom-frame'].bind(
|
||||
(callComponentDidCatch.react_stack_bottom_frame.bind(
|
||||
callComponentDidCatch,
|
||||
): any)
|
||||
: (null: any);
|
||||
|
||||
const callComponentWillUnmount = {
|
||||
'react-stack-bottom-frame': function (
|
||||
react_stack_bottom_frame: function (
|
||||
current: Fiber,
|
||||
nearestMountedAncestor: Fiber | null,
|
||||
instance: ClassInstance<any>,
|
||||
@@ -175,13 +175,13 @@ export const callComponentWillUnmountInDEV: (
|
||||
instance: ClassInstance<any>,
|
||||
) => void = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callComponentWillUnmount['react-stack-bottom-frame'].bind(
|
||||
(callComponentWillUnmount.react_stack_bottom_frame.bind(
|
||||
callComponentWillUnmount,
|
||||
): any)
|
||||
: (null: any);
|
||||
|
||||
const callCreate = {
|
||||
'react-stack-bottom-frame': function (
|
||||
react_stack_bottom_frame: function (
|
||||
effect: Effect,
|
||||
): (() => void) | {...} | void | null {
|
||||
if (!enableUseEffectCRUDOverload) {
|
||||
@@ -234,11 +234,11 @@ const callCreate = {
|
||||
|
||||
export const callCreateInDEV: (effect: Effect) => (() => void) | void = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callCreate['react-stack-bottom-frame'].bind(callCreate): any)
|
||||
(callCreate.react_stack_bottom_frame.bind(callCreate): any)
|
||||
: (null: any);
|
||||
|
||||
const callDestroy = {
|
||||
'react-stack-bottom-frame': function (
|
||||
react_stack_bottom_frame: function (
|
||||
current: Fiber,
|
||||
nearestMountedAncestor: Fiber | null,
|
||||
destroy: () => void,
|
||||
@@ -257,11 +257,11 @@ export const callDestroyInDEV: (
|
||||
destroy: (() => void) | (({...}) => void),
|
||||
) => void = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callDestroy['react-stack-bottom-frame'].bind(callDestroy): any)
|
||||
(callDestroy.react_stack_bottom_frame.bind(callDestroy): any)
|
||||
: (null: any);
|
||||
|
||||
const callLazyInit = {
|
||||
'react-stack-bottom-frame': function (lazy: LazyComponent<any, any>): any {
|
||||
react_stack_bottom_frame: function (lazy: LazyComponent<any, any>): any {
|
||||
const payload = lazy._payload;
|
||||
const init = lazy._init;
|
||||
return init(payload);
|
||||
@@ -270,5 +270,5 @@ const callLazyInit = {
|
||||
|
||||
export const callLazyInitInDEV: (lazy: LazyComponent<any, any>) => any = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callLazyInit['react-stack-bottom-frame'].bind(callLazyInit): any)
|
||||
(callLazyInit.react_stack_bottom_frame.bind(callLazyInit): any)
|
||||
: (null: any);
|
||||
|
||||
@@ -57,6 +57,7 @@ import {
|
||||
enableComponentPerformanceTrack,
|
||||
enableViewTransition,
|
||||
enableFragmentRefs,
|
||||
enableEagerAlternateStateNodeCleanup,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
FunctionComponent,
|
||||
@@ -1947,6 +1948,20 @@ function commitMutationEffectsOnFiber(
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (enableEagerAlternateStateNodeCleanup) {
|
||||
if (supportsPersistence) {
|
||||
if (finishedWork.alternate !== null) {
|
||||
// `finishedWork.alternate.stateNode` is pointing to a stale shadow
|
||||
// node at this point, retaining it and its subtree. To reclaim
|
||||
// memory, point `alternate.stateNode` to new shadow node. This
|
||||
// prevents shadow node from staying in memory longer than it
|
||||
// needs to. The correct behaviour of this is checked by test in
|
||||
// React Native: ShadowNodeReferenceCounter-itest.js#L150
|
||||
finishedWork.alternate.stateNode = finishedWork.stateNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-server-dom-esm",
|
||||
"description": "React Server Components bindings for DOM using ESM. This is intended to be integrated into meta-frameworks. It is not intended to be imported directly.",
|
||||
"version": "19.1.0",
|
||||
"version": "19.1.4",
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
@@ -46,16 +46,16 @@
|
||||
},
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/facebook/react.git",
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/react.git",
|
||||
"directory": "packages/react-server-dom-esm"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
"react": "^19.1.4",
|
||||
"react-dom": "^19.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn-loose": "^8.3.0",
|
||||
|
||||
@@ -88,6 +88,12 @@ function bind(this: ServerReference<any>): any {
|
||||
return newFn;
|
||||
}
|
||||
|
||||
const serverReferenceToString = {
|
||||
value: () => 'function () { [omitted code] }',
|
||||
configurable: true,
|
||||
writable: true,
|
||||
};
|
||||
|
||||
export function registerServerReference<T: Function>(
|
||||
reference: T,
|
||||
id: string,
|
||||
@@ -111,12 +117,14 @@ export function registerServerReference<T: Function>(
|
||||
configurable: true,
|
||||
},
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
}
|
||||
: {
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -222,16 +222,23 @@ function decodeReplyFromBusboy<T>(
|
||||
// we queue any fields we receive until the previous file is done.
|
||||
queuedFields.push(name, value);
|
||||
} else {
|
||||
resolveField(response, name, value);
|
||||
try {
|
||||
resolveField(response, name, value);
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
busboyStream.on('file', (name, value, {filename, encoding, mimeType}) => {
|
||||
if (encoding.toLowerCase() === 'base64') {
|
||||
throw new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
busboyStream.destroy(
|
||||
new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
pendingFiles++;
|
||||
const file = resolveFileInfo(response, name, filename, mimeType);
|
||||
@@ -239,14 +246,18 @@ function decodeReplyFromBusboy<T>(
|
||||
resolveFileChunk(response, file, chunk);
|
||||
});
|
||||
value.on('end', () => {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
try {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
"directory": "packages/react-server-dom-fb"
|
||||
},
|
||||
"dependencies": {
|
||||
"scheduler": "^0.25.0"
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-server-dom-parcel",
|
||||
"description": "React Server Components bindings for DOM using Parcel. This is intended to be integrated into meta-frameworks. It is not intended to be imported directly.",
|
||||
"version": "19.0.0",
|
||||
"version": "19.1.4",
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
@@ -71,15 +71,15 @@
|
||||
},
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/facebook/react.git",
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/react.git",
|
||||
"directory": "packages/react-server-dom-parcel"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
"react": "^19.1.4",
|
||||
"react-dom": "^19.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,12 @@ function bind(this: ServerReference<any>): any {
|
||||
return newFn;
|
||||
}
|
||||
|
||||
const serverReferenceToString = {
|
||||
value: () => 'function () { [omitted code] }',
|
||||
configurable: true,
|
||||
writable: true,
|
||||
};
|
||||
|
||||
export function registerServerReference<T>(
|
||||
reference: ServerReference<T>,
|
||||
id: string,
|
||||
@@ -118,12 +124,14 @@ export function registerServerReference<T>(
|
||||
configurable: true,
|
||||
},
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
}
|
||||
: {
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
} from '../shared/ReactFlightImportMetadata';
|
||||
import {prepareDestinationWithChunks} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
import hasOwnProperty from 'shared/hasOwnProperty';
|
||||
|
||||
export type ServerManifest = {
|
||||
[string]: Array<string>,
|
||||
};
|
||||
@@ -78,5 +80,8 @@ export function preloadModule<T>(
|
||||
|
||||
export function requireModule<T>(metadata: ClientReference<T>): T {
|
||||
const moduleExports = parcelRequire(metadata[ID]);
|
||||
return moduleExports[metadata[NAME]];
|
||||
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
|
||||
return moduleExports[metadata[NAME]];
|
||||
}
|
||||
return (undefined: any);
|
||||
}
|
||||
|
||||
@@ -231,16 +231,23 @@ export function decodeReplyFromBusboy<T>(
|
||||
// we queue any fields we receive until the previous file is done.
|
||||
queuedFields.push(name, value);
|
||||
} else {
|
||||
resolveField(response, name, value);
|
||||
try {
|
||||
resolveField(response, name, value);
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
busboyStream.on('file', (name, value, {filename, encoding, mimeType}) => {
|
||||
if (encoding.toLowerCase() === 'base64') {
|
||||
throw new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
busboyStream.destroy(
|
||||
new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
pendingFiles++;
|
||||
const file = resolveFileInfo(response, name, filename, mimeType);
|
||||
@@ -248,14 +255,18 @@ export function decodeReplyFromBusboy<T>(
|
||||
resolveFileChunk(response, file, chunk);
|
||||
});
|
||||
value.on('end', () => {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
try {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-server-dom-turbopack",
|
||||
"description": "React Server Components bindings for DOM using Turbopack. This is intended to be integrated into meta-frameworks. It is not intended to be imported directly.",
|
||||
"version": "19.1.0",
|
||||
"version": "19.1.4",
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
@@ -79,8 +79,8 @@
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
"react": "^19.1.4",
|
||||
"react-dom": "^19.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn-loose": "^8.3.0",
|
||||
|
||||
@@ -102,6 +102,12 @@ function bind(this: ServerReference<any>): any {
|
||||
return newFn;
|
||||
}
|
||||
|
||||
const serverReferenceToString = {
|
||||
value: () => 'function () { [omitted code] }',
|
||||
configurable: true,
|
||||
writable: true,
|
||||
};
|
||||
|
||||
export function registerServerReference<T: Function>(
|
||||
reference: T,
|
||||
id: string,
|
||||
@@ -125,12 +131,14 @@ export function registerServerReference<T: Function>(
|
||||
configurable: true,
|
||||
},
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
}
|
||||
: {
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import {prepareDestinationWithChunks} from 'react-client/src/ReactFlightClientCo
|
||||
|
||||
import {loadChunk} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
import hasOwnProperty from 'shared/hasOwnProperty';
|
||||
|
||||
export type ServerConsumerModuleMap = null | {
|
||||
[clientId: string]: {
|
||||
[clientExportName: string]: ClientReferenceManifestEntry,
|
||||
@@ -228,5 +230,8 @@ export function requireModule<T>(metadata: ClientReference<T>): T {
|
||||
// default property of this if it was an ESM interop module.
|
||||
return moduleExports.__esModule ? moduleExports.default : moduleExports;
|
||||
}
|
||||
return moduleExports[metadata[NAME]];
|
||||
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
|
||||
return moduleExports[metadata[NAME]];
|
||||
}
|
||||
return (undefined: any);
|
||||
}
|
||||
|
||||
@@ -224,16 +224,23 @@ function decodeReplyFromBusboy<T>(
|
||||
// we queue any fields we receive until the previous file is done.
|
||||
queuedFields.push(name, value);
|
||||
} else {
|
||||
resolveField(response, name, value);
|
||||
try {
|
||||
resolveField(response, name, value);
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
busboyStream.on('file', (name, value, {filename, encoding, mimeType}) => {
|
||||
if (encoding.toLowerCase() === 'base64') {
|
||||
throw new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
busboyStream.destroy(
|
||||
new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
pendingFiles++;
|
||||
const file = resolveFileInfo(response, name, filename, mimeType);
|
||||
@@ -241,14 +248,18 @@ function decodeReplyFromBusboy<T>(
|
||||
resolveFileChunk(response, file, chunk);
|
||||
});
|
||||
value.on('end', () => {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
try {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
5
packages/react-server-dom-unbundled/README.md
Normal file
5
packages/react-server-dom-unbundled/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# react-server-dom-unbundled
|
||||
|
||||
Test-only React Flight bindings for DOM using Node.js.
|
||||
|
||||
This only exists for internal testing.
|
||||
3
packages/react-server-dom-unbundled/esm/package.json
Normal file
3
packages/react-server-dom-unbundled/esm/package.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export * from '../src/ReactFlightUnbundledNodeLoader.js';
|
||||
10
packages/react-server-dom-unbundled/index.js
vendored
Normal file
10
packages/react-server-dom-unbundled/index.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
throw new Error('Use react-server-dom-webpack/client instead.');
|
||||
10
packages/react-server-dom-unbundled/node-register.js
vendored
Normal file
10
packages/react-server-dom-unbundled/node-register.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
module.exports = require('./src/ReactFlightUnbundledNodeRegister');
|
||||
7
packages/react-server-dom-unbundled/npm/client.js
vendored
Normal file
7
packages/react-server-dom-unbundled/npm/client.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-server-dom-unbundled-client.production.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-server-dom-unbundled-client.development.js');
|
||||
}
|
||||
3
packages/react-server-dom-unbundled/npm/esm/package.json
Normal file
3
packages/react-server-dom-unbundled/npm/esm/package.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
12
packages/react-server-dom-unbundled/npm/index.js
vendored
Normal file
12
packages/react-server-dom-unbundled/npm/index.js
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
throw new Error('Use react-server-dom-unbundled/client instead.');
|
||||
3
packages/react-server-dom-unbundled/npm/node-register.js
vendored
Normal file
3
packages/react-server-dom-unbundled/npm/node-register.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = require('./cjs/react-server-dom-unbundled-node-register.js');
|
||||
6
packages/react-server-dom-unbundled/npm/server.js
vendored
Normal file
6
packages/react-server-dom-unbundled/npm/server.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
throw new Error(
|
||||
'The React Server Writer cannot be used outside a react-server environment. ' +
|
||||
'You must configure Node.js using the `--conditions react-server` flag.'
|
||||
);
|
||||
20
packages/react-server-dom-unbundled/npm/server.node.js
Normal file
20
packages/react-server-dom-unbundled/npm/server.node.js
Normal file
@@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
var s;
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
s = require('./cjs/react-server-dom-unbundled-server.node.production.js');
|
||||
} else {
|
||||
s = require('./cjs/react-server-dom-unbundled-server.node.development.js');
|
||||
}
|
||||
|
||||
exports.renderToReadableStream = s.renderToReadableStream;
|
||||
exports.renderToPipeableStream = s.renderToPipeableStream;
|
||||
exports.decodeReply = s.decodeReply;
|
||||
exports.decodeReplyFromBusboy = s.decodeReplyFromBusboy;
|
||||
exports.decodeReplyFromAsyncIterable = s.decodeReplyFromAsyncIterable;
|
||||
exports.decodeAction = s.decodeAction;
|
||||
exports.decodeFormState = s.decodeFormState;
|
||||
exports.registerServerReference = s.registerServerReference;
|
||||
exports.registerClientReference = s.registerClientReference;
|
||||
exports.createClientModuleProxy = s.createClientModuleProxy;
|
||||
exports.createTemporaryReferenceSet = s.createTemporaryReferenceSet;
|
||||
6
packages/react-server-dom-unbundled/npm/static.js
vendored
Normal file
6
packages/react-server-dom-unbundled/npm/static.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
throw new Error(
|
||||
'The React Server Writer cannot be used outside a react-server environment. ' +
|
||||
'You must configure Node.js using the `--conditions react-server` flag.'
|
||||
);
|
||||
11
packages/react-server-dom-unbundled/npm/static.node.js
Normal file
11
packages/react-server-dom-unbundled/npm/static.node.js
Normal file
@@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
var s;
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
s = require('./cjs/react-server-dom-unbundled-server.node.production.js');
|
||||
} else {
|
||||
s = require('./cjs/react-server-dom-unbundled-server.node.development.js');
|
||||
}
|
||||
|
||||
exports.prerender = s.prerender;
|
||||
exports.prerenderToNodeStream = s.prerenderToNodeStream;
|
||||
46
packages/react-server-dom-unbundled/package.json
Normal file
46
packages/react-server-dom-unbundled/package.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "react-server-dom-unbundled",
|
||||
"description": "React Server Components bindings for DOM using Node.js. This only exists for internal testing.",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"index.js",
|
||||
"client.js",
|
||||
"server.js",
|
||||
"server.node.js",
|
||||
"static.js",
|
||||
"static.node.js",
|
||||
"node-register.js",
|
||||
"cjs/",
|
||||
"esm/"
|
||||
],
|
||||
"exports": {
|
||||
".": "./index.js",
|
||||
"./client": "./client.js",
|
||||
"./server": {
|
||||
"react-server": "./server.node.js",
|
||||
"default": "./server.js"
|
||||
},
|
||||
"./server.node": "./server.node.js",
|
||||
"./static": {
|
||||
"react-server": "./static.node.js",
|
||||
"default": "./static.js"
|
||||
},
|
||||
"./static.node": "./static.node.js",
|
||||
"./node-loader": "./esm/react-server-dom-unbundled-node-loader.production.js",
|
||||
"./node-register": "./node-register.js",
|
||||
"./src/*": "./src/*.js",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "index.js",
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn-loose": "^8.3.0",
|
||||
"webpack-sources": "^3.2.0"
|
||||
}
|
||||
}
|
||||
13
packages/react-server-dom-unbundled/server.js
vendored
Normal file
13
packages/react-server-dom-unbundled/server.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
throw new Error(
|
||||
'The React Server cannot be used outside a react-server environment. ' +
|
||||
'You must configure Node.js using the `--conditions react-server` flag.',
|
||||
);
|
||||
@@ -17,4 +17,4 @@ export {
|
||||
registerClientReference,
|
||||
createClientModuleProxy,
|
||||
createTemporaryReferenceSet,
|
||||
} from './src/server/react-flight-dom-server.node.unbundled';
|
||||
} from './src/server/react-flight-dom-server.node';
|
||||
804
packages/react-server-dom-unbundled/src/ReactFlightUnbundledNodeLoader.js
vendored
Normal file
804
packages/react-server-dom-unbundled/src/ReactFlightUnbundledNodeLoader.js
vendored
Normal file
@@ -0,0 +1,804 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as acorn from 'acorn-loose';
|
||||
|
||||
import readMappings from 'webpack-sources/lib/helpers/readMappings.js';
|
||||
import createMappingsSerializer from 'webpack-sources/lib/helpers/createMappingsSerializer.js';
|
||||
|
||||
type ResolveContext = {
|
||||
conditions: Array<string>,
|
||||
parentURL: string | void,
|
||||
};
|
||||
|
||||
type ResolveFunction = (
|
||||
string,
|
||||
ResolveContext,
|
||||
ResolveFunction,
|
||||
) => {url: string} | Promise<{url: string}>;
|
||||
|
||||
type GetSourceContext = {
|
||||
format: string,
|
||||
};
|
||||
|
||||
type GetSourceFunction = (
|
||||
string,
|
||||
GetSourceContext,
|
||||
GetSourceFunction,
|
||||
) => Promise<{source: Source}>;
|
||||
|
||||
type TransformSourceContext = {
|
||||
format: string,
|
||||
url: string,
|
||||
};
|
||||
|
||||
type TransformSourceFunction = (
|
||||
Source,
|
||||
TransformSourceContext,
|
||||
TransformSourceFunction,
|
||||
) => Promise<{source: Source}>;
|
||||
|
||||
type LoadContext = {
|
||||
conditions: Array<string>,
|
||||
format: string | null | void,
|
||||
importAssertions: Object,
|
||||
};
|
||||
|
||||
type LoadFunction = (
|
||||
string,
|
||||
LoadContext,
|
||||
LoadFunction,
|
||||
) => Promise<{format: string, shortCircuit?: boolean, source: Source}>;
|
||||
|
||||
type Source = string | ArrayBuffer | Uint8Array;
|
||||
|
||||
let warnedAboutConditionsFlag = false;
|
||||
|
||||
let stashedGetSource: null | GetSourceFunction = null;
|
||||
let stashedResolve: null | ResolveFunction = null;
|
||||
|
||||
export async function resolve(
|
||||
specifier: string,
|
||||
context: ResolveContext,
|
||||
defaultResolve: ResolveFunction,
|
||||
): Promise<{url: string}> {
|
||||
// We stash this in case we end up needing to resolve export * statements later.
|
||||
stashedResolve = defaultResolve;
|
||||
|
||||
if (!context.conditions.includes('react-server')) {
|
||||
context = {
|
||||
...context,
|
||||
conditions: [...context.conditions, 'react-server'],
|
||||
};
|
||||
if (!warnedAboutConditionsFlag) {
|
||||
warnedAboutConditionsFlag = true;
|
||||
// eslint-disable-next-line react-internal/no-production-logging
|
||||
console.warn(
|
||||
'You did not run Node.js with the `--conditions react-server` flag. ' +
|
||||
'Any "react-server" override will only work with ESM imports.',
|
||||
);
|
||||
}
|
||||
}
|
||||
return await defaultResolve(specifier, context, defaultResolve);
|
||||
}
|
||||
|
||||
export async function getSource(
|
||||
url: string,
|
||||
context: GetSourceContext,
|
||||
defaultGetSource: GetSourceFunction,
|
||||
): Promise<{source: Source}> {
|
||||
// We stash this in case we end up needing to resolve export * statements later.
|
||||
stashedGetSource = defaultGetSource;
|
||||
return defaultGetSource(url, context, defaultGetSource);
|
||||
}
|
||||
|
||||
type ExportedEntry = {
|
||||
localName: string,
|
||||
exportedName: string,
|
||||
type: null | string,
|
||||
loc: {
|
||||
start: {line: number, column: number},
|
||||
end: {line: number, column: number},
|
||||
},
|
||||
originalLine: number,
|
||||
originalColumn: number,
|
||||
originalSource: number,
|
||||
nameIndex: number,
|
||||
};
|
||||
|
||||
function addExportedEntry(
|
||||
exportedEntries: Array<ExportedEntry>,
|
||||
localNames: Set<string>,
|
||||
localName: string,
|
||||
exportedName: string,
|
||||
type: null | 'function',
|
||||
loc: {
|
||||
start: {line: number, column: number},
|
||||
end: {line: number, column: number},
|
||||
},
|
||||
) {
|
||||
if (localNames.has(localName)) {
|
||||
// If the same local name is exported more than once, we only need one of the names.
|
||||
return;
|
||||
}
|
||||
exportedEntries.push({
|
||||
localName,
|
||||
exportedName,
|
||||
type,
|
||||
loc,
|
||||
originalLine: -1,
|
||||
originalColumn: -1,
|
||||
originalSource: -1,
|
||||
nameIndex: -1,
|
||||
});
|
||||
}
|
||||
|
||||
function addLocalExportedNames(
|
||||
exportedEntries: Array<ExportedEntry>,
|
||||
localNames: Set<string>,
|
||||
node: any,
|
||||
) {
|
||||
switch (node.type) {
|
||||
case 'Identifier':
|
||||
addExportedEntry(
|
||||
exportedEntries,
|
||||
localNames,
|
||||
node.name,
|
||||
node.name,
|
||||
null,
|
||||
node.loc,
|
||||
);
|
||||
return;
|
||||
case 'ObjectPattern':
|
||||
for (let i = 0; i < node.properties.length; i++)
|
||||
addLocalExportedNames(exportedEntries, localNames, node.properties[i]);
|
||||
return;
|
||||
case 'ArrayPattern':
|
||||
for (let i = 0; i < node.elements.length; i++) {
|
||||
const element = node.elements[i];
|
||||
if (element)
|
||||
addLocalExportedNames(exportedEntries, localNames, element);
|
||||
}
|
||||
return;
|
||||
case 'Property':
|
||||
addLocalExportedNames(exportedEntries, localNames, node.value);
|
||||
return;
|
||||
case 'AssignmentPattern':
|
||||
addLocalExportedNames(exportedEntries, localNames, node.left);
|
||||
return;
|
||||
case 'RestElement':
|
||||
addLocalExportedNames(exportedEntries, localNames, node.argument);
|
||||
return;
|
||||
case 'ParenthesizedExpression':
|
||||
addLocalExportedNames(exportedEntries, localNames, node.expression);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function transformServerModule(
|
||||
source: string,
|
||||
program: any,
|
||||
url: string,
|
||||
sourceMap: any,
|
||||
loader: LoadFunction,
|
||||
): string {
|
||||
const body = program.body;
|
||||
|
||||
// This entry list needs to be in source location order.
|
||||
const exportedEntries: Array<ExportedEntry> = [];
|
||||
// Dedupe set.
|
||||
const localNames: Set<string> = new Set();
|
||||
|
||||
for (let i = 0; i < body.length; i++) {
|
||||
const node = body[i];
|
||||
switch (node.type) {
|
||||
case 'ExportAllDeclaration':
|
||||
// If export * is used, the other file needs to explicitly opt into "use server" too.
|
||||
break;
|
||||
case 'ExportDefaultDeclaration':
|
||||
if (node.declaration.type === 'Identifier') {
|
||||
addExportedEntry(
|
||||
exportedEntries,
|
||||
localNames,
|
||||
node.declaration.name,
|
||||
'default',
|
||||
null,
|
||||
node.declaration.loc,
|
||||
);
|
||||
} else if (node.declaration.type === 'FunctionDeclaration') {
|
||||
if (node.declaration.id) {
|
||||
addExportedEntry(
|
||||
exportedEntries,
|
||||
localNames,
|
||||
node.declaration.id.name,
|
||||
'default',
|
||||
'function',
|
||||
node.declaration.id.loc,
|
||||
);
|
||||
} else {
|
||||
// TODO: This needs to be rewritten inline because it doesn't have a local name.
|
||||
}
|
||||
}
|
||||
continue;
|
||||
case 'ExportNamedDeclaration':
|
||||
if (node.declaration) {
|
||||
if (node.declaration.type === 'VariableDeclaration') {
|
||||
const declarations = node.declaration.declarations;
|
||||
for (let j = 0; j < declarations.length; j++) {
|
||||
addLocalExportedNames(
|
||||
exportedEntries,
|
||||
localNames,
|
||||
declarations[j].id,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const name = node.declaration.id.name;
|
||||
addExportedEntry(
|
||||
exportedEntries,
|
||||
localNames,
|
||||
name,
|
||||
name,
|
||||
|
||||
node.declaration.type === 'FunctionDeclaration'
|
||||
? 'function'
|
||||
: null,
|
||||
node.declaration.id.loc,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (node.specifiers) {
|
||||
const specifiers = node.specifiers;
|
||||
for (let j = 0; j < specifiers.length; j++) {
|
||||
const specifier = specifiers[j];
|
||||
addExportedEntry(
|
||||
exportedEntries,
|
||||
localNames,
|
||||
specifier.local.name,
|
||||
specifier.exported.name,
|
||||
null,
|
||||
specifier.local.loc,
|
||||
);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mappings =
|
||||
sourceMap && typeof sourceMap.mappings === 'string'
|
||||
? sourceMap.mappings
|
||||
: '';
|
||||
let newSrc = source;
|
||||
|
||||
if (exportedEntries.length > 0) {
|
||||
let lastSourceIndex = 0;
|
||||
let lastOriginalLine = 0;
|
||||
let lastOriginalColumn = 0;
|
||||
let lastNameIndex = 0;
|
||||
let sourceLineCount = 0;
|
||||
let lastMappedLine = 0;
|
||||
|
||||
if (sourceMap) {
|
||||
// We iterate source mapping entries and our matched exports in parallel to source map
|
||||
// them to their original location.
|
||||
let nextEntryIdx = 0;
|
||||
let nextEntryLine = exportedEntries[nextEntryIdx].loc.start.line;
|
||||
let nextEntryColumn = exportedEntries[nextEntryIdx].loc.start.column;
|
||||
readMappings(
|
||||
mappings,
|
||||
(
|
||||
generatedLine: number,
|
||||
generatedColumn: number,
|
||||
sourceIndex: number,
|
||||
originalLine: number,
|
||||
originalColumn: number,
|
||||
nameIndex: number,
|
||||
) => {
|
||||
if (
|
||||
generatedLine > nextEntryLine ||
|
||||
(generatedLine === nextEntryLine &&
|
||||
generatedColumn > nextEntryColumn)
|
||||
) {
|
||||
// We're past the entry which means that the best match we have is the previous entry.
|
||||
if (lastMappedLine === nextEntryLine) {
|
||||
// Match
|
||||
exportedEntries[nextEntryIdx].originalLine = lastOriginalLine;
|
||||
exportedEntries[nextEntryIdx].originalColumn = lastOriginalColumn;
|
||||
exportedEntries[nextEntryIdx].originalSource = lastSourceIndex;
|
||||
exportedEntries[nextEntryIdx].nameIndex = lastNameIndex;
|
||||
} else {
|
||||
// Skip if we didn't have any mappings on the exported line.
|
||||
}
|
||||
nextEntryIdx++;
|
||||
if (nextEntryIdx < exportedEntries.length) {
|
||||
nextEntryLine = exportedEntries[nextEntryIdx].loc.start.line;
|
||||
nextEntryColumn = exportedEntries[nextEntryIdx].loc.start.column;
|
||||
} else {
|
||||
nextEntryLine = -1;
|
||||
nextEntryColumn = -1;
|
||||
}
|
||||
}
|
||||
lastMappedLine = generatedLine;
|
||||
if (sourceIndex > -1) {
|
||||
lastSourceIndex = sourceIndex;
|
||||
}
|
||||
if (originalLine > -1) {
|
||||
lastOriginalLine = originalLine;
|
||||
}
|
||||
if (originalColumn > -1) {
|
||||
lastOriginalColumn = originalColumn;
|
||||
}
|
||||
if (nameIndex > -1) {
|
||||
lastNameIndex = nameIndex;
|
||||
}
|
||||
},
|
||||
);
|
||||
if (nextEntryIdx < exportedEntries.length) {
|
||||
if (lastMappedLine === nextEntryLine) {
|
||||
// Match
|
||||
exportedEntries[nextEntryIdx].originalLine = lastOriginalLine;
|
||||
exportedEntries[nextEntryIdx].originalColumn = lastOriginalColumn;
|
||||
exportedEntries[nextEntryIdx].originalSource = lastSourceIndex;
|
||||
exportedEntries[nextEntryIdx].nameIndex = lastNameIndex;
|
||||
}
|
||||
}
|
||||
|
||||
for (
|
||||
let lastIdx = mappings.length - 1;
|
||||
lastIdx >= 0 && mappings[lastIdx] === ';';
|
||||
lastIdx--
|
||||
) {
|
||||
// If the last mapped lines don't contain any segments, we don't get a callback from readMappings
|
||||
// so we need to pad the number of mapped lines, with one for each empty line.
|
||||
lastMappedLine++;
|
||||
}
|
||||
|
||||
sourceLineCount = program.loc.end.line;
|
||||
if (sourceLineCount < lastMappedLine) {
|
||||
throw new Error(
|
||||
'The source map has more mappings than there are lines.',
|
||||
);
|
||||
}
|
||||
// If the original source string had more lines than there are mappings in the source map.
|
||||
// Add some extra padding of unmapped lines so that any lines that we add line up.
|
||||
for (
|
||||
let extraLines = sourceLineCount - lastMappedLine;
|
||||
extraLines > 0;
|
||||
extraLines--
|
||||
) {
|
||||
mappings += ';';
|
||||
}
|
||||
} else {
|
||||
// If a file doesn't have a source map then we generate a blank source map that just
|
||||
// contains the original content and segments pointing to the original lines.
|
||||
sourceLineCount = 1;
|
||||
let idx = -1;
|
||||
while ((idx = source.indexOf('\n', idx + 1)) !== -1) {
|
||||
sourceLineCount++;
|
||||
}
|
||||
mappings = 'AAAA' + ';AACA'.repeat(sourceLineCount - 1);
|
||||
sourceMap = {
|
||||
version: 3,
|
||||
sources: [url],
|
||||
sourcesContent: [source],
|
||||
mappings: mappings,
|
||||
sourceRoot: '',
|
||||
};
|
||||
lastSourceIndex = 0;
|
||||
lastOriginalLine = sourceLineCount;
|
||||
lastOriginalColumn = 0;
|
||||
lastNameIndex = -1;
|
||||
lastMappedLine = sourceLineCount;
|
||||
|
||||
for (let i = 0; i < exportedEntries.length; i++) {
|
||||
// Point each entry to original location.
|
||||
const entry = exportedEntries[i];
|
||||
entry.originalSource = 0;
|
||||
entry.originalLine = entry.loc.start.line;
|
||||
// We use column zero since we do the short-hand line-only source maps above.
|
||||
entry.originalColumn = 0; // entry.loc.start.column;
|
||||
}
|
||||
}
|
||||
|
||||
newSrc += '\n\n;';
|
||||
newSrc +=
|
||||
'import {registerServerReference} from "react-server-dom-webpack/server";\n';
|
||||
if (mappings) {
|
||||
mappings += ';;';
|
||||
}
|
||||
|
||||
const createMapping = createMappingsSerializer();
|
||||
|
||||
// Create an empty mapping pointing to where we last left off to reset the counters.
|
||||
let generatedLine = 1;
|
||||
createMapping(
|
||||
generatedLine,
|
||||
0,
|
||||
lastSourceIndex,
|
||||
lastOriginalLine,
|
||||
lastOriginalColumn,
|
||||
lastNameIndex,
|
||||
);
|
||||
for (let i = 0; i < exportedEntries.length; i++) {
|
||||
const entry = exportedEntries[i];
|
||||
generatedLine++;
|
||||
if (entry.type !== 'function') {
|
||||
// We first check if the export is a function and if so annotate it.
|
||||
newSrc += 'if (typeof ' + entry.localName + ' === "function") ';
|
||||
}
|
||||
newSrc += 'registerServerReference(' + entry.localName + ',';
|
||||
newSrc += JSON.stringify(url) + ',';
|
||||
newSrc += JSON.stringify(entry.exportedName) + ');\n';
|
||||
|
||||
mappings += createMapping(
|
||||
generatedLine,
|
||||
0,
|
||||
entry.originalSource,
|
||||
entry.originalLine,
|
||||
entry.originalColumn,
|
||||
entry.nameIndex,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceMap) {
|
||||
// Override with an new mappings and serialize an inline source map.
|
||||
sourceMap.mappings = mappings;
|
||||
newSrc +=
|
||||
'//# sourceMappingURL=data:application/json;charset=utf-8;base64,' +
|
||||
Buffer.from(JSON.stringify(sourceMap)).toString('base64');
|
||||
}
|
||||
|
||||
return newSrc;
|
||||
}
|
||||
|
||||
function addExportNames(names: Array<string>, node: any) {
|
||||
switch (node.type) {
|
||||
case 'Identifier':
|
||||
names.push(node.name);
|
||||
return;
|
||||
case 'ObjectPattern':
|
||||
for (let i = 0; i < node.properties.length; i++)
|
||||
addExportNames(names, node.properties[i]);
|
||||
return;
|
||||
case 'ArrayPattern':
|
||||
for (let i = 0; i < node.elements.length; i++) {
|
||||
const element = node.elements[i];
|
||||
if (element) addExportNames(names, element);
|
||||
}
|
||||
return;
|
||||
case 'Property':
|
||||
addExportNames(names, node.value);
|
||||
return;
|
||||
case 'AssignmentPattern':
|
||||
addExportNames(names, node.left);
|
||||
return;
|
||||
case 'RestElement':
|
||||
addExportNames(names, node.argument);
|
||||
return;
|
||||
case 'ParenthesizedExpression':
|
||||
addExportNames(names, node.expression);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function resolveClientImport(
|
||||
specifier: string,
|
||||
parentURL: string,
|
||||
): {url: string} | Promise<{url: string}> {
|
||||
// Resolve an import specifier as if it was loaded by the client. This doesn't use
|
||||
// the overrides that this loader does but instead reverts to the default.
|
||||
// This resolution algorithm will not necessarily have the same configuration
|
||||
// as the actual client loader. It should mostly work and if it doesn't you can
|
||||
// always convert to explicit exported names instead.
|
||||
const conditions = ['node', 'import'];
|
||||
if (stashedResolve === null) {
|
||||
throw new Error(
|
||||
'Expected resolve to have been called before transformSource',
|
||||
);
|
||||
}
|
||||
return stashedResolve(specifier, {conditions, parentURL}, stashedResolve);
|
||||
}
|
||||
|
||||
async function parseExportNamesInto(
|
||||
body: any,
|
||||
names: Array<string>,
|
||||
parentURL: string,
|
||||
loader: LoadFunction,
|
||||
): Promise<void> {
|
||||
for (let i = 0; i < body.length; i++) {
|
||||
const node = body[i];
|
||||
switch (node.type) {
|
||||
case 'ExportAllDeclaration':
|
||||
if (node.exported) {
|
||||
addExportNames(names, node.exported);
|
||||
continue;
|
||||
} else {
|
||||
const {url} = await resolveClientImport(node.source.value, parentURL);
|
||||
const {source} = await loader(
|
||||
url,
|
||||
{format: 'module', conditions: [], importAssertions: {}},
|
||||
loader,
|
||||
);
|
||||
if (typeof source !== 'string') {
|
||||
throw new Error('Expected the transformed source to be a string.');
|
||||
}
|
||||
let childBody;
|
||||
try {
|
||||
childBody = acorn.parse(source, {
|
||||
ecmaVersion: '2024',
|
||||
sourceType: 'module',
|
||||
}).body;
|
||||
} catch (x) {
|
||||
// eslint-disable-next-line react-internal/no-production-logging
|
||||
console.error('Error parsing %s %s', url, x.message);
|
||||
continue;
|
||||
}
|
||||
await parseExportNamesInto(childBody, names, url, loader);
|
||||
continue;
|
||||
}
|
||||
case 'ExportDefaultDeclaration':
|
||||
names.push('default');
|
||||
continue;
|
||||
case 'ExportNamedDeclaration':
|
||||
if (node.declaration) {
|
||||
if (node.declaration.type === 'VariableDeclaration') {
|
||||
const declarations = node.declaration.declarations;
|
||||
for (let j = 0; j < declarations.length; j++) {
|
||||
addExportNames(names, declarations[j].id);
|
||||
}
|
||||
} else {
|
||||
addExportNames(names, node.declaration.id);
|
||||
}
|
||||
}
|
||||
if (node.specifiers) {
|
||||
const specifiers = node.specifiers;
|
||||
for (let j = 0; j < specifiers.length; j++) {
|
||||
addExportNames(names, specifiers[j].exported);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function transformClientModule(
|
||||
program: any,
|
||||
url: string,
|
||||
sourceMap: any,
|
||||
loader: LoadFunction,
|
||||
): Promise<string> {
|
||||
const body = program.body;
|
||||
|
||||
const names: Array<string> = [];
|
||||
|
||||
await parseExportNamesInto(body, names, url, loader);
|
||||
|
||||
if (names.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let newSrc =
|
||||
'import {registerClientReference} from "react-server-dom-webpack/server";\n';
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
const name = names[i];
|
||||
if (name === 'default') {
|
||||
newSrc += 'export default ';
|
||||
newSrc += 'registerClientReference(function() {';
|
||||
newSrc +=
|
||||
'throw new Error(' +
|
||||
JSON.stringify(
|
||||
`Attempted to call the default export of ${url} from the server ` +
|
||||
`but it's on the client. It's not possible to invoke a client function from ` +
|
||||
`the server, it can only be rendered as a Component or passed to props of a ` +
|
||||
`Client Component.`,
|
||||
) +
|
||||
');';
|
||||
} else {
|
||||
newSrc += 'export const ' + name + ' = ';
|
||||
newSrc += 'registerClientReference(function() {';
|
||||
newSrc +=
|
||||
'throw new Error(' +
|
||||
JSON.stringify(
|
||||
`Attempted to call ${name}() from the server but ${name} is on the client. ` +
|
||||
`It's not possible to invoke a client function from the server, it can ` +
|
||||
`only be rendered as a Component or passed to props of a Client Component.`,
|
||||
) +
|
||||
');';
|
||||
}
|
||||
newSrc += '},';
|
||||
newSrc += JSON.stringify(url) + ',';
|
||||
newSrc += JSON.stringify(name) + ');\n';
|
||||
}
|
||||
|
||||
// TODO: Generate source maps for Client Reference functions so they can point to their
|
||||
// original locations.
|
||||
return newSrc;
|
||||
}
|
||||
|
||||
async function loadClientImport(
|
||||
url: string,
|
||||
defaultTransformSource: TransformSourceFunction,
|
||||
): Promise<{format: string, shortCircuit?: boolean, source: Source}> {
|
||||
if (stashedGetSource === null) {
|
||||
throw new Error(
|
||||
'Expected getSource to have been called before transformSource',
|
||||
);
|
||||
}
|
||||
// TODO: Validate that this is another module by calling getFormat.
|
||||
const {source} = await stashedGetSource(
|
||||
url,
|
||||
{format: 'module'},
|
||||
stashedGetSource,
|
||||
);
|
||||
const result = await defaultTransformSource(
|
||||
source,
|
||||
{format: 'module', url},
|
||||
defaultTransformSource,
|
||||
);
|
||||
return {format: 'module', source: result.source};
|
||||
}
|
||||
|
||||
async function transformModuleIfNeeded(
|
||||
source: string,
|
||||
url: string,
|
||||
loader: LoadFunction,
|
||||
): Promise<string> {
|
||||
// Do a quick check for the exact string. If it doesn't exist, don't
|
||||
// bother parsing.
|
||||
if (
|
||||
source.indexOf('use client') === -1 &&
|
||||
source.indexOf('use server') === -1
|
||||
) {
|
||||
return source;
|
||||
}
|
||||
|
||||
let sourceMappingURL = null;
|
||||
let sourceMappingStart = 0;
|
||||
let sourceMappingEnd = 0;
|
||||
let sourceMappingLines = 0;
|
||||
|
||||
let program;
|
||||
try {
|
||||
program = acorn.parse(source, {
|
||||
ecmaVersion: '2024',
|
||||
sourceType: 'module',
|
||||
locations: true,
|
||||
onComment(
|
||||
block: boolean,
|
||||
text: string,
|
||||
start: number,
|
||||
end: number,
|
||||
startLoc: {line: number, column: number},
|
||||
endLoc: {line: number, column: number},
|
||||
) {
|
||||
if (
|
||||
text.startsWith('# sourceMappingURL=') ||
|
||||
text.startsWith('@ sourceMappingURL=')
|
||||
) {
|
||||
sourceMappingURL = text.slice(19);
|
||||
sourceMappingStart = start;
|
||||
sourceMappingEnd = end;
|
||||
sourceMappingLines = endLoc.line - startLoc.line;
|
||||
}
|
||||
},
|
||||
});
|
||||
} catch (x) {
|
||||
// eslint-disable-next-line react-internal/no-production-logging
|
||||
console.error('Error parsing %s %s', url, x.message);
|
||||
return source;
|
||||
}
|
||||
|
||||
let useClient = false;
|
||||
let useServer = false;
|
||||
|
||||
const body = program.body;
|
||||
for (let i = 0; i < body.length; i++) {
|
||||
const node = body[i];
|
||||
if (node.type !== 'ExpressionStatement' || !node.directive) {
|
||||
break;
|
||||
}
|
||||
if (node.directive === 'use client') {
|
||||
useClient = true;
|
||||
}
|
||||
if (node.directive === 'use server') {
|
||||
useServer = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!useClient && !useServer) {
|
||||
return source;
|
||||
}
|
||||
|
||||
if (useClient && useServer) {
|
||||
throw new Error(
|
||||
'Cannot have both "use client" and "use server" directives in the same file.',
|
||||
);
|
||||
}
|
||||
|
||||
let sourceMap = null;
|
||||
if (sourceMappingURL) {
|
||||
const sourceMapResult = await loader(
|
||||
sourceMappingURL,
|
||||
// $FlowFixMe
|
||||
{
|
||||
format: 'json',
|
||||
conditions: [],
|
||||
importAssertions: {type: 'json'},
|
||||
importAttributes: {type: 'json'},
|
||||
},
|
||||
loader,
|
||||
);
|
||||
const sourceMapString =
|
||||
typeof sourceMapResult.source === 'string'
|
||||
? sourceMapResult.source
|
||||
: // $FlowFixMe
|
||||
sourceMapResult.source.toString('utf8');
|
||||
sourceMap = JSON.parse(sourceMapString);
|
||||
|
||||
// Strip the source mapping comment. We'll re-add it below if needed.
|
||||
source =
|
||||
source.slice(0, sourceMappingStart) +
|
||||
'\n'.repeat(sourceMappingLines) +
|
||||
source.slice(sourceMappingEnd);
|
||||
}
|
||||
|
||||
if (useClient) {
|
||||
return transformClientModule(program, url, sourceMap, loader);
|
||||
}
|
||||
|
||||
return transformServerModule(source, program, url, sourceMap, loader);
|
||||
}
|
||||
|
||||
export async function transformSource(
|
||||
source: Source,
|
||||
context: TransformSourceContext,
|
||||
defaultTransformSource: TransformSourceFunction,
|
||||
): Promise<{source: Source}> {
|
||||
const transformed = await defaultTransformSource(
|
||||
source,
|
||||
context,
|
||||
defaultTransformSource,
|
||||
);
|
||||
if (context.format === 'module') {
|
||||
const transformedSource = transformed.source;
|
||||
if (typeof transformedSource !== 'string') {
|
||||
throw new Error('Expected source to have been transformed to a string.');
|
||||
}
|
||||
const newSrc = await transformModuleIfNeeded(
|
||||
transformedSource,
|
||||
context.url,
|
||||
(url: string, ctx: LoadContext, defaultLoad: LoadFunction) => {
|
||||
return loadClientImport(url, defaultTransformSource);
|
||||
},
|
||||
);
|
||||
return {source: newSrc};
|
||||
}
|
||||
return transformed;
|
||||
}
|
||||
|
||||
export async function load(
|
||||
url: string,
|
||||
context: LoadContext,
|
||||
defaultLoad: LoadFunction,
|
||||
): Promise<{format: string, shortCircuit?: boolean, source: Source}> {
|
||||
const result = await defaultLoad(url, context, defaultLoad);
|
||||
if (result.format === 'module') {
|
||||
if (typeof result.source !== 'string') {
|
||||
throw new Error('Expected source to have been loaded into a string.');
|
||||
}
|
||||
const newSrc = await transformModuleIfNeeded(
|
||||
result.source,
|
||||
url,
|
||||
defaultLoad,
|
||||
);
|
||||
return {format: 'module', source: newSrc};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
109
packages/react-server-dom-unbundled/src/ReactFlightUnbundledNodeRegister.js
vendored
Normal file
109
packages/react-server-dom-unbundled/src/ReactFlightUnbundledNodeRegister.js
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
const acorn = require('acorn-loose');
|
||||
|
||||
const url = require('url');
|
||||
|
||||
const Module = require('module');
|
||||
|
||||
module.exports = function register() {
|
||||
const Server: any = require('react-server-dom-unbundled/server');
|
||||
const registerServerReference = Server.registerServerReference;
|
||||
const createClientModuleProxy = Server.createClientModuleProxy;
|
||||
|
||||
// $FlowFixMe[prop-missing] found when upgrading Flow
|
||||
const originalCompile = Module.prototype._compile;
|
||||
|
||||
// $FlowFixMe[prop-missing] found when upgrading Flow
|
||||
Module.prototype._compile = function (
|
||||
this: any,
|
||||
content: string,
|
||||
filename: string,
|
||||
): void {
|
||||
// Do a quick check for the exact string. If it doesn't exist, don't
|
||||
// bother parsing.
|
||||
if (
|
||||
content.indexOf('use client') === -1 &&
|
||||
content.indexOf('use server') === -1
|
||||
) {
|
||||
return originalCompile.apply(this, arguments);
|
||||
}
|
||||
|
||||
let body;
|
||||
try {
|
||||
body = acorn.parse(content, {
|
||||
ecmaVersion: '2024',
|
||||
sourceType: 'source',
|
||||
}).body;
|
||||
} catch (x) {
|
||||
console['error']('Error parsing %s %s', url, x.message);
|
||||
return originalCompile.apply(this, arguments);
|
||||
}
|
||||
|
||||
let useClient = false;
|
||||
let useServer = false;
|
||||
for (let i = 0; i < body.length; i++) {
|
||||
const node = body[i];
|
||||
if (node.type !== 'ExpressionStatement' || !node.directive) {
|
||||
break;
|
||||
}
|
||||
if (node.directive === 'use client') {
|
||||
useClient = true;
|
||||
}
|
||||
if (node.directive === 'use server') {
|
||||
useServer = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!useClient && !useServer) {
|
||||
return originalCompile.apply(this, arguments);
|
||||
}
|
||||
|
||||
if (useClient && useServer) {
|
||||
throw new Error(
|
||||
'Cannot have both "use client" and "use server" directives in the same file.',
|
||||
);
|
||||
}
|
||||
|
||||
if (useClient) {
|
||||
const moduleId: string = (url.pathToFileURL(filename).href: any);
|
||||
this.exports = createClientModuleProxy(moduleId);
|
||||
}
|
||||
|
||||
if (useServer) {
|
||||
originalCompile.apply(this, arguments);
|
||||
|
||||
const moduleId: string = (url.pathToFileURL(filename).href: any);
|
||||
|
||||
const exports = this.exports;
|
||||
|
||||
// This module is imported server to server, but opts in to exposing functions by
|
||||
// reference. If there are any functions in the export.
|
||||
if (typeof exports === 'function') {
|
||||
// The module exports a function directly,
|
||||
registerServerReference(
|
||||
(exports: any),
|
||||
moduleId,
|
||||
// Represents the whole Module object instead of a particular import.
|
||||
null,
|
||||
);
|
||||
} else {
|
||||
const keys = Object.keys(exports);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
const value = exports[keys[i]];
|
||||
if (typeof value === 'function') {
|
||||
registerServerReference((value: any), moduleId, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
360
packages/react-server-dom-unbundled/src/ReactFlightUnbundledReferences.js
vendored
Normal file
360
packages/react-server-dom-unbundled/src/ReactFlightUnbundledReferences.js
vendored
Normal file
@@ -0,0 +1,360 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
|
||||
|
||||
export type ServerReference<T: Function> = T & {
|
||||
$$typeof: symbol,
|
||||
$$id: string,
|
||||
$$bound: null | Array<ReactClientValue>,
|
||||
$$location?: Error,
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
export type ClientReference<T> = {
|
||||
$$typeof: symbol,
|
||||
$$id: string,
|
||||
$$async: boolean,
|
||||
};
|
||||
|
||||
const CLIENT_REFERENCE_TAG = Symbol.for('react.client.reference');
|
||||
const SERVER_REFERENCE_TAG = Symbol.for('react.server.reference');
|
||||
|
||||
export function isClientReference(reference: Object): boolean {
|
||||
return reference.$$typeof === CLIENT_REFERENCE_TAG;
|
||||
}
|
||||
|
||||
export function isServerReference(reference: Object): boolean {
|
||||
return reference.$$typeof === SERVER_REFERENCE_TAG;
|
||||
}
|
||||
|
||||
export function registerClientReference<T>(
|
||||
proxyImplementation: any,
|
||||
id: string,
|
||||
exportName: string,
|
||||
): ClientReference<T> {
|
||||
return registerClientReferenceImpl(
|
||||
proxyImplementation,
|
||||
id + '#' + exportName,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
function registerClientReferenceImpl<T>(
|
||||
proxyImplementation: any,
|
||||
id: string,
|
||||
async: boolean,
|
||||
): ClientReference<T> {
|
||||
return Object.defineProperties(proxyImplementation, {
|
||||
$$typeof: {value: CLIENT_REFERENCE_TAG},
|
||||
$$id: {value: id},
|
||||
$$async: {value: async},
|
||||
});
|
||||
}
|
||||
|
||||
// $FlowFixMe[method-unbinding]
|
||||
const FunctionBind = Function.prototype.bind;
|
||||
// $FlowFixMe[method-unbinding]
|
||||
const ArraySlice = Array.prototype.slice;
|
||||
function bind(this: ServerReference<any>): any {
|
||||
// $FlowFixMe[incompatible-call]
|
||||
const newFn = FunctionBind.apply(this, arguments);
|
||||
if (this.$$typeof === SERVER_REFERENCE_TAG) {
|
||||
if (__DEV__) {
|
||||
const thisBind = arguments[0];
|
||||
if (thisBind != null) {
|
||||
console.error(
|
||||
'Cannot bind "this" of a Server Action. Pass null or undefined as the first argument to .bind().',
|
||||
);
|
||||
}
|
||||
}
|
||||
const args = ArraySlice.call(arguments, 1);
|
||||
const $$typeof = {value: SERVER_REFERENCE_TAG};
|
||||
const $$id = {value: this.$$id};
|
||||
const $$bound = {value: this.$$bound ? this.$$bound.concat(args) : args};
|
||||
return Object.defineProperties(
|
||||
(newFn: any),
|
||||
__DEV__
|
||||
? {
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
$$location: {
|
||||
value: this.$$location,
|
||||
configurable: true,
|
||||
},
|
||||
bind: {value: bind, configurable: true},
|
||||
}
|
||||
: {
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
bind: {value: bind, configurable: true},
|
||||
},
|
||||
);
|
||||
}
|
||||
return newFn;
|
||||
}
|
||||
|
||||
const serverReferenceToString = {
|
||||
value: () => 'function () { [omitted code] }',
|
||||
configurable: true,
|
||||
writable: true,
|
||||
};
|
||||
|
||||
export function registerServerReference<T: Function>(
|
||||
reference: T,
|
||||
id: string,
|
||||
exportName: null | string,
|
||||
): ServerReference<T> {
|
||||
const $$typeof = {value: SERVER_REFERENCE_TAG};
|
||||
const $$id = {
|
||||
value: exportName === null ? id : id + '#' + exportName,
|
||||
configurable: true,
|
||||
};
|
||||
const $$bound = {value: null, configurable: true};
|
||||
return Object.defineProperties(
|
||||
(reference: any),
|
||||
__DEV__
|
||||
? {
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
$$location: {
|
||||
value: Error('react-stack-top-frame'),
|
||||
configurable: true,
|
||||
},
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
}
|
||||
: {
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const PROMISE_PROTOTYPE = Promise.prototype;
|
||||
|
||||
const deepProxyHandlers = {
|
||||
get: function (
|
||||
target: Function,
|
||||
name: string | symbol,
|
||||
receiver: Proxy<Function>,
|
||||
) {
|
||||
switch (name) {
|
||||
// These names are read by the Flight runtime if you end up using the exports object.
|
||||
case '$$typeof':
|
||||
// These names are a little too common. We should probably have a way to
|
||||
// have the Flight runtime extract the inner target instead.
|
||||
return target.$$typeof;
|
||||
case '$$id':
|
||||
return target.$$id;
|
||||
case '$$async':
|
||||
return target.$$async;
|
||||
case 'name':
|
||||
return target.name;
|
||||
case 'displayName':
|
||||
return undefined;
|
||||
// We need to special case this because createElement reads it if we pass this
|
||||
// reference.
|
||||
case 'defaultProps':
|
||||
return undefined;
|
||||
// React looks for debugInfo on thenables.
|
||||
case '_debugInfo':
|
||||
return undefined;
|
||||
// Avoid this attempting to be serialized.
|
||||
case 'toJSON':
|
||||
return undefined;
|
||||
case Symbol.toPrimitive:
|
||||
// $FlowFixMe[prop-missing]
|
||||
return Object.prototype[Symbol.toPrimitive];
|
||||
case Symbol.toStringTag:
|
||||
// $FlowFixMe[prop-missing]
|
||||
return Object.prototype[Symbol.toStringTag];
|
||||
case 'Provider':
|
||||
throw new Error(
|
||||
`Cannot render a Client Context Provider on the Server. ` +
|
||||
`Instead, you can export a Client Component wrapper ` +
|
||||
`that itself renders a Client Context Provider.`,
|
||||
);
|
||||
case 'then':
|
||||
throw new Error(
|
||||
`Cannot await or return from a thenable. ` +
|
||||
`You cannot await a client module from a server component.`,
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line react-internal/safe-string-coercion
|
||||
const expression = String(target.name) + '.' + String(name);
|
||||
throw new Error(
|
||||
`Cannot access ${expression} on the server. ` +
|
||||
'You cannot dot into a client module from a server component. ' +
|
||||
'You can only pass the imported name through.',
|
||||
);
|
||||
},
|
||||
set: function () {
|
||||
throw new Error('Cannot assign to a client module from a server module.');
|
||||
},
|
||||
};
|
||||
|
||||
function getReference(target: Function, name: string | symbol): $FlowFixMe {
|
||||
switch (name) {
|
||||
// These names are read by the Flight runtime if you end up using the exports object.
|
||||
case '$$typeof':
|
||||
return target.$$typeof;
|
||||
case '$$id':
|
||||
return target.$$id;
|
||||
case '$$async':
|
||||
return target.$$async;
|
||||
case 'name':
|
||||
return target.name;
|
||||
// We need to special case this because createElement reads it if we pass this
|
||||
// reference.
|
||||
case 'defaultProps':
|
||||
return undefined;
|
||||
// React looks for debugInfo on thenables.
|
||||
case '_debugInfo':
|
||||
return undefined;
|
||||
// Avoid this attempting to be serialized.
|
||||
case 'toJSON':
|
||||
return undefined;
|
||||
case Symbol.toPrimitive:
|
||||
// $FlowFixMe[prop-missing]
|
||||
return Object.prototype[Symbol.toPrimitive];
|
||||
case Symbol.toStringTag:
|
||||
// $FlowFixMe[prop-missing]
|
||||
return Object.prototype[Symbol.toStringTag];
|
||||
case '__esModule':
|
||||
// Something is conditionally checking which export to use. We'll pretend to be
|
||||
// an ESM compat module but then we'll check again on the client.
|
||||
const moduleId = target.$$id;
|
||||
target.default = registerClientReferenceImpl(
|
||||
(function () {
|
||||
throw new Error(
|
||||
`Attempted to call the default export of ${moduleId} from the server ` +
|
||||
`but it's on the client. It's not possible to invoke a client function from ` +
|
||||
`the server, it can only be rendered as a Component or passed to props of a ` +
|
||||
`Client Component.`,
|
||||
);
|
||||
}: any),
|
||||
target.$$id + '#',
|
||||
target.$$async,
|
||||
);
|
||||
return true;
|
||||
case 'then':
|
||||
if (target.then) {
|
||||
// Use a cached value
|
||||
return target.then;
|
||||
}
|
||||
if (!target.$$async) {
|
||||
// If this module is expected to return a Promise (such as an AsyncModule) then
|
||||
// we should resolve that with a client reference that unwraps the Promise on
|
||||
// the client.
|
||||
|
||||
const clientReference: ClientReference<any> =
|
||||
registerClientReferenceImpl(({}: any), target.$$id, true);
|
||||
const proxy = new Proxy(clientReference, proxyHandlers);
|
||||
|
||||
// Treat this as a resolved Promise for React's use()
|
||||
target.status = 'fulfilled';
|
||||
target.value = proxy;
|
||||
|
||||
const then = (target.then = registerClientReferenceImpl(
|
||||
(function then(resolve, reject: any) {
|
||||
// Expose to React.
|
||||
return Promise.resolve(resolve(proxy));
|
||||
}: any),
|
||||
// If this is not used as a Promise but is treated as a reference to a `.then`
|
||||
// export then we should treat it as a reference to that name.
|
||||
target.$$id + '#then',
|
||||
false,
|
||||
));
|
||||
return then;
|
||||
} else {
|
||||
// Since typeof .then === 'function' is a feature test we'd continue recursing
|
||||
// indefinitely if we return a function. Instead, we return an object reference
|
||||
// if we check further.
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
if (typeof name === 'symbol') {
|
||||
throw new Error(
|
||||
'Cannot read Symbol exports. Only named exports are supported on a client module ' +
|
||||
'imported on the server.',
|
||||
);
|
||||
}
|
||||
let cachedReference = target[name];
|
||||
if (!cachedReference) {
|
||||
const reference: ClientReference<any> = registerClientReferenceImpl(
|
||||
(function () {
|
||||
throw new Error(
|
||||
// eslint-disable-next-line react-internal/safe-string-coercion
|
||||
`Attempted to call ${String(name)}() from the server but ${String(
|
||||
name,
|
||||
)} is on the client. ` +
|
||||
`It's not possible to invoke a client function from the server, it can ` +
|
||||
`only be rendered as a Component or passed to props of a Client Component.`,
|
||||
);
|
||||
}: any),
|
||||
target.$$id + '#' + name,
|
||||
target.$$async,
|
||||
);
|
||||
Object.defineProperty((reference: any), 'name', {value: name});
|
||||
cachedReference = target[name] = new Proxy(reference, deepProxyHandlers);
|
||||
}
|
||||
return cachedReference;
|
||||
}
|
||||
|
||||
const proxyHandlers = {
|
||||
get: function (
|
||||
target: Function,
|
||||
name: string | symbol,
|
||||
receiver: Proxy<Function>,
|
||||
): $FlowFixMe {
|
||||
return getReference(target, name);
|
||||
},
|
||||
getOwnPropertyDescriptor: function (
|
||||
target: Function,
|
||||
name: string | symbol,
|
||||
): $FlowFixMe {
|
||||
let descriptor = Object.getOwnPropertyDescriptor(target, name);
|
||||
if (!descriptor) {
|
||||
descriptor = {
|
||||
value: getReference(target, name),
|
||||
writable: false,
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
};
|
||||
Object.defineProperty(target, name, descriptor);
|
||||
}
|
||||
return descriptor;
|
||||
},
|
||||
getPrototypeOf(target: Function): Object {
|
||||
// Pretend to be a Promise in case anyone asks.
|
||||
return PROMISE_PROTOTYPE;
|
||||
},
|
||||
set: function (): empty {
|
||||
throw new Error('Cannot assign to a client module from a server module.');
|
||||
},
|
||||
};
|
||||
|
||||
export function createClientModuleProxy<T>(
|
||||
moduleId: string,
|
||||
): ClientReference<T> {
|
||||
const clientReference: ClientReference<T> = registerClientReferenceImpl(
|
||||
({}: any),
|
||||
// Represents the whole Module object instead of a particular import.
|
||||
moduleId,
|
||||
false,
|
||||
);
|
||||
return new Proxy(clientReference, proxyHandlers);
|
||||
}
|
||||
@@ -24,6 +24,8 @@ import {
|
||||
} from '../shared/ReactFlightImportMetadata';
|
||||
import {prepareDestinationWithChunks} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
import hasOwnProperty from 'shared/hasOwnProperty';
|
||||
|
||||
export type ServerConsumerModuleMap = {
|
||||
[clientId: string]: {
|
||||
[clientExportName: string]: ClientReference<any>,
|
||||
@@ -158,5 +160,8 @@ export function requireModule<T>(metadata: ClientReference<T>): T {
|
||||
// default property of this if it was an ESM interop module.
|
||||
return moduleExports.default;
|
||||
}
|
||||
return moduleExports[metadata.name];
|
||||
if (hasOwnProperty.call(moduleExports, metadata.name)) {
|
||||
return moduleExports[metadata.name];
|
||||
}
|
||||
return (undefined: any);
|
||||
}
|
||||
32
packages/react-server-dom-unbundled/src/client/ReactFlightClientConfigTargetNodeServer.js
vendored
Normal file
32
packages/react-server-dom-unbundled/src/client/ReactFlightClientConfigTargetNodeServer.js
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import {preinitScriptForSSR} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
export type ModuleLoading = null | {
|
||||
prefix: string,
|
||||
crossOrigin?: 'use-credentials' | '',
|
||||
};
|
||||
|
||||
export function prepareDestinationWithChunks(
|
||||
moduleLoading: ModuleLoading,
|
||||
// Chunks are double-indexed [..., idx, filenamex, idy, filenamey, ...]
|
||||
chunks: Array<string>,
|
||||
nonce: ?string,
|
||||
) {
|
||||
if (moduleLoading !== null) {
|
||||
for (let i = 1; i < chunks.length; i += 2) {
|
||||
preinitScriptForSSR(
|
||||
moduleLoading.prefix + chunks[i],
|
||||
nonce,
|
||||
moduleLoading.crossOrigin,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
186
packages/react-server-dom-unbundled/src/client/ReactFlightDOMClientEdge.js
vendored
Normal file
186
packages/react-server-dom-unbundled/src/client/ReactFlightDOMClientEdge.js
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {Thenable, ReactCustomFormAction} from 'shared/ReactTypes.js';
|
||||
|
||||
import type {
|
||||
Response as FlightResponse,
|
||||
FindSourceMapURLCallback,
|
||||
} from 'react-client/src/ReactFlightClient';
|
||||
|
||||
import type {ReactServerValue} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
import type {
|
||||
ServerConsumerModuleMap,
|
||||
ModuleLoading,
|
||||
ServerManifest,
|
||||
} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
type ServerConsumerManifest = {
|
||||
moduleMap: ServerConsumerModuleMap,
|
||||
moduleLoading: ModuleLoading,
|
||||
serverModuleMap: null | ServerManifest,
|
||||
};
|
||||
|
||||
import {
|
||||
createResponse,
|
||||
getRoot,
|
||||
reportGlobalError,
|
||||
processBinaryChunk,
|
||||
close,
|
||||
} from 'react-client/src/ReactFlightClient';
|
||||
|
||||
import {
|
||||
processReply,
|
||||
createServerReference as createServerReferenceImpl,
|
||||
} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
export {registerServerReference} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
|
||||
export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
|
||||
export type {TemporaryReferenceSet};
|
||||
|
||||
function noServerCall() {
|
||||
throw new Error(
|
||||
'Server Functions cannot be called during initial render. ' +
|
||||
'This would create a fetch waterfall. Try to use a Server Component ' +
|
||||
'to pass data to Client Components instead.',
|
||||
);
|
||||
}
|
||||
|
||||
export function createServerReference<A: Iterable<any>, T>(
|
||||
id: any,
|
||||
callServer: any,
|
||||
): (...A) => Promise<T> {
|
||||
return createServerReferenceImpl(id, noServerCall);
|
||||
}
|
||||
|
||||
type EncodeFormActionCallback = <A>(
|
||||
id: any,
|
||||
args: Promise<A>,
|
||||
) => ReactCustomFormAction;
|
||||
|
||||
export type Options = {
|
||||
serverConsumerManifest: ServerConsumerManifest,
|
||||
nonce?: string,
|
||||
encodeFormAction?: EncodeFormActionCallback,
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
findSourceMapURL?: FindSourceMapURLCallback,
|
||||
replayConsoleLogs?: boolean,
|
||||
environmentName?: string,
|
||||
};
|
||||
|
||||
function createResponseFromOptions(options: Options) {
|
||||
return createResponse(
|
||||
options.serverConsumerManifest.moduleMap,
|
||||
options.serverConsumerManifest.serverModuleMap,
|
||||
options.serverConsumerManifest.moduleLoading,
|
||||
noServerCall,
|
||||
options.encodeFormAction,
|
||||
typeof options.nonce === 'string' ? options.nonce : undefined,
|
||||
options && options.temporaryReferences
|
||||
? options.temporaryReferences
|
||||
: undefined,
|
||||
__DEV__ && options && options.findSourceMapURL
|
||||
? options.findSourceMapURL
|
||||
: undefined,
|
||||
__DEV__ && options ? options.replayConsoleLogs === true : false, // defaults to false
|
||||
__DEV__ && options && options.environmentName
|
||||
? options.environmentName
|
||||
: undefined,
|
||||
);
|
||||
}
|
||||
|
||||
function startReadingFromStream(
|
||||
response: FlightResponse,
|
||||
stream: ReadableStream,
|
||||
): void {
|
||||
const reader = stream.getReader();
|
||||
function progress({
|
||||
done,
|
||||
value,
|
||||
}: {
|
||||
done: boolean,
|
||||
value: ?any,
|
||||
...
|
||||
}): void | Promise<void> {
|
||||
if (done) {
|
||||
close(response);
|
||||
return;
|
||||
}
|
||||
const buffer: Uint8Array = (value: any);
|
||||
processBinaryChunk(response, buffer);
|
||||
return reader.read().then(progress).catch(error);
|
||||
}
|
||||
function error(e: any) {
|
||||
reportGlobalError(response, e);
|
||||
}
|
||||
reader.read().then(progress).catch(error);
|
||||
}
|
||||
|
||||
function createFromReadableStream<T>(
|
||||
stream: ReadableStream,
|
||||
options: Options,
|
||||
): Thenable<T> {
|
||||
const response: FlightResponse = createResponseFromOptions(options);
|
||||
startReadingFromStream(response, stream);
|
||||
return getRoot(response);
|
||||
}
|
||||
|
||||
function createFromFetch<T>(
|
||||
promiseForResponse: Promise<Response>,
|
||||
options: Options,
|
||||
): Thenable<T> {
|
||||
const response: FlightResponse = createResponseFromOptions(options);
|
||||
promiseForResponse.then(
|
||||
function (r) {
|
||||
startReadingFromStream(response, (r.body: any));
|
||||
},
|
||||
function (e) {
|
||||
reportGlobalError(response, e);
|
||||
},
|
||||
);
|
||||
return getRoot(response);
|
||||
}
|
||||
|
||||
function encodeReply(
|
||||
value: ReactServerValue,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet, signal?: AbortSignal},
|
||||
): Promise<
|
||||
string | URLSearchParams | FormData,
|
||||
> /* We don't use URLSearchParams yet but maybe */ {
|
||||
return new Promise((resolve, reject) => {
|
||||
const abort = processReply(
|
||||
value,
|
||||
'',
|
||||
options && options.temporaryReferences
|
||||
? options.temporaryReferences
|
||||
: undefined,
|
||||
resolve,
|
||||
reject,
|
||||
);
|
||||
if (options && options.signal) {
|
||||
const signal = options.signal;
|
||||
if (signal.aborted) {
|
||||
abort((signal: any).reason);
|
||||
} else {
|
||||
const listener = () => {
|
||||
abort((signal: any).reason);
|
||||
signal.removeEventListener('abort', listener);
|
||||
};
|
||||
signal.addEventListener('abort', listener);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export {createFromFetch, createFromReadableStream, encodeReply};
|
||||
107
packages/react-server-dom-unbundled/src/client/ReactFlightDOMClientNode.js
vendored
Normal file
107
packages/react-server-dom-unbundled/src/client/ReactFlightDOMClientNode.js
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {Thenable, ReactCustomFormAction} from 'shared/ReactTypes.js';
|
||||
|
||||
import type {
|
||||
Response,
|
||||
FindSourceMapURLCallback,
|
||||
} from 'react-client/src/ReactFlightClient';
|
||||
|
||||
import type {
|
||||
ServerConsumerModuleMap,
|
||||
ModuleLoading,
|
||||
ServerManifest,
|
||||
} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
type ServerConsumerManifest = {
|
||||
moduleMap: ServerConsumerModuleMap,
|
||||
moduleLoading: ModuleLoading,
|
||||
serverModuleMap: null | ServerManifest,
|
||||
};
|
||||
|
||||
import type {Readable} from 'stream';
|
||||
|
||||
import {
|
||||
createResponse,
|
||||
getRoot,
|
||||
reportGlobalError,
|
||||
processStringChunk,
|
||||
processBinaryChunk,
|
||||
close,
|
||||
} from 'react-client/src/ReactFlightClient';
|
||||
|
||||
import {createServerReference as createServerReferenceImpl} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
export {registerServerReference} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
function noServerCall() {
|
||||
throw new Error(
|
||||
'Server Functions cannot be called during initial render. ' +
|
||||
'This would create a fetch waterfall. Try to use a Server Component ' +
|
||||
'to pass data to Client Components instead.',
|
||||
);
|
||||
}
|
||||
|
||||
export function createServerReference<A: Iterable<any>, T>(
|
||||
id: any,
|
||||
callServer: any,
|
||||
): (...A) => Promise<T> {
|
||||
return createServerReferenceImpl(id, noServerCall);
|
||||
}
|
||||
|
||||
type EncodeFormActionCallback = <A>(
|
||||
id: any,
|
||||
args: Promise<A>,
|
||||
) => ReactCustomFormAction;
|
||||
|
||||
export type Options = {
|
||||
nonce?: string,
|
||||
encodeFormAction?: EncodeFormActionCallback,
|
||||
findSourceMapURL?: FindSourceMapURLCallback,
|
||||
replayConsoleLogs?: boolean,
|
||||
environmentName?: string,
|
||||
};
|
||||
|
||||
function createFromNodeStream<T>(
|
||||
stream: Readable,
|
||||
serverConsumerManifest: ServerConsumerManifest,
|
||||
options?: Options,
|
||||
): Thenable<T> {
|
||||
const response: Response = createResponse(
|
||||
serverConsumerManifest.moduleMap,
|
||||
serverConsumerManifest.serverModuleMap,
|
||||
serverConsumerManifest.moduleLoading,
|
||||
noServerCall,
|
||||
options ? options.encodeFormAction : undefined,
|
||||
options && typeof options.nonce === 'string' ? options.nonce : undefined,
|
||||
undefined, // TODO: If encodeReply is supported, this should support temporaryReferences
|
||||
__DEV__ && options && options.findSourceMapURL
|
||||
? options.findSourceMapURL
|
||||
: undefined,
|
||||
__DEV__ && options ? options.replayConsoleLogs === true : false, // defaults to false
|
||||
__DEV__ && options && options.environmentName
|
||||
? options.environmentName
|
||||
: undefined,
|
||||
);
|
||||
stream.on('data', chunk => {
|
||||
if (typeof chunk === 'string') {
|
||||
processStringChunk(response, chunk);
|
||||
} else {
|
||||
processBinaryChunk(response, chunk);
|
||||
}
|
||||
});
|
||||
stream.on('error', error => {
|
||||
reportGlobalError(response, error);
|
||||
});
|
||||
stream.on('end', () => close(response));
|
||||
return getRoot(response);
|
||||
}
|
||||
|
||||
export {createFromNodeStream};
|
||||
307
packages/react-server-dom-unbundled/src/server/ReactFlightDOMServerNode.js
vendored
Normal file
307
packages/react-server-dom-unbundled/src/server/ReactFlightDOMServerNode.js
vendored
Normal file
@@ -0,0 +1,307 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {
|
||||
Request,
|
||||
ReactClientValue,
|
||||
} from 'react-server/src/ReactFlightServer';
|
||||
import type {Destination} from 'react-server/src/ReactServerStreamConfigNode';
|
||||
import type {ClientManifest} from './ReactFlightServerConfigUnbundledBundler';
|
||||
import type {ServerManifest} from 'react-client/src/ReactFlightClientConfig';
|
||||
import type {Busboy} from 'busboy';
|
||||
import type {Writable} from 'stream';
|
||||
import type {Thenable} from 'shared/ReactTypes';
|
||||
|
||||
import {Readable} from 'stream';
|
||||
|
||||
import {
|
||||
createRequest,
|
||||
createPrerenderRequest,
|
||||
startWork,
|
||||
startFlowing,
|
||||
stopFlowing,
|
||||
abort,
|
||||
} from 'react-server/src/ReactFlightServer';
|
||||
|
||||
import {
|
||||
createResponse,
|
||||
reportGlobalError,
|
||||
close,
|
||||
resolveField,
|
||||
resolveFileInfo,
|
||||
resolveFileChunk,
|
||||
resolveFileComplete,
|
||||
getRoot,
|
||||
} from 'react-server/src/ReactFlightReplyServer';
|
||||
|
||||
import {
|
||||
decodeAction,
|
||||
decodeFormState,
|
||||
} from 'react-server/src/ReactFlightActionServer';
|
||||
|
||||
export {
|
||||
registerServerReference,
|
||||
registerClientReference,
|
||||
createClientModuleProxy,
|
||||
} from '../ReactFlightUnbundledReferences';
|
||||
|
||||
import type {TemporaryReferenceSet} from 'react-server/src/ReactFlightServerTemporaryReferences';
|
||||
|
||||
export {createTemporaryReferenceSet} from 'react-server/src/ReactFlightServerTemporaryReferences';
|
||||
|
||||
export type {TemporaryReferenceSet};
|
||||
|
||||
function createDrainHandler(destination: Destination, request: Request) {
|
||||
return () => startFlowing(request, destination);
|
||||
}
|
||||
|
||||
function createCancelHandler(request: Request, reason: string) {
|
||||
return () => {
|
||||
stopFlowing(request);
|
||||
abort(request, new Error(reason));
|
||||
};
|
||||
}
|
||||
|
||||
type Options = {
|
||||
environmentName?: string | (() => string),
|
||||
filterStackFrame?: (url: string, functionName: string) => boolean,
|
||||
onError?: (error: mixed) => void,
|
||||
onPostpone?: (reason: string) => void,
|
||||
identifierPrefix?: string,
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
};
|
||||
|
||||
type PipeableStream = {
|
||||
abort(reason: mixed): void,
|
||||
pipe<T: Writable>(destination: T): T,
|
||||
};
|
||||
|
||||
function renderToPipeableStream(
|
||||
model: ReactClientValue,
|
||||
webpackMap: ClientManifest,
|
||||
options?: Options,
|
||||
): PipeableStream {
|
||||
const request = createRequest(
|
||||
model,
|
||||
webpackMap,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
options ? options.temporaryReferences : undefined,
|
||||
__DEV__ && options ? options.environmentName : undefined,
|
||||
__DEV__ && options ? options.filterStackFrame : undefined,
|
||||
);
|
||||
let hasStartedFlowing = false;
|
||||
startWork(request);
|
||||
return {
|
||||
pipe<T: Writable>(destination: T): T {
|
||||
if (hasStartedFlowing) {
|
||||
throw new Error(
|
||||
'React currently only supports piping to one writable stream.',
|
||||
);
|
||||
}
|
||||
hasStartedFlowing = true;
|
||||
startFlowing(request, destination);
|
||||
destination.on('drain', createDrainHandler(destination, request));
|
||||
destination.on(
|
||||
'error',
|
||||
createCancelHandler(
|
||||
request,
|
||||
'The destination stream errored while writing data.',
|
||||
),
|
||||
);
|
||||
destination.on(
|
||||
'close',
|
||||
createCancelHandler(request, 'The destination stream closed early.'),
|
||||
);
|
||||
return destination;
|
||||
},
|
||||
abort(reason: mixed) {
|
||||
abort(request, reason);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createFakeWritable(readable: any): Writable {
|
||||
// The current host config expects a Writable so we create
|
||||
// a fake writable for now to push into the Readable.
|
||||
return ({
|
||||
write(chunk) {
|
||||
return readable.push(chunk);
|
||||
},
|
||||
end() {
|
||||
readable.push(null);
|
||||
},
|
||||
destroy(error) {
|
||||
readable.destroy(error);
|
||||
},
|
||||
}: any);
|
||||
}
|
||||
|
||||
type PrerenderOptions = {
|
||||
environmentName?: string | (() => string),
|
||||
filterStackFrame?: (url: string, functionName: string) => boolean,
|
||||
onError?: (error: mixed) => void,
|
||||
onPostpone?: (reason: string) => void,
|
||||
identifierPrefix?: string,
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
signal?: AbortSignal,
|
||||
};
|
||||
|
||||
type StaticResult = {
|
||||
prelude: Readable,
|
||||
};
|
||||
|
||||
function prerenderToNodeStream(
|
||||
model: ReactClientValue,
|
||||
webpackMap: ClientManifest,
|
||||
options?: PrerenderOptions,
|
||||
): Promise<StaticResult> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const onFatalError = reject;
|
||||
function onAllReady() {
|
||||
const readable: Readable = new Readable({
|
||||
read() {
|
||||
startFlowing(request, writable);
|
||||
},
|
||||
});
|
||||
const writable = createFakeWritable(readable);
|
||||
resolve({prelude: readable});
|
||||
}
|
||||
|
||||
const request = createPrerenderRequest(
|
||||
model,
|
||||
webpackMap,
|
||||
onAllReady,
|
||||
onFatalError,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
options ? options.temporaryReferences : undefined,
|
||||
__DEV__ && options ? options.environmentName : undefined,
|
||||
__DEV__ && options ? options.filterStackFrame : undefined,
|
||||
);
|
||||
if (options && options.signal) {
|
||||
const signal = options.signal;
|
||||
if (signal.aborted) {
|
||||
const reason = (signal: any).reason;
|
||||
abort(request, reason);
|
||||
} else {
|
||||
const listener = () => {
|
||||
const reason = (signal: any).reason;
|
||||
abort(request, reason);
|
||||
signal.removeEventListener('abort', listener);
|
||||
};
|
||||
signal.addEventListener('abort', listener);
|
||||
}
|
||||
}
|
||||
startWork(request);
|
||||
});
|
||||
}
|
||||
|
||||
function decodeReplyFromBusboy<T>(
|
||||
busboyStream: Busboy,
|
||||
webpackMap: ServerManifest,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
): Thenable<T> {
|
||||
const response = createResponse(
|
||||
webpackMap,
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
);
|
||||
let pendingFiles = 0;
|
||||
const queuedFields: Array<string> = [];
|
||||
busboyStream.on('field', (name, value) => {
|
||||
if (pendingFiles > 0) {
|
||||
// Because the 'end' event fires two microtasks after the next 'field'
|
||||
// we would resolve files and fields out of order. To handle this properly
|
||||
// we queue any fields we receive until the previous file is done.
|
||||
queuedFields.push(name, value);
|
||||
} else {
|
||||
try {
|
||||
resolveField(response, name, value);
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
busboyStream.on('file', (name, value, {filename, encoding, mimeType}) => {
|
||||
if (encoding.toLowerCase() === 'base64') {
|
||||
busboyStream.destroy(
|
||||
new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
pendingFiles++;
|
||||
const file = resolveFileInfo(response, name, filename, mimeType);
|
||||
value.on('data', chunk => {
|
||||
resolveFileChunk(response, file, chunk);
|
||||
});
|
||||
value.on('end', () => {
|
||||
try {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
}
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
busboyStream.on('finish', () => {
|
||||
close(response);
|
||||
});
|
||||
busboyStream.on('error', err => {
|
||||
reportGlobalError(
|
||||
response,
|
||||
// $FlowFixMe[incompatible-call] types Error and mixed are incompatible
|
||||
err,
|
||||
);
|
||||
});
|
||||
return getRoot(response);
|
||||
}
|
||||
|
||||
function decodeReply<T>(
|
||||
body: string | FormData,
|
||||
webpackMap: ServerManifest,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
): Thenable<T> {
|
||||
if (typeof body === 'string') {
|
||||
const form = new FormData();
|
||||
form.append('0', body);
|
||||
body = form;
|
||||
}
|
||||
const response = createResponse(
|
||||
webpackMap,
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
body,
|
||||
);
|
||||
const root = getRoot<T>(response);
|
||||
close(response);
|
||||
return root;
|
||||
}
|
||||
|
||||
export {
|
||||
renderToPipeableStream,
|
||||
prerenderToNodeStream,
|
||||
decodeReplyFromBusboy,
|
||||
decodeReply,
|
||||
decodeAction,
|
||||
decodeFormState,
|
||||
};
|
||||
108
packages/react-server-dom-unbundled/src/server/ReactFlightServerConfigUnbundledBundler.js
vendored
Normal file
108
packages/react-server-dom-unbundled/src/server/ReactFlightServerConfigUnbundledBundler.js
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
|
||||
import type {
|
||||
ImportMetadata,
|
||||
ImportManifestEntry,
|
||||
} from '../shared/ReactFlightImportMetadata';
|
||||
|
||||
import type {
|
||||
ClientReference,
|
||||
ServerReference,
|
||||
} from '../ReactFlightUnbundledReferences';
|
||||
|
||||
export type {ClientReference, ServerReference};
|
||||
|
||||
export type ClientManifest = {
|
||||
[id: string]: ClientReferenceManifestEntry,
|
||||
};
|
||||
|
||||
export type ServerReferenceId = string;
|
||||
|
||||
export type ClientReferenceMetadata = ImportMetadata;
|
||||
export opaque type ClientReferenceManifestEntry = ImportManifestEntry;
|
||||
|
||||
export type ClientReferenceKey = string;
|
||||
|
||||
export {
|
||||
isClientReference,
|
||||
isServerReference,
|
||||
} from '../ReactFlightUnbundledReferences';
|
||||
|
||||
export function getClientReferenceKey(
|
||||
reference: ClientReference<any>,
|
||||
): ClientReferenceKey {
|
||||
return reference.$$async ? reference.$$id + '#async' : reference.$$id;
|
||||
}
|
||||
|
||||
export function resolveClientReferenceMetadata<T>(
|
||||
config: ClientManifest,
|
||||
clientReference: ClientReference<T>,
|
||||
): ClientReferenceMetadata {
|
||||
const modulePath = clientReference.$$id;
|
||||
let name = '';
|
||||
let resolvedModuleData = config[modulePath];
|
||||
if (resolvedModuleData) {
|
||||
// The potentially aliased name.
|
||||
name = resolvedModuleData.name;
|
||||
} else {
|
||||
// We didn't find this specific export name but we might have the * export
|
||||
// which contains this name as well.
|
||||
// TODO: It's unfortunate that we now have to parse this string. We should
|
||||
// probably go back to encoding path and name separately on the client reference.
|
||||
const idx = modulePath.lastIndexOf('#');
|
||||
if (idx !== -1) {
|
||||
name = modulePath.slice(idx + 1);
|
||||
resolvedModuleData = config[modulePath.slice(0, idx)];
|
||||
}
|
||||
if (!resolvedModuleData) {
|
||||
throw new Error(
|
||||
'Could not find the module "' +
|
||||
modulePath +
|
||||
'" in the React Client Manifest. ' +
|
||||
'This is probably a bug in the React Server Components bundler.',
|
||||
);
|
||||
}
|
||||
}
|
||||
if (resolvedModuleData.async === true && clientReference.$$async === true) {
|
||||
throw new Error(
|
||||
'The module "' +
|
||||
modulePath +
|
||||
'" is marked as an async ESM module but was loaded as a CJS proxy. ' +
|
||||
'This is probably a bug in the React Server Components bundler.',
|
||||
);
|
||||
}
|
||||
if (resolvedModuleData.async === true || clientReference.$$async === true) {
|
||||
return [resolvedModuleData.id, resolvedModuleData.chunks, name, 1];
|
||||
} else {
|
||||
return [resolvedModuleData.id, resolvedModuleData.chunks, name];
|
||||
}
|
||||
}
|
||||
|
||||
export function getServerReferenceId<T>(
|
||||
config: ClientManifest,
|
||||
serverReference: ServerReference<T>,
|
||||
): ServerReferenceId {
|
||||
return serverReference.$$id;
|
||||
}
|
||||
|
||||
export function getServerReferenceBoundArguments<T>(
|
||||
config: ClientManifest,
|
||||
serverReference: ServerReference<T>,
|
||||
): null | Array<ReactClientValue> {
|
||||
return serverReference.$$bound;
|
||||
}
|
||||
|
||||
export function getServerReferenceLocation<T>(
|
||||
config: ClientManifest,
|
||||
serverReference: ServerReference<T>,
|
||||
): void | Error {
|
||||
return serverReference.$$location;
|
||||
}
|
||||
44
packages/react-server-dom-unbundled/src/shared/ReactFlightImportMetadata.js
vendored
Normal file
44
packages/react-server-dom-unbundled/src/shared/ReactFlightImportMetadata.js
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export type ImportManifestEntry = {
|
||||
id: string,
|
||||
// chunks is a double indexed array of chunkId / chunkFilename pairs
|
||||
chunks: Array<string>,
|
||||
name: string,
|
||||
async?: boolean,
|
||||
};
|
||||
|
||||
// This is the parsed shape of the wire format which is why it is
|
||||
// condensed to only the essentialy information
|
||||
export type ImportMetadata =
|
||||
| [
|
||||
/* id */ string,
|
||||
/* chunks id/filename pairs, double indexed */ Array<string>,
|
||||
/* name */ string,
|
||||
/* async */ 1,
|
||||
]
|
||||
| [
|
||||
/* id */ string,
|
||||
/* chunks id/filename pairs, double indexed */ Array<string>,
|
||||
/* name */ string,
|
||||
];
|
||||
|
||||
export const ID = 0;
|
||||
export const CHUNKS = 1;
|
||||
export const NAME = 2;
|
||||
// export const ASYNC = 3;
|
||||
|
||||
// This logic is correct because currently only include the 4th tuple member
|
||||
// when the module is async. If that changes we will need to actually assert
|
||||
// the value is true. We don't index into the 4th slot because flow does not
|
||||
// like the potential out of bounds access
|
||||
export function isAsyncImport(metadata: ImportMetadata): boolean {
|
||||
return metadata.length === 4;
|
||||
}
|
||||
13
packages/react-server-dom-unbundled/static.js
vendored
Normal file
13
packages/react-server-dom-unbundled/static.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
throw new Error(
|
||||
'The React Server cannot be used outside a react-server environment. ' +
|
||||
'You must configure Node.js using the `--conditions react-server` flag.',
|
||||
);
|
||||
@@ -7,4 +7,4 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export {unstable_prerenderToNodeStream} from './src/server/react-flight-dom-server.node.unbundled';
|
||||
export {unstable_prerenderToNodeStream} from './src/server/react-flight-dom-server.node';
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-server-dom-webpack",
|
||||
"description": "React Server Components bindings for DOM using Webpack. This is intended to be integrated into meta-frameworks. It is not intended to be imported directly.",
|
||||
"version": "19.1.0",
|
||||
"version": "19.1.4",
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
@@ -17,17 +17,14 @@
|
||||
"client.browser.js",
|
||||
"client.edge.js",
|
||||
"client.node.js",
|
||||
"client.node.unbundled.js",
|
||||
"server.js",
|
||||
"server.browser.js",
|
||||
"server.edge.js",
|
||||
"server.node.js",
|
||||
"server.node.unbundled.js",
|
||||
"static.js",
|
||||
"static.browser.js",
|
||||
"static.edge.js",
|
||||
"static.node.js",
|
||||
"static.node.unbundled.js",
|
||||
"node-register.js",
|
||||
"cjs/",
|
||||
"esm/"
|
||||
@@ -39,10 +36,7 @@
|
||||
"workerd": "./client.edge.js",
|
||||
"deno": "./client.edge.js",
|
||||
"worker": "./client.edge.js",
|
||||
"node": {
|
||||
"webpack": "./client.node.js",
|
||||
"default": "./client.node.unbundled.js"
|
||||
},
|
||||
"node": "./client.node.js",
|
||||
"edge-light": "./client.edge.js",
|
||||
"browser": "./client.browser.js",
|
||||
"default": "./client.browser.js"
|
||||
@@ -50,15 +44,11 @@
|
||||
"./client.browser": "./client.browser.js",
|
||||
"./client.edge": "./client.edge.js",
|
||||
"./client.node": "./client.node.js",
|
||||
"./client.node.unbundled": "./client.node.unbundled.js",
|
||||
"./server": {
|
||||
"react-server": {
|
||||
"workerd": "./server.edge.js",
|
||||
"deno": "./server.browser.js",
|
||||
"node": {
|
||||
"webpack": "./server.node.js",
|
||||
"default": "./server.node.unbundled.js"
|
||||
},
|
||||
"node": "./server.node.js",
|
||||
"edge-light": "./server.edge.js",
|
||||
"browser": "./server.browser.js"
|
||||
},
|
||||
@@ -67,15 +57,11 @@
|
||||
"./server.browser": "./server.browser.js",
|
||||
"./server.edge": "./server.edge.js",
|
||||
"./server.node": "./server.node.js",
|
||||
"./server.node.unbundled": "./server.node.unbundled.js",
|
||||
"./static": {
|
||||
"react-server": {
|
||||
"workerd": "./static.edge.js",
|
||||
"deno": "./static.browser.js",
|
||||
"node": {
|
||||
"webpack": "./static.node.js",
|
||||
"default": "./static.node.unbundled.js"
|
||||
},
|
||||
"node": "./static.node.js",
|
||||
"edge-light": "./static.edge.js",
|
||||
"browser": "./static.browser.js"
|
||||
},
|
||||
@@ -84,7 +70,6 @@
|
||||
"./static.browser": "./static.browser.js",
|
||||
"./static.edge": "./static.edge.js",
|
||||
"./static.node": "./static.node.js",
|
||||
"./static.node.unbundled": "./static.node.unbundled.js",
|
||||
"./node-loader": "./esm/react-server-dom-webpack-node-loader.production.js",
|
||||
"./node-register": "./node-register.js",
|
||||
"./src/*": "./src/*.js",
|
||||
@@ -100,8 +85,8 @@
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react": "^19.1.4",
|
||||
"react-dom": "^19.1.4",
|
||||
"webpack": "^5.59.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -103,6 +103,12 @@ function bind(this: ServerReference<any>): any {
|
||||
return newFn;
|
||||
}
|
||||
|
||||
const serverReferenceToString = {
|
||||
value: () => 'function () { [omitted code] }',
|
||||
configurable: true,
|
||||
writable: true,
|
||||
};
|
||||
|
||||
export function registerServerReference<T: Function>(
|
||||
reference: T,
|
||||
id: string,
|
||||
@@ -126,12 +132,14 @@ export function registerServerReference<T: Function>(
|
||||
configurable: true,
|
||||
},
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
}
|
||||
: {
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -60,11 +60,11 @@ describe('ReactFlightDOM', () => {
|
||||
FlightReactDOM = require('react-dom');
|
||||
|
||||
jest.mock('react-server-dom-webpack/server', () =>
|
||||
require('react-server-dom-webpack/server.node.unbundled'),
|
||||
require('react-server-dom-unbundled/server.node'),
|
||||
);
|
||||
if (__EXPERIMENTAL__) {
|
||||
jest.mock('react-server-dom-webpack/static', () =>
|
||||
require('react-server-dom-webpack/static.node.unbundled'),
|
||||
require('react-server-dom-unbundled/static.node'),
|
||||
);
|
||||
}
|
||||
const WebpackMock = require('./utils/WebpackMock');
|
||||
@@ -172,6 +172,23 @@ describe('ReactFlightDOM', () => {
|
||||
};
|
||||
}
|
||||
|
||||
function createUnclosingStream(
|
||||
stream: ReadableStream<Uint8Array>,
|
||||
): ReadableStream<Uint8Array> {
|
||||
const reader = stream.getReader();
|
||||
|
||||
const s = new ReadableStream({
|
||||
async pull(controller) {
|
||||
const {done, value} = await reader.read();
|
||||
if (!done) {
|
||||
controller.enqueue(value);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
const theInfinitePromise = new Promise(() => {});
|
||||
function InfiniteSuspend() {
|
||||
throw theInfinitePromise;
|
||||
@@ -2949,7 +2966,7 @@ describe('ReactFlightDOM', () => {
|
||||
const {prelude} = await pendingResult;
|
||||
|
||||
const result = await ReactServerDOMClient.createFromReadableStream(
|
||||
Readable.toWeb(prelude),
|
||||
createUnclosingStream(Readable.toWeb(prelude)),
|
||||
);
|
||||
|
||||
const iterator = result.multiShotIterable[Symbol.asyncIterator]();
|
||||
|
||||
@@ -45,13 +45,13 @@ describe('ReactFlightDOMNode', () => {
|
||||
// Simulate the condition resolution
|
||||
jest.mock('react', () => require('react/react.react-server'));
|
||||
jest.mock('react-server-dom-webpack/server', () =>
|
||||
require('react-server-dom-webpack/server.node'),
|
||||
jest.requireActual('react-server-dom-webpack/server.node'),
|
||||
);
|
||||
ReactServer = require('react');
|
||||
ReactServerDOMServer = require('react-server-dom-webpack/server');
|
||||
if (__EXPERIMENTAL__) {
|
||||
jest.mock('react-server-dom-webpack/static', () =>
|
||||
require('react-server-dom-webpack/static.node'),
|
||||
jest.requireActual('react-server-dom-webpack/static.node'),
|
||||
);
|
||||
ReactServerDOMStaticServer = require('react-server-dom-webpack/static');
|
||||
}
|
||||
@@ -66,7 +66,7 @@ describe('ReactFlightDOMNode', () => {
|
||||
__unmockReact();
|
||||
jest.unmock('react-server-dom-webpack/server');
|
||||
jest.mock('react-server-dom-webpack/client', () =>
|
||||
require('react-server-dom-webpack/client.node'),
|
||||
jest.requireActual('react-server-dom-webpack/client.node'),
|
||||
);
|
||||
|
||||
React = require('react');
|
||||
|
||||
@@ -147,7 +147,7 @@ describe('ReactFlightDOMReplyEdge', () => {
|
||||
expect(await resultBlob.arrayBuffer()).toEqual(await blob.arrayBuffer());
|
||||
});
|
||||
|
||||
it('should supports ReadableStreams with typed arrays', async () => {
|
||||
it('should support ReadableStreams with typed arrays', async () => {
|
||||
const buffer = new Uint8Array([
|
||||
123, 4, 10, 5, 100, 255, 244, 45, 56, 67, 43, 124, 67, 89, 100, 20,
|
||||
]).buffer;
|
||||
@@ -246,6 +246,53 @@ describe('ReactFlightDOMReplyEdge', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should cancel the transported ReadableStream when we are cancelled', async () => {
|
||||
const s = new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue('hi');
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
|
||||
const body = await ReactServerDOMClient.encodeReply(s);
|
||||
|
||||
const iterable = {
|
||||
async *[Symbol.asyncIterator]() {
|
||||
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
|
||||
for (const entry of body) {
|
||||
if (entry[1] === 'C') {
|
||||
// Return before finishing the stream.
|
||||
return;
|
||||
}
|
||||
yield entry;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const result = await ReactServerDOMServer.decodeReplyFromAsyncIterable(
|
||||
iterable,
|
||||
webpackServerMap,
|
||||
);
|
||||
|
||||
const reader = result.getReader();
|
||||
|
||||
// We should be able to read the part we already emitted before the abort
|
||||
expect(await reader.read()).toEqual({
|
||||
value: 'hi',
|
||||
done: false,
|
||||
});
|
||||
|
||||
let error = null;
|
||||
try {
|
||||
await reader.read();
|
||||
} catch (x) {
|
||||
error = x;
|
||||
}
|
||||
|
||||
expect(error).not.toBe(null);
|
||||
expect(error.message).toBe('Connection closed.');
|
||||
});
|
||||
|
||||
it('should abort when parsing an incomplete payload', async () => {
|
||||
const infinitePromise = new Promise(() => {});
|
||||
const controller = new AbortController();
|
||||
|
||||
@@ -30,6 +30,8 @@ import {prepareDestinationWithChunks} from 'react-client/src/ReactFlightClientCo
|
||||
|
||||
import {loadChunk} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
import hasOwnProperty from 'shared/hasOwnProperty';
|
||||
|
||||
export type ServerConsumerModuleMap = null | {
|
||||
[clientId: string]: {
|
||||
[clientExportName: string]: ClientReferenceManifestEntry,
|
||||
@@ -249,5 +251,8 @@ export function requireModule<T>(metadata: ClientReference<T>): T {
|
||||
// default property of this if it was an ESM interop module.
|
||||
return moduleExports.__esModule ? moduleExports.default : moduleExports;
|
||||
}
|
||||
return moduleExports[metadata[NAME]];
|
||||
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
|
||||
return moduleExports[metadata[NAME]];
|
||||
}
|
||||
return (undefined: any);
|
||||
}
|
||||
|
||||
@@ -224,16 +224,23 @@ function decodeReplyFromBusboy<T>(
|
||||
// we queue any fields we receive until the previous file is done.
|
||||
queuedFields.push(name, value);
|
||||
} else {
|
||||
resolveField(response, name, value);
|
||||
try {
|
||||
resolveField(response, name, value);
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
busboyStream.on('file', (name, value, {filename, encoding, mimeType}) => {
|
||||
if (encoding.toLowerCase() === 'base64') {
|
||||
throw new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
busboyStream.destroy(
|
||||
new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
pendingFiles++;
|
||||
const file = resolveFileInfo(response, name, filename, mimeType);
|
||||
@@ -241,14 +248,18 @@ function decodeReplyFromBusboy<T>(
|
||||
resolveFileChunk(response, file, chunk);
|
||||
});
|
||||
value.on('end', () => {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
try {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ import type {LazyComponent} from 'react/src/ReactLazy';
|
||||
// TODO: Consider marking the whole bundle instead of these boundaries.
|
||||
|
||||
const callComponent = {
|
||||
'react-stack-bottom-frame': function <Props, Arg, R>(
|
||||
react_stack_bottom_frame: function <Props, Arg, R>(
|
||||
Component: (p: Props, arg: Arg) => R,
|
||||
props: Props,
|
||||
secondArg: Arg,
|
||||
@@ -28,7 +28,7 @@ export const callComponentInDEV: <Props, Arg, R>(
|
||||
secondArg: Arg,
|
||||
) => R = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callComponent['react-stack-bottom-frame'].bind(callComponent): any)
|
||||
(callComponent.react_stack_bottom_frame.bind(callComponent): any)
|
||||
: (null: any);
|
||||
|
||||
interface ClassInstance<R> {
|
||||
@@ -36,7 +36,7 @@ interface ClassInstance<R> {
|
||||
}
|
||||
|
||||
const callRender = {
|
||||
'react-stack-bottom-frame': function <R>(instance: ClassInstance<R>): R {
|
||||
react_stack_bottom_frame: function <R>(instance: ClassInstance<R>): R {
|
||||
return instance.render();
|
||||
},
|
||||
};
|
||||
@@ -44,11 +44,11 @@ const callRender = {
|
||||
export const callRenderInDEV: <R>(instance: ClassInstance<R>) => R => R =
|
||||
__DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callRender['react-stack-bottom-frame'].bind(callRender): any)
|
||||
(callRender.react_stack_bottom_frame.bind(callRender): any)
|
||||
: (null: any);
|
||||
|
||||
const callLazyInit = {
|
||||
'react-stack-bottom-frame': function (lazy: LazyComponent<any, any>): any {
|
||||
react_stack_bottom_frame: function (lazy: LazyComponent<any, any>): any {
|
||||
const payload = lazy._payload;
|
||||
const init = lazy._init;
|
||||
return init(payload);
|
||||
@@ -57,5 +57,5 @@ const callLazyInit = {
|
||||
|
||||
export const callLazyInitInDEV: (lazy: LazyComponent<any, any>) => any = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callLazyInit['react-stack-bottom-frame'].bind(callLazyInit): any)
|
||||
(callLazyInit.react_stack_bottom_frame.bind(callLazyInit): any)
|
||||
: (null: any);
|
||||
|
||||
@@ -19,7 +19,7 @@ import {setCurrentOwner} from './flight/ReactFlightCurrentOwner';
|
||||
// TODO: Consider marking the whole bundle instead of these boundaries.
|
||||
|
||||
const callComponent = {
|
||||
'react-stack-bottom-frame': function <Props, R>(
|
||||
react_stack_bottom_frame: function <Props, R>(
|
||||
Component: (p: Props, arg: void) => R,
|
||||
props: Props,
|
||||
componentDebugInfo: ReactComponentInfo,
|
||||
@@ -41,11 +41,11 @@ export const callComponentInDEV: <Props, R>(
|
||||
componentDebugInfo: ReactComponentInfo,
|
||||
) => R = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callComponent['react-stack-bottom-frame'].bind(callComponent): any)
|
||||
(callComponent.react_stack_bottom_frame.bind(callComponent): any)
|
||||
: (null: any);
|
||||
|
||||
const callLazyInit = {
|
||||
'react-stack-bottom-frame': function (lazy: LazyComponent<any, any>): any {
|
||||
react_stack_bottom_frame: function (lazy: LazyComponent<any, any>): any {
|
||||
const payload = lazy._payload;
|
||||
const init = lazy._init;
|
||||
return init(payload);
|
||||
@@ -54,11 +54,11 @@ const callLazyInit = {
|
||||
|
||||
export const callLazyInitInDEV: (lazy: LazyComponent<any, any>) => any = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callLazyInit['react-stack-bottom-frame'].bind(callLazyInit): any)
|
||||
(callLazyInit.react_stack_bottom_frame.bind(callLazyInit): any)
|
||||
: (null: any);
|
||||
|
||||
const callIterator = {
|
||||
'react-stack-bottom-frame': function (
|
||||
react_stack_bottom_frame: function (
|
||||
iterator: $AsyncIterator<ReactClientValue, ReactClientValue, void>,
|
||||
progress: (
|
||||
entry:
|
||||
@@ -81,5 +81,5 @@ export const callIteratorInDEV: (
|
||||
error: (reason: mixed) => void,
|
||||
) => void = __DEV__
|
||||
? // We use this technique to trick minifiers to preserve the function name.
|
||||
(callIterator['react-stack-bottom-frame'].bind(callIterator): any)
|
||||
(callIterator.react_stack_bottom_frame.bind(callIterator): any)
|
||||
: (null: any);
|
||||
|
||||
833
packages/react-server/src/ReactFlightReplyServer.js
vendored
833
packages/react-server/src/ReactFlightReplyServer.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -1904,7 +1904,7 @@ function serializePromiseID(id: number): string {
|
||||
}
|
||||
|
||||
function serializeServerReferenceID(id: number): string {
|
||||
return '$F' + id.toString(16);
|
||||
return '$h' + id.toString(16);
|
||||
}
|
||||
|
||||
function serializeSymbolReference(name: string): string {
|
||||
|
||||
@@ -45,7 +45,7 @@ export function parseStackTrace(
|
||||
// don't want/need.
|
||||
stack = stack.slice(29);
|
||||
}
|
||||
let idx = stack.indexOf('react-stack-bottom-frame');
|
||||
let idx = stack.indexOf('react_stack_bottom_frame');
|
||||
if (idx !== -1) {
|
||||
idx = stack.lastIndexOf('\n', idx);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import {AsyncLocalStorage} from 'async_hooks';
|
||||
|
||||
import type {Request} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from 'react-server-dom-unbundled/src/server/ReactFlightServerConfigUnbundledBundler';
|
||||
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
|
||||
export const supportsRequestStorage = true;
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> =
|
||||
new AsyncLocalStorage();
|
||||
|
||||
export const supportsComponentStorage = __DEV__;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
supportsComponentStorage ? new AsyncLocalStorage() : (null: any);
|
||||
|
||||
export {createHook as createAsyncHook, executionAsyncId} from 'async_hooks';
|
||||
|
||||
export * from '../ReactFlightServerConfigDebugNode';
|
||||
|
||||
export * from '../ReactFlightStackConfigV8';
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-test-renderer",
|
||||
"version": "19.1.0",
|
||||
"version": "19.1.4",
|
||||
"description": "React package for snapshot testing.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -19,11 +19,11 @@
|
||||
},
|
||||
"homepage": "https://react.dev/",
|
||||
"dependencies": {
|
||||
"react-is": "^19.0.0",
|
||||
"scheduler": "^0.25.0"
|
||||
"react-is": "^19.1.4",
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
"react": "^19.1.4"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
@@ -32,4 +32,4 @@
|
||||
"shallow.js",
|
||||
"cjs/"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
"version": "19.1.0",
|
||||
"version": "19.1.4",
|
||||
"homepage": "https://react.dev/",
|
||||
"bugs": "https://github.com/facebook/react/issues",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -66,7 +66,7 @@ function UnknownOwner() {
|
||||
return (() => Error('react-stack-top-frame'))();
|
||||
}
|
||||
const createFakeCallStack = {
|
||||
'react-stack-bottom-frame': function (callStackForError) {
|
||||
react_stack_bottom_frame: function (callStackForError) {
|
||||
return callStackForError();
|
||||
},
|
||||
};
|
||||
@@ -81,7 +81,7 @@ if (__DEV__) {
|
||||
didWarnAboutElementRef = {};
|
||||
|
||||
// We use this technique to trick minifiers to preserve the function name.
|
||||
unknownOwnerDebugStack = createFakeCallStack['react-stack-bottom-frame'].bind(
|
||||
unknownOwnerDebugStack = createFakeCallStack.react_stack_bottom_frame.bind(
|
||||
createFakeCallStack,
|
||||
UnknownOwner,
|
||||
)();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "scheduler",
|
||||
"version": "0.25.0",
|
||||
"version": "0.26.0",
|
||||
"description": "Cooperative scheduler for the browser environment.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -24,4 +24,4 @@
|
||||
"unstable_post_task.js",
|
||||
"cjs/"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,11 +96,6 @@ export const enableSwipeTransition = __EXPERIMENTAL__;
|
||||
|
||||
export const enableScrollEndPolyfill = __EXPERIMENTAL__;
|
||||
|
||||
/**
|
||||
* Switches the Fabric API from doing layout in commit work instead of complete work.
|
||||
*/
|
||||
export const enableFabricCompleteRootInCommitPhase = false;
|
||||
|
||||
/**
|
||||
* Switches Fiber creation to a simple object instead of a constructor.
|
||||
*/
|
||||
@@ -140,6 +135,8 @@ export const enableShallowPropDiffing = false;
|
||||
|
||||
export const enableSiblingPrerendering = true;
|
||||
|
||||
export const enableEagerAlternateStateNodeCleanup = true;
|
||||
|
||||
/**
|
||||
* Enables an expiration time for retry lanes to avoid starvation.
|
||||
*/
|
||||
|
||||
@@ -24,7 +24,7 @@ export function formatOwnerStack(error: Error): string {
|
||||
// Pop the JSX frame.
|
||||
stack = stack.slice(idx + 1);
|
||||
}
|
||||
idx = stack.indexOf('react-stack-bottom-frame');
|
||||
idx = stack.indexOf('react_stack_bottom_frame');
|
||||
if (idx !== -1) {
|
||||
idx = stack.lastIndexOf('\n', idx);
|
||||
}
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
// TODO: This module is used both by the release scripts and to expose a version
|
||||
// at runtime. We should instead inject the version number as part of the build
|
||||
// process, and use the ReactVersions.js module as the single source of truth.
|
||||
export default '19.1.0';
|
||||
export default '19.1.4';
|
||||
|
||||
@@ -22,8 +22,8 @@ export const enableObjectFiber = __VARIANT__;
|
||||
export const enableHiddenSubtreeInsertionEffectCleanup = __VARIANT__;
|
||||
export const enablePersistedModeClonedFlag = __VARIANT__;
|
||||
export const enableShallowPropDiffing = __VARIANT__;
|
||||
export const enableEagerAlternateStateNodeCleanup = __VARIANT__;
|
||||
export const passChildrenWhenCloningPersistedNodes = __VARIANT__;
|
||||
export const enableFabricCompleteRootInCommitPhase = __VARIANT__;
|
||||
export const enableSiblingPrerendering = __VARIANT__;
|
||||
export const enableUseEffectCRUDOverload = __VARIANT__;
|
||||
export const enableFastAddPropertiesInDiffing = __VARIANT__;
|
||||
|
||||
@@ -20,12 +20,12 @@ const dynamicFlags: DynamicExportsType = (dynamicFlagsUntyped: any);
|
||||
// the exports object every time a flag is read.
|
||||
export const {
|
||||
alwaysThrottleRetries,
|
||||
enableFabricCompleteRootInCommitPhase,
|
||||
enableHiddenSubtreeInsertionEffectCleanup,
|
||||
enableObjectFiber,
|
||||
enablePersistedModeClonedFlag,
|
||||
enableShallowPropDiffing,
|
||||
enableUseEffectCRUDOverload,
|
||||
enableEagerAlternateStateNodeCleanup,
|
||||
passChildrenWhenCloningPersistedNodes,
|
||||
enableSiblingPrerendering,
|
||||
enableFastAddPropertiesInDiffing,
|
||||
|
||||
@@ -30,7 +30,6 @@ export const enableAsyncIterableChildren = false;
|
||||
export const enableCPUSuspense = false;
|
||||
export const enableCreateEventHandleAPI = false;
|
||||
export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
|
||||
export const enableFabricCompleteRootInCommitPhase = false;
|
||||
export const enableMoveBefore = true;
|
||||
export const enableFizzExternalRuntime = true;
|
||||
export const enableHalt = false;
|
||||
@@ -50,6 +49,7 @@ export const enableSchedulingProfiler = __PROFILE__;
|
||||
export const enableComponentPerformanceTrack = false;
|
||||
export const enableScopeAPI = false;
|
||||
export const enableShallowPropDiffing = false;
|
||||
export const enableEagerAlternateStateNodeCleanup = true;
|
||||
export const enableSuspenseAvoidThisFallback = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
export const enableTaint = true;
|
||||
|
||||
@@ -36,7 +36,6 @@ export const enableUseEffectEventHook = false;
|
||||
export const favorSafetyOverHydrationPerf = true;
|
||||
export const enableLegacyFBSupport = false;
|
||||
export const enableMoveBefore = false;
|
||||
export const enableFabricCompleteRootInCommitPhase = false;
|
||||
export const enableHiddenSubtreeInsertionEffectCleanup = false;
|
||||
export const enableHydrationLaneScheduling = true;
|
||||
|
||||
@@ -66,6 +65,7 @@ export const enableShallowPropDiffing = false;
|
||||
export const enableSiblingPrerendering = true;
|
||||
|
||||
export const enableUseEffectCRUDOverload = false;
|
||||
export const enableEagerAlternateStateNodeCleanup = true;
|
||||
|
||||
export const enableYieldingBeforePassive = true;
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ export const enableSchedulingProfiler = __PROFILE__;
|
||||
export const enableComponentPerformanceTrack = false;
|
||||
export const enableScopeAPI = false;
|
||||
export const enableShallowPropDiffing = false;
|
||||
export const enableEagerAlternateStateNodeCleanup = true;
|
||||
export const enableSuspenseAvoidThisFallback = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
export const enableTaint = true;
|
||||
@@ -60,7 +61,6 @@ export const renameElementSymbol = false;
|
||||
export const retryLaneExpirationMs = 5000;
|
||||
export const syncLaneExpirationMs = 250;
|
||||
export const transitionLaneExpirationMs = 5000;
|
||||
export const enableFabricCompleteRootInCommitPhase = false;
|
||||
export const enableSiblingPrerendering = true;
|
||||
export const enableUseEffectCRUDOverload = true;
|
||||
export const enableHydrationLaneScheduling = true;
|
||||
|
||||
@@ -39,7 +39,6 @@ export const favorSafetyOverHydrationPerf = true;
|
||||
export const enableLegacyFBSupport = false;
|
||||
export const enableMoveBefore = false;
|
||||
export const enableRenderableContext = false;
|
||||
export const enableFabricCompleteRootInCommitPhase = false;
|
||||
export const enableHiddenSubtreeInsertionEffectCleanup = true;
|
||||
|
||||
export const enableRetryLaneExpiration = false;
|
||||
@@ -75,6 +74,7 @@ export const enableShallowPropDiffing = false;
|
||||
export const enableSiblingPrerendering = true;
|
||||
|
||||
export const enableUseEffectCRUDOverload = false;
|
||||
export const enableEagerAlternateStateNodeCleanup = true;
|
||||
|
||||
export const enableHydrationLaneScheduling = true;
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@ export const enableProfilerTimer = __PROFILE__;
|
||||
export const enableProfilerCommitHooks = __PROFILE__;
|
||||
export const enableProfilerNestedUpdatePhase = __PROFILE__;
|
||||
export const enableUpdaterTracking = __PROFILE__;
|
||||
export const enableFabricCompleteRootInCommitPhase = false;
|
||||
|
||||
export const enableSuspenseAvoidThisFallback = true;
|
||||
|
||||
@@ -111,6 +110,8 @@ export const disableLegacyMode = true;
|
||||
|
||||
export const enableShallowPropDiffing = false;
|
||||
|
||||
export const enableEagerAlternateStateNodeCleanup = true;
|
||||
|
||||
export const enableLazyPublicInstanceInFabric = false;
|
||||
|
||||
export const enableSwipeTransition = false;
|
||||
|
||||
@@ -21,6 +21,6 @@
|
||||
"rxjs": "^5.5.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"use-sync-external-store": "^1.4.0"
|
||||
"use-sync-external-store": "^1.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,5 +537,9 @@
|
||||
"549": "Cannot start a gesture with a disconnected AnimationTimeline.",
|
||||
"550": "useSwipeTransition is not yet supported in react-art.",
|
||||
"551": "useSwipeTransition is not yet supported in React Native.",
|
||||
"552": "Cannot use a useSwipeTransition() in a detached root."
|
||||
"552": "Cannot use a useSwipeTransition() in a detached root.",
|
||||
"567": "Already initialized stream.",
|
||||
"568": "Already initialized typed array.",
|
||||
"569": "Cannot have cyclic thenables.",
|
||||
"570": "Invalid reference."
|
||||
}
|
||||
|
||||
@@ -124,25 +124,34 @@ async function main() {
|
||||
throw new Error(`Unknown release channel ${argv.releaseChannel}`);
|
||||
}
|
||||
} else {
|
||||
// Running locally, no concurrency. Move each channel's build artifacts into
|
||||
// a temporary directory so that they don't conflict.
|
||||
buildForChannel('stable', '', '');
|
||||
const stableDir = tmp.dirSync().name;
|
||||
crossDeviceRenameSync('./build', stableDir);
|
||||
processStable(stableDir);
|
||||
buildForChannel('experimental', '', '');
|
||||
const experimentalDir = tmp.dirSync().name;
|
||||
crossDeviceRenameSync('./build', experimentalDir);
|
||||
processExperimental(experimentalDir);
|
||||
const releaseChannel = argv.releaseChannel;
|
||||
if (releaseChannel === 'stable') {
|
||||
buildForChannel('stable', '', '');
|
||||
processStable('./build');
|
||||
} else if (releaseChannel === 'experimental') {
|
||||
buildForChannel('experimental', '', '');
|
||||
processExperimental('./build');
|
||||
} else {
|
||||
// Running locally, no concurrency. Move each channel's build artifacts into
|
||||
// a temporary directory so that they don't conflict.
|
||||
buildForChannel('stable', '', '');
|
||||
const stableDir = tmp.dirSync().name;
|
||||
crossDeviceRenameSync('./build', stableDir);
|
||||
processStable(stableDir);
|
||||
buildForChannel('experimental', '', '');
|
||||
const experimentalDir = tmp.dirSync().name;
|
||||
crossDeviceRenameSync('./build', experimentalDir);
|
||||
processExperimental(experimentalDir);
|
||||
|
||||
// Then merge the experimental folder into the stable one. processExperimental
|
||||
// will have already removed conflicting files.
|
||||
//
|
||||
// In CI, merging is handled by the GitHub Download Artifacts plugin.
|
||||
mergeDirsSync(experimentalDir + '/', stableDir + '/');
|
||||
// Then merge the experimental folder into the stable one. processExperimental
|
||||
// will have already removed conflicting files.
|
||||
//
|
||||
// In CI, merging is handled by the GitHub Download Artifacts plugin.
|
||||
mergeDirsSync(experimentalDir + '/', stableDir + '/');
|
||||
|
||||
// Now restore the combined directory back to its original name
|
||||
crossDeviceRenameSync(stableDir, './build');
|
||||
// Now restore the combined directory back to its original name
|
||||
crossDeviceRenameSync(stableDir, './build');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,9 +240,16 @@ function processStable(buildDir) {
|
||||
}
|
||||
|
||||
if (fs.existsSync(buildDir + '/react-native')) {
|
||||
updatePlaceholderReactVersionInCompiledArtifactsFb(
|
||||
updatePlaceholderReactVersionInCompiledArtifacts(
|
||||
buildDir + '/react-native',
|
||||
rnVersionString
|
||||
rnVersionString,
|
||||
filename => filename.endsWith('.fb.js')
|
||||
);
|
||||
|
||||
updatePlaceholderReactVersionInCompiledArtifacts(
|
||||
buildDir + '/react-native',
|
||||
ReactVersion,
|
||||
filename => !filename.endsWith('.fb.js') && filename.endsWith('.js')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -340,9 +356,16 @@ function processExperimental(buildDir, version) {
|
||||
}
|
||||
|
||||
if (fs.existsSync(buildDir + '/react-native')) {
|
||||
updatePlaceholderReactVersionInCompiledArtifactsFb(
|
||||
updatePlaceholderReactVersionInCompiledArtifacts(
|
||||
buildDir + '/react-native',
|
||||
rnVersionString
|
||||
rnVersionString,
|
||||
filename => filename.endsWith('.fb.js')
|
||||
);
|
||||
|
||||
updatePlaceholderReactVersionInCompiledArtifacts(
|
||||
buildDir + '/react-native',
|
||||
ReactVersion,
|
||||
filename => !filename.endsWith('.fb.js') && filename.endsWith('.js')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -437,38 +460,15 @@ function updatePackageVersions(
|
||||
|
||||
function updatePlaceholderReactVersionInCompiledArtifacts(
|
||||
artifactsDirectory,
|
||||
newVersion
|
||||
newVersion,
|
||||
filteringClosure
|
||||
) {
|
||||
// Update the version of React in the compiled artifacts by searching for
|
||||
// the placeholder string and replacing it with a new one.
|
||||
const artifactFilenames = String(
|
||||
spawnSync('grep', [
|
||||
'-lr',
|
||||
PLACEHOLDER_REACT_VERSION,
|
||||
'--',
|
||||
artifactsDirectory,
|
||||
]).stdout
|
||||
)
|
||||
.trim()
|
||||
.split('\n')
|
||||
.filter(filename => filename.endsWith('.js'));
|
||||
|
||||
for (const artifactFilename of artifactFilenames) {
|
||||
const originalText = fs.readFileSync(artifactFilename, 'utf8');
|
||||
const replacedText = originalText.replaceAll(
|
||||
PLACEHOLDER_REACT_VERSION,
|
||||
newVersion
|
||||
);
|
||||
fs.writeFileSync(artifactFilename, replacedText);
|
||||
if (filteringClosure == null) {
|
||||
filteringClosure = filename => filename.endsWith('.js');
|
||||
}
|
||||
}
|
||||
|
||||
function updatePlaceholderReactVersionInCompiledArtifactsFb(
|
||||
artifactsDirectory,
|
||||
newVersion
|
||||
) {
|
||||
// Update the version of React in the compiled artifacts by searching for
|
||||
// the placeholder string and replacing it with a new one.
|
||||
const artifactFilenames = String(
|
||||
spawnSync('grep', [
|
||||
'-lr',
|
||||
@@ -479,7 +479,7 @@ function updatePlaceholderReactVersionInCompiledArtifactsFb(
|
||||
)
|
||||
.trim()
|
||||
.split('\n')
|
||||
.filter(filename => filename.endsWith('.fb.js'));
|
||||
.filter(filteringClosure);
|
||||
|
||||
for (const artifactFilename of artifactFilenames) {
|
||||
const originalText = fs.readFileSync(artifactFilename, 'utf8');
|
||||
|
||||
@@ -462,18 +462,6 @@ const bundles = [
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'util', 'crypto', 'async_hooks', 'react-dom'],
|
||||
},
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
entry:
|
||||
'react-server-dom-webpack/src/server/react-flight-dom-server.node.unbundled',
|
||||
name: 'react-server-dom-webpack-server.node.unbundled',
|
||||
condition: 'react-server',
|
||||
global: 'ReactServerDOMServer',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'util', 'crypto', 'async_hooks', 'react-dom'],
|
||||
},
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
@@ -505,15 +493,6 @@ const bundles = [
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'react-dom', 'util', 'crypto'],
|
||||
},
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
entry: 'react-server-dom-webpack/client.node.unbundled',
|
||||
global: 'ReactServerDOMClient',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'react-dom', 'util', 'crypto'],
|
||||
},
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
@@ -731,6 +710,61 @@ const bundles = [
|
||||
externals: ['acorn'],
|
||||
},
|
||||
|
||||
/******* React Server DOM Unbundled Server *******/
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
entry: 'react-server-dom-unbundled/server.node',
|
||||
condition: 'react-server',
|
||||
global: 'ReactServerDOMServer',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: [
|
||||
'react',
|
||||
'react-dom',
|
||||
'async_hooks',
|
||||
'crypto',
|
||||
'stream',
|
||||
'util',
|
||||
],
|
||||
},
|
||||
|
||||
/******* React Server DOM Unbundled Client *******/
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
entry: 'react-server-dom-unbundled/client',
|
||||
global: 'ReactServerDOMClient',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'react-dom', 'util', 'crypto'],
|
||||
},
|
||||
|
||||
/******* React Server DOM Unbundled Node.js Loader *******/
|
||||
{
|
||||
bundleTypes: [ESM_PROD],
|
||||
moduleType: RENDERER_UTILS,
|
||||
entry: 'react-server-dom-unbundled/node-loader',
|
||||
condition: 'react-server',
|
||||
global: 'ReactServerUnbundledNodeLoader',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['acorn'],
|
||||
},
|
||||
|
||||
/******* React Server DOM Unbundled Node.js CommonJS Loader *******/
|
||||
{
|
||||
bundleTypes: [NODE_ES2015],
|
||||
moduleType: RENDERER_UTILS,
|
||||
entry: 'react-server-dom-unbundled/src/ReactFlightUnbundledNodeRegister',
|
||||
name: 'react-server-dom-unbundled-node-register',
|
||||
condition: 'react-server',
|
||||
global: 'ReactFlightUnbundledNodeRegister',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['url', 'module', 'react-server-dom-unbundled/server'],
|
||||
},
|
||||
|
||||
/******* React Suspense Test Utils *******/
|
||||
{
|
||||
bundleTypes: [NODE_ES2015],
|
||||
|
||||
@@ -62,8 +62,6 @@ module.exports = [
|
||||
'react-dom/static.node',
|
||||
'react-dom/test-utils',
|
||||
'react-dom/unstable_server-external-runtime',
|
||||
'react-server-dom-webpack/client.node.unbundled',
|
||||
'react-server-dom-webpack/src/server/react-flight-dom-server.node.unbundled',
|
||||
],
|
||||
paths: [
|
||||
'react-dom',
|
||||
@@ -83,18 +81,13 @@ module.exports = [
|
||||
'react-dom-bindings/src/server/ReactFlightServerConfigDOM.js',
|
||||
'react-dom-bindings/src/shared/ReactFlightClientConfigDOM.js',
|
||||
'react-server-dom-webpack',
|
||||
'react-server-dom-webpack/client.node.unbundled',
|
||||
'react-server-dom-webpack/server',
|
||||
'react-server-dom-webpack/server.node.unbundled',
|
||||
'react-server-dom-webpack/static',
|
||||
'react-server-dom-webpack/static.node.unbundled',
|
||||
'react-server-dom-webpack/src/client/ReactFlightDOMClientEdge.js', // react-server-dom-webpack/client.node
|
||||
'react-server-dom-webpack/src/client/ReactFlightDOMClientNode.js', // react-server-dom-webpack/client.node
|
||||
'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerNode.js',
|
||||
'react-server-dom-webpack/src/server/react-flight-dom-server.node.unbundled',
|
||||
'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack.js',
|
||||
'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackServer.js',
|
||||
'react-server-dom-webpack/src/server/ReactFlightDOMServerNode.js', // react-server-dom-webpack/src/server/react-flight-dom-server.node
|
||||
'react-devtools',
|
||||
'react-devtools-core',
|
||||
'react-devtools-shell',
|
||||
'react-devtools-shared',
|
||||
'shared/ReactDOMSharedInternals',
|
||||
'react-server/src/ReactFlightServerConfigDebugNode.js',
|
||||
@@ -232,6 +225,49 @@ module.exports = [
|
||||
isFlowTyped: true,
|
||||
isServerSupported: true,
|
||||
},
|
||||
{
|
||||
shortName: 'dom-node-unbundled',
|
||||
entryPoints: [
|
||||
'react-server-dom-unbundled/client',
|
||||
'react-server-dom-unbundled/server.node',
|
||||
],
|
||||
paths: [
|
||||
'react-dom',
|
||||
'react-dom-bindings',
|
||||
'react-dom/client',
|
||||
'react-dom/profiling',
|
||||
'react-dom/server',
|
||||
'react-dom/server.node',
|
||||
'react-dom/static',
|
||||
'react-dom/static.node',
|
||||
'react-dom/src/server/react-dom-server.node',
|
||||
'react-dom/src/server/ReactDOMFizzServerNode.js', // react-dom/server.node
|
||||
'react-dom/src/server/ReactDOMFizzStaticNode.js',
|
||||
'react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js',
|
||||
'react-dom-bindings/src/server/ReactFlightServerConfigDOM.js',
|
||||
'react-dom-bindings/src/shared/ReactFlightClientConfigDOM.js',
|
||||
'react-server-dom-unbundled',
|
||||
'react-server-dom-unbundled/client',
|
||||
'react-server-dom-unbundled/server',
|
||||
'react-server-dom-unbundled/server.node',
|
||||
'react-server-dom-unbundled/static',
|
||||
'react-server-dom-unbundled/static.node',
|
||||
'react-server-dom-unbundled/src/client/ReactFlightDOMClientEdge.js', // react-server-dom-unbundled/client.node
|
||||
'react-server-dom-unbundled/src/client/ReactFlightDOMClientNode.js', // react-server-dom-unbundled/client.node
|
||||
'react-server-dom-unbundled/src/client/ReactFlightClientConfigBundlerNode.js',
|
||||
'react-server-dom-unbundled/src/client/react-flight-dom-client.node',
|
||||
'react-server-dom-unbundled/src/server/react-flight-dom-server.node',
|
||||
'react-server-dom-unbundled/src/server/ReactFlightDOMServerNode.js', // react-server-dom-unbundled/src/server/react-flight-dom-server.node
|
||||
'react-devtools',
|
||||
'react-devtools-core',
|
||||
'react-devtools-shell',
|
||||
'react-devtools-shared',
|
||||
'shared/ReactDOMSharedInternals',
|
||||
'react-server/src/ReactFlightServerConfigDebugNode.js',
|
||||
],
|
||||
isFlowTyped: true,
|
||||
isServerSupported: true,
|
||||
},
|
||||
{
|
||||
shortName: 'dom-bun',
|
||||
entryPoints: ['react-dom/src/server/react-dom-server.bun.js'],
|
||||
|
||||
Reference in New Issue
Block a user