Skip to content

Commit cc067b1

Browse files
committed
wip
1 parent ffbe259 commit cc067b1

27 files changed

+1011
-236
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ node_modules/
1010
/examples/*/dist
1111
/tutorial/dist
1212
/integration/playwright-report
13+
/playgrounds/
1314

1415
# v5 build files
1516
/packages/*/cjs/

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@
145145
"pnpm": {
146146
"patchedDependencies": {
147147
"@changesets/[email protected]": "patches/@[email protected]"
148+
},
149+
"overrides": {
150+
"react": "0.0.0-experimental-96c584661-20240412",
151+
"react-dom": "0.0.0-experimental-96c584661-20240412"
148152
}
149153
}
150154
}

packages/react-router-dom/ssr/single-fetch.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export function StreamTransfer({
7575
let scriptTag = value ? (
7676
<script
7777
dangerouslySetInnerHTML={{
78-
__html: `window.__remixContext.streamController.enqueue(${escapeHtml(
78+
__html: `window.__remixContext.streamControllers[${identifier}].enqueue(${escapeHtml(
7979
JSON.stringify(value)
8080
)});`,
8181
}}
@@ -88,7 +88,7 @@ export function StreamTransfer({
8888
{scriptTag}
8989
<script
9090
dangerouslySetInnerHTML={{
91-
__html: `window.__remixContext.streamController.close();`,
91+
__html: `window.__remixContext.streamControllers[${identifier}].close();`,
9292
}}
9393
/>
9494
</>

packages/remix-dev/config.ts

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ interface FutureConfig {
1616
v3_fetcherPersist: boolean;
1717
v3_relativeSplatPath: boolean;
1818
v3_throwAbortReason: boolean;
19+
unstable_serverComponents: boolean;
1920
unstable_singleFetch: boolean;
2021
}
2122

@@ -80,6 +81,11 @@ export interface RemixConfig {
8081
*/
8182
entryServerFilePath: string;
8283

84+
/**
85+
* The absolute path to the entry.react-server file.
86+
*/
87+
entryReactServerFilePath: string;
88+
8389
/**
8490
* An object of all available routes, keyed by route id.
8591
*/
@@ -120,9 +126,15 @@ export async function resolveConfig(
120126

121127
let userEntryClientFile = findEntry(appDirectory, "entry.client");
122128
let userEntryServerFile = findEntry(appDirectory, "entry.server");
129+
let userEntryReactServerFile = findEntry(appDirectory, "entry.react-server");
123130

124131
let entryServerFile: string;
125-
let entryClientFile = userEntryClientFile || "entry.client.tsx";
132+
let entryClientFile =
133+
userEntryClientFile ||
134+
(appConfig.future?.unstable_serverComponents
135+
? "entry.client-rsc.tsx"
136+
: "entry.client.tsx");
137+
let entryReactServerFile;
126138

127139
let pkgJson = await PackageJson.load(rootDirectory);
128140
let deps = pkgJson.content.dependencies ?? {};
@@ -185,6 +197,34 @@ export async function resolveConfig(
185197
entryServerFile = `entry.server.${serverRuntime}.tsx`;
186198
}
187199

200+
if (userEntryReactServerFile) {
201+
entryReactServerFile = userEntryReactServerFile;
202+
} else {
203+
let serverRuntime = deps["@react-router/deno"]
204+
? "deno"
205+
: deps["@react-router/cloudflare"]
206+
? "cloudflare"
207+
: deps["@react-router/node"]
208+
? "node"
209+
: undefined;
210+
211+
if (!serverRuntime) {
212+
let serverRuntimes = [
213+
"@react-router/deno",
214+
"@react-router/cloudflare",
215+
"@react-router/node",
216+
];
217+
let formattedList = disjunctionListFormat.format(serverRuntimes);
218+
throw new Error(
219+
`Could not determine server runtime. Please install one of the following: ${formattedList}`
220+
);
221+
}
222+
223+
entryReactServerFile = `entry.react-server.${
224+
serverRuntime === "node" ? "node" : "web"
225+
}.tsx`;
226+
}
227+
188228
let entryClientFilePath = userEntryClientFile
189229
? path.resolve(appDirectory, userEntryClientFile)
190230
: path.resolve(defaultsDirectory, entryClientFile);
@@ -193,6 +233,10 @@ export async function resolveConfig(
193233
? path.resolve(appDirectory, userEntryServerFile)
194234
: path.resolve(defaultsDirectory, entryServerFile);
195235

236+
let entryReactServerFilePath = userEntryReactServerFile
237+
? path.resolve(appDirectory, userEntryReactServerFile)
238+
: path.resolve(defaultsDirectory, entryReactServerFile);
239+
196240
let rootRouteFile = findEntry(appDirectory, "root");
197241
if (!rootRouteFile) {
198242
throw new Error(`Missing "root" route file in ${appDirectory}`);
@@ -215,17 +259,23 @@ export async function resolveConfig(
215259
}
216260
}
217261

262+
let unstable_serverComponents =
263+
appConfig.future?.unstable_serverComponents === true;
218264
let future: FutureConfig = {
219265
v3_fetcherPersist: appConfig.future?.v3_fetcherPersist === true,
220266
v3_relativeSplatPath: appConfig.future?.v3_relativeSplatPath === true,
221267
v3_throwAbortReason: appConfig.future?.v3_throwAbortReason === true,
222-
unstable_singleFetch: appConfig.future?.unstable_singleFetch === true,
268+
unstable_singleFetch:
269+
appConfig.future?.unstable_singleFetch === true ||
270+
unstable_serverComponents,
271+
unstable_serverComponents,
223272
};
224273

225274
return {
226275
appDirectory,
227276
entryClientFilePath,
228277
entryServerFilePath,
278+
entryReactServerFilePath,
229279
routes,
230280
serverModuleFormat,
231281
future,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { RouterProvider } from "react-router-dom";
2+
import { startTransition, StrictMode } from "react";
3+
import { hydrateRoot } from "react-dom/client";
4+
import RSD from "react-server-dom-diy/client";
5+
6+
export default function start() {
7+
startTransition(() => {
8+
hydrateRoot(
9+
document,
10+
<StrictMode>
11+
<RouterProvider />
12+
</StrictMode>
13+
);
14+
});
15+
}
16+
17+
export function createFromReadableStream(body: ReadableStream<Uint8Array>) {
18+
return RSD.createFromReadableStream(body);
19+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as stream from "node:stream";
2+
3+
import { createReadableStreamFromReadable } from "@react-router/node";
4+
// @ts-expect-error - no types
5+
import RSD from "react-server-dom-diy/server";
6+
7+
export function renderToReadableStream(data: unknown) {
8+
const passthrough = new stream.PassThrough();
9+
const { pipe } = RSD.renderToPipeableStream(data);
10+
pipe(passthrough);
11+
return createReadableStreamFromReadable(passthrough);
12+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// @ts-expect-error - no types
2+
import RSD from "react-server-dom-diy/server";
3+
4+
export function renderToReadableStream(data: unknown) {
5+
return RSD.renderToReadableStream(data);
6+
}

packages/remix-dev/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"set-cookie-parser": "^2.6.0"
5151
},
5252
"devDependencies": {
53+
"react-server-dom-diy": "0.0.0-experimental-15a3a5622-202404158",
5354
"@react-router/serve": "workspace:*",
5455
"@types/babel__core": "^7.20.5",
5556
"@types/babel__generator": "^7.6.8",
@@ -76,12 +77,16 @@
7677
},
7778
"peerDependencies": {
7879
"react-router-dom": "workspace:^",
80+
"react-server-dom-diy": "*",
7981
"@react-router/serve": "workspace:^",
8082
"typescript": "^5.1.0",
8183
"vite": "^5.1.0",
8284
"wrangler": "^3.28.2"
8385
},
8486
"peerDependenciesMeta": {
87+
"react-server-dom-diy": {
88+
"optional": true
89+
},
8590
"@react-router/serve": {
8691
"optional": true
8792
},

packages/remix-dev/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"strict": true,
1212
"skipLibCheck": true,
1313
"declaration": true,
14+
"declarationMap": true,
1415
"emitDeclarationOnly": true,
1516
"rootDir": ".",
1617
"outDir": "./dist"

packages/remix-dev/vite/build.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,36 @@ export async function build(
317317
})
318318
);
319319

320+
// Then run Vite React Server builds in parallel
321+
if (reactRouterConfig.future.unstable_serverComponents) {
322+
process.env.REACT_SERVER = "1";
323+
let {} = await getServerBuilds(ctx);
324+
await Promise.all(serverBuilds.map(viteBuild));
325+
326+
let _viteManifestPaths = getViteManifestPaths(ctx, serverBuilds);
327+
await Promise.all(
328+
viteManifestPaths.map(async ({ srcPath, destPath }) => {
329+
let manifestExists = await fse.pathExists(srcPath);
330+
if (!manifestExists) return;
331+
332+
// Move/delete original Vite manifest file
333+
if (ctx.viteManifestEnabled) {
334+
await fse.ensureDir(path.dirname(destPath));
335+
await fse.move(srcPath, destPath);
336+
} else {
337+
await fse.remove(srcPath);
338+
}
339+
340+
// Remove .vite dir if it's now empty
341+
let viteDir = path.dirname(srcPath);
342+
let viteDirFiles = await fse.readdir(viteDir);
343+
if (viteDirFiles.length === 0) {
344+
await fse.remove(viteDir);
345+
}
346+
})
347+
);
348+
}
349+
320350
if (ctx.reactRouterConfig.manifest) {
321351
await fse.ensureDir(
322352
path.join(ctx.reactRouterConfig.buildDirectory, ".react-router")

0 commit comments

Comments
 (0)