Skip to content
Open
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
12 changes: 10 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,16 @@
- [x] Reproduced locally: `Invalid context value... must return an instance of RouterContextProvider`
- [x] Revert `react-router` to `^7.17.0` (full v8 needs coordinated upgrade: Vite 7+, all `@react-router/*`, middleware context)
- [x] Verify build/tests + local wrangler returns 200
- [ ] Merge/deploy to restore production
- [ ] Follow-up: proper React Router v8 upgrade (or Dependabot ignore majors for `react-router`)
- [x] Merge/deploy fix (#140)
- [x] Confirm production is healthy after Workers rebuild (Worker returns 200)
- [ ] Follow-up: proper React Router v8 upgrade — tracked in https://github.com/SimeonGriggs/tints.dev/issues/141
- **React Router v8 upgrade (in progress, #141)**
- [ ] Bump Node engines to `>=22.22`, Vite 7, wrangler / Cloudflare Vite plugin peers
- [ ] Migrate Worker load context to `RouterContextProvider` + typed `createContext`
- [ ] Update OG loaders / `entry.server.tsx` off `AppLoadContext`
- [ ] Adopt remaining v8 future flags / remove graduated flags
- [ ] Bump `react-router` + `@react-router/*` to 8.x together
- [ ] Verify build/tests + open PR for Workers preview build
- **Cloudflare replatform (in progress)**
- [x] Replace Vercel React Router preset with Cloudflare Workers adapter
- [x] Migrate OG image generation to `@resvg/resvg-wasm`
Expand Down
8 changes: 8 additions & 0 deletions app/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createContext } from "react-router";

export type CloudflareContext = {
env: Env;
ctx: ExecutionContext;
};

export const cloudflareContext = createContext<CloudflareContext>();
3 changes: 1 addition & 2 deletions app/entry.server.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AppLoadContext, EntryContext } from "react-router";
import type { EntryContext } from "react-router";
import { ServerRouter } from "react-router";
import { isbot } from "isbot";
import { renderToReadableStream } from "react-dom/server";
Expand All @@ -8,7 +8,6 @@ export default async function handleRequest(
responseStatusCode: number,
responseHeaders: Headers,
routerContext: EntryContext,
_loadContext: AppLoadContext,
) {
let shellRendered = false;
const userAgent = request.headers.get("user-agent");
Expand Down
10 changes: 8 additions & 2 deletions app/lib/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,18 +140,24 @@ export function output(palettes: PaletteConfig[], mode: Mode = DEFAULT_MODE) {
return shaped;
}

function normalizeRequestPathname(pathname: string) {
// v8 pass-through requests keep `.data` / `/_.data` suffixes on the raw Request.
return pathname.replace(/\/_\.data$/, "/").replace(/\.data$/, "");
}

export function createRedirectResponse(
request: Request,
palette: PaletteConfig,
) {
const url = new URL(request.url);
const pathname = normalizeRequestPathname(url.pathname);
const hash = serializePalette(palette);

// Determine the new path based on the current path
let newPath = `/palette/${hash}`;
if (url.pathname.startsWith("/api/")) {
if (pathname.startsWith("/api/")) {
newPath = `/api${newPath}`;
} else if (url.pathname.endsWith("/og")) {
} else if (pathname.endsWith("/og")) {
newPath = `${newPath}/og`;
}

Expand Down
6 changes: 4 additions & 2 deletions app/routes/$name.$value.og.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { cloudflareContext } from "~/context";
import { generateOGImage } from "~/lib/generateOGImage.server";
import { createPaletteFromNameValue } from "~/lib/responses";

Expand All @@ -18,9 +19,10 @@ export const loader = async ({ params, context }: Route.LoaderArgs) => {
});
}

const png = await generateOGImage([palette], context.cloudflare.env.ASSETS);
const { env } = context.get(cloudflareContext);
const png = await generateOGImage([palette], env.ASSETS);

return new Response(new Blob([png]), {
return new Response(Uint8Array.from(png), {
status: 200,
headers: {
"Content-Type": "image/png",
Expand Down
6 changes: 4 additions & 2 deletions app/routes/palette.$hash.og.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { cloudflareContext } from "~/context";
import { generateOGImage } from "~/lib/generateOGImage.server";
import { deserializePalette } from "~/lib/paletteHash";

Expand All @@ -18,9 +19,10 @@ export const loader = async ({ params, context }: Route.LoaderArgs) => {
});
}

const png = await generateOGImage([palette], context.cloudflare.env.ASSETS);
const { env } = context.get(cloudflareContext);
const png = await generateOGImage([palette], env.ASSETS);

return new Response(new Blob([png]), {
return new Response(Uint8Array.from(png), {
status: 200,
headers: {
"Content-Type": "image/png",
Expand Down
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@headlessui/react": "^2.2.10",
"@heroicons/react": "^2.2.0",
"@portabletext/react": "^3.2.4",
"@react-router/fs-routes": "^7.17.0",
"@react-router/fs-routes": "^8.3.0",
"@resvg/resvg-wasm": "^2.6.2",
"chroma-js": "^3.2.0",
"clsx": "^2.1.1",
Expand All @@ -32,14 +32,14 @@
"react-dom": "^19.2.7",
"react-fast-compare": "^3.2.2",
"react-popper-tooltip": "^4.4.2",
"react-router": "^7.17.0",
"react-router": "^8.3.0",
"satori": "^0.13.2",
"usehooks-ts": "^3.1.1"
},
"devDependencies": {
"@cloudflare/vite-plugin": "^1.40.0",
"@cloudflare/workers-types": "^4.20260605.1",
"@react-router/dev": "^7.17.0",
"@cloudflare/vite-plugin": "^1.50.0",
"@cloudflare/workers-types": "^5.20260801.1",
"@react-router/dev": "^8.3.0",
"@tailwindcss/typography": "^0.5.19",
"@tailwindcss/vite": "^4.3.0",
"@types/chroma-js": "^3.1.2",
Expand All @@ -54,14 +54,14 @@
"prettier-plugin-tailwindcss": "^0.6.14",
"tailwindcss": "^4.3.0",
"typescript": "^5.9.3",
"vite": "^6.4.3",
"vite": "^7.3.6",
"vite-plugin-babel": "^1.7.3",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.2.6",
"wrangler": "^4.98.0"
"vitest": "^4.1.10",
"wrangler": "^4.118.0"
},
"engines": {
"node": ">=22"
"node": ">=22.22"
},
"sideEffects": false,
"packageManager": "pnpm@9.14.4+sha512.c8180b3fbe4e4bca02c94234717896b5529740a6cbadf19fa78254270403ea2f27d4e1d46a08a0f56c89b63dc8ebfd3ee53326da720273794e6200fcf0d184ab"
Expand Down
Loading