Update Node to 26 and dependencies to their latest versions

- Node 20 is end-of-life; use the current LTS in the Docker image
- Major updates: @sanity/client 8, @sanity/image-url 2,
  @portabletext/react 8, eslint-config-next 16
- Unpin React and update to 19.3
- ESLint stays on 9 and TypeScript on 6.0, since eslint-config-next's
  plugins and typescript-eslint don't support newer majors yet
- Use the native flat config from eslint-config-next 16
- Use useSyncExternalStore for WebGL detection to satisfy the new
  react-hooks lint rule

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
leanderandClaude Opus 5.5 committed 2026-10-30 22:01:56 +01:00
1 parent a1729494b4
commit aca584791e
6 files changed
+1049 -533

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:20-alpine FROM node:26-alpine
WORKDIR /app WORKDIR /app
+6 -17
View File
@@ -1,19 +1,8 @@
import { dirname } from "path"; import { defineConfig } from "eslint/config"
import { fileURLToPath } from "url"; import nextVitals from "eslint-config-next/core-web-vitals"
import { FlatCompat } from "@eslint/eslintrc"; import nextTs from "eslint-config-next/typescript"
const __filename = fileURLToPath(import.meta.url); // eslint-config-next already ignores .next, out, build and next-env.d.ts
const __dirname = dirname(__filename); const eslintConfig = defineConfig([...nextVitals, ...nextTs])
const compat = new FlatCompat({ export default eslintConfig
baseDirectory: __dirname,
});
const eslintConfig = [
{
ignores: [".next/**", "node_modules/**", "next-env.d.ts"],
},
...compat.extends("next/core-web-vitals", "next/typescript"),
];
export default eslintConfig;
+1001 -487
View File
File diff suppressed because it is too large. Load diff
+16 -17
View File
@@ -12,28 +12,27 @@
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"" "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\""
}, },
"dependencies": { "dependencies": {
"@portabletext/react": "^3.2.1", "@portabletext/react": "^8.0.1",
"@sanity/client": "^7.8.1", "@sanity/client": "^8.14.1",
"@sanity/image-url": "^1.1.0", "@sanity/image-url": "^2.1.1",
"@sanity/webhook": "^4.0.4", "@sanity/webhook": "^4.0.4",
"country-flag-icons": "^1.5.19", "country-flag-icons": "^1.6.20",
"install": "^0.13.0", "install": "^0.13.0",
"next": "^16.3.3", "next": "^16.4.3",
"next-intl": "^4.3.4", "next-intl": "^4.14.9",
"react": "19.1.0", "react": "^19.3.1",
"react-dom": "19.1.0", "react-dom": "^19.3.1",
"react-icons": "^5.5.0", "react-icons": "^5.7.0",
"react-photo-sphere-viewer": "^6.2.3" "react-photo-sphere-viewer": "^6.2.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3", "@types/node": "^26.6.6",
"@types/node": "^20", "@types/react": "^19.3.0",
"@types/react": "^19", "@types/react-dom": "^19.3.0",
"@types/react-dom": "^19", "eslint": "^9.39.5",
"eslint": "^9", "eslint-config-next": "^16.4.3",
"eslint-config-next": "15.4.4", "prettier": "^3.9.10",
"prettier": "^3.6.2", "typescript": "~6.0.3"
"typescript": "^5"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
+20 -8
View File
@@ -1,21 +1,33 @@
"use client" "use client"
import { useEffect, useState } from "react" import { useSyncExternalStore } from "react"
import { useTranslations } from "next-intl" import { useTranslations } from "next-intl"
import Image from "next/image" import Image from "next/image"
import { ReactPhotoSphereViewer } from "react-photo-sphere-viewer" import { ReactPhotoSphereViewer } from "react-photo-sphere-viewer"
import * as styles from "./office.module.css" import * as styles from "./office.module.css"
let webGL2Support: boolean | undefined
// Checked once, since creating a WebGL context is not free
const getWebGL2Support = () => {
webGL2Support ??= Boolean(
document.createElement("canvas").getContext("webgl2")
)
return webGL2Support
}
// WebGL support never changes, so there is nothing to subscribe to
const subscribe = () => () => {}
const Office = () => { const Office = () => {
const t = useTranslations("office") const t = useTranslations("office")
const [supportsWebGL2, setSupportsWebGL2] = useState<boolean | null>(null) // null on the server, which renders the fallback image
const supportsWebGL2 = useSyncExternalStore(
useEffect(() => { subscribe,
const canvas = document.createElement("canvas") getWebGL2Support,
const gl = canvas.getContext("webgl2") () => null
setSupportsWebGL2(Boolean(gl)) )
}, [])
return ( return (
<section id="office"> <section id="office">
+5 -3
View File
@@ -1,9 +1,11 @@
import imageUrlBuilder from "@sanity/image-url" import {
import type { SanityImageSource } from "@sanity/image-url/lib/types/types" createImageUrlBuilder,
type SanityImageSource,
} from "@sanity/image-url"
import { sanityClient } from "./sanity" import { sanityClient } from "./sanity"
const builder = imageUrlBuilder(sanityClient) const builder = createImageUrlBuilder(sanityClient)
export function urlFor(source: SanityImageSource) { export function urlFor(source: SanityImageSource) {
return builder.image(source) return builder.image(source)