Files
NextEdu/eslint.config.mjs
SpecialX 811ad11f9f feat(logging): enable ESLint no-console rule with client exemptions
全局启用 no-console: error 规则,强制服务端 .ts 文件使用 createModuleLogger。豁免场景: scripts/(脚本)、tests/(测试)、src/**/*.tsx(客户端组件,留待 Task 12-14 处理)、src/**/hooks/**/*.ts(客户端 hooks)、src/**/components/**/*.ts(客户端 utils)、src/shared/lib/query-client.ts(被客户端导入)。将 deletes/ 归档目录加入 globalIgnores。
2026-07-07 12:39:21 +08:00

152 lines
4.6 KiB
JavaScript

import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";
import { fileURLToPath, pathToFileURL } from "node:url";
import { dirname, join } from "node:path";
const __dirname = dirname(fileURLToPath(import.meta.url));
const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
{
rules: {
"react-hooks/incompatible-library": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
// 禁止硬编码 hex 颜色字面量(白名单:tokens 定义文件、邮件、manifest)
// 白名单文件内的 #hex 需在所在行上方加 // eslint-disable-next-line no-restricted-syntax 注释豁免
"no-restricted-syntax": [
"error",
{
selector: "Literal[value=/#[0-9a-fA-F]{3,8}/]",
message:
"禁止硬编码 hex 颜色,使用设计令牌 hsl(var(--*)) 或 Tailwind 类 bg-*",
},
],
// 禁止 console.* 调用,统一使用 createModuleLogger("module-name")
// 豁免场景:scripts/、tests/、客户端 .tsx(留待客户端错误上报机制处理)、被客户端导入的 .ts
"no-console": "error",
},
},
{
files: ["tests/**/*.ts"],
languageOptions: {
globals: {
describe: "readonly",
it: "readonly",
test: "readonly",
expect: "readonly",
beforeAll: "readonly",
afterAll: "readonly",
beforeEach: "readonly",
afterEach: "readonly",
vi: "readonly",
},
},
},
// 自定义规则:检测硬编码字体家族字面量
{
plugins: {
"design-tokens": {
rules: {
"no-hardcoded-fonts": await import(
pathToFileURL(join(__dirname, "eslint-rules/no-hardcoded-design-tokens.js")).href
).then((m) => m.default ?? m),
},
},
},
rules: {
"design-tokens/no-hardcoded-fonts": "error",
},
},
// 缓存策略规则:禁止在 actions / route.ts 中直接调用 revalidatePath/revalidateTag
{
files: ["src/modules/**/actions*.ts", "src/app/api/**/route.ts", "src/i18n/actions*.ts"],
rules: {
"no-restricted-syntax": [
"error",
{
selector: "CallExpression[callee.name='revalidatePath']",
message:
"使用 invalidateFor() 替代直接 revalidatePath(),参见 docs/architecture/004 缓存章节",
},
{
selector: "CallExpression[callee.name='revalidateTag']",
message:
"使用 invalidateFor() 替代直接 revalidateTag(),参见 docs/architecture/004 缓存章节",
},
],
},
},
// 豁免:shared/lib/cache/ 内部允许调用 revalidatePath/revalidateTag(invalidate.ts 内部实现)
{
files: ["src/shared/lib/cache/**/*.ts"],
rules: {
"no-restricted-syntax": "off",
},
},
// 豁免:scripts/ 和 tests/ 允许 console(脚本/测试场景不接入 logger)
{
files: ["scripts/**/*.{js,mjs,ts}", "tests/**/*.{ts,tsx,js}"],
rules: {
"no-console": "off",
},
},
// 暂时豁免:客户端 .tsx 文件中的 console 调用,留待客户端错误上报机制(Task 12-14)处理
// 客户端组件不能导入服务端 pino logger,需通过 useErrorReport Hook 上报
{
files: ["src/**/*.tsx"],
rules: {
"no-console": "off",
},
},
// 豁免:客户端 hooks(.ts)被客户端组件导入,不能使用服务端 logger
// 留待 Task 12-14 客户端错误上报机制处理
{
files: ["src/**/hooks/**/*.ts"],
rules: {
"no-console": "off",
},
},
// 豁免:components 目录中的 .ts utils(如 exam-preview-utils.ts)被客户端 hooks 导入
{
files: ["src/**/components/**/*.ts"],
rules: {
"no-console": "off",
},
},
// 豁免:被客户端组件导入的 .ts 文件不能使用服务端 logger(如 query-client.ts 被 providers.tsx 导入)
{
files: ["src/shared/lib/query-client.ts"],
rules: {
"no-console": "off",
},
},
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
"docs/scripts/**",
"playwright-report/**",
"test-results/**",
// Debug scripts using CommonJS
"tests/webapp/debug_drizzle.js",
// Migration/maintenance scripts using CommonJS require()
"scripts/**/*.js",
// 已删除文件的归档目录,不参与 lint
"deletes/**",
]),
]);
export default eslintConfig;