setup: Biome linter/formatter + Zustand + TanStack Query + RHF + Zod

This commit is contained in:
zetaloop
2026-02-20 12:11:00 +08:00
parent f24fb1704e
commit 1f87f4676e
10 changed files with 270 additions and 26 deletions
+9 -13
View File
@@ -1,34 +1,30 @@
import type { Metadata } from "next"; import type { Metadata } from "next"
import { Geist, Geist_Mono } from "next/font/google"; import { Geist, Geist_Mono } from "next/font/google"
import "./globals.css"; import "./globals.css"
const geistSans = Geist({ const geistSans = Geist({
variable: "--font-geist-sans", variable: "--font-geist-sans",
subsets: ["latin"], subsets: ["latin"],
}); })
const geistMono = Geist_Mono({ const geistMono = Geist_Mono({
variable: "--font-geist-mono", variable: "--font-geist-mono",
subsets: ["latin"], subsets: ["latin"],
}); })
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Create Next App", title: "Create Next App",
description: "Generated by create next app", description: "Generated by create next app",
}; }
export default function RootLayout({ export default function RootLayout({
children, children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode
}>) { }>) {
return ( return (
<html lang="en"> <html lang="en">
<body <body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>{children}</body>
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html> </html>
); )
} }
+2 -2
View File
@@ -1,4 +1,4 @@
import Image from "next/image"; import Image from "next/image"
export default function Home() { export default function Home() {
return ( return (
@@ -61,5 +61,5 @@ export default function Home() {
</div> </div>
</main> </main>
</div> </div>
); )
} }
+58
View File
@@ -0,0 +1,58 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.3/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"ignoreUnknown": true,
"includes": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.mjs", "**/*.json", "**/*.css"]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"noUnusedImports": "warn",
"useExhaustiveDependencies": "warn"
},
"style": {
"noNonNullAssertion": "warn",
"useImportType": "error"
},
"suspicious": {
"noExplicitAny": "warn"
}
}
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"semicolons": "asNeeded"
}
},
"css": {
"parser": {
"cssModules": true,
"tailwindDirectives": true
},
"formatter": {
"enabled": true
},
"linter": {
"enabled": true
}
},
"json": {
"formatter": {
"enabled": true
}
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { clsx, type ClassValue } from "clsx" import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge" import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
+1 -1
View File
@@ -1,6 +1,6 @@
/// <reference types="next" /> /// <reference types="next" />
/// <reference types="next/image-types/global" /> /// <reference types="next/image-types/global" />
import "./.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 -3
View File
@@ -1,7 +1,7 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next"
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
/* config options here */ /* config options here */
}; }
export default nextConfig; export default nextConfig
+14 -3
View File
@@ -3,11 +3,18 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev --turbopack",
"build": "next build", "build": "next build",
"start": "next start" "start": "next start",
"lint": "biome check .",
"lint:fix": "biome check --write .",
"format": "biome format --write .",
"check": "biome ci .",
"typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@hookform/resolvers": "^5.2.2",
"@tanstack/react-query": "^5.90.21",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-react": "^0.575.0", "lucide-react": "^0.575.0",
@@ -15,9 +22,13 @@
"radix-ui": "^1.4.3", "radix-ui": "^1.4.3",
"react": "19.2.3", "react": "19.2.3",
"react-dom": "19.2.3", "react-dom": "19.2.3",
"tailwind-merge": "^3.5.0" "react-hook-form": "^7.71.1",
"tailwind-merge": "^3.5.0",
"zod": "^4.3.6",
"zustand": "^5.0.11"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^2.4.3",
"@tailwindcss/postcss": "^4", "@tailwindcss/postcss": "^4",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^19", "@types/react": "^19",
+179
View File
@@ -8,6 +8,12 @@ importers:
.: .:
dependencies: dependencies:
'@hookform/resolvers':
specifier: ^5.2.2
version: 5.2.2(react-hook-form@7.71.1(react@19.2.3))
'@tanstack/react-query':
specifier: ^5.90.21
version: 5.90.21(react@19.2.3)
class-variance-authority: class-variance-authority:
specifier: ^0.7.1 specifier: ^0.7.1
version: 0.7.1 version: 0.7.1
@@ -29,10 +35,22 @@ importers:
react-dom: react-dom:
specifier: 19.2.3 specifier: 19.2.3
version: 19.2.3(react@19.2.3) version: 19.2.3(react@19.2.3)
react-hook-form:
specifier: ^7.71.1
version: 7.71.1(react@19.2.3)
tailwind-merge: tailwind-merge:
specifier: ^3.5.0 specifier: ^3.5.0
version: 3.5.0 version: 3.5.0
zod:
specifier: ^4.3.6
version: 4.3.6
zustand:
specifier: ^5.0.11
version: 5.0.11(@types/react@19.2.14)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))
devDependencies: devDependencies:
'@biomejs/biome':
specifier: ^2.4.3
version: 2.4.3
'@tailwindcss/postcss': '@tailwindcss/postcss':
specifier: ^4 specifier: ^4
version: 4.2.0 version: 4.2.0
@@ -197,6 +215,63 @@ packages:
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@biomejs/biome@2.4.3':
resolution: {integrity: sha512-cBrjf6PNF6yfL8+kcNl85AjiK2YHNsbU0EvDOwiZjBPbMbQ5QcgVGFpjD0O52p8nec5O8NYw7PKw3xUR7fPAkQ==}
engines: {node: '>=14.21.3'}
hasBin: true
'@biomejs/cli-darwin-arm64@2.4.3':
resolution: {integrity: sha512-eOafSFlI/CF4id2tlwq9CVHgeEqvTL5SrhWff6ZORp6S3NL65zdsR3ugybItkgF8Pf4D9GSgtbB6sE3UNgOM9w==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [darwin]
'@biomejs/cli-darwin-x64@2.4.3':
resolution: {integrity: sha512-V2+av4ilbWcBMNufTtMMXVW00nPwyIjI5qf7n9wSvUaZ+tt0EvMGk46g9sAFDJBEDOzSyoRXiSP6pCvKTOEbPA==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [darwin]
'@biomejs/cli-linux-arm64-musl@2.4.3':
resolution: {integrity: sha512-QuFzvsGo8BA4Xm7jGX5idkw6BqFblcCPySMTvq0AhGYnhUej5VJIDJbmTKfHqwjHepZiC4fA+T5i6wmiZolZNw==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@biomejs/cli-linux-arm64@2.4.3':
resolution: {integrity: sha512-0m+O0x9FgK99FAwDK+fiDtjs2wnqq7bvfj17KJVeCkTwT/liI+Q9njJG7lwXK0iSJVXeFNRIxukpVI3SifMYAA==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@biomejs/cli-linux-x64-musl@2.4.3':
resolution: {integrity: sha512-qEc0OCpj/uytruQ4wLM0yWNJLZy0Up8H1Er5MW3SrstqM6J2d4XqdNA86xzCy8MQCHpoVZ3lFye3GBlIL4/ljw==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
libc: [musl]
'@biomejs/cli-linux-x64@2.4.3':
resolution: {integrity: sha512-NVqh0saIU0u5OfOp/0jFdlKRE59+XyMvWmtx0f6Nm/2OpdxBl04coRIftBbY9d1gfu+23JVv4CItAqPYrjYh5w==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@biomejs/cli-win32-arm64@2.4.3':
resolution: {integrity: sha512-gRO96vrIARilv/Cp2ZnmNNL5LSZg3RO75GPp13hsLO3N4YVpE7saaMDp2bcyV48y2N2Pbit1brkGVGta0yd6VQ==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [win32]
'@biomejs/cli-win32-x64@2.4.3':
resolution: {integrity: sha512-vSm/vOJe06pf14aGHfHl3Ar91Nlx4YYmohElDJ+17UbRwe99n987S/MhAlQOkONqf1utJor04ChkCPmKb8SWdw==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [win32]
'@dotenvx/dotenvx@1.52.0': '@dotenvx/dotenvx@1.52.0':
resolution: {integrity: sha512-CaQcc8JvtzQhUSm9877b6V4Tb7HCotkcyud9X2YwdqtQKwgljkMRwU96fVYKnzN3V0Hj74oP7Es+vZ0mS+Aa1w==} resolution: {integrity: sha512-CaQcc8JvtzQhUSm9877b6V4Tb7HCotkcyud9X2YwdqtQKwgljkMRwU96fVYKnzN3V0Hj74oP7Es+vZ0mS+Aa1w==}
hasBin: true hasBin: true
@@ -231,6 +306,11 @@ packages:
peerDependencies: peerDependencies:
hono: ^4 hono: ^4
'@hookform/resolvers@5.2.2':
resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==}
peerDependencies:
react-hook-form: ^7.55.0
'@img/colour@1.0.0': '@img/colour@1.0.0':
resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -1234,6 +1314,9 @@ packages:
resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
'@standard-schema/utils@0.3.0':
resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
'@swc/helpers@0.5.15': '@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
@@ -1329,6 +1412,14 @@ packages:
'@tailwindcss/postcss@4.2.0': '@tailwindcss/postcss@4.2.0':
resolution: {integrity: sha512-u6YBacGpOm/ixPfKqfgrJEjMfrYmPD7gEFRoygS/hnQaRtV0VCBdpkx5Ouw9pnaLRwwlgGCuJw8xLpaR0hOrQg==} resolution: {integrity: sha512-u6YBacGpOm/ixPfKqfgrJEjMfrYmPD7gEFRoygS/hnQaRtV0VCBdpkx5Ouw9pnaLRwwlgGCuJw8xLpaR0hOrQg==}
'@tanstack/query-core@5.90.20':
resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==}
'@tanstack/react-query@5.90.21':
resolution: {integrity: sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg==}
peerDependencies:
react: ^18 || ^19
'@ts-morph/common@0.27.0': '@ts-morph/common@0.27.0':
resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==}
@@ -2334,6 +2425,12 @@ packages:
peerDependencies: peerDependencies:
react: ^19.2.3 react: ^19.2.3
react-hook-form@7.71.1:
resolution: {integrity: sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^16.8.0 || ^17 || ^18 || ^19
react-remove-scroll-bar@2.3.8: react-remove-scroll-bar@2.3.8:
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -2724,6 +2821,27 @@ packages:
zod@3.25.76: zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
zod@4.3.6:
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
zustand@5.0.11:
resolution: {integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==}
engines: {node: '>=12.20.0'}
peerDependencies:
'@types/react': '>=18.0.0'
immer: '>=9.0.6'
react: '>=18.0.0'
use-sync-external-store: '>=1.2.0'
peerDependenciesMeta:
'@types/react':
optional: true
immer:
optional: true
react:
optional: true
use-sync-external-store:
optional: true
snapshots: snapshots:
'@alloc/quick-lru@5.2.0': {} '@alloc/quick-lru@5.2.0': {}
@@ -2921,6 +3039,41 @@ snapshots:
'@babel/helper-string-parser': 7.27.1 '@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5 '@babel/helper-validator-identifier': 7.28.5
'@biomejs/biome@2.4.3':
optionalDependencies:
'@biomejs/cli-darwin-arm64': 2.4.3
'@biomejs/cli-darwin-x64': 2.4.3
'@biomejs/cli-linux-arm64': 2.4.3
'@biomejs/cli-linux-arm64-musl': 2.4.3
'@biomejs/cli-linux-x64': 2.4.3
'@biomejs/cli-linux-x64-musl': 2.4.3
'@biomejs/cli-win32-arm64': 2.4.3
'@biomejs/cli-win32-x64': 2.4.3
'@biomejs/cli-darwin-arm64@2.4.3':
optional: true
'@biomejs/cli-darwin-x64@2.4.3':
optional: true
'@biomejs/cli-linux-arm64-musl@2.4.3':
optional: true
'@biomejs/cli-linux-arm64@2.4.3':
optional: true
'@biomejs/cli-linux-x64-musl@2.4.3':
optional: true
'@biomejs/cli-linux-x64@2.4.3':
optional: true
'@biomejs/cli-win32-arm64@2.4.3':
optional: true
'@biomejs/cli-win32-x64@2.4.3':
optional: true
'@dotenvx/dotenvx@1.52.0': '@dotenvx/dotenvx@1.52.0':
dependencies: dependencies:
commander: 11.1.0 commander: 11.1.0
@@ -2963,6 +3116,11 @@ snapshots:
dependencies: dependencies:
hono: 4.12.0 hono: 4.12.0
'@hookform/resolvers@5.2.2(react-hook-form@7.71.1(react@19.2.3))':
dependencies:
'@standard-schema/utils': 0.3.0
react-hook-form: 7.71.1(react@19.2.3)
'@img/colour@1.0.0': '@img/colour@1.0.0':
optional: true optional: true
@@ -3944,6 +4102,8 @@ snapshots:
'@sindresorhus/merge-streams@4.0.0': {} '@sindresorhus/merge-streams@4.0.0': {}
'@standard-schema/utils@0.3.0': {}
'@swc/helpers@0.5.15': '@swc/helpers@0.5.15':
dependencies: dependencies:
tslib: 2.8.1 tslib: 2.8.1
@@ -4017,6 +4177,13 @@ snapshots:
postcss: 8.5.6 postcss: 8.5.6
tailwindcss: 4.2.0 tailwindcss: 4.2.0
'@tanstack/query-core@5.90.20': {}
'@tanstack/react-query@5.90.21(react@19.2.3)':
dependencies:
'@tanstack/query-core': 5.90.20
react: 19.2.3
'@ts-morph/common@0.27.0': '@ts-morph/common@0.27.0':
dependencies: dependencies:
fast-glob: 3.3.3 fast-glob: 3.3.3
@@ -4957,6 +5124,10 @@ snapshots:
react: 19.2.3 react: 19.2.3
scheduler: 0.27.0 scheduler: 0.27.0
react-hook-form@7.71.1(react@19.2.3):
dependencies:
react: 19.2.3
react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.3): react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.3):
dependencies: dependencies:
react: 19.2.3 react: 19.2.3
@@ -5376,3 +5547,11 @@ snapshots:
zod: 3.25.76 zod: 3.25.76
zod@3.25.76: {} zod@3.25.76: {}
zod@4.3.6: {}
zustand@5.0.11(@types/react@19.2.14)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)):
optionalDependencies:
'@types/react': 19.2.14
react: 19.2.3
use-sync-external-store: 1.6.0(react@19.2.3)
+2 -2
View File
@@ -2,6 +2,6 @@ const config = {
plugins: { plugins: {
"@tailwindcss/postcss": {}, "@tailwindcss/postcss": {},
}, },
}; }
export default config; export default config