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:
1 parent
60fb98ddeb
commit
3df47621a2
12 files changed
+1721
-736
No files matched your search
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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,
|
||||||
|
|||||||
Vendored
+1
-1
@@ -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,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" },
|
||||||
|
|||||||
Generated
+1652
-691
File diff suppressed because it is too large.
Load diff
+12
-8
@@ -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",
|
||||||
|
|||||||
@@ -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]
|
||||||
}
|
}
|
||||||
@@ -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
@@ -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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user