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-*", }, ], }, }, { 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", }, }, // 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", ]), ]); export default eslintConfig;