From c9142b033e359e0ee60060db9297dfe1e2cc01cb Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Thu, 21 Aug 2025 15:13:55 -0400 Subject: [PATCH 01/14] Remove prefix from client middleware --- docs/how-to/middleware.md | 4 +- docs/start/framework/route-module.md | 6 +- integration/middleware-test.ts | 58 +++++------ integration/rsc/rsc-test.ts | 98 +++++++++---------- packages/react-router-dev/typegen/generate.ts | 4 +- packages/react-router-dev/vite/plugin.ts | 45 ++++----- .../react-router-dev/vite/route-chunks.ts | 4 +- .../vite/rsc/virtual-route-modules.ts | 2 +- .../react-router/lib/dom/ssr/routeModules.ts | 5 +- packages/react-router/lib/dom/ssr/routes.tsx | 13 +-- .../lib/types/route-module-annotations.ts | 2 +- playground/middleware/app/root.tsx | 17 ++-- .../middleware/app/routes/client.a.b.tsx | 17 ++-- playground/middleware/app/routes/client.a.tsx | 17 ++-- 14 files changed, 140 insertions(+), 152 deletions(-) diff --git a/docs/how-to/middleware.md b/docs/how-to/middleware.md index 4285aaf09e..34270fa7d8 100644 --- a/docs/how-to/middleware.md +++ b/docs/how-to/middleware.md @@ -87,7 +87,7 @@ async function timingMiddleware({ context }, next) { console.log(`Navigation took ${duration}ms`); } -export const unstable_clientMiddleware: Route.unstable_ClientMiddlewareFunction[] = +export const clientMiddleware: Route.ClientMiddlewareFunction[] = [timingMiddleware]; export async function loader({ @@ -266,7 +266,7 @@ async function clientMiddleware({ request }, next) { } // Framework mode -export const unstable_clientMiddleware: Route.unstable_MiddlewareFunction[] = +export const clientMiddleware: Route.ClientMiddlewareFunction[] = [clientMiddleware]; // Or, Data mode diff --git a/docs/start/framework/route-module.md b/docs/start/framework/route-module.md index f613d83a32..441e388088 100644 --- a/docs/start/framework/route-module.md +++ b/docs/start/framework/route-module.md @@ -135,7 +135,7 @@ See also: - [`unstable_middleware` params][middleware-params] - [Middleware][middleware] -## `unstable_clientMiddleware` +## `clientMiddleware` This is the client-side equivalent of `unstable_middleware` and runs in the browser during client navigations. The only difference from server middleware is that client middleware doesn't return Responses because they're not wrapping an HTTP request on the server. @@ -158,9 +158,7 @@ async function loggingMiddleware( // ✅ No need to return anything } -export const unstable_clientMiddleware = [ - loggingMiddleware, -]; +export const clientMiddleware = [loggingMiddleware]; ``` See also: diff --git a/integration/middleware-test.ts b/integration/middleware-test.ts index 34b31a9db4..71cf5d7bda 100644 --- a/integration/middleware-test.ts +++ b/integration/middleware-test.ts @@ -120,7 +120,7 @@ test.describe("Middleware", () => { import { Link } from 'react-router' import { orderContext } from '../context' - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.set(orderContext, [...context.get(orderContext), 'a']); }, @@ -145,7 +145,7 @@ test.describe("Middleware", () => { "app/routes/about.tsx": js` import { orderContext } from '../context' - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.set(orderContext, [...context.get(orderContext), 'c']); }, @@ -211,7 +211,7 @@ test.describe("Middleware", () => { import { Link } from 'react-router' import { orderContext } from '../context' - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.set(orderContext, [...context.get(orderContext), 'a']); }, @@ -236,7 +236,7 @@ test.describe("Middleware", () => { "app/routes/about.tsx": js` import { orderContext } from '../context' - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.set(orderContext, [...context.get(orderContext), 'c']); }, @@ -299,7 +299,7 @@ test.describe("Middleware", () => { import { Form } from 'react-router' import { orderContext } from '../context'; - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ request, context }) => { context.set(orderContext, ['a']); }, @@ -376,7 +376,7 @@ test.describe("Middleware", () => { `, "app/routes/redirect.tsx": js` import { Link, redirect } from 'react-router' - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ request, context }) => { throw redirect('/target'); } ] export default function Component() { @@ -429,7 +429,7 @@ test.describe("Middleware", () => { `, "app/routes/redirect.tsx": js` import { Link, redirect } from 'react-router' - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ async ({ request, context }, next) => { await next(); throw redirect('/target'); @@ -485,7 +485,7 @@ test.describe("Middleware", () => { } `, "app/routes/broken.tsx": js` - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ async ({ request, context }, next) => { throw new Error('broken!'); } @@ -544,7 +544,7 @@ test.describe("Middleware", () => { } `, "app/routes/broken.tsx": js` - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ async ({ request, context }, next) => { await next(); throw new Error('broken!'); @@ -619,7 +619,7 @@ test.describe("Middleware", () => { "app/routes/a.tsx": js` import { Outlet } from 'react-router' import { orderContext } from '../context'; - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.set(orderContext, [...context.get(orderContext), 'a']); }, @@ -636,7 +636,7 @@ test.describe("Middleware", () => { "app/routes/a.b.tsx": js` import { Outlet } from 'react-router' import { orderContext } from '../context'; - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.set(orderContext, [...context.get(orderContext), 'b']); } @@ -697,7 +697,7 @@ test.describe("Middleware", () => { import { Link } from 'react-router' import { orderContext } from "../context";; - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.set(orderContext, [...context.get(orderContext), 'a']); }, @@ -721,7 +721,7 @@ test.describe("Middleware", () => { `, "app/routes/about.tsx": js` import { orderContext } from "../context";; - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.set(orderContext, ['c']); // reset order from hydration }, @@ -785,7 +785,7 @@ test.describe("Middleware", () => { import { Link } from 'react-router' import { orderContext } from "../context";; - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.set(orderContext, [...context.get(orderContext), 'a']); }, @@ -809,7 +809,7 @@ test.describe("Middleware", () => { `, "app/routes/about.tsx": js` import { orderContext } from "../context";; - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.set(orderContext, ['c']); // reset order from hydration }, @@ -865,7 +865,7 @@ test.describe("Middleware", () => { "app/routes/_index.tsx": js` import { Link } from 'react-router' - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { console.log('running index middleware') }, @@ -882,7 +882,7 @@ test.describe("Middleware", () => { `, "app/routes/about.tsx": js` import { Link } from 'react-router' - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { console.log('running about middleware') }, @@ -943,7 +943,7 @@ test.describe("Middleware", () => { import { Form } from 'react-router' import { orderContext } from "../context";; - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.set(orderContext, ['a']); }, @@ -1018,7 +1018,7 @@ test.describe("Middleware", () => { `, "app/routes/redirect.tsx": js` import { Link, redirect } from 'react-router' - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ request, context }) => { throw redirect('/target'); } ] export default function Component() { @@ -1069,7 +1069,7 @@ test.describe("Middleware", () => { `, "app/routes/redirect.tsx": js` import { Link, redirect } from 'react-router' - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ async ({ request, context }, next) => { await next(); throw redirect('/target'); @@ -1123,7 +1123,7 @@ test.describe("Middleware", () => { } `, "app/routes/broken.tsx": js` - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ async ({ request, context }, next) => { throw new Error('broken!') } @@ -1181,7 +1181,7 @@ test.describe("Middleware", () => { `, "app/routes/broken.tsx": js` import { useRouteError } from 'react-router' - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ async ({ request, context }, next) => { await next(); throw new Error('broken!') @@ -1254,7 +1254,7 @@ test.describe("Middleware", () => { "app/routes/a.tsx": js` import { Outlet } from 'react-router' import { orderContext } from '../context'; - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.set(orderContext, ['a']); } ]; @@ -1269,7 +1269,7 @@ test.describe("Middleware", () => { "app/routes/a.b.tsx": js` import { Outlet } from 'react-router' import { orderContext } from '../context'; - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.set(orderContext, [...context.get(orderContext), 'b']); }, @@ -1341,7 +1341,7 @@ test.describe("Middleware", () => { export function loader() { return 'PARENT' } - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.get(countContext).parent++ }, ]; @@ -1366,7 +1366,7 @@ test.describe("Middleware", () => { export function loader() { return 'CHILD' } - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.get(countContext).child++ }, ]; @@ -1464,7 +1464,7 @@ test.describe("Middleware", () => { export function loader() { return 'PARENT' } - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.get(countContext).parent++ }, ]; export default function Component({ loaderData }) { @@ -1485,7 +1485,7 @@ test.describe("Middleware", () => { export function loader() { return 'CHILD' } - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.get(countContext).child++ }, ]; export default function Component({ loaderData }) { @@ -1506,7 +1506,7 @@ test.describe("Middleware", () => { export function loader() { return 'INDEX' } - export const unstable_clientMiddleware = [ + export const clientMiddleware = [ ({ context }) => { context.get(countContext).index++ }, ]; export async function clientLoader({ serverLoader, context }) { diff --git a/integration/rsc/rsc-test.ts b/integration/rsc/rsc-test.ts index f1a0c40a67..82a9216f07 100644 --- a/integration/rsc/rsc-test.ts +++ b/integration/rsc/rsc-test.ts @@ -28,7 +28,7 @@ implementations.forEach((implementation) => { files: { "src/routes.ts": js` import type { unstable_RSCRouteConfig as RSCRouteConfig } from "react-router"; - + export const routes = [ { id: "root", @@ -299,7 +299,7 @@ implementations.forEach((implementation) => { files: { "src/routes.ts": js` import type { unstable_RSCRouteConfig as RSCRouteConfig } from "react-router"; - + export const routes = [ { id: "root", @@ -486,9 +486,9 @@ implementations.forEach((implementation) => { "src/config/request-context.ts": js` import { unstable_createContext, unstable_RouterContextProvider } from "react-router"; - + export const testContext = unstable_createContext("default-value"); - + export const requestContext = new unstable_RouterContextProvider( new Map([[testContext, "test-context-value"]]) ); @@ -496,9 +496,9 @@ implementations.forEach((implementation) => { "src/config/unstable-get-context.ts": js` // THIS FILE OVERRIDES THE DEFAULT IMPLEMENTATION import { unstable_createContext } from "react-router"; - + export const testContext = unstable_createContext("default-value"); - + export function unstable_getContext() { return new Map([[testContext, "client-context-value"]]); } @@ -506,11 +506,11 @@ implementations.forEach((implementation) => { "src/routes/soft-navigation/home.tsx": js` import { Link } from "react-router"; - + export function loader() { return { message: "Home Page Data" }; } - + export default function HomeRoute({ loaderData }) { return (
@@ -525,37 +525,37 @@ implementations.forEach((implementation) => { export function loader() { return { count: 1 }; } - + export { default } from "./dashboard.client"; `, "src/routes/soft-navigation/dashboard.client.tsx": js` "use client"; - + import { useState } from "react"; import { Link } from "react-router"; - + // Export the entire route as a client component export default function DashboardRoute({ loaderData }) { const [count, setCount] = useState(loaderData.count); - + return (

Dashboard

- + {/* Server data rendered in client component */}

Server count: {loaderData.count}

- + {/* Client interactive elements */}

Client count: {count}

- + - + Home
); @@ -564,11 +564,11 @@ implementations.forEach((implementation) => { "src/routes/request-context/home.tsx": js` import { testContext } from "../../config/request-context"; - + export function loader({ context }) { return { contextValue: context.get(testContext) }; } - + export default function HomeRoute({ loaderData }) { return (
@@ -580,16 +580,16 @@ implementations.forEach((implementation) => { "src/routes/resource-request-context/home.client.tsx": js` "use client"; - + import { useFetcher } from "react-router"; - + export function ResourceFetcher() { const fetcher = useFetcher(); - + const loadResource = () => { fetcher.submit({ hello: "world" }, { method: "post", action: "/resource-request-context/resource" }); }; - + return (