Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dull-pianos-matter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@react-router/dev": patch
---

Add Vite 7 support
6 changes: 6 additions & 0 deletions integration/helpers/vite-7-beta-template/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules

/.cache
/build
.env
.react-router
19 changes: 19 additions & 0 deletions integration/helpers/vite-7-beta-template/app/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router";

export default function App() {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<Outlet />
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
4 changes: 4 additions & 0 deletions integration/helpers/vite-7-beta-template/app/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { type RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";

export default flatRoutes() satisfies RouteConfig;
16 changes: 16 additions & 0 deletions integration/helpers/vite-7-beta-template/app/routes/_index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { MetaFunction } from "react-router";

export const meta: MetaFunction = () => {
return [
{ title: "New React Router App" },
{ name: "description", content: "Welcome to React Router!" },
];
};

export default function Index() {
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
<h1>Welcome to React Router</h1>
</div>
);
}
2 changes: 2 additions & 0 deletions integration/helpers/vite-7-beta-template/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="@react-router/node" />
/// <reference types="vite/client" />
41 changes: 41 additions & 0 deletions integration/helpers/vite-7-beta-template/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "integration-vite-7-beta-template",
"version": "0.0.0",
"private": true,
"sideEffects": false,
"type": "module",
"scripts": {
"dev": "react-router dev",
"build": "react-router build",
"start": "react-router-serve ./build/server/index.js",
"typecheck": "react-router typegen && tsc"
},
"dependencies": {
"@react-router/express": "workspace:*",
"@react-router/node": "workspace:*",
"@react-router/serve": "workspace:*",
"@vanilla-extract/css": "^1.10.0",
"@vanilla-extract/vite-plugin": "^3.9.2",
"express": "^4.19.2",
"isbot": "^5.1.11",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-router": "workspace:*",
"serialize-javascript": "^6.0.1"
},
"devDependencies": {
"@react-router/dev": "workspace:*",
"@react-router/fs-routes": "workspace:*",
"@react-router/remix-routes-option-adapter": "workspace:*",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"eslint": "^8.38.0",
"typescript": "^5.1.6",
"vite": "7.0.0-beta.0",
"vite-env-only": "^3.0.1",
"vite-tsconfig-paths": "^4.2.1"
},
"engines": {
"node": ">=20.0.0"
}
}
Binary file not shown.
22 changes: 22 additions & 0 deletions integration/helpers/vite-7-beta-template/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"include": ["env.d.ts", "**/*.ts", "**/*.tsx", ".react-router/types/**/*"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"verbatimModuleSyntax": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"target": "ES2022",
"strict": true,
"allowJs": true,
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"~/*": ["./app/*"]
},
"noEmit": true,
"rootDirs": [".", ".react-router/types/"]
}
}
8 changes: 8 additions & 0 deletions integration/helpers/vite-7-beta-template/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
// @ts-expect-error `dev` depends on Vite 6, Plugin type is mismatched.
plugins: [reactRouter(), tsconfigPaths()],
});
2 changes: 2 additions & 0 deletions integration/helpers/vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,14 @@ export type TemplateName =
| "cloudflare-dev-proxy-template"
| "vite-5-template"
| "vite-6-template"
| "vite-7-beta-template"
| "vite-plugin-cloudflare-template"
| "vite-rolldown-template";

export const viteMajorTemplates = [
{ templateName: "vite-5-template", templateDisplayName: "Vite 5" },
{ templateName: "vite-6-template", templateDisplayName: "Vite 6" },
{ templateName: "vite-7-beta-template", templateDisplayName: "Vite 7 Beta" },
{
templateName: "vite-rolldown-template",
templateDisplayName: "Vite Rolldown",
Expand Down
3 changes: 2 additions & 1 deletion packages/react-router-dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"react-refresh": "^0.14.0",
"semver": "^7.3.7",
"set-cookie-parser": "^2.6.0",
"tinyglobby": "^0.2.14",
"valibot": "^0.41.0",
"vite-node": "^3.1.4"
},
Expand Down Expand Up @@ -119,7 +120,7 @@
"@react-router/serve": "workspace:^",
"react-router": "workspace:^",
"typescript": "^5.1.0",
"vite": "^5.1.0 || ^6.0.0",
"vite": "^5.1.0 || ^6.0.0 || ^7.0.0",
"wrangler": "^3.28.2 || ^4.0.0"
},
"peerDependenciesMeta": {
Expand Down
10 changes: 8 additions & 2 deletions packages/react-router-dev/vite/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// We can only import types from Vite at the top level since we're in a CJS
// context but want to use Vite's ESM build to avoid deprecation warnings
// context but want to use Vite's ESM build since Vite 7+ is ESM only
import type * as Vite from "vite";
import { type BinaryLike, createHash } from "node:crypto";
import { existsSync, readFileSync, readdirSync, rmSync } from "node:fs";
Expand Down Expand Up @@ -31,6 +31,7 @@ import {
init as initEsModuleLexer,
parse as esModuleLexer,
} from "es-module-lexer";
import { escapePath as escapePathAsGlob } from "tinyglobby";
import pick from "lodash/pick";
import jsesc from "jsesc";
import colors from "picocolors";
Expand Down Expand Up @@ -1153,6 +1154,7 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {

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

viteUserConfig = _viteUserConfig;
viteConfigEnv = _viteConfigEnv;
Expand Down Expand Up @@ -1240,7 +1242,11 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
...Object.values(ctx.reactRouterConfig.routes).map((route) =>
resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)
),
]
].map((entry) =>
// In Vite 7, the `optimizeDeps.entries` option only accepts glob patterns.
// In prior versions, absolute file paths were treated differently.
viteMajorVersion >= 7 ? escapePathAsGlob(entry) : entry
)
: [],
include: [
// Pre-bundle React dependencies to avoid React duplicates,
Expand Down
19 changes: 14 additions & 5 deletions packages/react-router-dev/vite/vite-node.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { ViteNodeServer } from "vite-node/server";
import { ViteNodeRunner } from "vite-node/client";
import { installSourcemapsSupport } from "vite-node/source-map";
// We can only import types from vite-node at the top level since we're in a CJS
// context but want to use vite-node's ESM build since Vite 7+ is ESM only
import type { ViteNodeServer as ViteNodeServerType } from "vite-node/server";
import type { ViteNodeRunner as ViteNodeRunnerType } from "vite-node/client";
import type * as Vite from "vite";

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

export type Context = {
devServer: Vite.ViteDevServer;
server: ViteNodeServer;
runner: ViteNodeRunner;
server: ViteNodeServerType;
runner: ViteNodeRunnerType;
};

export async function createContext({
Expand All @@ -24,6 +25,14 @@ export async function createContext({
await preloadVite();
const vite = getVite();

// Ensure we're using the ESM build of vite-node since Vite 7+ is ESM only
const [{ ViteNodeServer }, { ViteNodeRunner }, { installSourcemapsSupport }] =
await Promise.all([
import("vite-node/server"),
import("vite-node/client"),
import("vite-node/source-map"),
]);

const devServer = await vite.createServer({
root,
mode,
Expand Down
6 changes: 6 additions & 0 deletions playground/framework-vite-7-beta/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules

/build
.env

.react-router/
43 changes: 43 additions & 0 deletions playground/framework-vite-7-beta/app/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {
Link,
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from "react-router";

export function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

<Meta />
<Links />
</head>
<body>
<ul>
<li>
<Link prefetch="intent" to="/">
Home
</Link>
</li>
<li>
<Link prefetch="intent" to="/products/abc">
Product
</Link>
</li>
</ul>
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}

export default function App() {
return <Outlet />;
}
6 changes: 6 additions & 0 deletions playground/framework-vite-7-beta/app/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { type RouteConfig, index, route } from "@react-router/dev/routes";

export default [
index("routes/_index.tsx"),
route("products/:id", "routes/product.tsx"),
] satisfies RouteConfig;
9 changes: 9 additions & 0 deletions playground/framework-vite-7-beta/app/routes/_index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Route } from "./+types/_index";

export function loader({ params }: Route.LoaderArgs) {
return { planet: "world", date: new Date(), fn: () => 1 };
}

export default function Index({ loaderData }: Route.ComponentProps) {
return <h1>Hello, {loaderData.planet}!</h1>;
}
9 changes: 9 additions & 0 deletions playground/framework-vite-7-beta/app/routes/product.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Route } from "./+types/product";

export function loader({ params }: Route.LoaderArgs) {
return { name: `Super cool product #${params.id}` };
}

export default function Component({ loaderData }: Route.ComponentProps) {
return <h1>{loaderData.name}</h1>;
}
32 changes: 32 additions & 0 deletions playground/framework-vite-7-beta/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@playground/framework-vite-7-beta",
"version": "0.0.0",
"private": true,
"sideEffects": false,
"type": "module",
"scripts": {
"build": "react-router build",
"dev": "react-router dev",
"start": "react-router-serve ./build/server/index.js",
"typecheck": "react-router typegen && tsc"
},
"dependencies": {
"@react-router/node": "workspace:*",
"@react-router/serve": "workspace:*",
"isbot": "^5.1.11",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-router": "workspace:*"
},
"devDependencies": {
"@react-router/dev": "workspace:*",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"typescript": "^5.1.6",
"vite": "7.0.0-beta.0",
"vite-tsconfig-paths": "^4.2.1"
},
"engines": {
"node": ">=20.0.0"
}
}
Binary file not shown.
9 changes: 9 additions & 0 deletions playground/framework-vite-7-beta/react-router.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Config } from "@react-router/dev/config";

export default {
future: {
unstable_subResourceIntegrity: true,
unstable_optimizeDeps: true,
unstable_viteEnvironmentApi: true,
},
} satisfies Config;
31 changes: 31 additions & 0 deletions playground/framework-vite-7-beta/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"include": [
"**/*.ts",
"**/*.tsx",
"**/.server/**/*.ts",
"**/.server/**/*.tsx",
"**/.client/**/*.ts",
"**/.client/**/*.tsx",
"./.react-router/types/**/*"
],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"types": ["@react-router/node", "vite/client"],
"verbatimModuleSyntax": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"target": "ES2022",
"strict": true,
"allowJs": true,
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"~/*": ["./app/*"]
},
"noEmit": true,
"rootDirs": [".", "./.react-router/types"]
}
}
8 changes: 8 additions & 0 deletions playground/framework-vite-7-beta/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
// @ts-expect-error `dev` depends on Vite 6, Plugin type is mismatched.
plugins: [reactRouter(), tsconfigPaths()],
});
Loading