@react-router/dev 0.0.0-experimental-cb25a21e1 → 0.0.0-experimental-27337b0d6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/cli/run.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @react-router/dev v0.0.0-experimental-cb25a21e1
3
+ * @react-router/dev v0.0.0-experimental-27337b0d6
4
4
  *
5
5
  * Copyright (c) Remix Software Inc.
6
6
  *
package/dist/colors.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -0,0 +1,90 @@
1
+ import { HydratedRouter } from "react-router";
2
+ import { startTransition, StrictMode } from "react";
3
+ import { hydrateRoot } from "react-dom/client";
4
+ // @ts-expect-error - no types
5
+ import ReactServerDOM from "react-server-dom-diy/client";
6
+
7
+ if (import.meta.env.PROD) {
8
+ window.__diy_client_manifest__ = {
9
+ _cache: new Map(),
10
+ resolveClientReference([id, exportName]) {
11
+ return {
12
+ preloadModule() {
13
+ if (window.__diy_client_manifest__._cache.has(id)) {
14
+ return window.__diy_client_manifest__._cache.get(id);
15
+ }
16
+ const promise = import("virtual:react-router/client-references")
17
+ .then(({ default: mods }) => mods[id]())
18
+ .then((mod) => {
19
+ promise.status = "fulfilled";
20
+ promise.value = mod;
21
+ })
22
+ .catch((res) => {
23
+ promise.status = "rejected";
24
+ promise.reason = res;
25
+ throw res;
26
+ });
27
+ promise.status = "pending";
28
+ window.__diy_client_manifest__._cache.set(id, promise);
29
+ return promise;
30
+ },
31
+ requireModule() {
32
+ const cached = window.__diy_client_manifest__._cache.get(id);
33
+ if (!cached) throw new Error(`Module ${id} not found`);
34
+ if (cached.reason) throw cached.reason;
35
+ return cached.value[exportName];
36
+ },
37
+ };
38
+ },
39
+ };
40
+ } else {
41
+ window.__diy_client_manifest__ = {
42
+ _cache: new Map(),
43
+ resolveClientReference([id, exportName]) {
44
+ return {
45
+ preloadModule() {
46
+ if (window.__diy_client_manifest__._cache.has(id)) {
47
+ return window.__diy_client_manifest__._cache.get(id);
48
+ }
49
+ const promise = import(id)
50
+ .then((mod) => {
51
+ promise.status = "fulfilled";
52
+ promise.value = mod;
53
+ })
54
+ .catch((res) => {
55
+ promise.status = "rejected";
56
+ promise.reason = res;
57
+ throw res;
58
+ });
59
+ promise.status = "pending";
60
+ window.__diy_client_manifest__._cache.set(id, promise);
61
+ return promise;
62
+ },
63
+ requireModule() {
64
+ const cached = window.__diy_client_manifest__._cache.get(id);
65
+ if (!cached) throw new Error(`Module ${id} not found`);
66
+ if (cached.reason) throw cached.reason;
67
+ return cached.value[exportName];
68
+ },
69
+ };
70
+ },
71
+ };
72
+ }
73
+
74
+ window.createFromReadableStream = function createFromReadableStream(
75
+ body: ReadableStream<Uint8Array>
76
+ ) {
77
+ return ReactServerDOM.createFromReadableStream(
78
+ body,
79
+ window.__diy_client_manifest__
80
+ );
81
+ };
82
+
83
+ startTransition(() => {
84
+ hydrateRoot(
85
+ document,
86
+ <StrictMode>
87
+ <HydratedRouter />
88
+ </StrictMode>
89
+ );
90
+ });
@@ -0,0 +1,15 @@
1
+ import * as stream from "node:stream";
2
+
3
+ import { createReadableStreamFromReadable } from "@react-router/node";
4
+ // @ts-expect-error - no types
5
+ import ReactServerDOM from "react-server-dom-diy/server";
6
+
7
+ export function renderToReadableStream(data: unknown) {
8
+ const passthrough = new stream.PassThrough();
9
+ const { pipe } = ReactServerDOM.renderToPipeableStream(
10
+ data,
11
+ global.__diy_server_manifest__
12
+ );
13
+ pipe(passthrough);
14
+ return createReadableStreamFromReadable(passthrough);
15
+ }
@@ -0,0 +1,9 @@
1
+ // @ts-expect-error - no types
2
+ import ReactServerDOM from "react-server-dom-diy/server";
3
+
4
+ export function renderToReadableStream(data: unknown) {
5
+ return ReactServerDOM.renderToReadableStream(
6
+ data,
7
+ global.__diy_server_manifest__
8
+ );
9
+ }
@@ -0,0 +1,164 @@
1
+ import { PassThrough, Readable } from "node:stream";
2
+
3
+ import type { AppLoadContext, EntryContext } from "@react-router/node";
4
+ import { createReadableStreamFromReadable } from "@react-router/node";
5
+ import { RemixServer } from "react-router";
6
+ import * as isbotModule from "isbot";
7
+ import { renderToPipeableStream } from "react-dom/server";
8
+ // @ts-expect-error - no types
9
+ import ReactServerDOM from "react-server-dom-diy/client";
10
+
11
+ const ABORT_DELAY = 5_000;
12
+
13
+ export function createFromReadableStream(body: ReadableStream<Uint8Array>) {
14
+ return ReactServerDOM.createFromNodeStream(
15
+ Readable.fromWeb(body as any),
16
+ global.__diy_client_manifest__
17
+ );
18
+ }
19
+
20
+ export default function handleRequest(
21
+ request: Request,
22
+ responseStatusCode: number,
23
+ responseHeaders: Headers,
24
+ remixContext: EntryContext,
25
+ loadContext: AppLoadContext
26
+ ) {
27
+ let prohibitOutOfOrderStreaming =
28
+ isBotRequest(request.headers.get("user-agent")) || remixContext.isSpaMode;
29
+
30
+ return prohibitOutOfOrderStreaming
31
+ ? handleBotRequest(
32
+ request,
33
+ responseStatusCode,
34
+ responseHeaders,
35
+ remixContext
36
+ )
37
+ : handleBrowserRequest(
38
+ request,
39
+ responseStatusCode,
40
+ responseHeaders,
41
+ remixContext
42
+ );
43
+ }
44
+
45
+ // We have some Remix apps in the wild already running with isbot@3 so we need
46
+ // to maintain backwards compatibility even though we want new apps to use
47
+ // isbot@4. That way, we can ship this as a minor Semver update to @react-router/dev.
48
+ function isBotRequest(userAgent: string | null) {
49
+ if (!userAgent) {
50
+ return false;
51
+ }
52
+
53
+ // isbot >= 3.8.0, >4
54
+ if ("isbot" in isbotModule && typeof isbotModule.isbot === "function") {
55
+ return isbotModule.isbot(userAgent);
56
+ }
57
+
58
+ // isbot < 3.8.0
59
+ if ("default" in isbotModule && typeof isbotModule.default === "function") {
60
+ return isbotModule.default(userAgent);
61
+ }
62
+
63
+ return false;
64
+ }
65
+
66
+ function handleBotRequest(
67
+ request: Request,
68
+ responseStatusCode: number,
69
+ responseHeaders: Headers,
70
+ remixContext: EntryContext
71
+ ) {
72
+ return new Promise((resolve, reject) => {
73
+ let shellRendered = false;
74
+ const { pipe, abort } = renderToPipeableStream(
75
+ <RemixServer
76
+ context={remixContext}
77
+ url={request.url}
78
+ abortDelay={ABORT_DELAY}
79
+ />,
80
+ {
81
+ onAllReady() {
82
+ shellRendered = true;
83
+ const body = new PassThrough();
84
+ const stream = createReadableStreamFromReadable(body);
85
+
86
+ responseHeaders.set("Content-Type", "text/html");
87
+
88
+ resolve(
89
+ new Response(stream, {
90
+ headers: responseHeaders,
91
+ status: responseStatusCode,
92
+ })
93
+ );
94
+
95
+ pipe(body);
96
+ },
97
+ onShellError(error: unknown) {
98
+ reject(error);
99
+ },
100
+ onError(error: unknown) {
101
+ responseStatusCode = 500;
102
+ // Log streaming rendering errors from inside the shell. Don't log
103
+ // errors encountered during initial shell rendering since they'll
104
+ // reject and get logged in handleDocumentRequest.
105
+ if (shellRendered) {
106
+ console.error(error);
107
+ }
108
+ },
109
+ }
110
+ );
111
+
112
+ setTimeout(abort, ABORT_DELAY);
113
+ });
114
+ }
115
+
116
+ function handleBrowserRequest(
117
+ request: Request,
118
+ responseStatusCode: number,
119
+ responseHeaders: Headers,
120
+ remixContext: EntryContext
121
+ ) {
122
+ return new Promise((resolve, reject) => {
123
+ let shellRendered = false;
124
+ const { pipe, abort } = renderToPipeableStream(
125
+ <RemixServer
126
+ context={remixContext}
127
+ url={request.url}
128
+ abortDelay={ABORT_DELAY}
129
+ />,
130
+ {
131
+ onShellReady() {
132
+ shellRendered = true;
133
+ const body = new PassThrough();
134
+ const stream = createReadableStreamFromReadable(body);
135
+
136
+ responseHeaders.set("Content-Type", "text/html");
137
+
138
+ resolve(
139
+ new Response(stream, {
140
+ headers: responseHeaders,
141
+ status: responseStatusCode,
142
+ })
143
+ );
144
+
145
+ pipe(body);
146
+ },
147
+ onShellError(error: unknown) {
148
+ reject(error);
149
+ },
150
+ onError(error: unknown) {
151
+ responseStatusCode = 500;
152
+ // Log streaming rendering errors from inside the shell. Don't log
153
+ // errors encountered during initial shell rendering since they'll
154
+ // reject and get logged in handleDocumentRequest.
155
+ if (shellRendered) {
156
+ console.error(error);
157
+ }
158
+ },
159
+ }
160
+ );
161
+
162
+ setTimeout(abort, ABORT_DELAY);
163
+ });
164
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/config.d.ts CHANGED
@@ -39,6 +39,7 @@ interface FutureConfig {
39
39
  v3_fetcherPersist: boolean;
40
40
  v3_relativeSplatPath: boolean;
41
41
  v3_throwAbortReason: boolean;
42
+ unstable_serverComponents: boolean;
42
43
  }
43
44
  export type BuildManifest = DefaultBuildManifest | ServerBundlesBuildManifest;
44
45
  type BuildEndHook = (args: {
@@ -256,6 +257,7 @@ export declare function resolveEntryFiles({ rootDirectory, reactRouterConfig, }:
256
257
  reactRouterConfig: ResolvedVitePluginConfig;
257
258
  }): Promise<{
258
259
  entryClientFilePath: string;
260
+ entryReactServerFilePath: string | undefined;
259
261
  entryServerFilePath: string;
260
262
  }>;
261
263
  export {};
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -187,7 +187,8 @@ async function resolveReactRouterConfig({
187
187
  let future = {
188
188
  v3_fetcherPersist: (userFuture === null || userFuture === void 0 ? void 0 : userFuture.v3_fetcherPersist) === true,
189
189
  v3_relativeSplatPath: (userFuture === null || userFuture === void 0 ? void 0 : userFuture.v3_relativeSplatPath) === true,
190
- v3_throwAbortReason: (userFuture === null || userFuture === void 0 ? void 0 : userFuture.v3_throwAbortReason) === true
190
+ v3_throwAbortReason: (userFuture === null || userFuture === void 0 ? void 0 : userFuture.v3_throwAbortReason) === true,
191
+ unstable_serverComponents: (userFuture === null || userFuture === void 0 ? void 0 : userFuture.unstable_serverComponents) === true
191
192
  };
192
193
  let reactRouterConfig = deepFreeze({
193
194
  appDirectory,
@@ -223,10 +224,13 @@ async function resolveEntryFiles({
223
224
  let defaultsDirectory = path__default["default"].resolve(__dirname, "config", "defaults");
224
225
  let userEntryClientFile = findEntry(appDirectory, "entry.client");
225
226
  let userEntryServerFile = findEntry(appDirectory, "entry.server");
227
+ let userEntryReactServerFile = findEntry(appDirectory, "entry.react-server");
226
228
  let entryServerFile;
227
- let entryClientFile = userEntryClientFile || "entry.client.tsx";
229
+ let entryReactServerFile;
230
+ let entryClientFile = userEntryClientFile || future.unstable_serverComponents ? "entry.client.rsc.tsx" : "entry.client.tsx";
228
231
  let pkgJson = await PackageJson__default["default"].load(rootDirectory);
229
232
  let deps = pkgJson.content.dependencies ?? {};
233
+ let serverRuntime = deps["@react-router/deno"] ? "deno" : deps["@react-router/cloudflare"] ? "cloudflare" : deps["@react-router/node"] ? "node" : undefined;
230
234
  if (!reactRouterConfig.ssr) {
231
235
  // This is a super-simple default since we don't need streaming in SPA Mode.
232
236
  // We can include this in a remix-spa template, but right now `npx remix reveal`
@@ -240,7 +244,6 @@ async function resolveEntryFiles({
240
244
  } else if (userEntryServerFile) {
241
245
  entryServerFile = userEntryServerFile;
242
246
  } else {
243
- let serverRuntime = deps["@react-router/deno"] ? "deno" : deps["@react-router/cloudflare"] ? "cloudflare" : deps["@react-router/node"] ? "node" : undefined;
244
247
  if (!serverRuntime) {
245
248
  let serverRuntimes = ["@react-router/deno", "@react-router/cloudflare", "@react-router/node"];
246
249
  let formattedList = disjunctionListFormat.format(serverRuntimes);
@@ -261,12 +264,26 @@ async function resolveEntryFiles({
261
264
  stdio: "inherit"
262
265
  });
263
266
  }
264
- entryServerFile = `entry.server.${serverRuntime}.tsx`;
267
+ entryServerFile = reactRouterConfig.future.unstable_serverComponents ? `entry.server.${serverRuntime}.rsc.tsx` : `entry.server.${serverRuntime}.tsx`;
268
+ }
269
+ if (future.unstable_serverComponents) {
270
+ if (userEntryReactServerFile) {
271
+ entryReactServerFile = userEntryReactServerFile;
272
+ } else {
273
+ if (!serverRuntime) {
274
+ let serverRuntimes = ["@react-router/deno", "@react-router/cloudflare", "@react-router/node"];
275
+ let formattedList = disjunctionListFormat.format(serverRuntimes);
276
+ throw new Error(`Could not determine server runtime. Please install one of the following: ${formattedList}`);
277
+ }
278
+ entryReactServerFile = `entry.react-server.${serverRuntime === "node" ? serverRuntime : "web"}.tsx`;
279
+ }
265
280
  }
266
281
  let entryClientFilePath = userEntryClientFile ? path__default["default"].resolve(reactRouterConfig.appDirectory, userEntryClientFile) : path__default["default"].resolve(defaultsDirectory, entryClientFile);
267
282
  let entryServerFilePath = userEntryServerFile ? path__default["default"].resolve(reactRouterConfig.appDirectory, userEntryServerFile) : path__default["default"].resolve(defaultsDirectory, entryServerFile);
283
+ let entryReactServerFilePath = userEntryReactServerFile ? path__default["default"].resolve(reactRouterConfig.appDirectory, userEntryReactServerFile) : entryReactServerFile ? path__default["default"].resolve(defaultsDirectory, entryReactServerFile) : undefined;
268
284
  return {
269
285
  entryClientFilePath,
286
+ entryReactServerFilePath,
270
287
  entryServerFilePath
271
288
  };
272
289
  }
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/invariant.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -0,0 +1 @@
1
+ export declare function createServerReference(_: unknown, mod: string, name: string): void;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
+ *
4
+ * Copyright (c) Remix Software Inc.
5
+ *
6
+ * This source code is licensed under the MIT license found in the
7
+ * LICENSE.md file in the root directory of this source tree.
8
+ *
9
+ * @license MIT
10
+ */
11
+ 'use strict';
12
+
13
+ Object.defineProperty(exports, '__esModule', { value: true });
14
+
15
+ function createServerReference(_, mod, name) {
16
+ throw new Error("Server references are not yet implemented.");
17
+ }
18
+
19
+ exports.createServerReference = createServerReference;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -227,7 +227,16 @@ async function build(root, {
227
227
  });
228
228
  }
229
229
  await cleanBuildDirectory(viteConfig, ctx);
230
- // Run the Vite client build first
230
+ // Then run React server build first
231
+ if (reactRouterConfig.future.unstable_serverComponents) {
232
+ // TODO: This will be handled by the vite env API in the future
233
+ process.env.REACT_SERVER_BUILD = "1";
234
+ await viteBuild({
235
+ ssr: true
236
+ });
237
+ }
238
+ process.env.REACT_SERVER_BUILD = "";
239
+ // Run the Vite client build second
231
240
  await viteBuild({
232
241
  ssr: false
233
242
  });
@@ -267,6 +276,10 @@ async function build(root, {
267
276
  reactRouterConfig,
268
277
  viteConfig
269
278
  }));
279
+ const {
280
+ serverModules
281
+ } = plugin.getReactServerOptions();
282
+ invariant["default"](!serverModules.size, "`use server` is not yet supported.");
270
283
  }
271
284
 
272
285
  exports.build = build;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/vite/dev.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -59,6 +59,7 @@ type ReactRouterPluginSsrBuildContext = {
59
59
  export type ReactRouterPluginContext = ReactRouterPluginSsrBuildContext & {
60
60
  rootDirectory: string;
61
61
  entryClientFilePath: string;
62
+ entryReactServerFilePath?: string;
62
63
  entryServerFilePath: string;
63
64
  reactRouterConfig: ResolvedVitePluginConfig;
64
65
  viteManifestEnabled: boolean;
@@ -68,4 +69,12 @@ type MaybePromise<T> = T | Promise<T>;
68
69
  export declare let setReactRouterDevLoadContext: (loadContext: (request: Request) => MaybePromise<Record<string, unknown>>) => void;
69
70
  export type ReactRouterVitePlugin = (config?: VitePluginConfig) => Vite.Plugin[];
70
71
  export declare const reactRouterVitePlugin: ReactRouterVitePlugin;
72
+ declare global {
73
+ var __clientModules: Set<string>;
74
+ var __serverModules: Set<string>;
75
+ }
76
+ export declare function getReactServerOptions(): {
77
+ clientModules: Set<string>;
78
+ serverModules: Set<string>;
79
+ };
71
80
  export {};
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -22,6 +22,7 @@ var reactRouter = require('react-router');
22
22
  var esModuleLexer = require('es-module-lexer');
23
23
  var jsesc = require('jsesc');
24
24
  var colors = require('picocolors');
25
+ var unpluginRsc = require('unplugin-rsc');
25
26
  var findConfig = require('../config/findConfig.js');
26
27
  var invariant = require('../invariant.js');
27
28
  var nodeAdapter = require('./node-adapter.js');
@@ -118,13 +119,16 @@ let serverManifestId = vmod.id("server-manifest");
118
119
  let browserManifestId = vmod.id("browser-manifest");
119
120
  let hmrRuntimeId = vmod.id("hmr-runtime");
120
121
  let injectHmrRuntimeId = vmod.id("inject-hmr-runtime");
122
+ let reactServerBuildId = vmod.id("react-server-build");
123
+ let clientReferencesId = vmod.id("client-references");
124
+ let serverReferencesId = vmod.id("server-references");
121
125
  const resolveRelativeRouteFilePath = (route, reactRouterConfig) => {
122
126
  let vite = importViteEsmSync.importViteEsmSync();
123
127
  let file = route.file;
124
128
  let fullPath = path__namespace.resolve(reactRouterConfig.appDirectory, file);
125
129
  return vite.normalizePath(fullPath);
126
130
  };
127
- let vmods = [serverBuildId, serverManifestId, browserManifestId];
131
+ let vmods = [serverBuildId, serverManifestId, browserManifestId, reactServerBuildId, clientReferencesId, serverReferencesId];
128
132
  const invalidateVirtualModules = viteDevServer => {
129
133
  vmods.forEach(vmod$1 => {
130
134
  let mod = viteDevServer.moduleGraph.getModuleById(vmod.resolve(vmod$1));
@@ -233,6 +237,7 @@ const getServerBundleBuildConfig = viteUserConfig => {
233
237
  return viteUserConfig.__reactRouterServerBundleBuildConfig;
234
238
  };
235
239
  let getServerBuildDirectory = ctx => path__namespace.join(ctx.reactRouterConfig.buildDirectory, "server", ...(ctx.serverBundleBuildConfig ? [ctx.serverBundleBuildConfig.serverBundleId] : []));
240
+ let getReactServerBuildDirectory = reactRouterConfig => path__namespace.join(reactRouterConfig.buildDirectory, "react-server");
236
241
  let getClientBuildDirectory = reactRouterConfig => path__namespace.join(reactRouterConfig.buildDirectory, "client");
237
242
  let defaultEntriesDir = path__namespace.resolve(__dirname, "..", "config", "defaults");
238
243
  let defaultEntries = fse__namespace.readdirSync(defaultEntriesDir).map(filename => path__namespace.join(defaultEntriesDir, filename));
@@ -280,6 +285,7 @@ const reactRouterVitePlugin = _config => {
280
285
  });
281
286
  let {
282
287
  entryClientFilePath,
288
+ entryReactServerFilePath,
283
289
  entryServerFilePath
284
290
  } = await config.resolveEntryFiles({
285
291
  rootDirectory,
@@ -297,6 +303,7 @@ const reactRouterVitePlugin = _config => {
297
303
  reactRouterConfig,
298
304
  rootDirectory,
299
305
  entryClientFilePath,
306
+ entryReactServerFilePath,
300
307
  entryServerFilePath,
301
308
  viteManifestEnabled,
302
309
  ...ssrBuildCtx
@@ -314,7 +321,7 @@ const reactRouterVitePlugin = _config => {
314
321
  ctx.serverBundleBuildConfig.routes :
315
322
  // Otherwise, all routes are imported as usual
316
323
  ctx.reactRouterConfig.routes;
317
- return `
324
+ let code = `
318
325
  import * as entryServer from ${JSON.stringify(resolveFileUrl.resolveFileUrl(ctx, ctx.entryServerFilePath))};
319
326
  ${Object.keys(routes).map((key, index) => {
320
327
  let route = routes[key];
@@ -340,6 +347,61 @@ const reactRouterVitePlugin = _config => {
340
347
  }`;
341
348
  }).join(",\n ")}
342
349
  };`;
350
+ if (ctx.reactRouterConfig.future.unstable_serverComponents) {
351
+ code += `
352
+ export { default as clientReferences } from ${JSON.stringify(clientReferencesId)};`;
353
+ }
354
+ return code;
355
+ };
356
+ let {
357
+ clientModules,
358
+ serverModules
359
+ } = getReactServerOptions();
360
+ let getReactServerEntry = () => {
361
+ invariant["default"](viteConfig, "viteconfig required to generate the react-server entry");
362
+ invariant["default"](ctx.entryReactServerFilePath, "entryReactServerFilePath required to generate the react-server entry");
363
+ let routes = ctx.serverBundleBuildConfig ?
364
+ // For server bundle builds, the server build should only import the
365
+ // routes for this bundle rather than importing all routes
366
+ ctx.serverBundleBuildConfig.routes :
367
+ // Otherwise, all routes are imported as usual
368
+ ctx.reactRouterConfig.routes;
369
+ return `
370
+ import * as entryServer from ${JSON.stringify(resolveFileUrl.resolveFileUrl(ctx, ctx.entryReactServerFilePath))};
371
+ ${Object.keys(routes).map((key, index) => {
372
+ let route = routes[key];
373
+ return `import * as route${index} from ${JSON.stringify(resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)))};`;
374
+ }).join("\n")}
375
+ export const future = ${JSON.stringify(ctx.reactRouterConfig.future)};
376
+ export const basename = ${JSON.stringify(ctx.reactRouterConfig.basename)};
377
+ export const entry = { module: entryServer };
378
+ export const routes = {
379
+ ${Object.keys(routes).map((key, index) => {
380
+ let route = routes[key];
381
+ return `${JSON.stringify(key)}: {
382
+ id: ${JSON.stringify(route.id)},
383
+ parentId: ${JSON.stringify(route.parentId)},
384
+ path: ${JSON.stringify(route.path)},
385
+ index: ${JSON.stringify(route.index)},
386
+ caseSensitive: ${JSON.stringify(route.caseSensitive)},
387
+ module: route${index}
388
+ }`;
389
+ }).join(",\n ")}
390
+ };`;
391
+ };
392
+ let getClientReferencesEntry = () => {
393
+ let result = "export default {";
394
+ for (let clientModule of clientModules) {
395
+ result += `${JSON.stringify(prodHash(clientModule))}: () => import(${JSON.stringify(clientModule)}),`;
396
+ }
397
+ return `${result}};`;
398
+ };
399
+ let getServerReferencesEntry = () => {
400
+ let result = "export default {";
401
+ for (let serverModule of serverModules) {
402
+ result += `${JSON.stringify(prodHash(serverModule))}: () => import(${JSON.stringify(serverModule)}),`;
403
+ }
404
+ return `${result}\};`;
343
405
  };
344
406
  let loadViteManifest = async directory => {
345
407
  let manifestContents = await fse__namespace.readFile(path__namespace.resolve(directory, ".vite", "manifest.json"), "utf-8");
@@ -538,9 +600,9 @@ const reactRouterVitePlugin = _config => {
538
600
  rollupOptions: {
539
601
  ...baseRollupOptions,
540
602
  preserveEntrySignatures: "exports-only",
541
- input: [ctx.entryClientFilePath, ...Object.values(ctx.reactRouterConfig.routes).map(route => `${path__namespace.resolve(ctx.reactRouterConfig.appDirectory, route.file)}${BUILD_CLIENT_ROUTE_QUERY_STRING}`)]
603
+ input: [ctx.entryClientFilePath, ...Object.values(ctx.reactRouterConfig.routes).map(route => `${path__namespace.resolve(ctx.reactRouterConfig.appDirectory, route.file)}${BUILD_CLIENT_ROUTE_QUERY_STRING}`), ...(ctx.reactRouterConfig.future.unstable_serverComponents ? clientModules : [])]
542
604
  }
543
- } : {
605
+ } : !ctx.reactRouterConfig.future.unstable_serverComponents || !process.env.REACT_SERVER_BUILD ? {
544
606
  // We move SSR-only assets to client assets. Note that the
545
607
  // SSR build can also emit code-split JS files (e.g. by
546
608
  // dynamic import) under the same assets directory
@@ -561,6 +623,28 @@ const reactRouterVitePlugin = _config => {
561
623
  format: ctx.reactRouterConfig.serverModuleFormat
562
624
  }
563
625
  }
626
+ } : {
627
+ // We move SSR-only assets to client assets. Note that the
628
+ // SSR build can also emit code-split JS files (e.g. by
629
+ // dynamic import) under the same assets directory
630
+ // regardless of "ssrEmitAssets" option, so we also need to
631
+ // keep these JS files have to be kept as-is.
632
+ ssrEmitAssets: true,
633
+ copyPublicDir: false,
634
+ // Assets in the public directory are only used by the client
635
+ manifest: true,
636
+ // We need the manifest to detect SSR-only assets
637
+ outDir: getReactServerBuildDirectory(ctx.reactRouterConfig),
638
+ rollupOptions: {
639
+ ...baseRollupOptions,
640
+ preserveEntrySignatures: "exports-only",
641
+ // TODO: Add server references (serverModules) to input
642
+ input: reactServerBuildId,
643
+ output: {
644
+ entryFileNames: ctx.reactRouterConfig.serverBuildFile,
645
+ format: ctx.reactRouterConfig.serverModuleFormat
646
+ }
647
+ }
564
648
  })
565
649
  }
566
650
  } : undefined),
@@ -714,7 +798,7 @@ const reactRouterVitePlugin = _config => {
714
798
  // After the SSR build is finished, we inspect the Vite manifest for
715
799
  // the SSR build and move server-only assets to client assets directory
716
800
  async handler() {
717
- if (!ctx.isSsrBuild) {
801
+ if (!ctx.isSsrBuild || process.env.REACT_SERVER_BUILD) {
718
802
  return;
719
803
  }
720
804
  invariant["default"](viteConfig);
@@ -798,6 +882,18 @@ const reactRouterVitePlugin = _config => {
798
882
  });
799
883
  return `window.__remixManifest=${reactRouterManifestString};`;
800
884
  }
885
+ case vmod.resolve(reactServerBuildId):
886
+ {
887
+ return getReactServerEntry();
888
+ }
889
+ case vmod.resolve(clientReferencesId):
890
+ {
891
+ return getClientReferencesEntry();
892
+ }
893
+ case vmod.resolve(serverReferencesId):
894
+ {
895
+ return getServerReferencesEntry();
896
+ }
801
897
  }
802
898
  }
803
899
  }, {
@@ -853,9 +949,20 @@ const reactRouterVitePlugin = _config => {
853
949
  }, {
854
950
  name: "react-router-route-exports",
855
951
  async transform(code, id, options) {
856
- if (options !== null && options !== void 0 && options.ssr) return;
952
+ var _ctx;
953
+ if (options !== null && options !== void 0 && options.ssr && !((_ctx = ctx) !== null && _ctx !== void 0 && _ctx.reactRouterConfig.future.unstable_serverComponents)) {
954
+ return;
955
+ }
857
956
  let route = getRoute(ctx.reactRouterConfig, id);
858
957
  if (!route) return;
958
+ let [filepath] = id.split("?");
959
+ if (ctx.reactRouterConfig.future.unstable_serverComponents && process.env.REACT_SERVER_BUILD) {
960
+ return removeExports.removeExports(code, CLIENT_ROUTE_EXPORTS, {
961
+ sourceMaps: true,
962
+ filename: id,
963
+ sourceFileName: filepath
964
+ });
965
+ }
859
966
  if (!ctx.reactRouterConfig.ssr) {
860
967
  let serverOnlyExports = esModuleLexer.parse(code)[1].map(exp => exp.n).filter(exp => SERVER_ONLY_ROUTE_EXPORTS.includes(exp));
861
968
  if (serverOnlyExports.length > 0) {
@@ -871,7 +978,6 @@ const reactRouterVitePlugin = _config => {
871
978
  }
872
979
  }
873
980
  }
874
- let [filepath] = id.split("?");
875
981
  return removeExports.removeExports(code, SERVER_ONLY_ROUTE_EXPORTS, {
876
982
  sourceMaps: true,
877
983
  filename: id,
@@ -967,6 +1073,82 @@ const reactRouterVitePlugin = _config => {
967
1073
  });
968
1074
  return modules;
969
1075
  }
1076
+ }, {
1077
+ name: "remix:react-server",
1078
+ config() {
1079
+ const env = process.env.REACT_SERVER_BUILD ? "server" : "client";
1080
+ switch (env) {
1081
+ case "server":
1082
+ return {
1083
+ optimizeDeps: {
1084
+ include: ["react", "react/jsx-runtime", "react/jsx-dev-runtime", "react-server-dom-diy/server"]
1085
+ },
1086
+ resolve: {
1087
+ conditions: ["react-server"]
1088
+ },
1089
+ ssr: {
1090
+ noExternal: ["react", "react/jsx-runtime", "react/jsx-dev-runtime", "react-server-dom-diy/server"],
1091
+ optimizeDeps: {
1092
+ include: ["react", "react/jsx-runtime", "react/jsx-dev-runtime", "react-server-dom-diy/server"]
1093
+ },
1094
+ resolve: {
1095
+ conditions: ["react-server"],
1096
+ externalConditions: ["react-server"]
1097
+ }
1098
+ }
1099
+ };
1100
+ }
1101
+ },
1102
+ configResolved(resolvedViteConfig) {
1103
+ viteConfig = resolvedViteConfig;
1104
+ invariant["default"](viteConfig);
1105
+ },
1106
+ transform(...args) {
1107
+ invariant["default"](viteConfig);
1108
+ const env = process.env.REACT_SERVER_BUILD ? "server" : "client";
1109
+ let hash = viteConfig.mode !== "production" ? devHash : prodHash;
1110
+ switch (env) {
1111
+ case "client":
1112
+ return unpluginRsc.rscClientPlugin.vite({
1113
+ include: ["**/*"],
1114
+ transformModuleId: hash,
1115
+ useServerRuntime: {
1116
+ function: "createServerReference",
1117
+ module: "@react-router/dev/dist/runtime.client.js"
1118
+ },
1119
+ onModuleFound(id, type) {
1120
+ switch (type) {
1121
+ case "use server":
1122
+ serverModules.add(id);
1123
+ break;
1124
+ }
1125
+ }
1126
+ }).transform.call(this, ...args);
1127
+ case "server":
1128
+ return unpluginRsc.rscServerPlugin.vite({
1129
+ include: ["**/*"],
1130
+ transformModuleId: hash,
1131
+ useClientRuntime: {
1132
+ function: "registerClientReference",
1133
+ module: "react-server-dom-diy/server"
1134
+ },
1135
+ useServerRuntime: {
1136
+ function: "registerServerReference",
1137
+ module: "react-server-dom-diy/server"
1138
+ },
1139
+ onModuleFound(id, type) {
1140
+ switch (type) {
1141
+ case "use client":
1142
+ clientModules.add(id);
1143
+ break;
1144
+ case "use server":
1145
+ serverModules.add(id);
1146
+ break;
1147
+ }
1148
+ }
1149
+ }).transform.call(this, ...args);
1150
+ }
1151
+ }
970
1152
  }];
971
1153
  };
972
1154
  function isInReactRouterMonorepo() {
@@ -1165,8 +1347,31 @@ function createPrerenderRoutes(manifest, parentId = "", routesByParentId = group
1165
1347
  };
1166
1348
  });
1167
1349
  }
1350
+ function prodHash(str, _) {
1351
+ return `/${path__namespace.relative(process.cwd(), str)}`;
1352
+ }
1353
+ function devHash(str, _) {
1354
+ const resolved = path__namespace.resolve(str);
1355
+ let unixPath = resolved.replace(/\\/g, "/");
1356
+ if (!unixPath.startsWith("/")) {
1357
+ unixPath = `/${unixPath}`;
1358
+ }
1359
+ if (resolved.startsWith(process.cwd())) {
1360
+ return `/${path__namespace.relative(process.cwd(), unixPath)}`;
1361
+ }
1362
+ return `/@fs${unixPath}`;
1363
+ }
1364
+ global.__clientModules = global.__clientModules || new Set();
1365
+ global.__serverModules = global.__serverModules || new Set();
1366
+ function getReactServerOptions() {
1367
+ return {
1368
+ clientModules: global.__clientModules,
1369
+ serverModules: global.__serverModules
1370
+ };
1371
+ }
1168
1372
 
1169
1373
  exports.extractPluginContext = extractPluginContext;
1374
+ exports.getReactServerOptions = getReactServerOptions;
1170
1375
  exports.getServerBuildDirectory = getServerBuildDirectory;
1171
1376
  exports.loadPluginContext = loadPluginContext;
1172
1377
  exports.reactRouterVitePlugin = reactRouterVitePlugin;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,2 +1,4 @@
1
1
  import type { GeneratorOptions } from "@babel/generator";
2
- export declare const removeExports: (source: string, exportsToRemove: string[], generateOptions?: GeneratorOptions) => import("@babel/generator").GeneratorResult;
2
+ export declare const removeExports: (source: string, exportsToRemove: string[], generateOptions?: GeneratorOptions) => import("@babel/generator").GeneratorResult | {
3
+ code: string;
4
+ };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -179,6 +179,20 @@ const removeExports = (source, exportsToRemove, generateOptions = {}) => {
179
179
  if (shouldRemove) {
180
180
  path.remove();
181
181
  }
182
+ },
183
+ ExportDefaultDeclaration(path) {
184
+ if (exportsToRemove.includes("default")) {
185
+ removedExports.add("default");
186
+ path.remove();
187
+ return false;
188
+ }
189
+ },
190
+ ExportDefaultSpecifier(path) {
191
+ if (exportsToRemove.includes("default")) {
192
+ removedExports.add("default");
193
+ path.remove();
194
+ return false;
195
+ }
182
196
  }
183
197
  });
184
198
  if (removedExports.size === 0) {
@@ -272,7 +286,9 @@ const removeExports = (source, exportsToRemove, generateOptions = {}) => {
272
286
  ImportNamespaceSpecifier: sweepImport
273
287
  });
274
288
  } while (referencesRemovedInThisPass);
275
- return babel.generate(document, generateOptions);
289
+ return {
290
+ code: babel.generate(document, generateOptions).code + `\n${[...removedExports].map(exp => exp === "default" ? "export default 1;" : `export const ${exp} = 1;`).join("\n")}`
291
+ };
276
292
  };
277
293
 
278
294
  exports.removeExports = removeExports;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/vite/vmod.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-cb25a21e1
2
+ * @react-router/dev v0.0.0-experimental-27337b0d6
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-router/dev",
3
- "version": "0.0.0-experimental-cb25a21e1",
3
+ "version": "0.0.0-experimental-27337b0d6",
4
4
  "description": "Dev tools and CLI for React Router",
5
5
  "homepage": "https://reactrouter.com",
6
6
  "bugs": {
@@ -42,9 +42,9 @@
42
42
  "react-refresh": "^0.14.0",
43
43
  "semver": "^7.3.7",
44
44
  "set-cookie-parser": "^2.6.0",
45
- "@react-router/node": "0.0.0-experimental-cb25a21e1",
46
- "@react-router/server-runtime": "0.0.0-experimental-cb25a21e1",
47
- "react-router": "0.0.0-experimental-cb25a21e1"
45
+ "@react-router/node": "0.0.0-experimental-27337b0d6",
46
+ "@react-router/server-runtime": "0.0.0-experimental-27337b0d6",
47
+ "react-router": "0.0.0-experimental-27337b0d6"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/babel__core": "^7.20.5",
@@ -65,23 +65,29 @@
65
65
  "express": "^4.17.1",
66
66
  "esbuild-register": "^3.3.2",
67
67
  "fast-glob": "3.2.11",
68
+ "react-server-dom-diy": "0.0.0-experimental-15a3a5622-202404158",
68
69
  "strip-ansi": "^6.0.1",
69
70
  "tiny-invariant": "^1.2.0",
71
+ "unplugin-rsc": "0.0.9",
70
72
  "vite": "^5.1.0",
71
73
  "wrangler": "^3.28.2",
72
- "@react-router/serve": "0.0.0-experimental-cb25a21e1"
74
+ "@react-router/serve": "0.0.0-experimental-27337b0d6"
73
75
  },
74
76
  "peerDependencies": {
77
+ "react-server-dom-diy": "*",
75
78
  "typescript": "^5.1.0",
76
79
  "vite": "^5.1.0",
77
80
  "wrangler": "^3.28.2",
78
- "react-router": "^0.0.0-experimental-cb25a21e1",
79
- "@react-router/serve": "^0.0.0-experimental-cb25a21e1"
81
+ "react-router": "^0.0.0-experimental-27337b0d6",
82
+ "@react-router/serve": "^0.0.0-experimental-27337b0d6"
80
83
  },
81
84
  "peerDependenciesMeta": {
82
85
  "@react-router/serve": {
83
86
  "optional": true
84
87
  },
88
+ "react-server-dom-diy": {
89
+ "optional": true
90
+ },
85
91
  "typescript": {
86
92
  "optional": true
87
93
  },