Skip to content

Commit d424be6

Browse files
fix(dev): add Vite 7 support (#13748)
1 parent 259c145 commit d424be6

File tree

25 files changed

+726
-19
lines changed

25 files changed

+726
-19
lines changed

.changeset/dull-pianos-matter.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-router/dev": patch
3+
---
4+
5+
Add Vite 7 support
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules
2+
3+
/.cache
4+
/build
5+
.env
6+
.react-router
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router";
2+
3+
export default function App() {
4+
return (
5+
<html lang="en">
6+
<head>
7+
<meta charSet="utf-8" />
8+
<meta name="viewport" content="width=device-width, initial-scale=1" />
9+
<Meta />
10+
<Links />
11+
</head>
12+
<body>
13+
<Outlet />
14+
<ScrollRestoration />
15+
<Scripts />
16+
</body>
17+
</html>
18+
);
19+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { type RouteConfig } from "@react-router/dev/routes";
2+
import { flatRoutes } from "@react-router/fs-routes";
3+
4+
export default flatRoutes() satisfies RouteConfig;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { MetaFunction } from "react-router";
2+
3+
export const meta: MetaFunction = () => {
4+
return [
5+
{ title: "New React Router App" },
6+
{ name: "description", content: "Welcome to React Router!" },
7+
];
8+
};
9+
10+
export default function Index() {
11+
return (
12+
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
13+
<h1>Welcome to React Router</h1>
14+
</div>
15+
);
16+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/// <reference types="@react-router/node" />
2+
/// <reference types="vite/client" />
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "integration-vite-7-beta-template",
3+
"version": "0.0.0",
4+
"private": true,
5+
"sideEffects": false,
6+
"type": "module",
7+
"scripts": {
8+
"dev": "react-router dev",
9+
"build": "react-router build",
10+
"start": "react-router-serve ./build/server/index.js",
11+
"typecheck": "react-router typegen && tsc"
12+
},
13+
"dependencies": {
14+
"@react-router/express": "workspace:*",
15+
"@react-router/node": "workspace:*",
16+
"@react-router/serve": "workspace:*",
17+
"@vanilla-extract/css": "^1.10.0",
18+
"@vanilla-extract/vite-plugin": "^3.9.2",
19+
"express": "^4.19.2",
20+
"isbot": "^5.1.11",
21+
"react": "^19.1.0",
22+
"react-dom": "^19.1.0",
23+
"react-router": "workspace:*",
24+
"serialize-javascript": "^6.0.1"
25+
},
26+
"devDependencies": {
27+
"@react-router/dev": "workspace:*",
28+
"@react-router/fs-routes": "workspace:*",
29+
"@react-router/remix-routes-option-adapter": "workspace:*",
30+
"@types/react": "^18.2.20",
31+
"@types/react-dom": "^18.2.7",
32+
"eslint": "^8.38.0",
33+
"typescript": "^5.1.6",
34+
"vite": "7.0.0-beta.0",
35+
"vite-env-only": "^3.0.1",
36+
"vite-tsconfig-paths": "^4.2.1"
37+
},
38+
"engines": {
39+
"node": ">=20.0.0"
40+
}
41+
}
Binary file not shown.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"include": ["env.d.ts", "**/*.ts", "**/*.tsx", ".react-router/types/**/*"],
3+
"compilerOptions": {
4+
"lib": ["DOM", "DOM.Iterable", "ES2022"],
5+
"verbatimModuleSyntax": true,
6+
"esModuleInterop": true,
7+
"jsx": "react-jsx",
8+
"module": "ESNext",
9+
"moduleResolution": "Bundler",
10+
"resolveJsonModule": true,
11+
"target": "ES2022",
12+
"strict": true,
13+
"allowJs": true,
14+
"skipLibCheck": true,
15+
"baseUrl": ".",
16+
"paths": {
17+
"~/*": ["./app/*"]
18+
},
19+
"noEmit": true,
20+
"rootDirs": [".", ".react-router/types/"]
21+
}
22+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { reactRouter } from "@react-router/dev/vite";
2+
import { defineConfig } from "vite";
3+
import tsconfigPaths from "vite-tsconfig-paths";
4+
5+
export default defineConfig({
6+
// @ts-expect-error `dev` depends on Vite 6, Plugin type is mismatched.
7+
plugins: [reactRouter(), tsconfigPaths()],
8+
});

integration/helpers/vite.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,12 +189,14 @@ export type TemplateName =
189189
| "cloudflare-dev-proxy-template"
190190
| "vite-5-template"
191191
| "vite-6-template"
192+
| "vite-7-beta-template"
192193
| "vite-plugin-cloudflare-template"
193194
| "vite-rolldown-template";
194195

195196
export const viteMajorTemplates = [
196197
{ templateName: "vite-5-template", templateDisplayName: "Vite 5" },
197198
{ templateName: "vite-6-template", templateDisplayName: "Vite 6" },
199+
{ templateName: "vite-7-beta-template", templateDisplayName: "Vite 7 Beta" },
198200
{
199201
templateName: "vite-rolldown-template",
200202
templateDisplayName: "Vite Rolldown",

packages/react-router-dev/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
"react-refresh": "^0.14.0",
8989
"semver": "^7.3.7",
9090
"set-cookie-parser": "^2.6.0",
91+
"tinyglobby": "^0.2.14",
9192
"valibot": "^0.41.0",
9293
"vite-node": "^3.1.4"
9394
},
@@ -119,7 +120,7 @@
119120
"@react-router/serve": "workspace:^",
120121
"react-router": "workspace:^",
121122
"typescript": "^5.1.0",
122-
"vite": "^5.1.0 || ^6.0.0",
123+
"vite": "^5.1.0 || ^6.0.0 || ^7.0.0",
123124
"wrangler": "^3.28.2 || ^4.0.0"
124125
},
125126
"peerDependenciesMeta": {

packages/react-router-dev/vite/plugin.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// We can only import types from Vite at the top level since we're in a CJS
2-
// context but want to use Vite's ESM build to avoid deprecation warnings
2+
// context but want to use Vite's ESM build since Vite 7+ is ESM only
33
import type * as Vite from "vite";
44
import { type BinaryLike, createHash } from "node:crypto";
55
import { existsSync, readFileSync, readdirSync, rmSync } from "node:fs";
@@ -31,6 +31,7 @@ import {
3131
init as initEsModuleLexer,
3232
parse as esModuleLexer,
3333
} from "es-module-lexer";
34+
import { escapePath as escapePathAsGlob } from "tinyglobby";
3435
import pick from "lodash/pick";
3536
import jsesc from "jsesc";
3637
import colors from "picocolors";
@@ -1153,6 +1154,7 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
11531154

11541155
// Ensure sync import of Vite works after async preload
11551156
let vite = getVite();
1157+
let viteMajorVersion = parseInt(vite.version.split(".")[0], 10);
11561158

11571159
viteUserConfig = _viteUserConfig;
11581160
viteConfigEnv = _viteConfigEnv;
@@ -1240,7 +1242,11 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
12401242
...Object.values(ctx.reactRouterConfig.routes).map((route) =>
12411243
resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)
12421244
),
1243-
]
1245+
].map((entry) =>
1246+
// In Vite 7, the `optimizeDeps.entries` option only accepts glob patterns.
1247+
// In prior versions, absolute file paths were treated differently.
1248+
viteMajorVersion >= 7 ? escapePathAsGlob(entry) : entry
1249+
)
12441250
: [],
12451251
include: [
12461252
// Pre-bundle React dependencies to avoid React duplicates,

packages/react-router-dev/vite/vite-node.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import { ViteNodeServer } from "vite-node/server";
2-
import { ViteNodeRunner } from "vite-node/client";
3-
import { installSourcemapsSupport } from "vite-node/source-map";
1+
// We can only import types from vite-node at the top level since we're in a CJS
2+
// context but want to use vite-node's ESM build since Vite 7+ is ESM only
3+
import type { ViteNodeServer as ViteNodeServerType } from "vite-node/server";
4+
import type { ViteNodeRunner as ViteNodeRunnerType } from "vite-node/client";
45
import type * as Vite from "vite";
56

67
import { preloadVite, getVite } from "./vite";
78
import { ssrExternals } from "./ssr-externals";
89

910
export type Context = {
1011
devServer: Vite.ViteDevServer;
11-
server: ViteNodeServer;
12-
runner: ViteNodeRunner;
12+
server: ViteNodeServerType;
13+
runner: ViteNodeRunnerType;
1314
};
1415

1516
export async function createContext({
@@ -24,6 +25,14 @@ export async function createContext({
2425
await preloadVite();
2526
const vite = getVite();
2627

28+
// Ensure we're using the ESM build of vite-node since Vite 7+ is ESM only
29+
const [{ ViteNodeServer }, { ViteNodeRunner }, { installSourcemapsSupport }] =
30+
await Promise.all([
31+
import("vite-node/server"),
32+
import("vite-node/client"),
33+
import("vite-node/source-map"),
34+
]);
35+
2736
const devServer = await vite.createServer({
2837
root,
2938
mode,
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules
2+
3+
/build
4+
.env
5+
6+
.react-router/
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {
2+
Link,
3+
Links,
4+
Meta,
5+
Outlet,
6+
Scripts,
7+
ScrollRestoration,
8+
} from "react-router";
9+
10+
export function Layout({ children }: { children: React.ReactNode }) {
11+
return (
12+
<html lang="en">
13+
<head>
14+
<meta charSet="utf-8" />
15+
<meta name="viewport" content="width=device-width, initial-scale=1" />
16+
17+
<Meta />
18+
<Links />
19+
</head>
20+
<body>
21+
<ul>
22+
<li>
23+
<Link prefetch="intent" to="/">
24+
Home
25+
</Link>
26+
</li>
27+
<li>
28+
<Link prefetch="intent" to="/products/abc">
29+
Product
30+
</Link>
31+
</li>
32+
</ul>
33+
{children}
34+
<ScrollRestoration />
35+
<Scripts />
36+
</body>
37+
</html>
38+
);
39+
}
40+
41+
export default function App() {
42+
return <Outlet />;
43+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { type RouteConfig, index, route } from "@react-router/dev/routes";
2+
3+
export default [
4+
index("routes/_index.tsx"),
5+
route("products/:id", "routes/product.tsx"),
6+
] satisfies RouteConfig;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { Route } from "./+types/_index";
2+
3+
export function loader({ params }: Route.LoaderArgs) {
4+
return { planet: "world", date: new Date(), fn: () => 1 };
5+
}
6+
7+
export default function Index({ loaderData }: Route.ComponentProps) {
8+
return <h1>Hello, {loaderData.planet}!</h1>;
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { Route } from "./+types/product";
2+
3+
export function loader({ params }: Route.LoaderArgs) {
4+
return { name: `Super cool product #${params.id}` };
5+
}
6+
7+
export default function Component({ loaderData }: Route.ComponentProps) {
8+
return <h1>{loaderData.name}</h1>;
9+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "@playground/framework-vite-7-beta",
3+
"version": "0.0.0",
4+
"private": true,
5+
"sideEffects": false,
6+
"type": "module",
7+
"scripts": {
8+
"build": "react-router build",
9+
"dev": "react-router dev",
10+
"start": "react-router-serve ./build/server/index.js",
11+
"typecheck": "react-router typegen && tsc"
12+
},
13+
"dependencies": {
14+
"@react-router/node": "workspace:*",
15+
"@react-router/serve": "workspace:*",
16+
"isbot": "^5.1.11",
17+
"react": "^19.1.0",
18+
"react-dom": "^19.1.0",
19+
"react-router": "workspace:*"
20+
},
21+
"devDependencies": {
22+
"@react-router/dev": "workspace:*",
23+
"@types/react": "^18.2.20",
24+
"@types/react-dom": "^18.2.7",
25+
"typescript": "^5.1.6",
26+
"vite": "7.0.0-beta.0",
27+
"vite-tsconfig-paths": "^4.2.1"
28+
},
29+
"engines": {
30+
"node": ">=20.0.0"
31+
}
32+
}
Binary file not shown.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { Config } from "@react-router/dev/config";
2+
3+
export default {
4+
future: {
5+
unstable_subResourceIntegrity: true,
6+
unstable_optimizeDeps: true,
7+
unstable_viteEnvironmentApi: true,
8+
},
9+
} satisfies Config;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"include": [
3+
"**/*.ts",
4+
"**/*.tsx",
5+
"**/.server/**/*.ts",
6+
"**/.server/**/*.tsx",
7+
"**/.client/**/*.ts",
8+
"**/.client/**/*.tsx",
9+
"./.react-router/types/**/*"
10+
],
11+
"compilerOptions": {
12+
"lib": ["DOM", "DOM.Iterable", "ES2022"],
13+
"types": ["@react-router/node", "vite/client"],
14+
"verbatimModuleSyntax": true,
15+
"esModuleInterop": true,
16+
"jsx": "react-jsx",
17+
"module": "ESNext",
18+
"moduleResolution": "Bundler",
19+
"resolveJsonModule": true,
20+
"target": "ES2022",
21+
"strict": true,
22+
"allowJs": true,
23+
"skipLibCheck": true,
24+
"baseUrl": ".",
25+
"paths": {
26+
"~/*": ["./app/*"]
27+
},
28+
"noEmit": true,
29+
"rootDirs": [".", "./.react-router/types"]
30+
}
31+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { reactRouter } from "@react-router/dev/vite";
2+
import { defineConfig } from "vite";
3+
import tsconfigPaths from "vite-tsconfig-paths";
4+
5+
export default defineConfig({
6+
// @ts-expect-error `dev` depends on Vite 6, Plugin type is mismatched.
7+
plugins: [reactRouter(), tsconfigPaths()],
8+
});

0 commit comments

Comments
 (0)