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 - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 22 node-version: 24
cache: npm cache: npm
- name: Install dependencies - name: Install dependencies
@@ -33,7 +33,7 @@ jobs:
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 22 node-version: 24
cache: npm cache: npm
- name: Install dependencies - name: Install dependencies
@@ -42,9 +42,8 @@ jobs:
- name: Test - name: Test
run: npm test run: npm test
build: typecheck:
name: Build name: Type Check
needs: [lint, test]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
@@ -53,7 +52,27 @@ jobs:
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: 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 cache: npm
- name: Install dependencies - 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 run format` — auto-fix lint issues
- `npm test` — run unit tests (Vitest, non-watch mode) - `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 ## Git Workflow
@@ -28,7 +28,7 @@ Feature branches are named `issue-XXX` where `XXX` is the related GitHub issue n
## Code Style ## 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`. **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 # Install dependencies only when needed
FROM base AS deps 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 { defineConfig, globalIgnores } from "eslint/config"
import nextVitals from "eslint-config-next/core-web-vitals"
import globals from "globals" import globals from "globals"
const __dirname = dirname(fileURLToPath(import.meta.url))
const compat = new FlatCompat({
baseDirectory: __dirname,
})
export default defineConfig([ export default defineConfig([
globalIgnores([ globalIgnores([
".next/**", ".next/**",
@@ -19,7 +11,7 @@ export default defineConfig([
"public/static/**", "public/static/**",
"next-env.d.ts", "next-env.d.ts",
]), ]),
...compat.extends("next/core-web-vitals"), ...nextVitals,
{ {
languageOptions: { languageOptions: {
globals: globals.browser, globals: globals.browser,
+1 -1
View File
@@ -1,2 +1,2 @@
[tools] [tools]
node = "22" node = "24"
+1 -1
View File
@@ -1,7 +1,7 @@
/// <reference types="next" /> /// <reference types="next" />
/// <reference types="next/image-types/global" /> /// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" /> /// <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 // NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. // 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 = { const config: NextConfig = {
output: "standalone", output: "standalone",
trailingSlash: false, trailingSlash: false,
eslint: {
ignoreDuringBuilds: true,
},
images: { images: {
remotePatterns: [ remotePatterns: [
{ hostname: "hudozkacdn.tmshv.com" }, { 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", "author": "Roman Timashev",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@radix-ui/react-navigation-menu": "^1.0.0", "@radix-ui/react-navigation-menu": "^1.2.0",
"@radix-ui/react-tooltip": "^1.0.0", "@radix-ui/react-tooltip": "^1.2.0",
"classnames": "^2.5.0", "classnames": "^2.5.0",
"dangerously-set-html-content": "^1.0.13", "dangerously-set-html-content": "^1.0.13",
"date-fns": "^4.0.0", "date-fns": "^4.0.0",
"markdown-it": "^13.0.1", "markdown-it": "^13.0.1",
"next": "^15.5.0", "next": "^16.1.0",
"next-seo": "^5.15.0", "next-seo": "^5.15.0",
"react": "^18.3.0", "react": "^19.2.0",
"react-dom": "^18.3.0", "react-dom": "^19.2.0",
"react-icons": "^4.2.0", "react-icons": "^4.2.0",
"sharp": "^0.34.0", "sharp": "^0.34.0",
"typograf": "^6.11.1", "typograf": "^6.11.1",
"valtio": "^2.1.7" "valtio": "^2.1.7"
}, },
"overrides": {
"dangerously-set-html-content": {
"react": "$react"
}
},
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3.3.1",
"@testing-library/jest-dom": "^6.9.1", "@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2", "@testing-library/react": "^16.3.2",
"@types/markdown-it": "^12.2.3", "@types/markdown-it": "^12.2.3",
"@types/node": "^22.19.9", "@types/node": "^22.19.9",
"@types/react": "^18.0.18", "@types/react": "^19.2.0",
"@typescript-eslint/eslint-plugin": "^8.54.0", "@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/parser": "^8.54.0", "@typescript-eslint/parser": "^8.54.0",
"eslint": "^9.0.0", "eslint": "^9.0.0",
"eslint-config-next": "^15.5.0", "eslint-config-next": "^16.1.0",
"globals": "^16.0.0", "globals": "^16.0.0",
"happy-dom": "^20.5.0", "happy-dom": "^20.5.0",
"typescript": "^5.9.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] { export function useToggle(initialValue: boolean): [boolean, (nextValue?: boolean) => void] {
return useReducer( const [state, setState] = useState(initialValue)
(state: boolean, next?: boolean) => typeof next === "boolean" ? next : !state, const toggle = useCallback((next?: boolean) => {
initialValue, 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 = { export type TransitionProps = {
children?: React.ReactNode children?: React.ReactNode
@@ -7,9 +7,11 @@ export type TransitionProps = {
show: boolean show: boolean
} }
export const Transition: React.FC<TransitionProps> = props => { export function Transition(props: TransitionProps) {
const [mounted, setMounted] = useState(props.show) const [mounted, setMounted] = useState(props.show)
const [opacity, setOpacity] = useState(0) const [opacity, setOpacity] = useState(0)
const timerRef = useRef<ReturnType<typeof setTimeout>>(null)
const onTransitionEnd = useCallback(() => { const onTransitionEnd = useCallback(() => {
if (opacity === 0) { if (opacity === 0) {
setMounted(false) setMounted(false)
@@ -20,19 +22,27 @@ export const Transition: React.FC<TransitionProps> = props => {
[props.duration], [props.duration],
) )
if (props.show && !mounted) {
setMounted(true)
}
if (!props.show && opacity !== 0) {
setOpacity(0)
}
useEffect(() => { useEffect(() => {
if (!props.show) { if (!props.show) {
setOpacity(0)
return return
} }
setMounted(true) timerRef.current = setTimeout(() => {
let t = setTimeout(() => {
setOpacity(1) setOpacity(1)
}, 16) }, 16)
return () => { return () => {
clearTimeout(t) if (timerRef.current) {
clearTimeout(timerRef.current)
}
} }
}, [props.show]) }, [props.show])
+3 -2
View File
@@ -23,7 +23,7 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"verbatimModuleSyntax": true, "verbatimModuleSyntax": true,
"moduleDetection": "force", "moduleDetection": "force",
"jsx": "preserve", "jsx": "react-jsx",
"incremental": true, "incremental": true,
"plugins": [ "plugins": [
{ {
@@ -39,6 +39,7 @@
"next-env.d.ts", "next-env.d.ts",
"**/*.ts", "**/*.ts",
"**/*.tsx", "**/*.tsx",
".next/types/**/*.ts" ".next/types/**/*.ts",
".next/dev/types/**/*.ts"
] ]
} }