import js from "@eslint/js"; import importPlugin from "eslint-plugin-import"; import perfectionist from "eslint-plugin-perfectionist"; import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; import reactHooks from "eslint-plugin-react-hooks"; import reactRefresh from "eslint-plugin-react-refresh"; import tseslint from "typescript-eslint"; import { enforceCatchType } from "./eslint-rules/enforce-catch-type.js"; import { noEmptyFunction } from "./eslint-rules/no-empty-function.js"; const noDirectConsoleMessage = "后端运行时代码禁止直接使用 console.*;请通过注入的 Logger 实例输出日志,配置加载失败前使用 createConsoleFallback()。"; export default tseslint.config( { ignores: [ "node_modules/**", "dist/**", ".build/**", "*.bun-build", "openspec/**", ".opencode/**", ".claude/**", ".codex/**", ".agents/**", "bun.lock", "data/**", "eslint-rules/**", ], }, js.configs.recommended, ...tseslint.configs.recommendedTypeChecked, ...tseslint.configs.stylisticTypeChecked, importPlugin.flatConfigs.recommended, importPlugin.flatConfigs.typescript, perfectionist.configs["recommended-natural"], { languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, settings: { "import/resolver": { node: true, typescript: true }, }, }, { rules: { "@typescript-eslint/array-type": ["error", { default: "array-simple" }], "@typescript-eslint/consistent-type-assertions": ["error", { assertionStyle: "as" }], "@typescript-eslint/consistent-type-imports": ["error", { prefer: "type-imports" }], "@typescript-eslint/no-empty-function": "off", "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], "@typescript-eslint/only-throw-error": "error", "@typescript-eslint/prefer-nullish-coalescing": "error", "@typescript-eslint/prefer-optional-chain": "error", "import/no-unresolved": ["error", { ignore: ["^bun:"] }], "no-restricted-syntax": [ "error", { message: "禁止 throw 字面量。项目约定只允许 throw new Error(...) 或 throw new AppError(msg, statusCode)。Re-throw 已捕获的 Error 实例时使用 throw e。", selector: "ThrowStatement > Literal", }, ], "no-undef": "off", }, }, { files: ["src/**/*.{ts,tsx}"], plugins: { local: { rules: { "enforce-catch-type": enforceCatchType, "no-empty-function": noEmptyFunction, }, }, }, rules: { "local/enforce-catch-type": "warn", "local/no-empty-function": "error", }, }, { files: ["eslint.config.js"], rules: { "import/no-named-as-default": "off", "import/no-named-as-default-member": "off", }, }, { files: ["src/server/**/*.ts"], ignores: ["src/server/logger.ts"], rules: { "no-restricted-syntax": [ "error", { message: noDirectConsoleMessage, selector: "MemberExpression[object.name='console']", }, ], }, }, { files: ["src/web/**/*.{ts,tsx}"], plugins: { "react-hooks": reactHooks, "react-refresh": reactRefresh, }, rules: { ...reactHooks.configs.recommended.rules, "no-restricted-imports": [ "error", { patterns: [ { group: [ "../server/*", "../server/**", "../**/server/*", "../**/server/**", "../../server/*", "../../server/**", "src/server/*", "src/server/**", ], message: "前端不得导入 src/server 后端运行时实现;请改用 src/shared 类型或 HTTP API。", }, ], }, ], "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], }, }, eslintPluginPrettierRecommended, );