@react-router/dev 7.13.2 → 7.14.0-pre.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,78 @@
1
1
  # `@react-router/dev`
2
2
 
3
+ ## 7.14.0-pre.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add support for Vite 8 ([#14876](https://github.com/remix-run/react-router/pull/14876))
8
+
9
+ ### Patch Changes
10
+
11
+ - support for prerendering multiple server bundles with v8_viteEnvironmentApi ([#14921](https://github.com/remix-run/react-router/pull/14921))
12
+ - rsc framework mode prerender / spa mode support ([#14907](https://github.com/remix-run/react-router/pull/14907))
13
+ - UNSTABLE RSC FRAMEWORK MODE BREAKING CHANGE - Existing route module exports remain unchanged from stable v7 non-RSC mode, but new exports are added for RSC mode. If you want to use RSC features, you will need to update your route modules to export the new annotations. ([#14901](https://github.com/remix-run/react-router/pull/14901))
14
+
15
+ If you are using RSC framework mode currently, you will need to update your route modules to the new conventions. The following route module components have their own mutually exclusive server component counterparts:
16
+
17
+ | Server Component Export | Client Component |
18
+ | ----------------------- | ----------------- |
19
+ | `ServerComponent` | `default` |
20
+ | `ServerErrorBoundary` | `ErrorBoundary` |
21
+ | `ServerLayout` | `Layout` |
22
+ | `ServerHydrateFallback` | `HydrateFallback` |
23
+
24
+ If you were previously exporting a `ServerComponent`, your `ErrorBoundary`, `Layout`, and `HydrateFallback` were also server components. If you want to keep those as server components, you can rename them and prefix them with `Server`. If you were previously importing the implementations of those components from a client module, you can simply inline them.
25
+
26
+ Example:
27
+
28
+ Before
29
+
30
+ ```tsx
31
+ import { ErrorBoundary as ClientErrorBoundary } from "./client";
32
+
33
+ export function ServerComponent() {
34
+ // ...
35
+ }
36
+
37
+ export function ErrorBoundary() {
38
+ return <ClientErrorBoundary />;
39
+ }
40
+
41
+ export function Layout() {
42
+ // ...
43
+ }
44
+
45
+ export function HydrateFallback() {
46
+ // ...
47
+ }
48
+ ```
49
+
50
+ After
51
+
52
+ ```tsx
53
+ export function ServerComponent() {
54
+ // ...
55
+ }
56
+
57
+ export function ErrorBoundary() {
58
+ // previous implementation of ClientErrorBoundary, this is now a client component
59
+ }
60
+
61
+ export function ServerLayout() {
62
+ // rename previous Layout export to ServerLayout to make it a server component
63
+ }
64
+
65
+ export function ServerHydrateFallback() {
66
+ // rename previous HydrateFallback export to ServerHydrateFallback to make it a server component
67
+ }
68
+ ```
69
+
70
+ - update the reveal command to support rsc for `entry.client`, `entry.rsc`, `entry.ssr` ([#14904](https://github.com/remix-run/react-router/pull/14904))
71
+ - Updated dependencies:
72
+ - `react-router@7.14.0-pre.0`
73
+ - `@react-router/node@7.14.0-pre.0`
74
+ - `@react-router/serve@7.14.0-pre.0`
75
+
3
76
  ## 7.13.2
4
77
 
5
78
  ### Patch Changes
@@ -13,7 +86,6 @@
13
86
  By default, React Router normalizes the `request.url` passed to your `loader`, `action`, and `middleware` functions by removing React Router's internal implementation details (`.data` suffixes, `index` + `_routes` query params).
14
87
 
15
88
  Enabling this flag removes that normalization and passes the raw HTTP `request` instance to your handlers. This provides a few benefits:
16
-
17
89
  - Reduces server-side overhead by eliminating multiple `new Request()` calls on the critical path
18
90
  - Allows you to distinguish document from data requests in your handlers base don the presence of a `.data` suffix (useful for observability purposes)
19
91
 
@@ -89,25 +161,25 @@
89
161
 
90
162
  | URL `/a/b/c` | **HTTP pathname** | **`request` pathname\`** |
91
163
  | ------------ | ----------------- | ------------------------ |
92
- | **Document** | `/a/b/c` | `/a/b/c` ✅ |
93
- | **Data** | `/a/b/c.data` | `/a/b/c` ✅ |
164
+ | **Document** | `/a/b/c` | `/a/b/c` ✅ |
165
+ | **Data** | `/a/b/c.data` | `/a/b/c` ✅ |
94
166
 
95
167
  | URL `/a/b/c/` | **HTTP pathname** | **`request` pathname\`** |
96
168
  | ------------- | ----------------- | ------------------------ |
97
- | **Document** | `/a/b/c/` | `/a/b/c/` ✅ |
169
+ | **Document** | `/a/b/c/` | `/a/b/c/` ✅ |
98
170
  | **Data** | `/a/b/c.data` | `/a/b/c` ⚠️ |
99
171
 
100
172
  With this flag enabled, these pathnames will be made consistent though a new `_.data` format for client-side `.data` requests:
101
173
 
102
174
  | URL `/a/b/c` | **HTTP pathname** | **`request` pathname\`** |
103
175
  | ------------ | ----------------- | ------------------------ |
104
- | **Document** | `/a/b/c` | `/a/b/c` ✅ |
105
- | **Data** | `/a/b/c.data` | `/a/b/c` ✅ |
176
+ | **Document** | `/a/b/c` | `/a/b/c` ✅ |
177
+ | **Data** | `/a/b/c.data` | `/a/b/c` ✅ |
106
178
 
107
179
  | URL `/a/b/c/` | **HTTP pathname** | **`request` pathname\`** |
108
180
  | ------------- | ------------------ | ------------------------ |
109
- | **Document** | `/a/b/c/` | `/a/b/c/` ✅ |
110
- | **Data** | `/a/b/c/_.data` ⬅️ | `/a/b/c/` ✅ |
181
+ | **Document** | `/a/b/c/` | `/a/b/c/` ✅ |
182
+ | **Data** | `/a/b/c/_.data` ⬅️ | `/a/b/c/` ✅ |
111
183
 
112
184
  This a bug fix but we are putting it behind an opt-in flag because it has the potential to be a "breaking bug fix" if you are relying on the URL format for any other application or caching logic.
113
185
 
@@ -365,7 +437,6 @@
365
437
  - Stabilize middleware and context APIs. ([#14215](https://github.com/remix-run/react-router/pull/14215))
366
438
 
367
439
  We have removed the `unstable_` prefix from the following APIs and they are now considered stable and ready for production use:
368
-
369
440
  - [`RouterContextProvider`](https://reactrouter.com/api/utils/RouterContextProvider)
370
441
  - [`createContext`](https://reactrouter.com/api/utils/createContext)
371
442
  - `createBrowserRouter` [`getContext`](https://reactrouter.com/api/data-routers/createBrowserRouter#optsgetcontext) option
@@ -1108,7 +1179,6 @@
1108
1179
  ```
1109
1180
 
1110
1181
  This initial implementation targets type inference for:
1111
-
1112
1182
  - `Params` : Path parameters from your routing config in `routes.ts` including file-based routing
1113
1183
  - `LoaderData` : Loader data from `loader` and/or `clientLoader` within your route module
1114
1184
  - `ActionData` : Action data from `action` and/or `clientAction` within your route module
@@ -1123,7 +1193,6 @@
1123
1193
  ```
1124
1194
 
1125
1195
  Check out our docs for more:
1126
-
1127
1196
  - [_Explanations > Type Safety_](https://reactrouter.com/dev/guides/explanation/type-safety)
1128
1197
  - [_How-To > Setting up type safety_](https://reactrouter.com/dev/guides/how-to/setting-up-type-safety)
1129
1198
 
@@ -1323,7 +1392,6 @@
1323
1392
  - Vite: Provide `Unstable_ServerBundlesFunction` and `Unstable_VitePluginConfig` types ([#8654](https://github.com/remix-run/remix/pull/8654))
1324
1393
 
1325
1394
  - Vite: add `--sourcemapClient` and `--sourcemapServer` flags to `remix vite:build` ([#8613](https://github.com/remix-run/remix/pull/8613))
1326
-
1327
1395
  - `--sourcemapClient`
1328
1396
 
1329
1397
  - `--sourcemapClient=inline`
@@ -1660,7 +1728,6 @@
1660
1728
  - Add support for `clientLoader`/`clientAction`/`HydrateFallback` route exports ([RFC](https://github.com/remix-run/remix/discussions/7634)) ([#8173](https://github.com/remix-run/remix/pull/8173))
1661
1729
 
1662
1730
  Remix now supports loaders/actions that run on the client (in addition to, or instead of the loader/action that runs on the server). While we still recommend server loaders/actions for the majority of your data needs in a Remix app - these provide some levers you can pull for more advanced use-cases such as:
1663
-
1664
1731
  - Leveraging a data source local to the browser (i.e., `localStorage`)
1665
1732
  - Managing a client-side cache of server data (like `IndexedDB`)
1666
1733
  - Bypassing the Remix server in a BFF setup and hitting your API directly from the browser
@@ -2064,7 +2131,6 @@
2064
2131
  - Output esbuild metafiles for bundle analysis ([#6772](https://github.com/remix-run/remix/pull/6772))
2065
2132
 
2066
2133
  Written to server build directory (`build/` by default):
2067
-
2068
2134
  - `metafile.css.json`
2069
2135
  - `metafile.js.json` (browser JS)
2070
2136
  - `metafile.server.json` (server JS)
@@ -2162,7 +2228,6 @@
2162
2228
  - built-in tls support ([#6483](https://github.com/remix-run/remix/pull/6483))
2163
2229
 
2164
2230
  New options:
2165
-
2166
2231
  - `--tls-key` / `tlsKey`: TLS key
2167
2232
  - `--tls-cert` / `tlsCert`: TLS Certificate
2168
2233
 
@@ -2433,7 +2498,6 @@
2433
2498
  ```
2434
2499
 
2435
2500
  The dev server will:
2436
-
2437
2501
  - force `NODE_ENV=development` and warn you if it was previously set to something else
2438
2502
  - rebuild your app whenever your Remix app code changes
2439
2503
  - restart your app server whenever rebuilds succeed
package/dist/cli/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @react-router/dev v7.13.2
3
+ * @react-router/dev v7.14.0-pre.0
4
4
  *
5
5
  * Copyright (c) Remix Software Inc.
6
6
  *
@@ -511,7 +511,7 @@ async function resolveConfig({
511
511
  unstable_previewServerPrerendering: userAndPresetConfigs.future?.unstable_previewServerPrerendering ?? false,
512
512
  v8_middleware: userAndPresetConfigs.future?.v8_middleware ?? false,
513
513
  v8_splitRouteModules: userAndPresetConfigs.future?.v8_splitRouteModules ?? false,
514
- v8_viteEnvironmentApi: userAndPresetConfigs.future?.v8_viteEnvironmentApi ?? false
514
+ v8_viteEnvironmentApi: (userAndPresetConfigs.future?.v8_viteEnvironmentApi || userAndPresetConfigs.future?.unstable_previewServerPrerendering) ?? false
515
515
  };
516
516
  let allowedActionOrigins = userAndPresetConfigs.allowedActionOrigins ?? false;
517
517
  let reactRouterConfig = deepFreeze({
@@ -1183,7 +1183,7 @@ function getRouteAnnotations({
1183
1183
  module: Module
1184
1184
  }>
1185
1185
  ` + "\n\n" + generate(matchesType).code + "\n\n" + import_dedent.default`
1186
- type Annotations = GetAnnotations<Info & { module: Module, matches: Matches }, ${ctx.rsc}>;
1186
+ type Annotations = GetAnnotations<Info & { module: Module, matches: Matches }>;
1187
1187
 
1188
1188
  export namespace Route {
1189
1189
  // links
@@ -1220,11 +1220,20 @@ function getRouteAnnotations({
1220
1220
  // HydrateFallback
1221
1221
  export type HydrateFallbackProps = Annotations["HydrateFallbackProps"];
1222
1222
 
1223
+ // ServerHydrateFallback
1224
+ export type ServerHydrateFallbackProps = Annotations["ServerHydrateFallbackProps"];
1225
+
1223
1226
  // Component
1224
1227
  export type ComponentProps = Annotations["ComponentProps"];
1225
1228
 
1229
+ // ServerComponent
1230
+ export type ServerComponentProps = Annotations["ServerComponentProps"];
1231
+
1226
1232
  // ErrorBoundary
1227
1233
  export type ErrorBoundaryProps = Annotations["ErrorBoundaryProps"];
1234
+
1235
+ // ServerErrorBoundary
1236
+ export type ServerErrorBoundaryProps = Annotations["ServerErrorBoundaryProps"];
1228
1237
  }
1229
1238
  `;
1230
1239
  return { filename: filename2, content };
@@ -1370,7 +1379,8 @@ async function hasReactRouterRscPlugin({
1370
1379
  root,
1371
1380
  viteBuildOptions: { config, logLevel, mode }
1372
1381
  }) {
1373
- const vite2 = await import("vite");
1382
+ await preloadVite();
1383
+ const vite2 = getVite();
1374
1384
  const viteConfig = await vite2.resolveConfig(
1375
1385
  {
1376
1386
  configFile: config,
@@ -1392,6 +1402,7 @@ async function hasReactRouterRscPlugin({
1392
1402
  var init_has_rsc_plugin = __esm({
1393
1403
  "vite/has-rsc-plugin.ts"() {
1394
1404
  "use strict";
1405
+ init_vite();
1395
1406
  }
1396
1407
  });
1397
1408
 
@@ -2306,12 +2317,16 @@ async function dev2(root, options = {}) {
2306
2317
  var clientEntries = ["entry.client.tsx", "entry.client.js", "entry.client.jsx"];
2307
2318
  var serverEntries = ["entry.server.tsx", "entry.server.js", "entry.server.jsx"];
2308
2319
  var entries = ["entry.client", "entry.server"];
2320
+ var rscEntries = ["entry.client", "entry.rsc", "entry.ssr"];
2309
2321
  var conjunctionListFormat = new Intl.ListFormat("en", {
2310
2322
  style: "long",
2311
2323
  type: "conjunction"
2312
2324
  });
2313
2325
  async function generateEntry(entry, rootDirectory, flags = {}) {
2314
2326
  rootDirectory = resolveRootDirectory(rootDirectory, flags);
2327
+ let configDir = "defaults";
2328
+ let entriesToUse = entries;
2329
+ let isRsc = false;
2315
2330
  if (await hasReactRouterRscPlugin({
2316
2331
  root: rootDirectory,
2317
2332
  viteBuildOptions: {
@@ -2319,12 +2334,15 @@ async function generateEntry(entry, rootDirectory, flags = {}) {
2319
2334
  mode: flags.mode
2320
2335
  }
2321
2336
  })) {
2322
- console.error(
2323
- import_picocolors8.default.red(
2324
- `The reveal command is currently not supported in RSC Framework Mode.`
2325
- )
2326
- );
2327
- process.exit(1);
2337
+ if (!entry) {
2338
+ await generateEntry("entry.client", rootDirectory, flags);
2339
+ await generateEntry("entry.rsc", rootDirectory, flags);
2340
+ await generateEntry("entry.ssr", rootDirectory, flags);
2341
+ return;
2342
+ }
2343
+ configDir = "default-rsc-entries";
2344
+ entriesToUse = rscEntries;
2345
+ isRsc = true;
2328
2346
  }
2329
2347
  if (!entry) {
2330
2348
  await generateEntry("entry.client", rootDirectory, flags);
@@ -2340,46 +2358,65 @@ async function generateEntry(entry, rootDirectory, flags = {}) {
2340
2358
  return;
2341
2359
  }
2342
2360
  let appDirectory = configResult.value.appDirectory;
2343
- if (!entries.includes(entry)) {
2344
- let entriesArray = Array.from(entries);
2361
+ if (!entriesToUse.includes(entry)) {
2362
+ let entriesArray = Array.from(entriesToUse);
2345
2363
  let list = conjunctionListFormat.format(entriesArray);
2346
2364
  console.error(
2347
2365
  import_picocolors8.default.red(`Invalid entry file. Valid entry files are ${list}`)
2348
2366
  );
2349
2367
  return;
2350
2368
  }
2351
- let { readPackageJSON } = await import("pkg-types");
2352
- let pkgJson = await readPackageJSON(rootDirectory);
2353
- let deps = pkgJson.dependencies ?? {};
2354
- if (!deps["@react-router/node"]) {
2355
- console.error(import_picocolors8.default.red(`No default server entry detected.`));
2356
- return;
2357
- }
2358
2369
  let defaultsDirectory = path9.resolve(
2359
2370
  path9.dirname(require.resolve("@react-router/dev/package.json")),
2360
2371
  "dist",
2361
2372
  "config",
2362
- "defaults"
2363
- );
2364
- let defaultEntryClient = path9.resolve(defaultsDirectory, "entry.client.tsx");
2365
- let defaultEntryServer = path9.resolve(
2366
- defaultsDirectory,
2367
- `entry.server.node.tsx`
2373
+ configDir
2368
2374
  );
2369
- let isServerEntry = entry === "entry.server";
2370
- let contents = isServerEntry ? await createServerEntry(rootDirectory, appDirectory, defaultEntryServer) : await createClientEntry(rootDirectory, appDirectory, defaultEntryClient);
2371
- let useTypeScript = flags.typescript ?? true;
2372
- let outputExtension = useTypeScript ? "tsx" : "jsx";
2373
- let outputEntry = `${entry}.${outputExtension}`;
2374
- let outputFile = path9.resolve(appDirectory, outputEntry);
2375
- if (!useTypeScript) {
2376
- let javascript = await transpile(contents, {
2377
- cwd: rootDirectory,
2378
- filename: isServerEntry ? defaultEntryServer : defaultEntryClient
2379
- });
2380
- await (0, import_promises4.writeFile)(outputFile, javascript, "utf-8");
2375
+ let outputFile;
2376
+ if (isRsc) {
2377
+ let defaultEntry = path9.resolve(defaultsDirectory, `${entry}.tsx`);
2378
+ outputFile = path9.resolve(appDirectory, `${entry}.tsx`);
2379
+ if ((0, import_node_fs4.existsSync)(outputFile)) {
2380
+ let relative7 = path9.relative(rootDirectory, outputFile);
2381
+ console.error(import_picocolors8.default.red(`Entry file ${relative7} already exists.`));
2382
+ return;
2383
+ }
2384
+ await (0, import_promises4.copyFile)(defaultEntry, outputFile);
2381
2385
  } else {
2382
- await (0, import_promises4.writeFile)(outputFile, contents, "utf-8");
2386
+ let { readPackageJSON } = await import("pkg-types");
2387
+ let pkgJson = await readPackageJSON(rootDirectory);
2388
+ let deps = pkgJson.dependencies ?? {};
2389
+ if (!deps["@react-router/node"]) {
2390
+ console.error(import_picocolors8.default.red(`No default server entry detected.`));
2391
+ return;
2392
+ }
2393
+ let defaultEntryClient = path9.resolve(
2394
+ defaultsDirectory,
2395
+ "entry.client.tsx"
2396
+ );
2397
+ let defaultEntryServer = path9.resolve(
2398
+ defaultsDirectory,
2399
+ `entry.server.node.tsx`
2400
+ );
2401
+ let isServerEntry = entry === "entry.server";
2402
+ let contents = isServerEntry ? await createServerEntry(rootDirectory, appDirectory, defaultEntryServer) : await createClientEntry(
2403
+ rootDirectory,
2404
+ appDirectory,
2405
+ defaultEntryClient
2406
+ );
2407
+ let useTypeScript = flags.typescript ?? true;
2408
+ let outputExtension = useTypeScript ? "tsx" : "jsx";
2409
+ let outputEntry = `${entry}.${outputExtension}`;
2410
+ outputFile = path9.resolve(appDirectory, outputEntry);
2411
+ if (!useTypeScript) {
2412
+ let javascript = await transpile(contents, {
2413
+ cwd: rootDirectory,
2414
+ filename: isServerEntry ? defaultEntryServer : defaultEntryClient
2415
+ });
2416
+ await (0, import_promises4.writeFile)(outputFile, javascript, "utf-8");
2417
+ } else {
2418
+ await (0, import_promises4.writeFile)(outputFile, contents, "utf-8");
2419
+ }
2383
2420
  }
2384
2421
  console.log(
2385
2422
  import_picocolors8.default.blue(
@@ -24,7 +24,6 @@ setServerCallback(
24
24
  );
25
25
 
26
26
  createFromReadableStream<RSCPayload>(getRSCStream()).then((payload) => {
27
- // @ts-expect-error - on 18 types, requires 19.
28
27
  startTransition(async () => {
29
28
  const formState =
30
29
  payload.type === "render" ? await payload.formState : undefined;
@@ -33,12 +32,11 @@ createFromReadableStream<RSCPayload>(getRSCStream()).then((payload) => {
33
32
  document,
34
33
  <StrictMode>
35
34
  <RSCHydratedRouter
36
- payload={payload}
37
35
  createFromReadableStream={createFromReadableStream}
36
+ payload={payload}
38
37
  />
39
38
  </StrictMode>,
40
39
  {
41
- // @ts-expect-error - no types for this yet
42
40
  formState,
43
41
  },
44
42
  );
@@ -13,6 +13,7 @@ import {
13
13
 
14
14
  // Import the routes generated by routes.ts
15
15
  import routes from "virtual:react-router/unstable_rsc/routes";
16
+ import routeDiscovery from "virtual:react-router/unstable_rsc/route-discovery";
16
17
  import basename from "virtual:react-router/unstable_rsc/basename";
17
18
  import unstable_reactRouterServeConfig from "virtual:react-router/unstable_rsc/react-router-serve-config";
18
19
 
@@ -35,6 +36,8 @@ export function fetchServer(
35
36
  requestContext,
36
37
  // The app routes.
37
38
  routes,
39
+ // The route discovery configuration.
40
+ routeDiscovery,
38
41
  // Encode the match with the React Server implementation.
39
42
  generateResponse(match, options) {
40
43
  return new Response(renderToReadableStream(match.payload, options), {
@@ -1,5 +1,4 @@
1
1
  import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr";
2
- // @ts-expect-error - no types for this, can import from root once on latest 19
3
2
  import { renderToReadableStream } from "react-dom/server.edge";
4
3
  import {
5
4
  unstable_routeRSCServerRequest as routeRSCServerRequest,
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v7.13.2
2
+ * @react-router/dev v7.14.0-pre.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/routes.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v7.13.2
2
+ * @react-router/dev v7.14.0-pre.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v7.13.2
2
+ * @react-router/dev v7.14.0-pre.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -537,7 +537,7 @@ async function resolveConfig({
537
537
  unstable_previewServerPrerendering: userAndPresetConfigs.future?.unstable_previewServerPrerendering ?? false,
538
538
  v8_middleware: userAndPresetConfigs.future?.v8_middleware ?? false,
539
539
  v8_splitRouteModules: userAndPresetConfigs.future?.v8_splitRouteModules ?? false,
540
- v8_viteEnvironmentApi: userAndPresetConfigs.future?.v8_viteEnvironmentApi ?? false
540
+ v8_viteEnvironmentApi: (userAndPresetConfigs.future?.v8_viteEnvironmentApi || userAndPresetConfigs.future?.unstable_previewServerPrerendering) ?? false
541
541
  };
542
542
  let allowedActionOrigins = userAndPresetConfigs.allowedActionOrigins ?? false;
543
543
  let reactRouterConfig = deepFreeze({