Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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 @@ -190,12 +190,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 @@ -89,6 +89,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 @@ -121,7 +122,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 * as fs from "node:fs";
Expand All @@ -23,6 +23,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 @@ -1145,6 +1146,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 @@ -1232,7 +1234,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