mirror of
https://github.com/facebook/react.git
synced 2026-02-26 02:54:59 +00:00
[Flight] Move react-server-dom-webpack/*.unbundled to private react-server-dom-unbundled (#35290)
Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>
This commit is contained in:
committed by
GitHub
parent
3016ff87d8
commit
378973b387
@@ -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',
|
||||
|
||||
84
.github/workflows/runtime_build_and_test.yml
vendored
84
.github/workflows/runtime_build_and_test.yml
vendored
@@ -41,7 +41,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') }}
|
||||
lookup-only: true
|
||||
- uses: actions/setup-node@v4
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
@@ -55,10 +55,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
|
||||
@@ -67,7 +65,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
|
||||
@@ -82,7 +80,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') }}
|
||||
lookup-only: true
|
||||
- uses: actions/setup-node@v4
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
@@ -98,10 +96,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
|
||||
@@ -112,7 +108,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:
|
||||
@@ -154,10 +150,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
|
||||
@@ -184,10 +178,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
|
||||
@@ -216,7 +208,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
|
||||
@@ -274,10 +266,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
|
||||
@@ -306,7 +296,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') }}
|
||||
- name: Install runtime dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
@@ -349,10 +339,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
|
||||
@@ -440,10 +428,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
|
||||
@@ -483,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
|
||||
@@ -548,10 +532,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
|
||||
@@ -588,10 +570,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
|
||||
@@ -740,10 +720,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
|
||||
@@ -802,10 +780,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
|
||||
|
||||
@@ -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');
|
||||
@@ -76,7 +77,7 @@ function getDebugChannel(req) {
|
||||
|
||||
async function renderApp(res, returnValue, formState, noCache, debugChannel) {
|
||||
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');
|
||||
@@ -134,7 +135,7 @@ async function renderApp(res, returnValue, formState, noCache, debugChannel) {
|
||||
|
||||
async function prerenderApp(res, returnValue, formState, noCache) {
|
||||
const {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');
|
||||
@@ -202,7 +203,7 @@ app.get('/', async function (req, res) {
|
||||
app.post('/', bodyParser.text(), async function (req, res) {
|
||||
const noCache = req.headers['cache-control'] === 'no-cache';
|
||||
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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import {renderToReadableStream} from 'react-server-dom-webpack/server';
|
||||
import {renderToReadableStream} from 'react-server-dom-unbundled/server';
|
||||
import {createFromReadableStream} from 'react-server-dom-webpack/client';
|
||||
import {PassThrough, Readable} from 'stream';
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
"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",
|
||||
"flow-typed-install": "yarn flow-typed install --skip --skipFlowRestart --ignore-deps=dev",
|
||||
"linc": "node ./scripts/tasks/linc.js",
|
||||
|
||||
@@ -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;
|
||||
|
||||
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.
|
||||
@@ -7,4 +7,4 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export * from './src/client/react-flight-dom-client.node.unbundled';
|
||||
export * from './src/client/react-flight-dom-client.node';
|
||||
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.node.production.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-server-dom-unbundled-client.node.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.',
|
||||
);
|
||||
@@ -19,4 +19,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
352
packages/react-server-dom-unbundled/src/ReactFlightUnbundledReferences.js
vendored
Normal file
352
packages/react-server-dom-unbundled/src/ReactFlightUnbundledReferences.js
vendored
Normal file
@@ -0,0 +1,352 @@
|
||||
/**
|
||||
* 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},
|
||||
}) as PropertyDescriptorMap,
|
||||
);
|
||||
}
|
||||
return newFn;
|
||||
}
|
||||
|
||||
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},
|
||||
} as PropertyDescriptorMap)
|
||||
: ({
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
bind: {value: bind, configurable: true},
|
||||
} as PropertyDescriptorMap),
|
||||
);
|
||||
}
|
||||
|
||||
const PROMISE_PROTOTYPE = Promise.prototype;
|
||||
|
||||
const deepProxyHandlers: Proxy$traps<mixed> = {
|
||||
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);
|
||||
}
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
255
packages/react-server-dom-unbundled/src/client/ReactFlightDOMClientEdge.js
vendored
Normal file
255
packages/react-server-dom-unbundled/src/client/ReactFlightDOMClientEdge.js
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* 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 {
|
||||
DebugChannel,
|
||||
FindSourceMapURLCallback,
|
||||
Response as FlightResponse,
|
||||
} 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,
|
||||
createStreamState,
|
||||
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,
|
||||
startTime?: number,
|
||||
endTime?: number,
|
||||
// For the Edge client we only support a single-direction debug channel.
|
||||
debugChannel?: {readable?: ReadableStream, ...},
|
||||
};
|
||||
|
||||
function createResponseFromOptions(options: Options) {
|
||||
const debugChannel: void | DebugChannel =
|
||||
__DEV__ && options && options.debugChannel !== undefined
|
||||
? {
|
||||
hasReadable: options.debugChannel.readable !== undefined,
|
||||
callback: null,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
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,
|
||||
__DEV__ && options && options.startTime != null
|
||||
? options.startTime
|
||||
: undefined,
|
||||
__DEV__ && options && options.endTime != null ? options.endTime : undefined,
|
||||
debugChannel,
|
||||
);
|
||||
}
|
||||
|
||||
function startReadingFromStream(
|
||||
response: FlightResponse,
|
||||
stream: ReadableStream,
|
||||
onDone: () => void,
|
||||
debugValue: mixed,
|
||||
): void {
|
||||
const streamState = createStreamState(response, debugValue);
|
||||
const reader = stream.getReader();
|
||||
function progress({
|
||||
done,
|
||||
value,
|
||||
}: {
|
||||
done: boolean,
|
||||
value: ?any,
|
||||
...
|
||||
}): void | Promise<void> {
|
||||
if (done) {
|
||||
return onDone();
|
||||
}
|
||||
const buffer: Uint8Array = (value: any);
|
||||
processBinaryChunk(response, streamState, 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);
|
||||
|
||||
if (
|
||||
__DEV__ &&
|
||||
options &&
|
||||
options.debugChannel &&
|
||||
options.debugChannel.readable
|
||||
) {
|
||||
let streamDoneCount = 0;
|
||||
const handleDone = () => {
|
||||
if (++streamDoneCount === 2) {
|
||||
close(response);
|
||||
}
|
||||
};
|
||||
startReadingFromStream(response, options.debugChannel.readable, handleDone);
|
||||
startReadingFromStream(response, stream, handleDone, stream);
|
||||
} else {
|
||||
startReadingFromStream(
|
||||
response,
|
||||
stream,
|
||||
close.bind(null, response),
|
||||
stream,
|
||||
);
|
||||
}
|
||||
|
||||
return getRoot(response);
|
||||
}
|
||||
|
||||
function createFromFetch<T>(
|
||||
promiseForResponse: Promise<Response>,
|
||||
options: Options,
|
||||
): Thenable<T> {
|
||||
const response: FlightResponse = createResponseFromOptions(options);
|
||||
promiseForResponse.then(
|
||||
function (r) {
|
||||
if (
|
||||
__DEV__ &&
|
||||
options &&
|
||||
options.debugChannel &&
|
||||
options.debugChannel.readable
|
||||
) {
|
||||
let streamDoneCount = 0;
|
||||
const handleDone = () => {
|
||||
if (++streamDoneCount === 2) {
|
||||
close(response);
|
||||
}
|
||||
};
|
||||
startReadingFromStream(
|
||||
response,
|
||||
options.debugChannel.readable,
|
||||
handleDone,
|
||||
);
|
||||
startReadingFromStream(response, (r.body: any), handleDone, r);
|
||||
} else {
|
||||
startReadingFromStream(
|
||||
response,
|
||||
(r.body: any),
|
||||
close.bind(null, response),
|
||||
r,
|
||||
);
|
||||
}
|
||||
},
|
||||
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};
|
||||
139
packages/react-server-dom-unbundled/src/client/ReactFlightDOMClientNode.js
vendored
Normal file
139
packages/react-server-dom-unbundled/src/client/ReactFlightDOMClientNode.js
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* 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 {
|
||||
DebugChannel,
|
||||
FindSourceMapURLCallback,
|
||||
Response,
|
||||
} 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,
|
||||
createStreamState,
|
||||
getRoot,
|
||||
reportGlobalError,
|
||||
processStringChunk,
|
||||
processBinaryChunk,
|
||||
close,
|
||||
} from 'react-client/src/ReactFlightClient';
|
||||
|
||||
export * from './ReactFlightDOMClientEdge';
|
||||
|
||||
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.',
|
||||
);
|
||||
}
|
||||
|
||||
type EncodeFormActionCallback = <A>(
|
||||
id: any,
|
||||
args: Promise<A>,
|
||||
) => ReactCustomFormAction;
|
||||
|
||||
export type Options = {
|
||||
nonce?: string,
|
||||
encodeFormAction?: EncodeFormActionCallback,
|
||||
findSourceMapURL?: FindSourceMapURLCallback,
|
||||
replayConsoleLogs?: boolean,
|
||||
environmentName?: string,
|
||||
startTime?: number,
|
||||
endTime?: number,
|
||||
// For the Node.js client we only support a single-direction debug channel.
|
||||
debugChannel?: Readable,
|
||||
};
|
||||
|
||||
function startReadingFromStream(
|
||||
response: Response,
|
||||
stream: Readable,
|
||||
onEnd: () => void,
|
||||
): void {
|
||||
const streamState = createStreamState(response, stream);
|
||||
|
||||
stream.on('data', chunk => {
|
||||
if (typeof chunk === 'string') {
|
||||
processStringChunk(response, streamState, chunk);
|
||||
} else {
|
||||
processBinaryChunk(response, streamState, chunk);
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('error', error => {
|
||||
reportGlobalError(response, error);
|
||||
});
|
||||
|
||||
stream.on('end', onEnd);
|
||||
}
|
||||
|
||||
function createFromNodeStream<T>(
|
||||
stream: Readable,
|
||||
serverConsumerManifest: ServerConsumerManifest,
|
||||
options?: Options,
|
||||
): Thenable<T> {
|
||||
const debugChannel: void | DebugChannel =
|
||||
__DEV__ && options && options.debugChannel !== undefined
|
||||
? {hasReadable: true, callback: null}
|
||||
: undefined;
|
||||
|
||||
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,
|
||||
__DEV__ && options && options.startTime != null
|
||||
? options.startTime
|
||||
: undefined,
|
||||
__DEV__ && options && options.endTime != null ? options.endTime : undefined,
|
||||
debugChannel,
|
||||
);
|
||||
|
||||
if (__DEV__ && options && options.debugChannel) {
|
||||
let streamEndedCount = 0;
|
||||
const handleEnd = () => {
|
||||
if (++streamEndedCount === 2) {
|
||||
close(response);
|
||||
}
|
||||
};
|
||||
startReadingFromStream(response, options.debugChannel, handleEnd);
|
||||
startReadingFromStream(response, stream, handleEnd);
|
||||
} else {
|
||||
startReadingFromStream(response, stream, close.bind(null, response));
|
||||
}
|
||||
|
||||
return getRoot(response);
|
||||
}
|
||||
|
||||
export {createFromNodeStream};
|
||||
695
packages/react-server-dom-unbundled/src/server/ReactFlightDOMServerNode.js
vendored
Normal file
695
packages/react-server-dom-unbundled/src/server/ReactFlightDOMServerNode.js
vendored
Normal file
@@ -0,0 +1,695 @@
|
||||
/**
|
||||
* 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 type {Duplex} from 'stream';
|
||||
|
||||
import {Readable} from 'stream';
|
||||
|
||||
import {ASYNC_ITERATOR} from 'shared/ReactSymbols';
|
||||
|
||||
import {
|
||||
createRequest,
|
||||
createPrerenderRequest,
|
||||
startWork,
|
||||
startFlowing,
|
||||
startFlowingDebug,
|
||||
stopFlowing,
|
||||
abort,
|
||||
resolveDebugMessage,
|
||||
closeDebugChannel,
|
||||
} from 'react-server/src/ReactFlightServer';
|
||||
|
||||
import {
|
||||
createResponse,
|
||||
reportGlobalError,
|
||||
close,
|
||||
resolveField,
|
||||
resolveFile,
|
||||
resolveFileInfo,
|
||||
resolveFileChunk,
|
||||
resolveFileComplete,
|
||||
getRoot,
|
||||
} from 'react-server/src/ReactFlightReplyServer';
|
||||
|
||||
import {
|
||||
decodeAction,
|
||||
decodeFormState,
|
||||
} from 'react-server/src/ReactFlightActionServer';
|
||||
|
||||
export {
|
||||
registerServerReference,
|
||||
registerClientReference,
|
||||
createClientModuleProxy,
|
||||
} from '../ReactFlightUnbundledReferences';
|
||||
|
||||
import {
|
||||
createStringDecoder,
|
||||
readPartialStringChunk,
|
||||
readFinalStringChunk,
|
||||
} from 'react-client/src/ReactFlightClientStreamConfigNode';
|
||||
|
||||
import {textEncoder} from 'react-server/src/ReactServerStreamConfigNode';
|
||||
|
||||
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));
|
||||
};
|
||||
}
|
||||
|
||||
function startReadingFromDebugChannelReadable(
|
||||
request: Request,
|
||||
stream: Readable | WebSocket,
|
||||
): void {
|
||||
const stringDecoder = createStringDecoder();
|
||||
let lastWasPartial = false;
|
||||
let stringBuffer = '';
|
||||
function onData(chunk: string | Uint8Array) {
|
||||
if (typeof chunk === 'string') {
|
||||
if (lastWasPartial) {
|
||||
stringBuffer += readFinalStringChunk(stringDecoder, new Uint8Array(0));
|
||||
lastWasPartial = false;
|
||||
}
|
||||
stringBuffer += chunk;
|
||||
} else {
|
||||
const buffer: Uint8Array = (chunk: any);
|
||||
stringBuffer += readPartialStringChunk(stringDecoder, buffer);
|
||||
lastWasPartial = true;
|
||||
}
|
||||
const messages = stringBuffer.split('\n');
|
||||
for (let i = 0; i < messages.length - 1; i++) {
|
||||
resolveDebugMessage(request, messages[i]);
|
||||
}
|
||||
stringBuffer = messages[messages.length - 1];
|
||||
}
|
||||
function onError(error: mixed) {
|
||||
abort(
|
||||
request,
|
||||
new Error('Lost connection to the Debug Channel.', {
|
||||
cause: error,
|
||||
}),
|
||||
);
|
||||
}
|
||||
function onClose() {
|
||||
closeDebugChannel(request);
|
||||
}
|
||||
if (
|
||||
// $FlowFixMe[method-unbinding]
|
||||
typeof stream.addEventListener === 'function' &&
|
||||
// $FlowFixMe[method-unbinding]
|
||||
typeof stream.binaryType === 'string'
|
||||
) {
|
||||
const ws: WebSocket = (stream: any);
|
||||
ws.binaryType = 'arraybuffer';
|
||||
ws.addEventListener('message', event => {
|
||||
// $FlowFixMe
|
||||
onData(event.data);
|
||||
});
|
||||
ws.addEventListener('error', event => {
|
||||
// $FlowFixMe
|
||||
onError(event.error);
|
||||
});
|
||||
ws.addEventListener('close', onClose);
|
||||
} else {
|
||||
const readable: Readable = (stream: any);
|
||||
readable.on('data', onData);
|
||||
readable.on('error', onError);
|
||||
readable.on('end', onClose);
|
||||
}
|
||||
}
|
||||
|
||||
type Options = {
|
||||
debugChannel?: Readable | Writable | Duplex | WebSocket,
|
||||
environmentName?: string | (() => string),
|
||||
filterStackFrame?: (url: string, functionName: string) => boolean,
|
||||
onError?: (error: mixed) => 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 debugChannel = __DEV__ && options ? options.debugChannel : undefined;
|
||||
const debugChannelReadable: void | Readable | WebSocket =
|
||||
__DEV__ &&
|
||||
debugChannel !== undefined &&
|
||||
// $FlowFixMe[method-unbinding]
|
||||
(typeof debugChannel.read === 'function' ||
|
||||
typeof debugChannel.readyState === 'number')
|
||||
? (debugChannel: any)
|
||||
: undefined;
|
||||
const debugChannelWritable: void | Writable =
|
||||
__DEV__ && debugChannel !== undefined
|
||||
? // $FlowFixMe[method-unbinding]
|
||||
typeof debugChannel.write === 'function'
|
||||
? (debugChannel: any)
|
||||
: // $FlowFixMe[method-unbinding]
|
||||
typeof debugChannel.send === 'function'
|
||||
? createFakeWritableFromWebSocket((debugChannel: any))
|
||||
: undefined
|
||||
: undefined;
|
||||
const request = createRequest(
|
||||
model,
|
||||
webpackMap,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.temporaryReferences : undefined,
|
||||
__DEV__ && options ? options.environmentName : undefined,
|
||||
__DEV__ && options ? options.filterStackFrame : undefined,
|
||||
debugChannelReadable !== undefined,
|
||||
);
|
||||
let hasStartedFlowing = false;
|
||||
startWork(request);
|
||||
if (debugChannelWritable !== undefined) {
|
||||
startFlowingDebug(request, debugChannelWritable);
|
||||
}
|
||||
if (debugChannelReadable !== undefined) {
|
||||
startReadingFromDebugChannelReadable(request, debugChannelReadable);
|
||||
}
|
||||
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.',
|
||||
),
|
||||
);
|
||||
// We don't close until the debug channel closes.
|
||||
if (!__DEV__ || debugChannelReadable === undefined) {
|
||||
destination.on(
|
||||
'close',
|
||||
createCancelHandler(request, 'The destination stream closed early.'),
|
||||
);
|
||||
}
|
||||
return destination;
|
||||
},
|
||||
abort(reason: mixed) {
|
||||
abort(request, reason);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createFakeWritableFromWebSocket(webSocket: WebSocket): Writable {
|
||||
return ({
|
||||
write(chunk: string | Uint8Array) {
|
||||
webSocket.send((chunk: any));
|
||||
return true;
|
||||
},
|
||||
end() {
|
||||
webSocket.close();
|
||||
},
|
||||
destroy(reason) {
|
||||
if (typeof reason === 'object' && reason !== null) {
|
||||
reason = reason.message;
|
||||
}
|
||||
if (typeof reason === 'string') {
|
||||
webSocket.close(1011, reason);
|
||||
} else {
|
||||
webSocket.close(1011);
|
||||
}
|
||||
},
|
||||
}: any);
|
||||
}
|
||||
|
||||
function createFakeWritableFromReadableStreamController(
|
||||
controller: ReadableStreamController,
|
||||
): Writable {
|
||||
// The current host config expects a Writable so we create
|
||||
// a fake writable for now to push into the Readable.
|
||||
return ({
|
||||
write(chunk: string | Uint8Array) {
|
||||
if (typeof chunk === 'string') {
|
||||
chunk = textEncoder.encode(chunk);
|
||||
}
|
||||
controller.enqueue(chunk);
|
||||
// in web streams there is no backpressure so we can always write more
|
||||
return true;
|
||||
},
|
||||
end() {
|
||||
controller.close();
|
||||
},
|
||||
destroy(error) {
|
||||
// $FlowFixMe[method-unbinding]
|
||||
if (typeof controller.error === 'function') {
|
||||
// $FlowFixMe[incompatible-call]: This is an Error object or the destination accepts other types.
|
||||
controller.error(error);
|
||||
} else {
|
||||
controller.close();
|
||||
}
|
||||
},
|
||||
}: any);
|
||||
}
|
||||
|
||||
function startReadingFromDebugChannelReadableStream(
|
||||
request: Request,
|
||||
stream: ReadableStream,
|
||||
): void {
|
||||
const reader = stream.getReader();
|
||||
const stringDecoder = createStringDecoder();
|
||||
let stringBuffer = '';
|
||||
function progress({
|
||||
done,
|
||||
value,
|
||||
}: {
|
||||
done: boolean,
|
||||
value: ?any,
|
||||
...
|
||||
}): void | Promise<void> {
|
||||
const buffer: Uint8Array = (value: any);
|
||||
stringBuffer += done
|
||||
? readFinalStringChunk(stringDecoder, new Uint8Array(0))
|
||||
: readPartialStringChunk(stringDecoder, buffer);
|
||||
const messages = stringBuffer.split('\n');
|
||||
for (let i = 0; i < messages.length - 1; i++) {
|
||||
resolveDebugMessage(request, messages[i]);
|
||||
}
|
||||
stringBuffer = messages[messages.length - 1];
|
||||
if (done) {
|
||||
closeDebugChannel(request);
|
||||
return;
|
||||
}
|
||||
return reader.read().then(progress).catch(error);
|
||||
}
|
||||
function error(e: any) {
|
||||
abort(
|
||||
request,
|
||||
new Error('Lost connection to the Debug Channel.', {
|
||||
cause: e,
|
||||
}),
|
||||
);
|
||||
}
|
||||
reader.read().then(progress).catch(error);
|
||||
}
|
||||
|
||||
function renderToReadableStream(
|
||||
model: ReactClientValue,
|
||||
webpackMap: ClientManifest,
|
||||
options?: Omit<Options, 'debugChannel'> & {
|
||||
debugChannel?: {readable?: ReadableStream, writable?: WritableStream, ...},
|
||||
signal?: AbortSignal,
|
||||
},
|
||||
): ReadableStream {
|
||||
const debugChannelReadable =
|
||||
__DEV__ && options && options.debugChannel
|
||||
? options.debugChannel.readable
|
||||
: undefined;
|
||||
const debugChannelWritable =
|
||||
__DEV__ && options && options.debugChannel
|
||||
? options.debugChannel.writable
|
||||
: undefined;
|
||||
const request = createRequest(
|
||||
model,
|
||||
webpackMap,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.temporaryReferences : undefined,
|
||||
__DEV__ && options ? options.environmentName : undefined,
|
||||
__DEV__ && options ? options.filterStackFrame : undefined,
|
||||
debugChannelReadable !== undefined,
|
||||
);
|
||||
if (options && options.signal) {
|
||||
const signal = options.signal;
|
||||
if (signal.aborted) {
|
||||
abort(request, (signal: any).reason);
|
||||
} else {
|
||||
const listener = () => {
|
||||
abort(request, (signal: any).reason);
|
||||
signal.removeEventListener('abort', listener);
|
||||
};
|
||||
signal.addEventListener('abort', listener);
|
||||
}
|
||||
}
|
||||
if (debugChannelWritable !== undefined) {
|
||||
let debugWritable: Writable;
|
||||
const debugStream = new ReadableStream(
|
||||
{
|
||||
type: 'bytes',
|
||||
start: (controller): ?Promise<void> => {
|
||||
debugWritable =
|
||||
createFakeWritableFromReadableStreamController(controller);
|
||||
},
|
||||
pull: (controller): ?Promise<void> => {
|
||||
startFlowingDebug(request, debugWritable);
|
||||
},
|
||||
},
|
||||
// $FlowFixMe[prop-missing] size() methods are not allowed on byte streams.
|
||||
{highWaterMark: 0},
|
||||
);
|
||||
debugStream.pipeTo(debugChannelWritable);
|
||||
}
|
||||
if (debugChannelReadable !== undefined) {
|
||||
startReadingFromDebugChannelReadableStream(request, debugChannelReadable);
|
||||
}
|
||||
let writable: Writable;
|
||||
const stream = new ReadableStream(
|
||||
{
|
||||
type: 'bytes',
|
||||
start: (controller): ?Promise<void> => {
|
||||
writable = createFakeWritableFromReadableStreamController(controller);
|
||||
startWork(request);
|
||||
},
|
||||
pull: (controller): ?Promise<void> => {
|
||||
startFlowing(request, writable);
|
||||
},
|
||||
cancel: (reason): ?Promise<void> => {
|
||||
stopFlowing(request);
|
||||
abort(request, reason);
|
||||
},
|
||||
},
|
||||
// $FlowFixMe[prop-missing] size() methods are not allowed on byte streams.
|
||||
{highWaterMark: 0},
|
||||
);
|
||||
return stream;
|
||||
}
|
||||
|
||||
function createFakeWritableFromNodeReadable(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: string | Uint8Array) {
|
||||
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,
|
||||
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 = createFakeWritableFromNodeReadable(readable);
|
||||
resolve({prelude: readable});
|
||||
}
|
||||
|
||||
const request = createPrerenderRequest(
|
||||
model,
|
||||
webpackMap,
|
||||
onAllReady,
|
||||
onFatalError,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.temporaryReferences : undefined,
|
||||
__DEV__ && options ? options.environmentName : undefined,
|
||||
__DEV__ && options ? options.filterStackFrame : undefined,
|
||||
false,
|
||||
);
|
||||
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 prerender(
|
||||
model: ReactClientValue,
|
||||
webpackMap: ClientManifest,
|
||||
options?: Options & {
|
||||
signal?: AbortSignal,
|
||||
},
|
||||
): Promise<{
|
||||
prelude: ReadableStream,
|
||||
}> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const onFatalError = reject;
|
||||
function onAllReady() {
|
||||
let writable: Writable;
|
||||
const stream = new ReadableStream(
|
||||
{
|
||||
type: 'bytes',
|
||||
start: (controller): ?Promise<void> => {
|
||||
writable =
|
||||
createFakeWritableFromReadableStreamController(controller);
|
||||
},
|
||||
pull: (controller): ?Promise<void> => {
|
||||
startFlowing(request, writable);
|
||||
},
|
||||
cancel: (reason): ?Promise<void> => {
|
||||
stopFlowing(request);
|
||||
abort(request, reason);
|
||||
},
|
||||
},
|
||||
// $FlowFixMe[prop-missing] size() methods are not allowed on byte streams.
|
||||
{highWaterMark: 0},
|
||||
);
|
||||
resolve({prelude: stream});
|
||||
}
|
||||
const request = createPrerenderRequest(
|
||||
model,
|
||||
webpackMap,
|
||||
onAllReady,
|
||||
onFatalError,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.temporaryReferences : undefined,
|
||||
__DEV__ && options ? options.environmentName : undefined,
|
||||
__DEV__ && options ? options.filterStackFrame : undefined,
|
||||
false,
|
||||
);
|
||||
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;
|
||||
}
|
||||
|
||||
function decodeReplyFromAsyncIterable<T>(
|
||||
iterable: AsyncIterable<[string, string | File]>,
|
||||
webpackMap: ServerManifest,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
): Thenable<T> {
|
||||
const iterator: AsyncIterator<[string, string | File]> =
|
||||
iterable[ASYNC_ITERATOR]();
|
||||
|
||||
const response = createResponse(
|
||||
webpackMap,
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
);
|
||||
|
||||
function progress(
|
||||
entry:
|
||||
| {done: false, +value: [string, string | File], ...}
|
||||
| {done: true, +value: void, ...},
|
||||
) {
|
||||
if (entry.done) {
|
||||
close(response);
|
||||
} else {
|
||||
const [name, value] = entry.value;
|
||||
if (typeof value === 'string') {
|
||||
resolveField(response, name, value);
|
||||
} else {
|
||||
resolveFile(response, name, value);
|
||||
}
|
||||
iterator.next().then(progress, error);
|
||||
}
|
||||
}
|
||||
function error(reason: Error) {
|
||||
reportGlobalError(response, reason);
|
||||
if (typeof (iterator: any).throw === 'function') {
|
||||
// The iterator protocol doesn't necessarily include this but a generator do.
|
||||
// $FlowFixMe should be able to pass mixed
|
||||
iterator.throw(reason).then(error, error);
|
||||
}
|
||||
}
|
||||
|
||||
iterator.next().then(progress, error);
|
||||
|
||||
return getRoot(response);
|
||||
}
|
||||
|
||||
export {
|
||||
renderToReadableStream,
|
||||
renderToPipeableStream,
|
||||
prerender,
|
||||
prerenderToNodeStream,
|
||||
decodeReply,
|
||||
decodeReplyFromBusboy,
|
||||
decodeReplyFromAsyncIterable,
|
||||
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.',
|
||||
);
|
||||
@@ -10,4 +10,4 @@
|
||||
export {
|
||||
prerender,
|
||||
prerenderToNodeStream,
|
||||
} from './src/server/react-flight-dom-server.node.unbundled';
|
||||
} from './src/server/react-flight-dom-server.node';
|
||||
@@ -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",
|
||||
|
||||
@@ -60,10 +60,10 @@ 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'),
|
||||
);
|
||||
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');
|
||||
clientExports = WebpackMock.clientExports;
|
||||
|
||||
@@ -45,12 +45,12 @@ 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');
|
||||
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');
|
||||
|
||||
@@ -64,7 +64,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');
|
||||
|
||||
@@ -41,7 +41,7 @@ describe('ReactFlightAsyncDebugInfo', () => {
|
||||
|
||||
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');
|
||||
@@ -54,7 +54,7 @@ describe('ReactFlightAsyncDebugInfo', () => {
|
||||
__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');
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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 * from '../ReactFlightServerConfigDebugNode';
|
||||
|
||||
export * from '../ReactFlightStackConfigV8';
|
||||
export * from '../ReactServerConsoleConfigServer';
|
||||
@@ -476,25 +476,6 @@ const bundles = [
|
||||
'util',
|
||||
],
|
||||
},
|
||||
{
|
||||
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',
|
||||
'react-dom',
|
||||
'async_hooks',
|
||||
'crypto',
|
||||
'stream',
|
||||
'util',
|
||||
],
|
||||
},
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
@@ -529,17 +510,6 @@ const bundles = [
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'react-dom', 'util', 'crypto'],
|
||||
},
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
entry:
|
||||
'react-server-dom-webpack/src/client/react-flight-dom-client.node.unbundled',
|
||||
name: '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,
|
||||
@@ -786,6 +756,63 @@ const bundles = [
|
||||
externals: ['acorn'],
|
||||
},
|
||||
|
||||
/******* React Server DOM Unbundled Server *******/
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
entry: 'react-server-dom-unbundled/src/server/react-flight-dom-server.node',
|
||||
name: '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/src/client/react-flight-dom-client.node',
|
||||
name: 'react-server-dom-unbundled-client.node',
|
||||
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],
|
||||
|
||||
@@ -63,8 +63,8 @@ module.exports = [
|
||||
'react-dom/src/server/react-dom-server.node.js',
|
||||
'react-dom/test-utils',
|
||||
'react-dom/unstable_server-external-runtime',
|
||||
'react-server-dom-webpack/src/client/react-flight-dom-client.node.unbundled',
|
||||
'react-server-dom-webpack/src/server/react-flight-dom-server.node.unbundled',
|
||||
'react-server-dom-webpack/src/client/react-flight-dom-client.node',
|
||||
'react-server-dom-webpack/src/server/react-flight-dom-server.node',
|
||||
],
|
||||
paths: [
|
||||
'react-dom',
|
||||
@@ -84,20 +84,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/client/react-flight-dom-client.node.unbundled',
|
||||
'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',
|
||||
@@ -240,6 +233,49 @@ module.exports = [
|
||||
isFlowTyped: true,
|
||||
isServerSupported: true,
|
||||
},
|
||||
{
|
||||
shortName: 'dom-node-unbundled',
|
||||
entryPoints: [
|
||||
'react-server-dom-unbundled/src/client/react-flight-dom-client.node',
|
||||
'react-server-dom-unbundled/src/server/react-flight-dom-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