diff --git a/.github/workflows/release-comments-manual.yml b/.github/workflows/release-comments-manual.yml index 53dc19ffb0..9b4165b2c9 100644 --- a/.github/workflows/release-comments-manual.yml +++ b/.github/workflows/release-comments-manual.yml @@ -2,6 +2,10 @@ name: Release Comments (manual) on: workflow_dispatch: + inputs: + release: + required: true + description: Release version, e.g. 7.18.0 concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -35,4 +39,4 @@ jobs: - name: Comment on released issues and pull requests env: GH_TOKEN: ${{ github.token }} - run: pnpm run release-comments + run: pnpm run release-comments --release=${{ github.event.inputs.release }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9f1b6888a8..c4f8dec097 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,10 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + # Don't cancel in-progress runs - once a release flow has started we want it + # to run all the way through so subsequent commits (like formatting) don't + # interrupt publishing/commenting + cancel-in-progress: false jobs: check: diff --git a/CHANGELOG.md b/CHANGELOG.md index 444b313b49..f1548a9f61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ We manage release notes in this file instead of the paginated Github Releases Pa Table of Contents - [React Router Releases](#react-router-releases) + - [v8.0.1](#v801) - [v8.0.0](#v800) - [Baseline Support](#baseline-support) - [Adopted Future Flag Behavior](#adopted-future-flag-behavior) @@ -103,6 +104,16 @@ We manage release notes in this file instead of the paginated Github Releases Pa +## v8.0.1 + +Date: 2026-06-18 + +### Patch Changes + +- `react-router` - Remove the obsolete `AppLoadContext` type export accidentally left over from v7 now that middleware is always enabled and server request context is provided through `RouterContextProvider`. ([#15207](https://github.com/remix-run/react-router/pull/15207)) + +**Full Changelog**: [`v8.0.0...v8.0.1`](https://github.com/remix-run/react-router/compare/react-router@8.0.0...react-router@8.0.1) + ## v8.0.0 Date: 2026-06-17 diff --git a/docs/api/other-api/adapter.md b/docs/api/other-api/adapter.md index 3cefc85ee2..5a7251a118 100644 --- a/docs/api/other-api/adapter.md +++ b/docs/api/other-api/adapter.md @@ -144,16 +144,16 @@ export const handler = createRequestHandler({ Here's an example with Cloudflare: ```ts -import { createRequestHandler } from "react-router"; - -declare module "react-router" { - export interface AppLoadContext { - cloudflare: { - env: Env; - ctx: ExecutionContext; - }; - } -} +import { + RouterContextProvider, + createContext, + createRequestHandler, +} from "react-router"; + +const cloudflareContext = createContext<{ + env: Env; + ctx: ExecutionContext; +}>(); const requestHandler = createRequestHandler( () => import("virtual:react-router/server-build"), @@ -162,9 +162,9 @@ const requestHandler = createRequestHandler( export default { async fetch(request, env, ctx) { - return requestHandler(request, { - cloudflare: { env, ctx }, - }); + let routerContext = new RouterContextProvider(); + routerContext.set(cloudflareContext, { env, ctx }); + return requestHandler(request, routerContext); }, } satisfies ExportedHandler; ``` diff --git a/docs/how-to/middleware.md b/docs/how-to/middleware.md index 7a25e98e82..9a9751a6a7 100644 --- a/docs/how-to/middleware.md +++ b/docs/how-to/middleware.md @@ -335,7 +335,7 @@ Client middleware is simpler because since we are already on the client and are ### Context API -The new context system provides type safety and prevents naming conflicts and allows you to provide data to nested middlewares and `action`/`loader` functions. In Framework Mode, this replaces the previous `AppLoadContext` API. +The context system provides type safety, prevents naming conflicts, and allows you to provide data to nested middlewares and `action`/`loader` functions. ```ts // ✅ Type-safe diff --git a/docs/how-to/route-module-type-safety.md b/docs/how-to/route-module-type-safety.md index 06a3a4a3a3..f4fff55e90 100644 --- a/docs/how-to/route-module-type-safety.md +++ b/docs/how-to/route-module-type-safety.md @@ -50,22 +50,7 @@ If you want to run type checking as its own command — for example, as part of } ``` -## 4. Typing `AppLoadContext` - -## Extending app `Context` types - -To define your app's `context` type, add the following in a `.ts` or `.d.ts` file within your project: - -```typescript -import "react-router"; -declare module "react-router" { - interface AppLoadContext { - // add context properties here - } -} -``` - -## 5. Type-only auto-imports (optional) +## 4. Type-only auto-imports (optional) When auto-importing the `Route` type helper, TypeScript will generate: diff --git a/eslint.config.ts b/eslint.config.ts index 2298eaadcf..9bd465aa15 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -1,11 +1,10 @@ import { createRequire } from "node:module"; import { fixupPluginRules } from "@eslint/compat"; import { defineConfig } from "eslint/config"; +import globals from "globals"; const require = createRequire(import.meta.url); -const eslintRequire = createRequire(require.resolve("eslint/package.json")); -const globals = eslintRequire("globals"); const reactAppConfig = require("eslint-config-react-app"); const flowtypePlugin = fixupPluginRules(require("eslint-plugin-flowtype")); @@ -27,6 +26,9 @@ const reactAppTsOverride = reactAppConfig.overrides.find( if (!reactAppTsOverride) { throw new Error("Could not find the react-app TypeScript override."); } +if (!jestPlugin.configs) { + throw new Error("Could not find the jest plugin configs."); +} const jestRecommended = jestPlugin.configs["flat/recommended"]; @@ -119,6 +121,12 @@ export default defineConfig([ "react/jsx-uses-vars": "warn", }, }, + { + files: ["**/*.config.js", "**/jest.config*.js"], + rules: { + "import/no-anonymous-default-export": "off", + }, + }, { files: ["**/*.{ts,tsx}"], languageOptions: { diff --git a/integration/helpers/vite-plugin-cloudflare-template/app/entry.server.tsx b/integration/helpers/vite-plugin-cloudflare-template/app/entry.server.tsx index 113e32c94e..6ac910a0f8 100644 --- a/integration/helpers/vite-plugin-cloudflare-template/app/entry.server.tsx +++ b/integration/helpers/vite-plugin-cloudflare-template/app/entry.server.tsx @@ -1,4 +1,4 @@ -import type { AppLoadContext, EntryContext } from "react-router"; +import type { EntryContext, RouterContextProvider } from "react-router"; import { ServerRouter } from "react-router"; import { isbot } from "isbot"; import { renderToReadableStream } from "react-dom/server"; @@ -8,7 +8,7 @@ export default async function handleRequest( responseStatusCode: number, responseHeaders: Headers, routerContext: EntryContext, - _loadContext: AppLoadContext, + _loadContext: RouterContextProvider, ) { let shellRendered = false; const userAgent = request.headers.get("user-agent"); diff --git a/integration/middleware-test.ts b/integration/middleware-test.ts index af507ba7da..f15aa656d6 100644 --- a/integration/middleware-test.ts +++ b/integration/middleware-test.ts @@ -1986,7 +1986,7 @@ test.describe("Middleware", () => { "app/entry.server.tsx": js` import { PassThrough } from "node:stream"; - import type { AppLoadContext, EntryContext } from "react-router"; + import type { EntryContext } from "react-router"; import { createReadableStreamFromReadable } from "@react-router/node"; import { ServerRouter } from "react-router"; import type { RenderToPipeableStreamOptions } from "react-dom/server"; diff --git a/integration/vite-spa-mode-test.ts b/integration/vite-spa-mode-test.ts index b462624a80..703af4a852 100644 --- a/integration/vite-spa-mode-test.ts +++ b/integration/vite-spa-mode-test.ts @@ -377,7 +377,7 @@ test.describe("SPA Mode", () => { import * as path from "node:path"; import { PassThrough } from "node:stream"; - import type { AppLoadContext, EntryContext } from "react-router"; + import type { EntryContext, RouterContextProvider } from "react-router"; import { createReadableStreamFromReadable } from "@react-router/node"; import { ServerRouter } from "react-router"; import { renderToPipeableStream } from "react-dom/server"; @@ -387,7 +387,7 @@ test.describe("SPA Mode", () => { responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext, - loadContext: AppLoadContext + loadContext: RouterContextProvider ) { return handleBotRequest( request, diff --git a/package.json b/package.json index 52da1154ed..4c296ed363 100644 --- a/package.json +++ b/package.json @@ -81,10 +81,13 @@ "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.1.1", "fast-glob": "3.3.3", + "globals": "^17.6.0", "isbot": "^5.1.40", "jest": "^30.4.2", "jest-environment-jsdom": "^30.4.1", "jsdom": "^29.1.1", + "react": "catalog:", + "react-dom": "catalog:", "picocolors": "^1.1.1", "prettier": "^3.8.3", "prompts": "^2.4.2", diff --git a/packages/create-react-router/CHANGELOG.md b/packages/create-react-router/CHANGELOG.md index 1efc28735e..d2ad382a70 100644 --- a/packages/create-react-router/CHANGELOG.md +++ b/packages/create-react-router/CHANGELOG.md @@ -1,5 +1,11 @@ # `create-react-router` +## v8.0.1 + +### Patch Changes + +- _No changes_ + ## v8.0.0 ### Major Changes diff --git a/packages/create-react-router/package.json b/packages/create-react-router/package.json index ee57e71cf2..69c5420a47 100644 --- a/packages/create-react-router/package.json +++ b/packages/create-react-router/package.json @@ -1,7 +1,7 @@ { "name": "create-react-router", "type": "module", - "version": "8.0.0", + "version": "8.0.1", "description": "Create a new React Router app", "homepage": "https://reactrouter.com", "bugs": { diff --git a/packages/react-router-architect/CHANGELOG.md b/packages/react-router-architect/CHANGELOG.md index c10be2d554..5212dbb44f 100644 --- a/packages/react-router-architect/CHANGELOG.md +++ b/packages/react-router-architect/CHANGELOG.md @@ -1,5 +1,13 @@ # `@react-router/architect` +## v8.0.1 + +### Patch Changes + +- Updated dependencies: + - [`react-router@8.0.1`](https://github.com/remix-run/react-router/releases/tag/react-router@8.0.1) + - [`@react-router/node@8.0.1`](https://github.com/remix-run/react-router/releases/tag/@react-router/node@8.0.1) + ## v8.0.0 ### Major Changes diff --git a/packages/react-router-architect/package.json b/packages/react-router-architect/package.json index d6a3ec1e74..49f6223109 100644 --- a/packages/react-router-architect/package.json +++ b/packages/react-router-architect/package.json @@ -1,7 +1,7 @@ { "name": "@react-router/architect", "type": "module", - "version": "8.0.0", + "version": "8.0.1", "description": "Architect server request handler for React Router", "bugs": { "url": "https://github.com/remix-run/react-router/issues" diff --git a/packages/react-router-cloudflare/CHANGELOG.md b/packages/react-router-cloudflare/CHANGELOG.md index bdb4f69818..a445375b34 100644 --- a/packages/react-router-cloudflare/CHANGELOG.md +++ b/packages/react-router-cloudflare/CHANGELOG.md @@ -1,5 +1,12 @@ # `@react-router/cloudflare` +## v8.0.1 + +### Patch Changes + +- Updated dependencies: + - [`react-router@8.0.1`](https://github.com/remix-run/react-router/releases/tag/react-router@8.0.1) + ## v8.0.0 ### Major Changes diff --git a/packages/react-router-cloudflare/package.json b/packages/react-router-cloudflare/package.json index c7ef69605c..537596c13f 100644 --- a/packages/react-router-cloudflare/package.json +++ b/packages/react-router-cloudflare/package.json @@ -1,7 +1,7 @@ { "name": "@react-router/cloudflare", "type": "module", - "version": "8.0.0", + "version": "8.0.1", "description": "Cloudflare platform abstractions for React Router", "bugs": { "url": "https://github.com/remix-run/react-router/issues" diff --git a/packages/react-router-dev/CHANGELOG.md b/packages/react-router-dev/CHANGELOG.md index 00f065d2fb..0ac3bd9a96 100644 --- a/packages/react-router-dev/CHANGELOG.md +++ b/packages/react-router-dev/CHANGELOG.md @@ -1,5 +1,14 @@ # `@react-router/dev` +## v8.0.1 + +### Patch Changes + +- Updated dependencies: + - [`react-router@8.0.1`](https://github.com/remix-run/react-router/releases/tag/react-router@8.0.1) + - [`@react-router/node@8.0.1`](https://github.com/remix-run/react-router/releases/tag/@react-router/node@8.0.1) + - [`@react-router/serve@8.0.1`](https://github.com/remix-run/react-router/releases/tag/@react-router/serve@8.0.1) + ## v8.0.0 ### Major Changes diff --git a/packages/react-router-dev/cli/run.ts b/packages/react-router-dev/cli/run.ts index 58e96c2aad..fe96f51a46 100644 --- a/packages/react-router-dev/cli/run.ts +++ b/packages/react-router-dev/cli/run.ts @@ -1,5 +1,3 @@ -import { readFileSync } from "node:fs"; -import path from "node:path"; import arg from "arg"; import semver from "semver"; import colors from "picocolors"; diff --git a/packages/react-router-dev/config/defaults/entry.server.node.tsx b/packages/react-router-dev/config/defaults/entry.server.node.tsx index 657d19c76f..34d8af2ad3 100644 --- a/packages/react-router-dev/config/defaults/entry.server.node.tsx +++ b/packages/react-router-dev/config/defaults/entry.server.node.tsx @@ -1,6 +1,6 @@ import { PassThrough } from "node:stream"; -import type { AppLoadContext, EntryContext } from "react-router"; +import type { EntryContext, RouterContextProvider } from "react-router"; import { createReadableStreamFromReadable } from "@react-router/node"; import { ServerRouter } from "react-router"; import { isbot } from "isbot"; @@ -14,9 +14,7 @@ export default function handleRequest( responseStatusCode: number, responseHeaders: Headers, routerContext: EntryContext, - loadContext: AppLoadContext, - // If you have middleware enabled: - // loadContext: RouterContextProvider + loadContext: RouterContextProvider, ) { // https://httpwg.org/specs/rfc9110.html#HEAD if (request.method.toUpperCase() === "HEAD") { diff --git a/packages/react-router-dev/package.json b/packages/react-router-dev/package.json index 2101cc2047..5fe7f8861b 100644 --- a/packages/react-router-dev/package.json +++ b/packages/react-router-dev/package.json @@ -1,7 +1,7 @@ { "name": "@react-router/dev", "type": "module", - "version": "8.0.0", + "version": "8.0.1", "description": "Dev tools and CLI for React Router", "homepage": "https://reactrouter.com", "bugs": { diff --git a/packages/react-router-dev/vite/has-dependency.ts b/packages/react-router-dev/vite/has-dependency.ts index ae73796bbb..4f0a7e8269 100644 --- a/packages/react-router-dev/vite/has-dependency.ts +++ b/packages/react-router-dev/vite/has-dependency.ts @@ -11,7 +11,10 @@ export function hasDependency({ }) { try { return Boolean(nodeRequire.resolve(name, { paths: [rootDirectory] })); - } catch (err) { + } catch ( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + e + ) { return false; } } diff --git a/packages/react-router-dev/vite/plugin.ts b/packages/react-router-dev/vite/plugin.ts index fe08de6ec5..d28ab90657 100644 --- a/packages/react-router-dev/vite/plugin.ts +++ b/packages/react-router-dev/vite/plugin.ts @@ -2,7 +2,7 @@ // 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"; +import { existsSync, readFileSync, readdirSync } from "node:fs"; import { cp, mkdir, @@ -221,28 +221,6 @@ const isRouteVirtualModule = (id: string): boolean => { return isRouteEntryModuleId(id) || isRouteChunkModuleId(id); }; -const isServerBuildVirtualModuleId = (id: string): boolean => { - return id.split("?")[0] === virtual.serverBuild.id; -}; - -const getServerBuildFile = (viteManifest: Vite.Manifest): string => { - let serverBuildIds = Object.keys(viteManifest).filter( - isServerBuildVirtualModuleId, - ); - - invariant( - serverBuildIds.length <= 1, - "Multiple server build files found in manifest", - ); - - invariant( - serverBuildIds.length === 1, - "Server build file not found in manifest", - ); - - return viteManifest[serverBuildIds[0]].file; -}; - type ReactRouterPluginContext = { buildManifest: BuildManifest | null; rootDirectory: string; diff --git a/packages/react-router-express/CHANGELOG.md b/packages/react-router-express/CHANGELOG.md index 9cb8b5c815..886cfb1de4 100644 --- a/packages/react-router-express/CHANGELOG.md +++ b/packages/react-router-express/CHANGELOG.md @@ -1,5 +1,13 @@ # `@react-router/express` +## v8.0.1 + +### Patch Changes + +- Updated dependencies: + - [`react-router@8.0.1`](https://github.com/remix-run/react-router/releases/tag/react-router@8.0.1) + - [`@react-router/node@8.0.1`](https://github.com/remix-run/react-router/releases/tag/@react-router/node@8.0.1) + ## v8.0.0 ### Major Changes diff --git a/packages/react-router-express/package.json b/packages/react-router-express/package.json index 3b1e71e4b0..75e11a4dfc 100644 --- a/packages/react-router-express/package.json +++ b/packages/react-router-express/package.json @@ -1,7 +1,7 @@ { "name": "@react-router/express", "type": "module", - "version": "8.0.0", + "version": "8.0.1", "description": "Express server request handler for React Router", "bugs": { "url": "https://github.com/remix-run/react-router/issues" diff --git a/packages/react-router-fs-routes/CHANGELOG.md b/packages/react-router-fs-routes/CHANGELOG.md index 5cdb1adaea..1dcf7dad87 100644 --- a/packages/react-router-fs-routes/CHANGELOG.md +++ b/packages/react-router-fs-routes/CHANGELOG.md @@ -1,5 +1,12 @@ # `@react-router/fs-routes` +## v8.0.1 + +### Patch Changes + +- Updated dependencies: + - [`@react-router/dev@8.0.1`](https://github.com/remix-run/react-router/releases/tag/@react-router/dev@8.0.1) + ## v8.0.0 ### Major Changes diff --git a/packages/react-router-fs-routes/package.json b/packages/react-router-fs-routes/package.json index 7aa7da7126..00609bc202 100644 --- a/packages/react-router-fs-routes/package.json +++ b/packages/react-router-fs-routes/package.json @@ -1,7 +1,7 @@ { "name": "@react-router/fs-routes", "type": "module", - "version": "8.0.0", + "version": "8.0.1", "description": "File system routing conventions for React Router, for use within routes.ts", "bugs": { "url": "https://github.com/remix-run/react-router/issues" diff --git a/packages/react-router-node/CHANGELOG.md b/packages/react-router-node/CHANGELOG.md index 0a19a28531..ff6c9afe16 100644 --- a/packages/react-router-node/CHANGELOG.md +++ b/packages/react-router-node/CHANGELOG.md @@ -1,5 +1,12 @@ # `@react-router/node` +## v8.0.1 + +### Patch Changes + +- Updated dependencies: + - [`react-router@8.0.1`](https://github.com/remix-run/react-router/releases/tag/react-router@8.0.1) + ## v8.0.0 ### Major Changes diff --git a/packages/react-router-node/package.json b/packages/react-router-node/package.json index c719cf42bc..9682997ee1 100644 --- a/packages/react-router-node/package.json +++ b/packages/react-router-node/package.json @@ -1,7 +1,7 @@ { "name": "@react-router/node", "type": "module", - "version": "8.0.0", + "version": "8.0.1", "description": "Node.js platform abstractions for React Router", "bugs": { "url": "https://github.com/remix-run/react-router/issues" diff --git a/packages/react-router-remix-routes-option-adapter/CHANGELOG.md b/packages/react-router-remix-routes-option-adapter/CHANGELOG.md index 2c94179182..bd935d047c 100644 --- a/packages/react-router-remix-routes-option-adapter/CHANGELOG.md +++ b/packages/react-router-remix-routes-option-adapter/CHANGELOG.md @@ -1,5 +1,12 @@ # `@react-router/remix-config-routes-adapter` +## v8.0.1 + +### Patch Changes + +- Updated dependencies: + - [`@react-router/dev@8.0.1`](https://github.com/remix-run/react-router/releases/tag/@react-router/dev@8.0.1) + ## v8.0.0 ### Major Changes diff --git a/packages/react-router-remix-routes-option-adapter/package.json b/packages/react-router-remix-routes-option-adapter/package.json index 021c14780c..c0be299b18 100644 --- a/packages/react-router-remix-routes-option-adapter/package.json +++ b/packages/react-router-remix-routes-option-adapter/package.json @@ -1,7 +1,7 @@ { "name": "@react-router/remix-routes-option-adapter", "type": "module", - "version": "8.0.0", + "version": "8.0.1", "description": "Adapter for Remix's \"routes\" config option, for use within routes.ts", "bugs": { "url": "https://github.com/remix-run/react-router/issues" diff --git a/packages/react-router-serve/CHANGELOG.md b/packages/react-router-serve/CHANGELOG.md index c2e38da779..e0d1f51e1b 100644 --- a/packages/react-router-serve/CHANGELOG.md +++ b/packages/react-router-serve/CHANGELOG.md @@ -1,5 +1,14 @@ # `@react-router/serve` +## v8.0.1 + +### Patch Changes + +- Updated dependencies: + - [`react-router@8.0.1`](https://github.com/remix-run/react-router/releases/tag/react-router@8.0.1) + - [`@react-router/express@8.0.1`](https://github.com/remix-run/react-router/releases/tag/@react-router/express@8.0.1) + - [`@react-router/node@8.0.1`](https://github.com/remix-run/react-router/releases/tag/@react-router/node@8.0.1) + ## v8.0.0 ### Major Changes diff --git a/packages/react-router-serve/package.json b/packages/react-router-serve/package.json index 880c23f8c8..d34923eefd 100644 --- a/packages/react-router-serve/package.json +++ b/packages/react-router-serve/package.json @@ -1,7 +1,7 @@ { "name": "@react-router/serve", "type": "module", - "version": "8.0.0", + "version": "8.0.1", "description": "Production application server for React Router", "bugs": { "url": "https://github.com/remix-run/react-router/issues" diff --git a/packages/react-router/CHANGELOG.md b/packages/react-router/CHANGELOG.md index 4e126ea536..a348e2e47b 100644 --- a/packages/react-router/CHANGELOG.md +++ b/packages/react-router/CHANGELOG.md @@ -1,5 +1,11 @@ # `react-router` +## v8.0.1 + +### Patch Changes + +- Remove the obsolete `AppLoadContext` type export accidentally left over from v7 now that middleware is always enabled and server request context is provided through `RouterContextProvider`. ([#15207](https://github.com/remix-run/react-router/pull/15207)) + ## v8.0.0 ### Major Changes diff --git a/packages/react-router/index.ts b/packages/react-router/index.ts index 16206b7b69..34a03bbc64 100644 --- a/packages/react-router/index.ts +++ b/packages/react-router/index.ts @@ -277,8 +277,6 @@ export type { CookieSignatureOptions, } from "./lib/server-runtime/cookies"; -export type { AppLoadContext } from "./lib/server-runtime/data"; - export type { PageLinkDescriptor, HtmlLinkDescriptor, diff --git a/packages/react-router/lib/dom/ssr/routes-test-stub.tsx b/packages/react-router/lib/dom/ssr/routes-test-stub.tsx index a8afd831d2..9b0f707838 100644 --- a/packages/react-router/lib/dom/ssr/routes-test-stub.tsx +++ b/packages/react-router/lib/dom/ssr/routes-test-stub.tsx @@ -166,7 +166,7 @@ export function createRoutesStub( function processRoutes( routes: StubRouteObject[], - context: unknown, + context: RouterContextProvider, manifest: AssetsManifest, routeModules: RouteModules, parentId?: string, @@ -192,27 +192,16 @@ function processRoutes( ? withErrorBoundaryProps(route.ErrorBoundary) : undefined, action: route.action - ? (args: ActionFunctionArgs) => - route.action!({ - ...args, - context: context as Readonly, - }) + ? (args: ActionFunctionArgs) => route.action!({ ...args, context }) : undefined, loader: route.loader - ? (args: LoaderFunctionArgs) => - route.loader!({ - ...args, - context: context as Readonly, - }) + ? (args: LoaderFunctionArgs) => route.loader!({ ...args, context }) : undefined, middleware: route.middleware ? route.middleware.map( (mw) => (...args: Parameters) => - mw( - { ...args[0], context: context as RouterContextProvider }, - args[1], - ), + mw({ ...args[0], context }, args[1]), ) : undefined, handle: route.handle, diff --git a/packages/react-router/lib/router/router.ts b/packages/react-router/lib/router/router.ts index a68036c589..dace75a307 100644 --- a/packages/react-router/lib/router/router.ts +++ b/packages/react-router/lib/router/router.ts @@ -4226,9 +4226,7 @@ export function createStaticHandler( pattern: getRoutePattern(matches), matches, params: matches[0].params, - // If we're calling middleware then it must be enabled so we can cast - // this to the proper type knowing it's not an `AppLoadContext` - context: requestContext as RouterContextProvider, + context: requestContext, }, async () => { let res = await generateMiddlewareResponse( @@ -4471,9 +4469,7 @@ export function createStaticHandler( pattern: getRoutePattern(matches), matches, params: matches[0].params, - // If we're calling middleware then it must be enabled so we can cast - // this to the proper type knowing it's not an `AppLoadContext` - context: requestContext as RouterContextProvider, + context: requestContext, }, async () => { let res = await generateMiddlewareResponse( diff --git a/packages/react-router/lib/rsc/browser.tsx b/packages/react-router/lib/rsc/browser.tsx index ae8369203c..3ee49224d2 100644 --- a/packages/react-router/lib/rsc/browser.tsx +++ b/packages/react-router/lib/rsc/browser.tsx @@ -488,11 +488,7 @@ export function getRSCSingleFetchDataStrategy( // requests returning multiple server payloads (due to clientLoaders, fine // grained revalidation, etc.). This lets us stitch them all together and // patch them all at the end - // This cast should be fine since this is always run client side and - // `context` is always of this type on the client -- unlike on the server - // in framework mode when it could be `AppLoadContext` - let context = args.context as RouterContextProvider; - context.set(renderedRoutesContext, []); + args.context.set(renderedRoutesContext, []); let results = await dataStrategy(args); // patch into router from all payloads in map // TODO: Confirm that it's correct for us to have multiple rendered routes @@ -500,7 +496,7 @@ export function getRSCSingleFetchDataStrategy( // where we're calling `fetchAndDecode` multiple times. This may be a // sign of a logical error in how we're handling client loader routes. const renderedRoutesById = new Map(); - for (const route of context.get(renderedRoutesContext)) { + for (const route of args.context.get(renderedRoutesContext)) { if (!renderedRoutesById.has(route.id)) { renderedRoutesById.set(route.id, []); } @@ -531,10 +527,7 @@ function getFetchAndDecodeViaRSC( createFromReadableStream: BrowserCreateFromReadableStreamFunction, fetchImplementation: (request: Request) => Promise, ): FetchAndDecodeFunction { - return async ( - args: DataStrategyFunctionArgs, - targetRoutes?: string[], - ) => { + return async (args: DataStrategyFunctionArgs, targetRoutes?: string[]) => { let { request, context } = args; let url = singleFetchUrl(request.url, "rsc"); if (request.method === "GET") { @@ -584,13 +577,8 @@ function getFetchAndDecodeViaRSC( } // Track routes rendered per-single-fetch call so we can gather them up - // and patch them in together at the end. This cast should be fine since - // this is always run client side and `context` is always of this type on - // the client -- unlike on the server in framework mode when it could be - // `AppLoadContext` - (context as RouterContextProvider) - .get(renderedRoutesContext) - .push(...payload.matches); + // and patch them in together at the end. + context.get(renderedRoutesContext).push(...payload.matches); let results: DecodedSingleFetchResults = { routes: {} }; const dataKey = isMutationMethod(request.method) diff --git a/packages/react-router/lib/rsc/server.rsc.ts b/packages/react-router/lib/rsc/server.rsc.ts index 2cdc0026e6..55fe9240aa 100644 --- a/packages/react-router/lib/rsc/server.rsc.ts +++ b/packages/react-router/lib/rsc/server.rsc.ts @@ -24,7 +24,6 @@ import { type Params, type ShouldRevalidateFunction, type RouteMatch, - type RouteObject, type RouterContextProvider, type TrackedPromise, isAbsoluteUrl, diff --git a/packages/react-router/lib/server-runtime/data.ts b/packages/react-router/lib/server-runtime/data.ts index d3070adcfa..0f63bcf5ca 100644 --- a/packages/react-router/lib/server-runtime/data.ts +++ b/packages/react-router/lib/server-runtime/data.ts @@ -6,16 +6,6 @@ import type { } from "../router/utils"; import { isDataWithResponseInit, isRedirectStatusCode } from "../router/router"; -/** - * An object of unknown type for route loaders and actions provided by the - * server's `getLoadContext()` function. This is defined as an empty interface - * specifically so apps can leverage declaration merging to augment this type - * globally: https://www.typescriptlang.org/docs/handbook/declaration-merging.html - */ -export interface AppLoadContext { - [key: string]: unknown; -} - // Need to use RR's version here to permit the optional context even // though we know it'll always be provided in remix export async function callRouteHandler( diff --git a/packages/react-router/package.json b/packages/react-router/package.json index c78ffe3e52..173f68aa64 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -1,7 +1,7 @@ { "name": "react-router", "type": "module", - "version": "8.0.0", + "version": "8.0.1", "description": "Declarative routing for React", "keywords": [ "react", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 45624629e6..3809cc307d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -119,6 +119,9 @@ importers: fast-glob: specifier: 3.3.3 version: 3.3.3 + globals: + specifier: ^17.6.0 + version: 17.6.0 isbot: specifier: ^5.1.40 version: 5.1.40 @@ -140,6 +143,12 @@ importers: prompts: specifier: ^2.4.2 version: 2.4.2 + react: + specifier: 'catalog:' + version: 19.2.7 + react-dom: + specifier: 'catalog:' + version: 19.2.7(react@19.2.7) typedoc: specifier: ^0.28.19 version: 0.28.19(typescript@6.0.3) @@ -831,7 +840,7 @@ importers: version: 0.18.0 react-server-dom-webpack: specifier: 'catalog:' - version: 19.2.7(react-dom@19.3.0-canary-d763f313-20251210(react@19.3.0-canary-d763f313-20251210))(react@19.3.0-canary-d763f313-20251210)(webpack@5.103.0(esbuild@0.28.0)(lightningcss@1.32.0)) + version: 19.2.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(webpack@5.103.0(esbuild@0.28.0)(lightningcss@1.32.0)) semver: specifier: ^7.8.1 version: 7.8.1 @@ -877,7 +886,7 @@ importers: version: 7.7.1 '@vitejs/plugin-rsc': specifier: 'catalog:' - version: 0.5.26(react-dom@19.3.0-canary-d763f313-20251210(react@19.3.0-canary-d763f313-20251210))(react-server-dom-webpack@19.2.7(react-dom@19.3.0-canary-d763f313-20251210(react@19.3.0-canary-d763f313-20251210))(react@19.3.0-canary-d763f313-20251210)(webpack@5.103.0(esbuild@0.28.0)(lightningcss@1.32.0)))(react@19.3.0-canary-d763f313-20251210)(vite@7.3.1(@types/node@22.19.19)(jiti@2.4.2)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 0.5.26(react-dom@19.2.7(react@19.2.7))(react-server-dom-webpack@19.2.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(webpack@5.103.0(esbuild@0.28.0)(lightningcss@1.32.0)))(react@19.2.7)(vite@7.3.1(@types/node@22.19.19)(jiti@2.4.2)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)) esbuild-register: specifier: ^3.6.0 version: 3.6.0(esbuild@0.28.0) @@ -6362,6 +6371,10 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + globals@17.6.0: + resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==} + engines: {node: '>=18'} + globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} @@ -7844,11 +7857,6 @@ packages: peerDependencies: react: ^19.2.7 - react-dom@19.3.0-canary-d763f313-20251210: - resolution: {integrity: sha512-loJ3Y0TFRxwbNl+YA8ra1F0gDYIzJXXAVfe6hPwv8SKkV8eTWBP449oy5CqupcxjDy+4o730euIBTOZDTIQKvQ==} - peerDependencies: - react: 19.3.0-canary-d763f313-20251210 - react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -7886,10 +7894,6 @@ packages: resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} - react@19.3.0-canary-d763f313-20251210: - resolution: {integrity: sha512-XOiCIlc4XiIkKMnnZ8O6Z7yX3Ou1/Ix5MEZG0rPduJPX6pqNZaY679pKlJVUHE4qyqPdYHuwECDerI+jkfeHAQ==} - engines: {node: '>=0.10.0'} - read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} @@ -8145,9 +8149,6 @@ packages: scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - scheduler@0.28.0-canary-d763f313-20251210: - resolution: {integrity: sha512-Wvi6OlrTVL/PVxHeZmC+pjwfP68DC99Tsnq6jQiBozXlexKz+gcPtAAe6LhJvyS2A5U1TLBO4zxfJHkIKzxtEw==} - schema-utils@4.3.3: resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} engines: {node: '>= 10.13.0'} @@ -12342,22 +12343,6 @@ snapshots: optionalDependencies: react-server-dom-webpack: 19.2.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(webpack@5.103.0(esbuild@0.28.0)(lightningcss@1.32.0)) - '@vitejs/plugin-rsc@0.5.26(react-dom@19.3.0-canary-d763f313-20251210(react@19.3.0-canary-d763f313-20251210))(react-server-dom-webpack@19.2.7(react-dom@19.3.0-canary-d763f313-20251210(react@19.3.0-canary-d763f313-20251210))(react@19.3.0-canary-d763f313-20251210)(webpack@5.103.0(esbuild@0.28.0)(lightningcss@1.32.0)))(react@19.3.0-canary-d763f313-20251210)(vite@7.3.1(@types/node@22.19.19)(jiti@2.4.2)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))': - dependencies: - '@rolldown/pluginutils': 1.0.0-rc.18 - es-module-lexer: 2.1.0 - estree-walker: 3.0.3 - magic-string: 0.30.21 - react: 19.3.0-canary-d763f313-20251210 - react-dom: 19.3.0-canary-d763f313-20251210(react@19.3.0-canary-d763f313-20251210) - srvx: 0.11.16 - strip-literal: 3.1.0 - turbo-stream: 3.2.0 - vite: 7.3.1(@types/node@22.19.19)(jiti@2.4.2)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0) - vitefu: 1.1.3(vite@7.3.1(@types/node@22.19.19)(jiti@2.4.2)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)) - optionalDependencies: - react-server-dom-webpack: 19.2.7(react-dom@19.3.0-canary-d763f313-20251210(react@19.3.0-canary-d763f313-20251210))(react@19.3.0-canary-d763f313-20251210)(webpack@5.103.0(esbuild@0.28.0)(lightningcss@1.32.0)) - '@vitest/expect@4.1.7': dependencies: '@standard-schema/spec': 1.1.0 @@ -14340,6 +14325,8 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + globals@17.6.0: {} + globalthis@1.0.4: dependencies: define-properties: 1.2.1 @@ -16239,11 +16226,6 @@ snapshots: react: 19.2.7 scheduler: 0.27.0 - react-dom@19.3.0-canary-d763f313-20251210(react@19.3.0-canary-d763f313-20251210): - dependencies: - react: 19.3.0-canary-d763f313-20251210 - scheduler: 0.28.0-canary-d763f313-20251210 - react-is@16.13.1: {} react-is@17.0.2: {} @@ -16283,15 +16265,6 @@ snapshots: webpack: 5.103.0(esbuild@0.28.0)(lightningcss@1.32.0) webpack-sources: 3.5.0 - react-server-dom-webpack@19.2.7(react-dom@19.3.0-canary-d763f313-20251210(react@19.3.0-canary-d763f313-20251210))(react@19.3.0-canary-d763f313-20251210)(webpack@5.103.0(esbuild@0.28.0)(lightningcss@1.32.0)): - dependencies: - acorn-loose: 8.5.2 - neo-async: 2.6.2 - react: 19.3.0-canary-d763f313-20251210 - react-dom: 19.3.0-canary-d763f313-20251210(react@19.3.0-canary-d763f313-20251210) - webpack: 5.103.0(esbuild@0.28.0)(lightningcss@1.32.0) - webpack-sources: 3.5.0 - react-test-renderer@19.2.7(react@19.2.7): dependencies: react: 19.2.7 @@ -16300,8 +16273,6 @@ snapshots: react@19.2.7: {} - react@19.3.0-canary-d763f313-20251210: {} - read-cache@1.0.0: dependencies: pify: 2.3.0 @@ -16639,8 +16610,6 @@ snapshots: scheduler@0.27.0: {} - scheduler@0.28.0-canary-d763f313-20251210: {} - schema-utils@4.3.3: dependencies: '@types/json-schema': 7.0.15 diff --git a/scripts/release-comments.ts b/scripts/release-comments.ts index 27afcbef08..62e2de2bdb 100644 --- a/scripts/release-comments.ts +++ b/scripts/release-comments.ts @@ -6,6 +6,7 @@ // 6. close issues that are referenced in the PRs using `gh issue close` import semver from "semver"; +import { parseArgs } from "node:util"; import { logAndExec } from "./utils/process.ts"; let PACKAGE_NAME = "react-router"; // Package name used in git tags: react-router@x.x.x @@ -14,13 +15,34 @@ let GITHUB_REPOSITORY = "remix-run/react-router"; let PR_LABELS_TO_REMOVE = "awaiting release"; let ISSUE_LABELS_TO_REMOVE = "awaiting release"; let ISSUE_LABELS_TO_KEEP_OPEN = "🗺 Roadmap"; -let DRY_RUN = process.argv.includes("--dry-run"); + +let { values } = parseArgs({ + strict: true, + options: { + "dry-run": { + type: "boolean", + }, + release: { + type: "string", + }, + }, +}); + +let DRY_RUN = values["dry-run"]; +let RELEASE = values.release; + +if (RELEASE) { + invariant( + semver.valid(RELEASE), + `Expected --release to be a semver version, got ${RELEASE}`, + ); +} if (DRY_RUN) { console.log("⚠️ Running in dry-run mode -- no changes will be made"); } -let { latest, previous } = findBoundingTags(); +let { latest, previous } = findBoundingTags(RELEASE); // Find the git comments between the tags let gitCommits = getCommits(previous, latest); @@ -38,7 +60,7 @@ for (let pr of prs) { await commentOnPrAndLinkedIssues(pr, latest); } -function findBoundingTags() { +function findBoundingTags(release?: string) { // Determine the tags making up the delta from the prior release to this release let packageRegex = new RegExp(`^${PACKAGE_NAME}@`); let stdout = logAndExec( @@ -54,7 +76,14 @@ function findBoundingTags() { .split("\n") .map((tag): Tag => ({ raw: tag, clean: tag.replace(packageRegex, "") })); - let latest = stableGitTags[0]; + let latest = release + ? stableGitTags.find( + (tag) => + tag.raw === `${PACKAGE_NAME}@${release}` || tag.clean === release, + ) + : stableGitTags[0]; + invariant(latest, `No stable release found for ${release}`); + let expectedMajor = semver.major(latest.clean); if (semver.minor(latest.clean) === 0 && semver.patch(latest.clean) === 0) { expectedMajor -= 1; @@ -91,7 +120,7 @@ function getCommits(from: Tag, to: Tag): Array { } async function commentOnPrAndLinkedIssues(pr: MergedPR, latest: Tag) { - let prComment = `🤖 Hello there,\n\nWe just published version \`${latest.clean}\` which includes this pull request. If you'd like to take it for a test run please try it out and let us know what you think!\n\nThanks!`; + let prComment = `🤖 Hello there,\n\nWe recently published version \`${latest.clean}\` which includes this pull request. If you'd like to take it for a test run please try it out and let us know what you think!\n\nThanks!`; debug(`\nPR: https://github.com/${GITHUB_REPOSITORY}/pull/${pr.number}`); @@ -124,7 +153,7 @@ async function commentOnPrAndLinkedIssues(pr: MergedPR, latest: Tag) { } async function commentOnIssue(issue: number, latest: Tag) { - let issueComment = `🤖 Hello there,\n\nWe just published version \`${latest.clean}\` which involves this issue. If you'd like to take it for a test run please try it out and let us know what you think!\n\nThanks!`; + let issueComment = `🤖 Hello there,\n\nWe recently published version \`${latest.clean}\` which involves this issue. If you'd like to take it for a test run please try it out and let us know what you think!\n\nThanks!`; debug(`Issue: https://github.com/${GITHUB_REPOSITORY}/issues/${issue}`);