mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
Implement react-server-dom-parcel (#31725)
This adds a new `react-server-dom-parcel-package`, which is an RSC integration for the Parcel bundler. It is mostly copied from the existing webpack/turbopack integrations, with some changes to utilize Parcel runtime APIs for loading and executing bundles/modules. See https://github.com/parcel-bundler/parcel/pull/10043 for the Parcel side of this, which includes the plugin needed to generate client and server references. https://github.com/parcel-bundler/rsc-examples also includes examples of various ways to use RSCs with Parcel. Differences from other integrations: * Client and server modules are all part of the same graph, and we use Parcel's [environments](https://parceljs.org/plugin-system/transformer/#the-environment) to distinguish them. The server is the Parcel build entry point, and it imports and renders server components in route handlers. When a `"use client"` directive is seen, the environment changes and Parcel creates a new client bundle for the page, combining all client modules together. CSS from both client and server components are also combined automatically. * There is no separate manifest file that needs to be passed around by the user. A [Runtime](https://parceljs.org/plugin-system/runtime/) plugin injects client and server references as needed into the relevant bundles, and registers server action ids using `react-server-dom-parcel` automatically. * A special `<Resources>` component is also generated by Parcel to render the `<script>` and `<link rel="stylesheet">` elements needed for a page, using the relevant info from the bundle graph. Note: I've already published a 0.0.x version of this package to npm for testing purposes but happy to add whoever needs access to it as well. ### Questions * How to test this in the React repo. I'll have integration tests in Parcel, but setting up all the different mocks and environments to simulate that here seems challenging. I could try to copy how Webpack/Turbopack do it but it's a bit different. * Where to put TypeScript types. Right now I have some ambient types in my [example repo](https://github.com/parcel-bundler/rsc-examples/blob/main/types.d.ts) but it would be nice for users not to copy and paste these. Can I include them in the package or do they need to maintained separately in definitelytyped? I would really prefer not to have to maintain code in three different repos ideally. --------- Co-authored-by: Sebastian Markbage <sebastian@calyptus.eu>
This commit is contained in:
parent
a4964987dc
commit
ca587425fe
|
|
@ -330,6 +330,7 @@ module.exports = {
|
|||
'packages/react-server-dom-esm/**/*.js',
|
||||
'packages/react-server-dom-webpack/**/*.js',
|
||||
'packages/react-server-dom-turbopack/**/*.js',
|
||||
'packages/react-server-dom-parcel/**/*.js',
|
||||
'packages/react-server-dom-fb/**/*.js',
|
||||
'packages/react-test-renderer/**/*.js',
|
||||
'packages/react-debug-tools/**/*.js',
|
||||
|
|
@ -481,6 +482,12 @@ module.exports = {
|
|||
__turbopack_require__: 'readonly',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/react-server-dom-parcel/**/*.js'],
|
||||
globals: {
|
||||
parcelRequire: 'readonly',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/scheduler/**/*.js'],
|
||||
globals: {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ const stablePackages = {
|
|||
'react-dom': ReactVersion,
|
||||
'react-server-dom-webpack': ReactVersion,
|
||||
'react-server-dom-turbopack': ReactVersion,
|
||||
'react-server-dom-parcel': ReactVersion,
|
||||
'react-is': ReactVersion,
|
||||
'react-reconciler': '0.31.0',
|
||||
'react-refresh': '0.16.0',
|
||||
|
|
|
|||
5
fixtures/flight-parcel/.gitignore
vendored
Normal file
5
fixtures/flight-parcel/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.parcel-cache
|
||||
.DS_Store
|
||||
node_modules
|
||||
dist
|
||||
todos.json
|
||||
4
fixtures/flight-parcel/.parcelrc
Normal file
4
fixtures/flight-parcel/.parcelrc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"extends": "@parcel/config-default",
|
||||
"runtimes": ["...", "@parcel/runtime-rsc"]
|
||||
}
|
||||
51
fixtures/flight-parcel/package.json
Normal file
51
fixtures/flight-parcel/package.json
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"name": "flight-parcel",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"examples/*"
|
||||
],
|
||||
"server": "dist/server.js",
|
||||
"targets": {
|
||||
"server": {
|
||||
"source": "src/server.tsx",
|
||||
"context": "react-server",
|
||||
"outputFormat": "commonjs",
|
||||
"includeNodeModules": {
|
||||
"express": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"predev": "cp -r ../../build/oss-experimental/* ./node_modules/",
|
||||
"prebuild": "cp -r ../../build/oss-experimental/* ./node_modules/",
|
||||
"dev": "concurrently \"npm run dev:watch\" \"npm run dev:start\"",
|
||||
"dev:watch": "NODE_ENV=development parcel watch",
|
||||
"dev:start": "NODE_ENV=development node dist/server.js",
|
||||
"build": "parcel build",
|
||||
"start": "node dist/server.js"
|
||||
},
|
||||
"@parcel/resolver-default": {
|
||||
"packageExports": true
|
||||
},
|
||||
"dependencies": {
|
||||
"@parcel/config-default": "2.0.0-dev.1789",
|
||||
"@parcel/runtime-rsc": "2.13.3-dev.3412",
|
||||
"@types/parcel-env": "^0.0.6",
|
||||
"@types/express": "*",
|
||||
"@types/node": "^22.10.1",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"concurrently": "^7.3.0",
|
||||
"express": "^4.18.2",
|
||||
"parcel": "2.0.0-dev.1787",
|
||||
"process": "^0.11.10",
|
||||
"react": "experimental",
|
||||
"react-dom": "experimental",
|
||||
"react-server-dom-parcel": "experimental",
|
||||
"rsc-html-stream": "^0.0.4",
|
||||
"ws": "^8.8.1"
|
||||
},
|
||||
"@parcel/bundler-default": {
|
||||
"minBundleSize": 0
|
||||
}
|
||||
}
|
||||
21
fixtures/flight-parcel/src/Dialog.tsx
Normal file
21
fixtures/flight-parcel/src/Dialog.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
'use client';
|
||||
|
||||
import {ReactNode, useRef} from 'react';
|
||||
|
||||
export function Dialog({
|
||||
trigger,
|
||||
children,
|
||||
}: {
|
||||
trigger: ReactNode;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
let ref = useRef<HTMLDialogElement | null>(null);
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => ref.current?.showModal()}>{trigger}</button>
|
||||
<dialog ref={ref} onSubmit={() => ref.current?.close()}>
|
||||
{children}
|
||||
</dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
18
fixtures/flight-parcel/src/TodoCreate.tsx
Normal file
18
fixtures/flight-parcel/src/TodoCreate.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import {createTodo} from './actions';
|
||||
|
||||
export function TodoCreate() {
|
||||
return (
|
||||
<form action={createTodo}>
|
||||
<label>
|
||||
Title: <input name="title" />
|
||||
</label>
|
||||
<label>
|
||||
Description: <textarea name="description" />
|
||||
</label>
|
||||
<label>
|
||||
Due date: <input type="date" name="dueDate" />
|
||||
</label>
|
||||
<button>Add todo</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
25
fixtures/flight-parcel/src/TodoDetail.tsx
Normal file
25
fixtures/flight-parcel/src/TodoDetail.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import {getTodo, updateTodo} from './actions';
|
||||
|
||||
export async function TodoDetail({id}: {id: number}) {
|
||||
let todo = await getTodo(id);
|
||||
if (!todo) {
|
||||
return <p>Todo not found</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="todo" action={updateTodo.bind(null, todo.id)}>
|
||||
<label>
|
||||
Title: <input name="title" defaultValue={todo.title} />
|
||||
</label>
|
||||
<label>
|
||||
Description:{' '}
|
||||
<textarea name="description" defaultValue={todo.description} />
|
||||
</label>
|
||||
<label>
|
||||
Due date:{' '}
|
||||
<input type="date" name="dueDate" defaultValue={todo.dueDate} />
|
||||
</label>
|
||||
<button type="submit">Update todo</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
37
fixtures/flight-parcel/src/TodoItem.tsx
Normal file
37
fixtures/flight-parcel/src/TodoItem.tsx
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
'use client';
|
||||
|
||||
import {startTransition, useOptimistic} from 'react';
|
||||
import {deleteTodo, setTodoComplete, type Todo as ITodo} from './actions';
|
||||
|
||||
export function TodoItem({
|
||||
todo,
|
||||
isSelected,
|
||||
}: {
|
||||
todo: ITodo;
|
||||
isSelected: boolean;
|
||||
}) {
|
||||
let [isOptimisticComplete, setOptimisticComplete] = useOptimistic(
|
||||
todo.isComplete,
|
||||
);
|
||||
|
||||
return (
|
||||
<li data-selected={isSelected || undefined}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isOptimisticComplete}
|
||||
onChange={e => {
|
||||
startTransition(async () => {
|
||||
setOptimisticComplete(e.target.checked);
|
||||
await setTodoComplete(todo.id, e.target.checked);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<a
|
||||
href={`/todos/${todo.id}`}
|
||||
aria-current={isSelected ? 'page' : undefined}>
|
||||
{todo.title}
|
||||
</a>
|
||||
<button onClick={() => deleteTodo(todo.id)}>x</button>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
13
fixtures/flight-parcel/src/TodoList.tsx
Normal file
13
fixtures/flight-parcel/src/TodoList.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import {TodoItem} from './TodoItem';
|
||||
import {getTodos} from './actions';
|
||||
|
||||
export async function TodoList({id}: {id: number | undefined}) {
|
||||
let todos = await getTodos();
|
||||
return (
|
||||
<ul className="todo-list">
|
||||
{todos.map(todo => (
|
||||
<TodoItem key={todo.id} todo={todo} isSelected={todo.id === id} />
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
63
fixtures/flight-parcel/src/Todos.css
Normal file
63
fixtures/flight-parcel/src/Todos.css
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
body {
|
||||
font-family: system-ui;
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
form {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
flex-direction: column;
|
||||
max-width: 400px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.todo-column {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
max-width: 250px;
|
||||
padding: 8px;
|
||||
padding-right: 40px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.todo-list {
|
||||
max-width: 250px;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
padding-right: 32px;
|
||||
border-right: 1px solid gray;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
accent-color: light-dark(black, white);
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&[data-selected] {
|
||||
background-color: light-dark(#222, #ddd);
|
||||
color: light-dark(#ddd, #222);
|
||||
accent-color: light-dark(white, black);
|
||||
}
|
||||
}
|
||||
}
|
||||
35
fixtures/flight-parcel/src/Todos.tsx
Normal file
35
fixtures/flight-parcel/src/Todos.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
'use server-entry';
|
||||
|
||||
import './client';
|
||||
import './Todos.css';
|
||||
import {Resources} from '@parcel/runtime-rsc';
|
||||
import {Dialog} from './Dialog';
|
||||
import {TodoDetail} from './TodoDetail';
|
||||
import {TodoCreate} from './TodoCreate';
|
||||
import {TodoList} from './TodoList';
|
||||
|
||||
export async function Todos({id}: {id?: number}) {
|
||||
return (
|
||||
<html style={{colorScheme: 'dark light'}}>
|
||||
<head>
|
||||
<title>Todos</title>
|
||||
<Resources />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Todos</h1>
|
||||
<Dialog trigger="+">
|
||||
<h2>Add todo</h2>
|
||||
<TodoCreate />
|
||||
</Dialog>
|
||||
</header>
|
||||
<main>
|
||||
<div className="todo-column">
|
||||
<TodoList id={id} />
|
||||
</div>
|
||||
{id != null ? <TodoDetail key={id} id={id} /> : <p>Select a todo</p>}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
75
fixtures/flight-parcel/src/actions.ts
Normal file
75
fixtures/flight-parcel/src/actions.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
'use server';
|
||||
|
||||
import fs from 'fs/promises';
|
||||
|
||||
export interface Todo {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
dueDate: string;
|
||||
isComplete: boolean;
|
||||
}
|
||||
|
||||
export async function getTodos(): Promise<Todo[]> {
|
||||
try {
|
||||
let contents = await fs.readFile('todos.json', 'utf8');
|
||||
return JSON.parse(contents);
|
||||
} catch {
|
||||
await fs.writeFile('todos.json', '[]');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTodo(id: number): Promise<Todo | undefined> {
|
||||
let todos = await getTodos();
|
||||
return todos.find(todo => todo.id === id);
|
||||
}
|
||||
|
||||
export async function createTodo(formData: FormData) {
|
||||
let todos = await getTodos();
|
||||
let title = formData.get('title');
|
||||
let description = formData.get('description');
|
||||
let dueDate = formData.get('dueDate');
|
||||
let id = todos.length > 0 ? Math.max(...todos.map(todo => todo.id)) + 1 : 0;
|
||||
todos.push({
|
||||
id,
|
||||
title: typeof title === 'string' ? title : '',
|
||||
description: typeof description === 'string' ? description : '',
|
||||
dueDate: typeof dueDate === 'string' ? dueDate : new Date().toISOString(),
|
||||
isComplete: false,
|
||||
});
|
||||
await fs.writeFile('todos.json', JSON.stringify(todos));
|
||||
}
|
||||
|
||||
export async function updateTodo(id: number, formData: FormData) {
|
||||
let todos = await getTodos();
|
||||
let title = formData.get('title');
|
||||
let description = formData.get('description');
|
||||
let dueDate = formData.get('dueDate');
|
||||
let todo = todos.find(todo => todo.id === id);
|
||||
if (todo) {
|
||||
todo.title = typeof title === 'string' ? title : '';
|
||||
todo.description = typeof description === 'string' ? description : '';
|
||||
todo.dueDate =
|
||||
typeof dueDate === 'string' ? dueDate : new Date().toISOString();
|
||||
await fs.writeFile('todos.json', JSON.stringify(todos));
|
||||
}
|
||||
}
|
||||
|
||||
export async function setTodoComplete(id: number, isComplete: boolean) {
|
||||
let todos = await getTodos();
|
||||
let todo = todos.find(todo => todo.id === id);
|
||||
if (todo) {
|
||||
todo.isComplete = isComplete;
|
||||
await fs.writeFile('todos.json', JSON.stringify(todos));
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteTodo(id: number) {
|
||||
let todos = await getTodos();
|
||||
let index = todos.findIndex(todo => todo.id === id);
|
||||
if (index >= 0) {
|
||||
todos.splice(index, 1);
|
||||
await fs.writeFile('todos.json', JSON.stringify(todos));
|
||||
}
|
||||
}
|
||||
113
fixtures/flight-parcel/src/client.tsx
Normal file
113
fixtures/flight-parcel/src/client.tsx
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
'use client-entry';
|
||||
|
||||
import {
|
||||
useState,
|
||||
use,
|
||||
startTransition,
|
||||
useInsertionEffect,
|
||||
ReactElement,
|
||||
} from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import {
|
||||
createFromReadableStream,
|
||||
createFromFetch,
|
||||
encodeReply,
|
||||
setServerCallback,
|
||||
} from 'react-server-dom-parcel/client';
|
||||
import {rscStream} from 'rsc-html-stream/client';
|
||||
|
||||
// Stream in initial RSC payload embedded in the HTML.
|
||||
let initialRSCPayload = createFromReadableStream<ReactElement>(rscStream);
|
||||
let updateRoot:
|
||||
| ((root: ReactElement, cb?: (() => void) | null) => void)
|
||||
| null = null;
|
||||
|
||||
function Content() {
|
||||
// Store the current root element in state, along with a callback
|
||||
// to call once rendering is complete.
|
||||
let [[root, cb], setRoot] = useState<[ReactElement, (() => void) | null]>([
|
||||
use(initialRSCPayload),
|
||||
null,
|
||||
]);
|
||||
updateRoot = (root, cb) => setRoot([root, cb ?? null]);
|
||||
useInsertionEffect(() => cb?.());
|
||||
return root;
|
||||
}
|
||||
|
||||
// Hydrate initial page content.
|
||||
startTransition(() => {
|
||||
ReactDOM.hydrateRoot(document, <Content />);
|
||||
});
|
||||
|
||||
// A very simple router. When we navigate, we'll fetch a new RSC payload from the server,
|
||||
// and in a React transition, stream in the new page. Once complete, we'll pushState to
|
||||
// update the URL in the browser.
|
||||
async function navigate(pathname: string, push = false) {
|
||||
let res = fetch(pathname, {
|
||||
headers: {
|
||||
Accept: 'text/x-component',
|
||||
},
|
||||
});
|
||||
let root = await createFromFetch<ReactElement>(res);
|
||||
startTransition(() => {
|
||||
updateRoot!(root, () => {
|
||||
if (push) {
|
||||
history.pushState(null, '', pathname);
|
||||
push = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Intercept link clicks to perform RSC navigation.
|
||||
document.addEventListener('click', e => {
|
||||
let link = (e.target as Element).closest('a');
|
||||
if (
|
||||
link &&
|
||||
link instanceof HTMLAnchorElement &&
|
||||
link.href &&
|
||||
(!link.target || link.target === '_self') &&
|
||||
link.origin === location.origin &&
|
||||
!link.hasAttribute('download') &&
|
||||
e.button === 0 && // left clicks only
|
||||
!e.metaKey && // open in new tab (mac)
|
||||
!e.ctrlKey && // open in new tab (windows)
|
||||
!e.altKey && // download
|
||||
!e.shiftKey &&
|
||||
!e.defaultPrevented
|
||||
) {
|
||||
e.preventDefault();
|
||||
navigate(link.pathname, true);
|
||||
}
|
||||
});
|
||||
|
||||
// When the user clicks the back button, navigate with RSC.
|
||||
window.addEventListener('popstate', e => {
|
||||
navigate(location.pathname);
|
||||
});
|
||||
|
||||
// Intercept HMR window reloads, and do it with RSC instead.
|
||||
window.addEventListener('parcelhmrreload', e => {
|
||||
e.preventDefault();
|
||||
navigate(location.pathname);
|
||||
});
|
||||
|
||||
// Setup a callback to perform server actions.
|
||||
// This sends a POST request to the server, and updates the page with the response.
|
||||
setServerCallback(async function (id: string, args: any[]) {
|
||||
console.log('Handling server action', id, args);
|
||||
const response = fetch(location.pathname, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'text/x-component',
|
||||
'rsc-action-id': id,
|
||||
},
|
||||
body: await encodeReply(args),
|
||||
});
|
||||
const {result, root} = await createFromFetch<{
|
||||
root: JSX.Element;
|
||||
result: any;
|
||||
}>(response);
|
||||
startTransition(() => updateRoot!(root));
|
||||
return result;
|
||||
});
|
||||
137
fixtures/flight-parcel/src/server.tsx
Normal file
137
fixtures/flight-parcel/src/server.tsx
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// Server dependencies.
|
||||
import express, {
|
||||
type Request as ExpressRequest,
|
||||
type Response as ExpressResponse,
|
||||
} from 'express';
|
||||
import {Readable} from 'node:stream';
|
||||
import type {ReadableStream as NodeReadableStream} from 'stream/web';
|
||||
import {
|
||||
renderToReadableStream,
|
||||
loadServerAction,
|
||||
decodeReply,
|
||||
decodeAction,
|
||||
} from 'react-server-dom-parcel/server.edge';
|
||||
import {injectRSCPayload} from 'rsc-html-stream/server';
|
||||
|
||||
// Client dependencies, used for SSR.
|
||||
// These must run in the same environment as client components (e.g. same instance of React).
|
||||
import {createFromReadableStream} from 'react-server-dom-parcel/client' with {env: 'react-client'};
|
||||
import {renderToReadableStream as renderHTMLToReadableStream} from 'react-dom/server' with {env: 'react-client'};
|
||||
import ReactClient, {ReactElement} from 'react' with {env: 'react-client'};
|
||||
|
||||
// Page components. These must have "use server-entry" so they are treated as code splitting entry points.
|
||||
import {Todos} from './Todos';
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET,HEAD,POST');
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'rsc-action');
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(express.static('dist'));
|
||||
|
||||
app.get('/', async (req, res) => {
|
||||
await render(req, res, <Todos />);
|
||||
});
|
||||
|
||||
app.post('/', async (req, res) => {
|
||||
await handleAction(req, res, <Todos />);
|
||||
});
|
||||
|
||||
app.get('/todos/:id', async (req, res) => {
|
||||
await render(req, res, <Todos id={Number(req.params.id)} />);
|
||||
});
|
||||
|
||||
app.post('/todos/:id', async (req, res) => {
|
||||
await handleAction(req, res, <Todos id={Number(req.params.id)} />);
|
||||
});
|
||||
|
||||
async function render(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
component: ReactElement,
|
||||
actionResult?: any,
|
||||
) {
|
||||
// Render RSC payload.
|
||||
let root: any = component;
|
||||
if (actionResult) {
|
||||
root = {result: actionResult, root};
|
||||
}
|
||||
let stream = renderToReadableStream(root);
|
||||
if (req.accepts('text/html')) {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
|
||||
// Use client react to render the RSC payload to HTML.
|
||||
let [s1, s2] = stream.tee();
|
||||
let data = createFromReadableStream<ReactElement>(s1);
|
||||
function Content() {
|
||||
return ReactClient.use(data);
|
||||
}
|
||||
|
||||
let htmlStream = await renderHTMLToReadableStream(<Content />);
|
||||
let response = htmlStream.pipeThrough(injectRSCPayload(s2));
|
||||
Readable.fromWeb(response as NodeReadableStream).pipe(res);
|
||||
} else {
|
||||
res.set('Content-Type', 'text/x-component');
|
||||
Readable.fromWeb(stream as NodeReadableStream).pipe(res);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle server actions.
|
||||
async function handleAction(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
component: ReactElement,
|
||||
) {
|
||||
let id = req.get('rsc-action-id');
|
||||
let request = new Request('http://localhost' + req.url, {
|
||||
method: 'POST',
|
||||
headers: req.headers as any,
|
||||
body: Readable.toWeb(req) as ReadableStream,
|
||||
// @ts-ignore
|
||||
duplex: 'half',
|
||||
});
|
||||
|
||||
if (id) {
|
||||
let action = await loadServerAction(id);
|
||||
let body = req.is('multipart/form-data')
|
||||
? await request.formData()
|
||||
: await request.text();
|
||||
let args = await decodeReply<any[]>(body);
|
||||
let result = action.apply(null, args);
|
||||
try {
|
||||
// Wait for any mutations
|
||||
await result;
|
||||
} catch (x) {
|
||||
// We handle the error on the client
|
||||
}
|
||||
|
||||
await render(req, res, component, result);
|
||||
} else {
|
||||
// Form submitted by browser (progressive enhancement).
|
||||
let formData = await request.formData();
|
||||
let action = await decodeAction(formData);
|
||||
try {
|
||||
// Wait for any mutations
|
||||
await action();
|
||||
} catch (err) {
|
||||
// TODO render error page?
|
||||
}
|
||||
await render(req, res, component);
|
||||
}
|
||||
}
|
||||
|
||||
let server = app.listen(3001);
|
||||
console.log('Server listening on port 3001');
|
||||
|
||||
// Restart the server when it changes.
|
||||
if (module.hot) {
|
||||
module.hot.dispose(() => {
|
||||
server.close();
|
||||
});
|
||||
|
||||
module.hot.accept();
|
||||
}
|
||||
12
fixtures/flight-parcel/tsconfig.json
Normal file
12
fixtures/flight-parcel/tsconfig.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"jsx": "react-jsx",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"moduleResolution": "node",
|
||||
"module": "esnext",
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"target": "es2022"
|
||||
}
|
||||
}
|
||||
21
fixtures/flight-parcel/types.d.ts
vendored
Normal file
21
fixtures/flight-parcel/types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// TODO: move these into their respective packages.
|
||||
|
||||
declare module 'react-server-dom-parcel/client' {
|
||||
export function createFromFetch<T>(res: Promise<Response>): Promise<T>;
|
||||
export function createFromReadableStream<T>(stream: ReadableStream): Promise<T>;
|
||||
export function encodeReply(value: any): Promise<string | URLSearchParams | FormData>;
|
||||
|
||||
type CallServerCallback = <T>(id: string, args: any[]) => Promise<T>;
|
||||
export function setServerCallback(cb: CallServerCallback): void;
|
||||
}
|
||||
|
||||
declare module 'react-server-dom-parcel/server.edge' {
|
||||
export function renderToReadableStream(value: any): ReadableStream;
|
||||
export function loadServerAction(id: string): Promise<(...args: any[]) => any>;
|
||||
export function decodeReply<T>(body: string | FormData): Promise<T>;
|
||||
export function decodeAction(body: FormData): Promise<(...args: any[]) => any>;
|
||||
}
|
||||
|
||||
declare module '@parcel/runtime-rsc' {
|
||||
export function Resources(): JSX.Element;
|
||||
}
|
||||
2330
fixtures/flight-parcel/yarn.lock
Normal file
2330
fixtures/flight-parcel/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* 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 {default as rendererVersion} from 'shared/ReactVersion';
|
||||
export const rendererPackageName = 'react-server-dom-parcel';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
|
||||
export * from 'react-server-dom-parcel/src/shared/ReactFlightClientConfigBundlerParcel';
|
||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||
export const usedWithSSR = false;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* 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 {default as rendererVersion} from 'shared/ReactVersion';
|
||||
export const rendererPackageName = 'react-server-dom-parcel';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||
export * from 'react-server-dom-parcel/src/shared/ReactFlightClientConfigBundlerParcel';
|
||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||
export const usedWithSSR = true;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* 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 {default as rendererVersion} from 'shared/ReactVersion';
|
||||
export const rendererPackageName = 'react-server-dom-parcel';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||
export * from 'react-server-dom-parcel/src/shared/ReactFlightClientConfigBundlerParcel';
|
||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||
export const usedWithSSR = true;
|
||||
5
packages/react-server-dom-parcel/README.md
Normal file
5
packages/react-server-dom-parcel/README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# react-server-dom-parcel
|
||||
|
||||
Experimental React Flight bindings for DOM using Parcel.
|
||||
|
||||
**Use it at your own risk.**
|
||||
10
packages/react-server-dom-parcel/client.browser.js
Normal file
10
packages/react-server-dom-parcel/client.browser.js
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
|
||||
*/
|
||||
|
||||
export * from './src/client/ReactFlightDOMClientBrowser';
|
||||
10
packages/react-server-dom-parcel/client.edge.js
Normal file
10
packages/react-server-dom-parcel/client.edge.js
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
|
||||
*/
|
||||
|
||||
export * from './src/client/ReactFlightDOMClientEdge';
|
||||
10
packages/react-server-dom-parcel/client.js
vendored
Normal file
10
packages/react-server-dom-parcel/client.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
|
||||
*/
|
||||
|
||||
export * from './client.browser';
|
||||
10
packages/react-server-dom-parcel/client.node.js
Normal file
10
packages/react-server-dom-parcel/client.node.js
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
|
||||
*/
|
||||
|
||||
export * from './src/client/ReactFlightDOMClientNode';
|
||||
10
packages/react-server-dom-parcel/index.js
vendored
Normal file
10
packages/react-server-dom-parcel/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-parcel/client instead.');
|
||||
7
packages/react-server-dom-parcel/npm/client.browser.js
Normal file
7
packages/react-server-dom-parcel/npm/client.browser.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-server-dom-parcel-client.browser.production.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-server-dom-parcel-client.browser.development.js');
|
||||
}
|
||||
7
packages/react-server-dom-parcel/npm/client.edge.js
Normal file
7
packages/react-server-dom-parcel/npm/client.edge.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-server-dom-parcel-client.edge.production.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-server-dom-parcel-client.edge.development.js');
|
||||
}
|
||||
3
packages/react-server-dom-parcel/npm/client.js
vendored
Normal file
3
packages/react-server-dom-parcel/npm/client.js
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = require('./client.browser');
|
||||
7
packages/react-server-dom-parcel/npm/client.node.js
Normal file
7
packages/react-server-dom-parcel/npm/client.node.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-server-dom-parcel-client.node.production.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-server-dom-parcel-client.node.development.js');
|
||||
}
|
||||
12
packages/react-server-dom-parcel/npm/index.js
vendored
Normal file
12
packages/react-server-dom-parcel/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-parcel/client instead.');
|
||||
7
packages/react-server-dom-parcel/npm/server.browser.js
Normal file
7
packages/react-server-dom-parcel/npm/server.browser.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-server-dom-parcel-server.browser.production.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-server-dom-parcel-server.browser.development.js');
|
||||
}
|
||||
7
packages/react-server-dom-parcel/npm/server.edge.js
Normal file
7
packages/react-server-dom-parcel/npm/server.edge.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-server-dom-parcel-server.edge.production.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-server-dom-parcel-server.edge.development.js');
|
||||
}
|
||||
6
packages/react-server-dom-parcel/npm/server.js
vendored
Normal file
6
packages/react-server-dom-parcel/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.'
|
||||
);
|
||||
7
packages/react-server-dom-parcel/npm/server.node.js
Normal file
7
packages/react-server-dom-parcel/npm/server.node.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-server-dom-parcel-server.node.production.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-server-dom-parcel-server.node.development.js');
|
||||
}
|
||||
86
packages/react-server-dom-parcel/package.json
Normal file
86
packages/react-server-dom-parcel/package.json
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
"name": "react-server-dom-parcel",
|
||||
"description": "React Server Components bindings for DOM using Parcel. This is intended to be integrated into meta-frameworks. It is not intended to be imported directly.",
|
||||
"version": "19.0.0",
|
||||
"private": true,
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
"homepage": "https://reactjs.org/",
|
||||
"bugs": "https://github.com/facebook/react/issues",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"index.js",
|
||||
"client.js",
|
||||
"client.browser.js",
|
||||
"client.edge.js",
|
||||
"client.node.js",
|
||||
"server.js",
|
||||
"server.browser.js",
|
||||
"server.edge.js",
|
||||
"server.node.js",
|
||||
"static.js",
|
||||
"static.browser.js",
|
||||
"static.edge.js",
|
||||
"static.node.js",
|
||||
"cjs/"
|
||||
],
|
||||
"exports": {
|
||||
".": "./index.js",
|
||||
"./client": {
|
||||
"workerd": "./client.edge.js",
|
||||
"deno": "./client.edge.js",
|
||||
"worker": "./client.edge.js",
|
||||
"node": "./client.node.js",
|
||||
"edge-light": "./client.edge.js",
|
||||
"browser": "./client.browser.js",
|
||||
"default": "./client.browser.js"
|
||||
},
|
||||
"./client.browser": "./client.browser.js",
|
||||
"./client.edge": "./client.edge.js",
|
||||
"./client.node": "./client.node.js",
|
||||
"./server": {
|
||||
"react-server": {
|
||||
"workerd": "./server.edge.js",
|
||||
"deno": "./server.browser.js",
|
||||
"node": "./server.node.js",
|
||||
"edge-light": "./server.edge.js",
|
||||
"browser": "./server.browser.js"
|
||||
},
|
||||
"default": "./server.js"
|
||||
},
|
||||
"./server.browser": "./server.browser.js",
|
||||
"./server.edge": "./server.edge.js",
|
||||
"./server.node": "./server.node.js",
|
||||
"./static": {
|
||||
"react-server": {
|
||||
"workerd": "./static.edge.js",
|
||||
"deno": "./static.browser.js",
|
||||
"node": "./static.node.js",
|
||||
"edge-light": "./static.edge.js",
|
||||
"browser": "./static.browser.js"
|
||||
},
|
||||
"default": "./static.js"
|
||||
},
|
||||
"./static.browser": "./static.browser.js",
|
||||
"./static.edge": "./static.edge.js",
|
||||
"./static.node": "./static.node.js",
|
||||
"./src/*": "./src/*.js",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/facebook/react.git",
|
||||
"directory": "packages/react-server-dom-parcel"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
}
|
||||
}
|
||||
20
packages/react-server-dom-parcel/server.browser.js
Normal file
20
packages/react-server-dom-parcel/server.browser.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* 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 {
|
||||
renderToReadableStream,
|
||||
decodeReply,
|
||||
decodeAction,
|
||||
decodeFormState,
|
||||
createClientReference,
|
||||
registerServerReference,
|
||||
createTemporaryReferenceSet,
|
||||
registerServerActions,
|
||||
loadServerAction,
|
||||
} from './src/server/react-flight-dom-server.browser';
|
||||
20
packages/react-server-dom-parcel/server.edge.js
Normal file
20
packages/react-server-dom-parcel/server.edge.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* 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 {
|
||||
renderToReadableStream,
|
||||
decodeReply,
|
||||
decodeAction,
|
||||
decodeFormState,
|
||||
createClientReference,
|
||||
registerServerReference,
|
||||
createTemporaryReferenceSet,
|
||||
registerServerActions,
|
||||
loadServerAction,
|
||||
} from './src/server/react-flight-dom-server.edge';
|
||||
13
packages/react-server-dom-parcel/server.js
vendored
Normal file
13
packages/react-server-dom-parcel/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.',
|
||||
);
|
||||
21
packages/react-server-dom-parcel/server.node.js
Normal file
21
packages/react-server-dom-parcel/server.node.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* 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 {
|
||||
renderToPipeableStream,
|
||||
decodeReplyFromBusboy,
|
||||
decodeReply,
|
||||
decodeAction,
|
||||
decodeFormState,
|
||||
createClientReference,
|
||||
registerServerReference,
|
||||
createTemporaryReferenceSet,
|
||||
registerServerActions,
|
||||
loadServerAction,
|
||||
} from './src/server/react-flight-dom-server.node';
|
||||
126
packages/react-server-dom-parcel/src/ReactFlightParcelReferences.js
vendored
Normal file
126
packages/react-server-dom-parcel/src/ReactFlightParcelReferences.js
vendored
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
* 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,
|
||||
$$name: string,
|
||||
$$bundles: Array<string>,
|
||||
};
|
||||
|
||||
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 createClientReference<T>(
|
||||
id: string,
|
||||
exportName: string,
|
||||
bundles: Array<string>,
|
||||
): ClientReference<T> {
|
||||
return {
|
||||
$$typeof: CLIENT_REFERENCE_TAG,
|
||||
$$id: id,
|
||||
$$name: exportName,
|
||||
$$bundles: bundles,
|
||||
};
|
||||
}
|
||||
|
||||
// $FlowFixMe[method-unbinding]
|
||||
const FunctionBind = Function.prototype.bind;
|
||||
// $FlowFixMe[method-unbinding]
|
||||
const ArraySlice = Array.prototype.slice;
|
||||
function bind(this: ServerReference<any>): any {
|
||||
// $FlowFixMe[prop-missing]
|
||||
const newFn = FunctionBind.apply(this, arguments);
|
||||
if (this.$$typeof === SERVER_REFERENCE_TAG) {
|
||||
if (__DEV__) {
|
||||
const thisBind = arguments[0];
|
||||
if (thisBind != null) {
|
||||
console.error(
|
||||
'Cannot bind "this" of a Server Action. Pass null or undefined as the first argument to .bind().',
|
||||
);
|
||||
}
|
||||
}
|
||||
const args = ArraySlice.call(arguments, 1);
|
||||
const $$typeof = {value: SERVER_REFERENCE_TAG};
|
||||
const $$id = {value: this.$$id};
|
||||
const $$bound = {value: this.$$bound ? this.$$bound.concat(args) : args};
|
||||
return Object.defineProperties(
|
||||
(newFn: any),
|
||||
__DEV__
|
||||
? {
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
$$location: {
|
||||
value: this.$$location,
|
||||
configurable: true,
|
||||
},
|
||||
bind: {value: bind, configurable: true},
|
||||
}
|
||||
: {
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
bind: {value: bind, configurable: true},
|
||||
},
|
||||
);
|
||||
}
|
||||
return newFn;
|
||||
}
|
||||
|
||||
export function registerServerReference<T>(
|
||||
reference: ServerReference<T>,
|
||||
id: string,
|
||||
exportName: string,
|
||||
): ServerReference<T> {
|
||||
const $$typeof = {value: SERVER_REFERENCE_TAG};
|
||||
const $$id = {
|
||||
value: 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},
|
||||
}
|
||||
: {
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
bind: {value: bind, configurable: true},
|
||||
},
|
||||
);
|
||||
}
|
||||
179
packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js
vendored
Normal file
179
packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js
vendored
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/**
|
||||
* 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} from 'shared/ReactTypes.js';
|
||||
import type {Response as FlightResponse} from 'react-client/src/ReactFlightClient';
|
||||
import type {ReactServerValue} from 'react-client/src/ReactFlightReplyClient';
|
||||
import type {ServerReferenceId} from '../shared/ReactFlightClientConfigBundlerParcel';
|
||||
|
||||
import {
|
||||
createResponse,
|
||||
getRoot,
|
||||
reportGlobalError,
|
||||
processBinaryChunk,
|
||||
close,
|
||||
} from 'react-client/src/ReactFlightClient';
|
||||
|
||||
import {
|
||||
processReply,
|
||||
createServerReference as createServerReferenceImpl,
|
||||
} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
|
||||
export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
export type {TemporaryReferenceSet};
|
||||
|
||||
type CallServerCallback = <A, T>(id: string, args: A) => Promise<T>;
|
||||
|
||||
let callServer: CallServerCallback | null = null;
|
||||
export function setServerCallback(fn: CallServerCallback) {
|
||||
callServer = fn;
|
||||
}
|
||||
|
||||
function callCurrentServerCallback<A, T>(
|
||||
id: ServerReferenceId,
|
||||
args: A,
|
||||
): Promise<T> {
|
||||
if (!callServer) {
|
||||
throw new Error(
|
||||
'No server callback has been registered. Call setServerCallback to register one.',
|
||||
);
|
||||
}
|
||||
return callServer(id, args);
|
||||
}
|
||||
|
||||
export function createServerReference<A: Iterable<any>, T>(
|
||||
id: string,
|
||||
exportName: string,
|
||||
): (...A) => Promise<T> {
|
||||
return createServerReferenceImpl(
|
||||
id + '#' + exportName,
|
||||
callCurrentServerCallback,
|
||||
);
|
||||
}
|
||||
|
||||
function startReadingFromStream(
|
||||
response: FlightResponse,
|
||||
stream: ReadableStream,
|
||||
): void {
|
||||
const reader = stream.getReader();
|
||||
function progress({
|
||||
done,
|
||||
value,
|
||||
}: {
|
||||
done: boolean,
|
||||
value: ?any,
|
||||
...
|
||||
}): void | Promise<void> {
|
||||
if (done) {
|
||||
close(response);
|
||||
return;
|
||||
}
|
||||
const buffer: Uint8Array = (value: any);
|
||||
processBinaryChunk(response, buffer);
|
||||
return reader.read().then(progress).catch(error);
|
||||
}
|
||||
function error(e: any) {
|
||||
reportGlobalError(response, e);
|
||||
}
|
||||
reader.read().then(progress).catch(error);
|
||||
}
|
||||
|
||||
export type Options = {
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
replayConsoleLogs?: boolean,
|
||||
environmentName?: string,
|
||||
};
|
||||
|
||||
export function createFromReadableStream<T>(
|
||||
stream: ReadableStream,
|
||||
options?: Options,
|
||||
): Thenable<T> {
|
||||
const response: FlightResponse = createResponse(
|
||||
null, // bundlerConfig
|
||||
null, // serverReferenceConfig
|
||||
null, // moduleLoading
|
||||
callCurrentServerCallback,
|
||||
undefined, // encodeFormAction
|
||||
undefined, // nonce
|
||||
options && options.temporaryReferences
|
||||
? options.temporaryReferences
|
||||
: undefined,
|
||||
undefined, // TODO: findSourceMapUrl
|
||||
__DEV__ ? (options ? options.replayConsoleLogs !== false : true) : false, // defaults to true
|
||||
__DEV__ && options && options.environmentName
|
||||
? options.environmentName
|
||||
: undefined,
|
||||
);
|
||||
startReadingFromStream(response, stream);
|
||||
return getRoot(response);
|
||||
}
|
||||
|
||||
export function createFromFetch<T>(
|
||||
promiseForResponse: Promise<Response>,
|
||||
options?: Options,
|
||||
): Thenable<T> {
|
||||
const response: FlightResponse = createResponse(
|
||||
null, // bundlerConfig
|
||||
null, // serverReferenceConfig
|
||||
null, // moduleLoading
|
||||
callCurrentServerCallback,
|
||||
undefined, // encodeFormAction
|
||||
undefined, // nonce
|
||||
options && options.temporaryReferences
|
||||
? options.temporaryReferences
|
||||
: undefined,
|
||||
undefined, // TODO: findSourceMapUrl
|
||||
__DEV__ ? (options ? options.replayConsoleLogs !== false : true) : false, // defaults to true
|
||||
__DEV__ && options && options.environmentName
|
||||
? options.environmentName
|
||||
: undefined,
|
||||
);
|
||||
promiseForResponse.then(
|
||||
function (r) {
|
||||
startReadingFromStream(response, (r.body: any));
|
||||
},
|
||||
function (e) {
|
||||
reportGlobalError(response, e);
|
||||
},
|
||||
);
|
||||
return getRoot(response);
|
||||
}
|
||||
|
||||
export 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,
|
||||
'', // formFieldPrefix
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
161
packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js
vendored
Normal file
161
packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js
vendored
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {Thenable, ReactCustomFormAction} from 'shared/ReactTypes.js';
|
||||
|
||||
import type {Response as FlightResponse} from 'react-client/src/ReactFlightClient';
|
||||
import type {ReactServerValue} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
import {
|
||||
createResponse,
|
||||
getRoot,
|
||||
reportGlobalError,
|
||||
processBinaryChunk,
|
||||
close,
|
||||
} from 'react-client/src/ReactFlightClient';
|
||||
|
||||
import {
|
||||
processReply,
|
||||
createServerReference as createServerReferenceImpl,
|
||||
} 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: string,
|
||||
exportName: string,
|
||||
): (...A) => Promise<T> {
|
||||
return createServerReferenceImpl(id + '#' + exportName, noServerCall);
|
||||
}
|
||||
|
||||
type EncodeFormActionCallback = <A>(
|
||||
id: any,
|
||||
args: Promise<A>,
|
||||
) => ReactCustomFormAction;
|
||||
|
||||
export type Options = {
|
||||
nonce?: string,
|
||||
encodeFormAction?: EncodeFormActionCallback,
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
replayConsoleLogs?: boolean,
|
||||
environmentName?: string,
|
||||
};
|
||||
|
||||
function createResponseFromOptions(options?: Options) {
|
||||
return createResponse(
|
||||
null, // bundlerConfig
|
||||
null, // serverReferenceConfig
|
||||
null, // moduleLoading
|
||||
noServerCall,
|
||||
options ? options.encodeFormAction : undefined,
|
||||
options && typeof options.nonce === 'string' ? options.nonce : undefined,
|
||||
options && options.temporaryReferences
|
||||
? options.temporaryReferences
|
||||
: undefined,
|
||||
undefined, // TODO: findSourceMapUrl
|
||||
__DEV__ && options ? options.replayConsoleLogs === true : false, // defaults to false
|
||||
__DEV__ && options && options.environmentName
|
||||
? options.environmentName
|
||||
: undefined,
|
||||
);
|
||||
}
|
||||
|
||||
function startReadingFromStream(
|
||||
response: FlightResponse,
|
||||
stream: ReadableStream,
|
||||
): void {
|
||||
const reader = stream.getReader();
|
||||
function progress({
|
||||
done,
|
||||
value,
|
||||
}: {
|
||||
done: boolean,
|
||||
value: ?any,
|
||||
...
|
||||
}): void | Promise<void> {
|
||||
if (done) {
|
||||
close(response);
|
||||
return;
|
||||
}
|
||||
const buffer: Uint8Array = (value: any);
|
||||
processBinaryChunk(response, buffer);
|
||||
return reader.read().then(progress).catch(error);
|
||||
}
|
||||
function error(e: any) {
|
||||
reportGlobalError(response, e);
|
||||
}
|
||||
reader.read().then(progress).catch(error);
|
||||
}
|
||||
|
||||
export function createFromReadableStream<T>(
|
||||
stream: ReadableStream,
|
||||
options?: Options,
|
||||
): Thenable<T> {
|
||||
const response: FlightResponse = createResponseFromOptions(options);
|
||||
startReadingFromStream(response, stream);
|
||||
return getRoot(response);
|
||||
}
|
||||
|
||||
export function createFromFetch<T>(
|
||||
promiseForResponse: Promise<Response>,
|
||||
options?: Options,
|
||||
): Thenable<T> {
|
||||
const response: FlightResponse = createResponseFromOptions(options);
|
||||
promiseForResponse.then(
|
||||
function (r) {
|
||||
startReadingFromStream(response, (r.body: any));
|
||||
},
|
||||
function (e) {
|
||||
reportGlobalError(response, e);
|
||||
},
|
||||
);
|
||||
return getRoot(response);
|
||||
}
|
||||
|
||||
export 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
77
packages/react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js
vendored
Normal file
77
packages/react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {Thenable, ReactCustomFormAction} from 'shared/ReactTypes.js';
|
||||
import type {Response} from 'react-client/src/ReactFlightClient';
|
||||
import type {Readable} from 'stream';
|
||||
|
||||
import {
|
||||
createResponse,
|
||||
getRoot,
|
||||
reportGlobalError,
|
||||
processBinaryChunk,
|
||||
close,
|
||||
} from 'react-client/src/ReactFlightClient';
|
||||
|
||||
import {createServerReference as createServerReferenceImpl} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
function noServerCall() {
|
||||
throw new Error(
|
||||
'Server Functions cannot be called during initial render. ' +
|
||||
'This would create a fetch waterfall. Try to use a Server Component ' +
|
||||
'to pass data to Client Components instead.',
|
||||
);
|
||||
}
|
||||
|
||||
export function createServerReference<A: Iterable<any>, T>(
|
||||
id: string,
|
||||
exportName: string,
|
||||
): (...A) => Promise<T> {
|
||||
return createServerReferenceImpl(id + '#' + exportName, noServerCall);
|
||||
}
|
||||
|
||||
type EncodeFormActionCallback = <A>(
|
||||
id: any,
|
||||
args: Promise<A>,
|
||||
) => ReactCustomFormAction;
|
||||
|
||||
export type Options = {
|
||||
nonce?: string,
|
||||
encodeFormAction?: EncodeFormActionCallback,
|
||||
replayConsoleLogs?: boolean,
|
||||
environmentName?: string,
|
||||
};
|
||||
|
||||
export function createFromNodeStream<T>(
|
||||
stream: Readable,
|
||||
options?: Options,
|
||||
): Thenable<T> {
|
||||
const response: Response = createResponse(
|
||||
null, // bundlerConfig
|
||||
null, // serverReferenceConfig
|
||||
null, // moduleLoading
|
||||
noServerCall,
|
||||
options ? options.encodeFormAction : undefined,
|
||||
options && typeof options.nonce === 'string' ? options.nonce : undefined,
|
||||
undefined, // TODO: If encodeReply is supported, this should support temporaryReferences
|
||||
undefined, // TODO: findSourceMapUrl
|
||||
__DEV__ && options ? options.replayConsoleLogs === true : false, // defaults to false
|
||||
__DEV__ && options && options.environmentName
|
||||
? options.environmentName
|
||||
: undefined,
|
||||
);
|
||||
stream.on('data', chunk => {
|
||||
processBinaryChunk(response, chunk);
|
||||
});
|
||||
stream.on('error', error => {
|
||||
reportGlobalError(response, error);
|
||||
});
|
||||
stream.on('end', () => close(response));
|
||||
return getRoot(response);
|
||||
}
|
||||
214
packages/react-server-dom-parcel/src/server/ReactFlightDOMServerBrowser.js
vendored
Normal file
214
packages/react-server-dom-parcel/src/server/ReactFlightDOMServerBrowser.js
vendored
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/**
|
||||
* 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 {ReactFormState, Thenable} from 'shared/ReactTypes';
|
||||
import {
|
||||
preloadModule,
|
||||
requireModule,
|
||||
resolveServerReference,
|
||||
type ServerManifest,
|
||||
type ServerReferenceId,
|
||||
} from '../shared/ReactFlightClientConfigBundlerParcel';
|
||||
|
||||
import {
|
||||
createRequest,
|
||||
createPrerenderRequest,
|
||||
startWork,
|
||||
startFlowing,
|
||||
stopFlowing,
|
||||
abort,
|
||||
} from 'react-server/src/ReactFlightServer';
|
||||
|
||||
import {
|
||||
createResponse,
|
||||
close,
|
||||
getRoot,
|
||||
} from 'react-server/src/ReactFlightReplyServer';
|
||||
|
||||
import {
|
||||
decodeAction as decodeActionImpl,
|
||||
decodeFormState as decodeFormStateImpl,
|
||||
} from 'react-server/src/ReactFlightActionServer';
|
||||
|
||||
export {
|
||||
createClientReference,
|
||||
registerServerReference,
|
||||
} from '../ReactFlightParcelReferences';
|
||||
|
||||
import type {TemporaryReferenceSet} from 'react-server/src/ReactFlightServerTemporaryReferences';
|
||||
|
||||
export {createTemporaryReferenceSet} from 'react-server/src/ReactFlightServerTemporaryReferences';
|
||||
export type {TemporaryReferenceSet};
|
||||
|
||||
type Options = {
|
||||
environmentName?: string | (() => string),
|
||||
filterStackFrame?: (url: string, functionName: string) => boolean,
|
||||
identifierPrefix?: string,
|
||||
signal?: AbortSignal,
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
onError?: (error: mixed) => void,
|
||||
onPostpone?: (reason: string) => void,
|
||||
};
|
||||
|
||||
export function renderToReadableStream(
|
||||
model: ReactClientValue,
|
||||
options?: Options,
|
||||
): ReadableStream {
|
||||
const request = createRequest(
|
||||
model,
|
||||
null,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
options ? options.temporaryReferences : undefined,
|
||||
__DEV__ && options ? options.environmentName : undefined,
|
||||
__DEV__ && options ? options.filterStackFrame : undefined,
|
||||
);
|
||||
if (options && options.signal) {
|
||||
const signal = options.signal;
|
||||
if (signal.aborted) {
|
||||
abort(request, (signal: any).reason);
|
||||
} else {
|
||||
const listener = () => {
|
||||
abort(request, (signal: any).reason);
|
||||
signal.removeEventListener('abort', listener);
|
||||
};
|
||||
signal.addEventListener('abort', listener);
|
||||
}
|
||||
}
|
||||
const stream = new ReadableStream(
|
||||
{
|
||||
type: 'bytes',
|
||||
start: (controller): ?Promise<void> => {
|
||||
startWork(request);
|
||||
},
|
||||
pull: (controller): ?Promise<void> => {
|
||||
startFlowing(request, controller);
|
||||
},
|
||||
cancel: (reason): ?Promise<void> => {
|
||||
stopFlowing(request);
|
||||
abort(request, reason);
|
||||
},
|
||||
},
|
||||
// $FlowFixMe[prop-missing] size() methods are not allowed on byte streams.
|
||||
{highWaterMark: 0},
|
||||
);
|
||||
return stream;
|
||||
}
|
||||
|
||||
type StaticResult = {
|
||||
prelude: ReadableStream,
|
||||
};
|
||||
|
||||
export function prerender(
|
||||
model: ReactClientValue,
|
||||
options?: Options,
|
||||
): Promise<StaticResult> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const onFatalError = reject;
|
||||
function onAllReady() {
|
||||
const stream = new ReadableStream(
|
||||
{
|
||||
type: 'bytes',
|
||||
start: (controller): ?Promise<void> => {
|
||||
startWork(request);
|
||||
},
|
||||
pull: (controller): ?Promise<void> => {
|
||||
startFlowing(request, controller);
|
||||
},
|
||||
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,
|
||||
null,
|
||||
onAllReady,
|
||||
onFatalError,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
options ? options.temporaryReferences : undefined,
|
||||
__DEV__ && options ? options.environmentName : undefined,
|
||||
__DEV__ && options ? options.filterStackFrame : undefined,
|
||||
);
|
||||
if (options && options.signal) {
|
||||
const signal = options.signal;
|
||||
if (signal.aborted) {
|
||||
const reason = (signal: any).reason;
|
||||
abort(request, reason);
|
||||
} else {
|
||||
const listener = () => {
|
||||
const reason = (signal: any).reason;
|
||||
abort(request, reason);
|
||||
signal.removeEventListener('abort', listener);
|
||||
};
|
||||
signal.addEventListener('abort', listener);
|
||||
}
|
||||
}
|
||||
startWork(request);
|
||||
});
|
||||
}
|
||||
|
||||
let serverManifest = {};
|
||||
export function registerServerActions(manifest: ServerManifest) {
|
||||
// This function is called by the bundler to register the manifest.
|
||||
serverManifest = manifest;
|
||||
}
|
||||
|
||||
export function decodeReply<T>(
|
||||
body: string | FormData,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
): Thenable<T> {
|
||||
if (typeof body === 'string') {
|
||||
const form = new FormData();
|
||||
form.append('0', body);
|
||||
body = form;
|
||||
}
|
||||
const response = createResponse(
|
||||
serverManifest,
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
body,
|
||||
);
|
||||
const root = getRoot<T>(response);
|
||||
close(response);
|
||||
return root;
|
||||
}
|
||||
|
||||
export function decodeAction<T>(body: FormData): Promise<() => T> | null {
|
||||
return decodeActionImpl(body, serverManifest);
|
||||
}
|
||||
|
||||
export function decodeFormState<S>(
|
||||
actionResult: S,
|
||||
body: FormData,
|
||||
): Promise<ReactFormState<S, ServerReferenceId> | null> {
|
||||
return decodeFormStateImpl(actionResult, body, serverManifest);
|
||||
}
|
||||
|
||||
export function loadServerAction<F: (...any[]) => any>(id: string): Promise<F> {
|
||||
const reference = resolveServerReference<any>(serverManifest, id);
|
||||
return Promise.resolve(reference)
|
||||
.then(() => preloadModule(reference))
|
||||
.then(() => {
|
||||
const fn = requireModule(reference);
|
||||
if (typeof fn !== 'function') {
|
||||
throw new Error('Server actions must be functions');
|
||||
}
|
||||
return fn;
|
||||
});
|
||||
}
|
||||
214
packages/react-server-dom-parcel/src/server/ReactFlightDOMServerEdge.js
vendored
Normal file
214
packages/react-server-dom-parcel/src/server/ReactFlightDOMServerEdge.js
vendored
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/**
|
||||
* 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 {ReactFormState, Thenable} from 'shared/ReactTypes';
|
||||
import {
|
||||
preloadModule,
|
||||
requireModule,
|
||||
resolveServerReference,
|
||||
type ServerManifest,
|
||||
type ServerReferenceId,
|
||||
} from '../shared/ReactFlightClientConfigBundlerParcel';
|
||||
|
||||
import {
|
||||
createRequest,
|
||||
createPrerenderRequest,
|
||||
startWork,
|
||||
startFlowing,
|
||||
stopFlowing,
|
||||
abort,
|
||||
} from 'react-server/src/ReactFlightServer';
|
||||
|
||||
import {
|
||||
createResponse,
|
||||
close,
|
||||
getRoot,
|
||||
} from 'react-server/src/ReactFlightReplyServer';
|
||||
|
||||
import {
|
||||
decodeAction as decodeActionImpl,
|
||||
decodeFormState as decodeFormStateImpl,
|
||||
} from 'react-server/src/ReactFlightActionServer';
|
||||
|
||||
export {
|
||||
createClientReference,
|
||||
registerServerReference,
|
||||
} from '../ReactFlightParcelReferences';
|
||||
|
||||
import type {TemporaryReferenceSet} from 'react-server/src/ReactFlightServerTemporaryReferences';
|
||||
|
||||
export {createTemporaryReferenceSet} from 'react-server/src/ReactFlightServerTemporaryReferences';
|
||||
export type {TemporaryReferenceSet};
|
||||
|
||||
type Options = {
|
||||
environmentName?: string | (() => string),
|
||||
filterStackFrame?: (url: string, functionName: string) => boolean,
|
||||
identifierPrefix?: string,
|
||||
signal?: AbortSignal,
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
onError?: (error: mixed) => void,
|
||||
onPostpone?: (reason: string) => void,
|
||||
};
|
||||
|
||||
export function renderToReadableStream(
|
||||
model: ReactClientValue,
|
||||
options?: Options,
|
||||
): ReadableStream {
|
||||
const request = createRequest(
|
||||
model,
|
||||
null,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
options ? options.temporaryReferences : undefined,
|
||||
__DEV__ && options ? options.environmentName : undefined,
|
||||
__DEV__ && options ? options.filterStackFrame : undefined,
|
||||
);
|
||||
if (options && options.signal) {
|
||||
const signal = options.signal;
|
||||
if (signal.aborted) {
|
||||
abort(request, (signal: any).reason);
|
||||
} else {
|
||||
const listener = () => {
|
||||
abort(request, (signal: any).reason);
|
||||
signal.removeEventListener('abort', listener);
|
||||
};
|
||||
signal.addEventListener('abort', listener);
|
||||
}
|
||||
}
|
||||
const stream = new ReadableStream(
|
||||
{
|
||||
type: 'bytes',
|
||||
start: (controller): ?Promise<void> => {
|
||||
startWork(request);
|
||||
},
|
||||
pull: (controller): ?Promise<void> => {
|
||||
startFlowing(request, controller);
|
||||
},
|
||||
cancel: (reason): ?Promise<void> => {
|
||||
stopFlowing(request);
|
||||
abort(request, reason);
|
||||
},
|
||||
},
|
||||
// $FlowFixMe[prop-missing] size() methods are not allowed on byte streams.
|
||||
{highWaterMark: 0},
|
||||
);
|
||||
return stream;
|
||||
}
|
||||
|
||||
type StaticResult = {
|
||||
prelude: ReadableStream,
|
||||
};
|
||||
|
||||
export function prerender(
|
||||
model: ReactClientValue,
|
||||
options?: Options,
|
||||
): Promise<StaticResult> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const onFatalError = reject;
|
||||
function onAllReady() {
|
||||
const stream = new ReadableStream(
|
||||
{
|
||||
type: 'bytes',
|
||||
start: (controller): ?Promise<void> => {
|
||||
startWork(request);
|
||||
},
|
||||
pull: (controller): ?Promise<void> => {
|
||||
startFlowing(request, controller);
|
||||
},
|
||||
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,
|
||||
null,
|
||||
onAllReady,
|
||||
onFatalError,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
options ? options.temporaryReferences : undefined,
|
||||
__DEV__ && options ? options.environmentName : undefined,
|
||||
__DEV__ && options ? options.filterStackFrame : undefined,
|
||||
);
|
||||
if (options && options.signal) {
|
||||
const signal = options.signal;
|
||||
if (signal.aborted) {
|
||||
const reason = (signal: any).reason;
|
||||
abort(request, reason);
|
||||
} else {
|
||||
const listener = () => {
|
||||
const reason = (signal: any).reason;
|
||||
abort(request, reason);
|
||||
signal.removeEventListener('abort', listener);
|
||||
};
|
||||
signal.addEventListener('abort', listener);
|
||||
}
|
||||
}
|
||||
startWork(request);
|
||||
});
|
||||
}
|
||||
|
||||
let serverManifest = {};
|
||||
export function registerServerActions(manifest: ServerManifest) {
|
||||
// This function is called by the bundler to register the manifest.
|
||||
serverManifest = manifest;
|
||||
}
|
||||
|
||||
export function decodeReply<T>(
|
||||
body: string | FormData,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
): Thenable<T> {
|
||||
if (typeof body === 'string') {
|
||||
const form = new FormData();
|
||||
form.append('0', body);
|
||||
body = form;
|
||||
}
|
||||
const response = createResponse(
|
||||
serverManifest,
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
body,
|
||||
);
|
||||
const root = getRoot<T>(response);
|
||||
close(response);
|
||||
return root;
|
||||
}
|
||||
|
||||
export function decodeAction<T>(body: FormData): Promise<() => T> | null {
|
||||
return decodeActionImpl(body, serverManifest);
|
||||
}
|
||||
|
||||
export function decodeFormState<S>(
|
||||
actionResult: S,
|
||||
body: FormData,
|
||||
): Promise<ReactFormState<S, ServerReferenceId> | null> {
|
||||
return decodeFormStateImpl(actionResult, body, serverManifest);
|
||||
}
|
||||
|
||||
export function loadServerAction<F: (...any[]) => any>(id: string): Promise<F> {
|
||||
const reference = resolveServerReference<any>(serverManifest, id);
|
||||
return Promise.resolve(reference)
|
||||
.then(() => preloadModule(reference))
|
||||
.then(() => {
|
||||
const fn = requireModule(reference);
|
||||
if (typeof fn !== 'function') {
|
||||
throw new Error('Server actions must be functions');
|
||||
}
|
||||
return fn;
|
||||
});
|
||||
}
|
||||
317
packages/react-server-dom-parcel/src/server/ReactFlightDOMServerNode.js
vendored
Normal file
317
packages/react-server-dom-parcel/src/server/ReactFlightDOMServerNode.js
vendored
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
/**
|
||||
* 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 {Busboy} from 'busboy';
|
||||
import type {Writable} from 'stream';
|
||||
import type {ReactFormState, Thenable} from 'shared/ReactTypes';
|
||||
import type {
|
||||
ServerManifest,
|
||||
ServerReferenceId,
|
||||
} from '../shared/ReactFlightClientConfigBundlerParcel';
|
||||
|
||||
import {Readable} from 'stream';
|
||||
import {
|
||||
createRequest,
|
||||
createPrerenderRequest,
|
||||
startWork,
|
||||
startFlowing,
|
||||
stopFlowing,
|
||||
abort,
|
||||
} from 'react-server/src/ReactFlightServer';
|
||||
|
||||
import {
|
||||
createResponse,
|
||||
reportGlobalError,
|
||||
close,
|
||||
resolveField,
|
||||
resolveFileInfo,
|
||||
resolveFileChunk,
|
||||
resolveFileComplete,
|
||||
getRoot,
|
||||
} from 'react-server/src/ReactFlightReplyServer';
|
||||
|
||||
import {
|
||||
decodeAction as decodeActionImpl,
|
||||
decodeFormState as decodeFormStateImpl,
|
||||
} from 'react-server/src/ReactFlightActionServer';
|
||||
import {
|
||||
preloadModule,
|
||||
requireModule,
|
||||
resolveServerReference,
|
||||
} from '../shared/ReactFlightClientConfigBundlerParcel';
|
||||
|
||||
export {
|
||||
createClientReference,
|
||||
registerServerReference,
|
||||
} from '../ReactFlightParcelReferences';
|
||||
|
||||
import type {TemporaryReferenceSet} from 'react-server/src/ReactFlightServerTemporaryReferences';
|
||||
|
||||
export {createTemporaryReferenceSet} from 'react-server/src/ReactFlightServerTemporaryReferences';
|
||||
export type {TemporaryReferenceSet};
|
||||
|
||||
function createDrainHandler(destination: Destination, request: Request) {
|
||||
return () => startFlowing(request, destination);
|
||||
}
|
||||
|
||||
function createCancelHandler(request: Request, reason: string) {
|
||||
return () => {
|
||||
stopFlowing(request);
|
||||
abort(request, new Error(reason));
|
||||
};
|
||||
}
|
||||
|
||||
type Options = {
|
||||
environmentName?: string | (() => string),
|
||||
filterStackFrame?: (url: string, functionName: string) => boolean,
|
||||
onError?: (error: mixed) => void,
|
||||
onPostpone?: (reason: string) => void,
|
||||
identifierPrefix?: string,
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
};
|
||||
|
||||
type PipeableStream = {
|
||||
abort(reason: mixed): void,
|
||||
pipe<T: Writable>(destination: T): T,
|
||||
};
|
||||
|
||||
export function renderToPipeableStream(
|
||||
model: ReactClientValue,
|
||||
options?: Options,
|
||||
): PipeableStream {
|
||||
const request = createRequest(
|
||||
model,
|
||||
null,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
options ? options.temporaryReferences : undefined,
|
||||
__DEV__ && options ? options.environmentName : undefined,
|
||||
__DEV__ && options ? options.filterStackFrame : undefined,
|
||||
);
|
||||
let hasStartedFlowing = false;
|
||||
startWork(request);
|
||||
return {
|
||||
pipe<T: Writable>(destination: T): T {
|
||||
if (hasStartedFlowing) {
|
||||
throw new Error(
|
||||
'React currently only supports piping to one writable stream.',
|
||||
);
|
||||
}
|
||||
hasStartedFlowing = true;
|
||||
startFlowing(request, destination);
|
||||
destination.on('drain', createDrainHandler(destination, request));
|
||||
destination.on(
|
||||
'error',
|
||||
createCancelHandler(
|
||||
request,
|
||||
'The destination stream errored while writing data.',
|
||||
),
|
||||
);
|
||||
destination.on(
|
||||
'close',
|
||||
createCancelHandler(request, 'The destination stream closed early.'),
|
||||
);
|
||||
return destination;
|
||||
},
|
||||
abort(reason: mixed) {
|
||||
abort(request, reason);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createFakeWritable(readable: any): Writable {
|
||||
// The current host config expects a Writable so we create
|
||||
// a fake writable for now to push into the Readable.
|
||||
return ({
|
||||
write(chunk) {
|
||||
return readable.push(chunk);
|
||||
},
|
||||
end() {
|
||||
readable.push(null);
|
||||
},
|
||||
destroy(error) {
|
||||
readable.destroy(error);
|
||||
},
|
||||
}: any);
|
||||
}
|
||||
|
||||
type PrerenderOptions = {
|
||||
environmentName?: string | (() => string),
|
||||
filterStackFrame?: (url: string, functionName: string) => boolean,
|
||||
onError?: (error: mixed) => void,
|
||||
onPostpone?: (reason: string) => void,
|
||||
identifierPrefix?: string,
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
signal?: AbortSignal,
|
||||
};
|
||||
|
||||
type StaticResult = {
|
||||
prelude: Readable,
|
||||
};
|
||||
|
||||
export function prerenderToNodeStream(
|
||||
model: ReactClientValue,
|
||||
options?: PrerenderOptions,
|
||||
): Promise<StaticResult> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const onFatalError = reject;
|
||||
function onAllReady() {
|
||||
const readable: Readable = new Readable({
|
||||
read() {
|
||||
startFlowing(request, writable);
|
||||
},
|
||||
});
|
||||
const writable = createFakeWritable(readable);
|
||||
resolve({prelude: readable});
|
||||
}
|
||||
|
||||
const request = createPrerenderRequest(
|
||||
model,
|
||||
null,
|
||||
onAllReady,
|
||||
onFatalError,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
options ? options.temporaryReferences : undefined,
|
||||
__DEV__ && options ? options.environmentName : undefined,
|
||||
__DEV__ && options ? options.filterStackFrame : undefined,
|
||||
);
|
||||
if (options && options.signal) {
|
||||
const signal = options.signal;
|
||||
if (signal.aborted) {
|
||||
const reason = (signal: any).reason;
|
||||
abort(request, reason);
|
||||
} else {
|
||||
const listener = () => {
|
||||
const reason = (signal: any).reason;
|
||||
abort(request, reason);
|
||||
signal.removeEventListener('abort', listener);
|
||||
};
|
||||
signal.addEventListener('abort', listener);
|
||||
}
|
||||
}
|
||||
startWork(request);
|
||||
});
|
||||
}
|
||||
|
||||
let serverManifest = {};
|
||||
export function registerServerActions(manifest: ServerManifest) {
|
||||
// This function is called by the bundler to register the manifest.
|
||||
serverManifest = manifest;
|
||||
}
|
||||
|
||||
export function decodeReplyFromBusboy<T>(
|
||||
busboyStream: Busboy,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
): Thenable<T> {
|
||||
const response = createResponse(
|
||||
serverManifest,
|
||||
'',
|
||||
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 {
|
||||
resolveField(response, name, value);
|
||||
}
|
||||
});
|
||||
busboyStream.on('file', (name, value, {filename, encoding, mimeType}) => {
|
||||
if (encoding.toLowerCase() === 'base64') {
|
||||
throw new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
);
|
||||
}
|
||||
pendingFiles++;
|
||||
const file = resolveFileInfo(response, name, filename, mimeType);
|
||||
value.on('data', chunk => {
|
||||
resolveFileChunk(response, file, chunk);
|
||||
});
|
||||
value.on('end', () => {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
busboyStream.on('finish', () => {
|
||||
close(response);
|
||||
});
|
||||
busboyStream.on('error', err => {
|
||||
reportGlobalError(
|
||||
response,
|
||||
// $FlowFixMe[incompatible-call] types Error and mixed are incompatible
|
||||
err,
|
||||
);
|
||||
});
|
||||
return getRoot(response);
|
||||
}
|
||||
|
||||
export function decodeReply<T>(
|
||||
body: string | FormData,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
): Thenable<T> {
|
||||
if (typeof body === 'string') {
|
||||
const form = new FormData();
|
||||
form.append('0', body);
|
||||
body = form;
|
||||
}
|
||||
const response = createResponse(
|
||||
serverManifest,
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
body,
|
||||
);
|
||||
const root = getRoot<T>(response);
|
||||
close(response);
|
||||
return root;
|
||||
}
|
||||
|
||||
export function decodeAction<T>(body: FormData): Promise<() => T> | null {
|
||||
return decodeActionImpl(body, serverManifest);
|
||||
}
|
||||
|
||||
export function decodeFormState<S>(
|
||||
actionResult: S,
|
||||
body: FormData,
|
||||
): Promise<ReactFormState<S, ServerReferenceId> | null> {
|
||||
return decodeFormStateImpl(actionResult, body, serverManifest);
|
||||
}
|
||||
|
||||
export function loadServerAction<F: (...any[]) => any>(id: string): Promise<F> {
|
||||
const reference = resolveServerReference<any>(serverManifest, id);
|
||||
return Promise.resolve(reference)
|
||||
.then(() => preloadModule(reference))
|
||||
.then(() => {
|
||||
const fn = requireModule(reference);
|
||||
if (typeof fn !== 'function') {
|
||||
throw new Error('Server actions must be functions');
|
||||
}
|
||||
return fn;
|
||||
});
|
||||
}
|
||||
66
packages/react-server-dom-parcel/src/server/ReactFlightServerConfigParcelBundler.js
vendored
Normal file
66
packages/react-server-dom-parcel/src/server/ReactFlightServerConfigParcelBundler.js
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* 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} from '../shared/ReactFlightImportMetadata';
|
||||
|
||||
import type {
|
||||
ClientReference,
|
||||
ServerReference,
|
||||
} from '../ReactFlightParcelReferences';
|
||||
|
||||
export type {ClientReference, ServerReference};
|
||||
|
||||
export type ClientManifest = null;
|
||||
export type ServerReferenceId = string;
|
||||
export type ClientReferenceMetadata = ImportMetadata;
|
||||
export type ClientReferenceKey = string;
|
||||
|
||||
export {
|
||||
isClientReference,
|
||||
isServerReference,
|
||||
} from '../ReactFlightParcelReferences';
|
||||
|
||||
export function getClientReferenceKey(
|
||||
reference: ClientReference<any>,
|
||||
): ClientReferenceKey {
|
||||
return reference.$$id + '#' + reference.$$name;
|
||||
}
|
||||
|
||||
export function resolveClientReferenceMetadata<T>(
|
||||
config: ClientManifest,
|
||||
clientReference: ClientReference<T>,
|
||||
): ClientReferenceMetadata {
|
||||
return [
|
||||
clientReference.$$id,
|
||||
clientReference.$$name,
|
||||
clientReference.$$bundles,
|
||||
];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* 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 {
|
||||
renderToReadableStream,
|
||||
prerender,
|
||||
decodeReply,
|
||||
decodeAction,
|
||||
decodeFormState,
|
||||
createClientReference,
|
||||
registerServerReference,
|
||||
createTemporaryReferenceSet,
|
||||
registerServerActions,
|
||||
loadServerAction,
|
||||
} from './ReactFlightDOMServerBrowser';
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* 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 {
|
||||
renderToReadableStream,
|
||||
decodeReply,
|
||||
decodeAction,
|
||||
decodeFormState,
|
||||
createClientReference,
|
||||
registerServerReference,
|
||||
createTemporaryReferenceSet,
|
||||
registerServerActions,
|
||||
loadServerAction,
|
||||
} from './ReactFlightDOMServerBrowser';
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* 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 {
|
||||
renderToReadableStream,
|
||||
prerender,
|
||||
decodeReply,
|
||||
decodeAction,
|
||||
decodeFormState,
|
||||
createClientReference,
|
||||
registerServerReference,
|
||||
createTemporaryReferenceSet,
|
||||
registerServerActions,
|
||||
loadServerAction,
|
||||
} from './ReactFlightDOMServerEdge';
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* 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 {
|
||||
renderToReadableStream,
|
||||
decodeReply,
|
||||
decodeAction,
|
||||
decodeFormState,
|
||||
createClientReference,
|
||||
registerServerReference,
|
||||
createTemporaryReferenceSet,
|
||||
registerServerActions,
|
||||
loadServerAction,
|
||||
} from './ReactFlightDOMServerEdge';
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* 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 {
|
||||
renderToPipeableStream,
|
||||
prerenderToNodeStream,
|
||||
decodeReplyFromBusboy,
|
||||
decodeReply,
|
||||
decodeAction,
|
||||
decodeFormState,
|
||||
createClientReference,
|
||||
registerServerReference,
|
||||
createTemporaryReferenceSet,
|
||||
registerServerActions,
|
||||
loadServerAction,
|
||||
} from './ReactFlightDOMServerNode';
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* 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 {
|
||||
renderToPipeableStream,
|
||||
decodeReplyFromBusboy,
|
||||
decodeReply,
|
||||
decodeAction,
|
||||
decodeFormState,
|
||||
createClientReference,
|
||||
registerServerReference,
|
||||
createTemporaryReferenceSet,
|
||||
registerServerActions,
|
||||
loadServerAction,
|
||||
} from './ReactFlightDOMServerNode';
|
||||
83
packages/react-server-dom-parcel/src/shared/ReactFlightClientConfigBundlerParcel.js
vendored
Normal file
83
packages/react-server-dom-parcel/src/shared/ReactFlightClientConfigBundlerParcel.js
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* 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} from 'shared/ReactTypes';
|
||||
|
||||
import type {ImportMetadata} from './ReactFlightImportMetadata';
|
||||
|
||||
import {ID, NAME, BUNDLES} from './ReactFlightImportMetadata';
|
||||
|
||||
export type ServerManifest = {
|
||||
[string]: Array<string>,
|
||||
};
|
||||
export type SSRModuleMap = null;
|
||||
export type ModuleLoading = null;
|
||||
export type ServerConsumerModuleMap = null;
|
||||
export type ServerReferenceId = string;
|
||||
|
||||
export opaque type ClientReferenceMetadata = ImportMetadata;
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
export opaque type ClientReference<T> = {
|
||||
// Module id.
|
||||
id: string,
|
||||
// Export name.
|
||||
name: string,
|
||||
// List of bundle URLs, relative to the distDir.
|
||||
bundles: Array<string>,
|
||||
};
|
||||
|
||||
export function prepareDestinationForModule(
|
||||
moduleLoading: ModuleLoading,
|
||||
nonce: ?string,
|
||||
metadata: ClientReferenceMetadata,
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
export function resolveClientReference<T>(
|
||||
bundlerConfig: null,
|
||||
metadata: ClientReferenceMetadata,
|
||||
): ClientReference<T> {
|
||||
// Reference is already resolved during the build.
|
||||
return {
|
||||
id: metadata[ID],
|
||||
name: metadata[NAME],
|
||||
bundles: metadata[BUNDLES],
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveServerReference<T>(
|
||||
bundlerConfig: ServerManifest,
|
||||
ref: ServerReferenceId,
|
||||
): ClientReference<T> {
|
||||
const idx = ref.lastIndexOf('#');
|
||||
const id = ref.slice(0, idx);
|
||||
const name = ref.slice(idx + 1);
|
||||
const bundles = bundlerConfig[id];
|
||||
if (!bundles) {
|
||||
throw new Error('Invalid server action: ' + ref);
|
||||
}
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
bundles,
|
||||
};
|
||||
}
|
||||
|
||||
export function preloadModule<T>(
|
||||
metadata: ClientReference<T>,
|
||||
): null | Thenable<any> {
|
||||
return Promise.all(metadata.bundles.map(url => parcelRequire.load(url)));
|
||||
}
|
||||
|
||||
export function requireModule<T>(metadata: ClientReference<T>): T {
|
||||
const moduleExports = parcelRequire(metadata.id);
|
||||
return moduleExports[metadata.name];
|
||||
}
|
||||
20
packages/react-server-dom-parcel/src/shared/ReactFlightImportMetadata.js
vendored
Normal file
20
packages/react-server-dom-parcel/src/shared/ReactFlightImportMetadata.js
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
// 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,
|
||||
/* name */ string,
|
||||
/* bundles */ Array<string>,
|
||||
];
|
||||
|
||||
export const ID = 0;
|
||||
export const NAME = 1;
|
||||
export const BUNDLES = 2;
|
||||
10
packages/react-server-dom-parcel/static.browser.js
Normal file
10
packages/react-server-dom-parcel/static.browser.js
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
|
||||
*/
|
||||
|
||||
export {prerender} from './src/server/react-flight-dom-server.browser';
|
||||
10
packages/react-server-dom-parcel/static.edge.js
Normal file
10
packages/react-server-dom-parcel/static.edge.js
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
|
||||
*/
|
||||
|
||||
export {prerender} from './src/server/react-flight-dom-server.edge';
|
||||
13
packages/react-server-dom-parcel/static.js
vendored
Normal file
13
packages/react-server-dom-parcel/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
packages/react-server-dom-parcel/static.node.js
Normal file
10
packages/react-server-dom-parcel/static.node.js
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
|
||||
*/
|
||||
|
||||
export {prerenderToNodeStream} from './src/server/react-flight-dom-server.node';
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* 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} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from 'react-server-dom-parcel/src/server/ReactFlightServerConfigParcelBundler';
|
||||
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
|
||||
export const supportsRequestStorage = false;
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
|
||||
|
||||
export const supportsComponentStorage = false;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
(null: any);
|
||||
|
||||
export * from '../ReactFlightServerConfigDebugNoop';
|
||||
|
||||
export * from '../ReactFlightStackConfigV8';
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* 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} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from 'react-server-dom-parcel/src/server/ReactFlightServerConfigParcelBundler';
|
||||
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
|
||||
// For now, we get this from the global scope, but this will likely move to a module.
|
||||
export const supportsRequestStorage = typeof AsyncLocalStorage === 'function';
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> =
|
||||
supportsRequestStorage ? new AsyncLocalStorage() : (null: any);
|
||||
|
||||
export const supportsComponentStorage: boolean =
|
||||
__DEV__ && supportsRequestStorage;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
supportsComponentStorage ? new AsyncLocalStorage() : (null: any);
|
||||
|
||||
// We use the Node version but get access to async_hooks from a global.
|
||||
import type {HookCallbacks, AsyncHook} from 'async_hooks';
|
||||
export const createAsyncHook: HookCallbacks => AsyncHook =
|
||||
typeof async_hooks === 'object'
|
||||
? async_hooks.createHook
|
||||
: function () {
|
||||
return ({
|
||||
enable() {},
|
||||
disable() {},
|
||||
}: any);
|
||||
};
|
||||
export const executionAsyncId: () => number =
|
||||
typeof async_hooks === 'object' ? async_hooks.executionAsyncId : (null: any);
|
||||
|
||||
export * from '../ReactFlightServerConfigDebugNode';
|
||||
|
||||
export * from '../ReactFlightStackConfigV8';
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import {AsyncLocalStorage} from 'async_hooks';
|
||||
|
||||
import type {Request} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from 'react-server-dom-parcel/src/server/ReactFlightServerConfigParcelBundler';
|
||||
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
|
||||
export const supportsRequestStorage = true;
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> =
|
||||
new AsyncLocalStorage();
|
||||
|
||||
export const supportsComponentStorage = __DEV__;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
supportsComponentStorage ? new AsyncLocalStorage() : (null: any);
|
||||
|
||||
export {createHook as createAsyncHook, executionAsyncId} from 'async_hooks';
|
||||
|
||||
export * from '../ReactFlightServerConfigDebugNode';
|
||||
|
||||
export * from '../ReactFlightStackConfigV8';
|
||||
|
|
@ -103,6 +103,11 @@ declare const __turbopack_require__: ((id: string) => any) & {
|
|||
u: string => string,
|
||||
};
|
||||
|
||||
declare var parcelRequire: {
|
||||
(id: string): any,
|
||||
load: (url: string) => Promise<mixed>,
|
||||
};
|
||||
|
||||
declare module 'fs/promises' {
|
||||
declare const access: (path: string, mode?: number) => Promise<void>;
|
||||
declare const lstat: (
|
||||
|
|
|
|||
|
|
@ -621,6 +621,70 @@ const bundles = [
|
|||
externals: ['react', 'react-dom'],
|
||||
},
|
||||
|
||||
/******* React Server DOM Parcel Server *******/
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
entry: 'react-server-dom-parcel/src/server/react-flight-dom-server.browser',
|
||||
name: 'react-server-dom-parcel-server.browser',
|
||||
condition: 'react-server',
|
||||
global: 'ReactServerDOMServer',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'react-dom'],
|
||||
},
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
entry: 'react-server-dom-parcel/src/server/react-flight-dom-server.node',
|
||||
name: 'react-server-dom-parcel-server.node',
|
||||
condition: 'react-server',
|
||||
global: 'ReactServerDOMServer',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'util', 'async_hooks', 'react-dom'],
|
||||
},
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
entry: 'react-server-dom-parcel/src/server/react-flight-dom-server.edge',
|
||||
name: 'react-server-dom-parcel-server.edge',
|
||||
condition: 'react-server',
|
||||
global: 'ReactServerDOMServer',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'util', 'async_hooks', 'react-dom'],
|
||||
},
|
||||
|
||||
/******* React Server DOM Parcel Client *******/
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
entry: 'react-server-dom-parcel/client.browser',
|
||||
global: 'ReactServerDOMClient',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'react-dom'],
|
||||
},
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
entry: 'react-server-dom-parcel/client.node',
|
||||
global: 'ReactServerDOMClient',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'react-dom', 'util'],
|
||||
},
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: RENDERER,
|
||||
entry: 'react-server-dom-parcel/client.edge',
|
||||
global: 'ReactServerDOMClient',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'react-dom'],
|
||||
},
|
||||
|
||||
/******* React Server DOM ESM Server *******/
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ module.exports = {
|
|||
__turbopack_load__: 'readonly',
|
||||
__turbopack_require__: 'readonly',
|
||||
|
||||
// Flight Parcel
|
||||
parcelRequire: 'readonly',
|
||||
|
||||
// jest
|
||||
expect: 'readonly',
|
||||
jest: 'readonly',
|
||||
|
|
|
|||
|
|
@ -62,6 +62,9 @@ module.exports = {
|
|||
__turbopack_load__: 'readonly',
|
||||
__turbopack_require__: 'readonly',
|
||||
|
||||
// Flight Parcel
|
||||
parcelRequire: 'readonly',
|
||||
|
||||
// jest
|
||||
expect: 'readonly',
|
||||
jest: 'readonly',
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ module.exports = {
|
|||
__turbopack_load__: 'readonly',
|
||||
__turbopack_require__: 'readonly',
|
||||
|
||||
// Flight Parcel
|
||||
parcelRequire: 'readonly',
|
||||
|
||||
// jest
|
||||
expect: 'readonly',
|
||||
jest: 'readonly',
|
||||
|
|
|
|||
|
|
@ -189,6 +189,49 @@ module.exports = [
|
|||
isFlowTyped: true,
|
||||
isServerSupported: true,
|
||||
},
|
||||
{
|
||||
shortName: 'dom-node-parcel',
|
||||
entryPoints: [
|
||||
'react-server-dom-parcel/client.node',
|
||||
'react-server-dom-parcel/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-parcel',
|
||||
'react-server-dom-parcel/client.node',
|
||||
'react-server-dom-parcel/server',
|
||||
'react-server-dom-parcel/server.node',
|
||||
'react-server-dom-parcel/static',
|
||||
'react-server-dom-parcel/static.node',
|
||||
'react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js', // react-server-dom-parcel/client.node
|
||||
'react-server-dom-parcel/src/shared/ReactFlightClientConfigBundlerParcel.js',
|
||||
'react-server-dom-parcel/src/server/react-flight-dom-server.node',
|
||||
'react-server-dom-parcel/src/server/ReactFlightDOMServerNode.js', // react-server-dom-parcel/src/server/react-flight-dom-server.node
|
||||
'react-server-dom-parcel/node-register',
|
||||
'react-server-dom-parcel/src/ReactFlightParcelNodeRegister.js',
|
||||
'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'],
|
||||
|
|
@ -270,6 +313,40 @@ module.exports = [
|
|||
isFlowTyped: true,
|
||||
isServerSupported: true,
|
||||
},
|
||||
{
|
||||
shortName: 'dom-browser-parcel',
|
||||
entryPoints: [
|
||||
'react-server-dom-parcel/client.browser',
|
||||
'react-server-dom-parcel/src/server/react-flight-dom-server.browser',
|
||||
],
|
||||
paths: [
|
||||
'react-dom',
|
||||
'react-dom/client',
|
||||
'react-dom/profiling',
|
||||
'react-dom/server',
|
||||
'react-dom/server.node',
|
||||
'react-dom-bindings',
|
||||
'react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js',
|
||||
'react-dom-bindings/src/server/ReactFlightServerConfigDOM.js',
|
||||
'react-dom-bindings/src/shared/ReactFlightClientConfigDOM.js',
|
||||
'react-server-dom-parcel',
|
||||
'react-server-dom-parcel/client',
|
||||
'react-server-dom-parcel/client.browser',
|
||||
'react-server-dom-parcel/server.browser',
|
||||
'react-server-dom-parcel/static.browser',
|
||||
'react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js', // react-server-dom-parcel/client.browser
|
||||
'react-server-dom-parcel/src/shared/ReactFlightClientConfigBundlerParcel.js',
|
||||
'react-server-dom-parcel/src/server/react-flight-dom-server.browser',
|
||||
'react-server-dom-parcel/src/server/ReactFlightDOMServerBrowser.js', // react-server-dom-parcel/src/server/react-flight-dom-server.browser
|
||||
'react-devtools',
|
||||
'react-devtools-core',
|
||||
'react-devtools-shell',
|
||||
'react-devtools-shared',
|
||||
'shared/ReactDOMSharedInternals',
|
||||
],
|
||||
isFlowTyped: true,
|
||||
isServerSupported: true,
|
||||
},
|
||||
{
|
||||
shortName: 'dom-edge-webpack',
|
||||
entryPoints: [
|
||||
|
|
@ -352,6 +429,45 @@ module.exports = [
|
|||
isFlowTyped: true,
|
||||
isServerSupported: true,
|
||||
},
|
||||
{
|
||||
shortName: 'dom-edge-parcel',
|
||||
entryPoints: [
|
||||
'react-server-dom-parcel/client.edge',
|
||||
'react-server-dom-parcel/src/server/react-flight-dom-server.edge',
|
||||
],
|
||||
paths: [
|
||||
'react-dom',
|
||||
'react-dom/src/ReactDOMReactServer.js',
|
||||
'react-dom-bindings',
|
||||
'react-dom/client',
|
||||
'react-dom/profiling',
|
||||
'react-dom/server.edge',
|
||||
'react-dom/static.edge',
|
||||
'react-dom/unstable_testing',
|
||||
'react-dom/src/server/react-dom-server.edge',
|
||||
'react-dom/src/server/ReactDOMFizzServerEdge.js', // react-dom/server.edge
|
||||
'react-dom/src/server/ReactDOMFizzStaticEdge.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-parcel',
|
||||
'react-server-dom-parcel/client.edge',
|
||||
'react-server-dom-parcel/server.edge',
|
||||
'react-server-dom-parcel/static.edge',
|
||||
'react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js', // react-server-dom-parcel/client.edge
|
||||
'react-server-dom-parcel/src/shared/ReactFlightClientConfigBundlerParcel.js',
|
||||
'react-server-dom-parcel/src/server/react-flight-dom-server.edge',
|
||||
'react-server-dom-parcel/src/server/ReactFlightDOMServerEdge.js', // react-server-dom-parcel/src/server/react-flight-dom-server.edge
|
||||
'react-devtools',
|
||||
'react-devtools-core',
|
||||
'react-devtools-shell',
|
||||
'react-devtools-shared',
|
||||
'shared/ReactDOMSharedInternals',
|
||||
'react-server/src/ReactFlightServerConfigDebugNode.js',
|
||||
],
|
||||
isFlowTyped: true,
|
||||
isServerSupported: true,
|
||||
},
|
||||
{
|
||||
shortName: 'dom-node-esm',
|
||||
entryPoints: [
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user