next 16; react 19; node 24 (#263)

* next 16; react 19

* fix build errors

* add typecheck to workflow

* use node 24

* claude update
This commit is contained in:
tmshv authored and GitHub committed 2026-02-08 23:07:25 +07:00
1 parent 60fb98ddeb
commit 3df47621a2
12 files changed
+1720 -735

No files matched your search

+25 -6
View File
@@ -14,7 +14,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24
cache: npm
- name: Install dependencies
@@ -33,7 +33,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24
cache: npm
- name: Install dependencies
@@ -42,9 +42,8 @@ jobs:
- name: Test
run: npm test
build:
name: Build
needs: [lint, test]
typecheck:
name: Type Check
runs-on: ubuntu-latest
steps:
- name: Checkout
@@ -53,7 +52,27 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24
cache: npm
- name: Install dependencies
run: npm ci
- name: Type check
run: npx tsc --noEmit
build:
name: Build
needs: [lint, test, typecheck]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- name: Install dependencies
+2 -2
View File
@@ -14,7 +14,7 @@ Website for the Art School of Shlisselburg ([art.shlisselburg.org](https://art.s
- `npm run format` — auto-fix lint issues
- `npm test` — run unit tests (Vitest, non-watch mode)
Node 22 required (see `mise.toml`). Deployed on Vercel.
Node 24 required (see `mise.toml`). Deployed on Vercel.
## Git Workflow
@@ -28,7 +28,7 @@ Feature branches are named `issue-XXX` where `XXX` is the related GitHub issue n
## Code Style
ESLint 9 flat config (`eslint.config.js`) using `FlatCompat` from `@eslint/eslintrc` to bridge `eslint-config-next` (which doesn't support native flat config in Next.js 15.x). Extends `next/core-web-vitals`. Enforces: no semicolons, double quotes, 4-space indentation, always-multiline trailing commas, `eol-last`.
ESLint 9 flat config (`eslint.config.js`) with native `eslint-config-next` 16.x support. Extends `next/core-web-vitals`. Enforces: no semicolons, double quotes, 4-space indentation, always-multiline trailing commas, `eol-last`.
**React components:** Use regular function declarations (not arrow functions) and `type` for props. Do not use `React.FC`.
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:22.19.0-alpine AS base
FROM node:24.12.0-alpine AS base
# Install dependencies only when needed
FROM base AS deps
+2 -10
View File
@@ -1,15 +1,7 @@
import { dirname } from "path"
import { fileURLToPath } from "url"
import { FlatCompat } from "@eslint/eslintrc"
import { defineConfig, globalIgnores } from "eslint/config"
import nextVitals from "eslint-config-next/core-web-vitals"
import globals from "globals"
const __dirname = dirname(fileURLToPath(import.meta.url))
const compat = new FlatCompat({
baseDirectory: __dirname,
})
export default defineConfig([
globalIgnores([
".next/**",
@@ -19,7 +11,7 @@ export default defineConfig([
"public/static/**",
"next-env.d.ts",
]),
...compat.extends("next/core-web-vitals"),
...nextVitals,
{
languageOptions: {
globals: globals.browser,
+1 -1
View File
@@ -1,2 +1,2 @@
[tools]
node = "22"
node = "24"
+1 -1
View File
@@ -1,7 +1,7 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />
/// <reference path="./.next/types/routes.d.ts" />
import "./.next/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
-3
View File
@@ -3,9 +3,6 @@ import type { NextConfig } from "next"
const config: NextConfig = {
output: "standalone",
trailingSlash: false,
eslint: {
ignoreDuringBuilds: true,
},
images: {
remotePatterns: [
{ hostname: "hudozkacdn.tmshv.com" },
+1651 -690
View File
File diff suppressed because it is too large. Load diff
+12 -8
View File
@@ -19,32 +19,36 @@
"author": "Roman Timashev",
"license": "ISC",
"dependencies": {
"@radix-ui/react-navigation-menu": "^1.0.0",
"@radix-ui/react-tooltip": "^1.0.0",
"@radix-ui/react-navigation-menu": "^1.2.0",
"@radix-ui/react-tooltip": "^1.2.0",
"classnames": "^2.5.0",
"dangerously-set-html-content": "^1.0.13",
"date-fns": "^4.0.0",
"markdown-it": "^13.0.1",
"next": "^15.5.0",
"next": "^16.1.0",
"next-seo": "^5.15.0",
"react": "^18.3.0",
"react-dom": "^18.3.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-icons": "^4.2.0",
"sharp": "^0.34.0",
"typograf": "^6.11.1",
"valtio": "^2.1.7"
},
"overrides": {
"dangerously-set-html-content": {
"react": "$react"
}
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@types/markdown-it": "^12.2.3",
"@types/node": "^22.19.9",
"@types/react": "^18.0.18",
"@types/react": "^19.2.0",
"@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/parser": "^8.54.0",
"eslint": "^9.0.0",
"eslint-config-next": "^15.5.0",
"eslint-config-next": "^16.1.0",
"globals": "^16.0.0",
"happy-dom": "^20.5.0",
"typescript": "^5.9.0",
+6 -5
View File
@@ -1,8 +1,9 @@
import { useReducer } from "react"
import { useCallback, useState } from "react"
export function useToggle(initialValue: boolean): [boolean, (nextValue?: boolean) => void] {
return useReducer(
(state: boolean, next?: boolean) => typeof next === "boolean" ? next : !state,
initialValue,
)
const [state, setState] = useState(initialValue)
const toggle = useCallback((next?: boolean) => {
setState(prev => typeof next === "boolean" ? next : !prev)
}, [])
return [state, toggle]
}
+16 -6
View File
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useState } from "react"
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
export type TransitionProps = {
children?: React.ReactNode
@@ -7,9 +7,11 @@ export type TransitionProps = {
show: boolean
}
export const Transition: React.FC<TransitionProps> = props => {
export function Transition(props: TransitionProps) {
const [mounted, setMounted] = useState(props.show)
const [opacity, setOpacity] = useState(0)
const timerRef = useRef<ReturnType<typeof setTimeout>>(null)
const onTransitionEnd = useCallback(() => {
if (opacity === 0) {
setMounted(false)
@@ -20,19 +22,27 @@ export const Transition: React.FC<TransitionProps> = props => {
[props.duration],
)
if (props.show && !mounted) {
setMounted(true)
}
if (!props.show && opacity !== 0) {
setOpacity(0)
}
useEffect(() => {
if (!props.show) {
setOpacity(0)
return
}
setMounted(true)
let t = setTimeout(() => {
timerRef.current = setTimeout(() => {
setOpacity(1)
}, 16)
return () => {
clearTimeout(t)
if (timerRef.current) {
clearTimeout(timerRef.current)
}
}
}, [props.show])
+3 -2
View File
@@ -23,7 +23,7 @@
"resolveJsonModule": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"jsx": "preserve",
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
@@ -39,6 +39,7 @@
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
]
}