/** * portal-shell ESLint flat config (ESM) * * 包含设计令牌强制规则(project_rules §3.10): * - 禁止 #hex 颜色字面量 * - 禁止 'Inter'/'Fraunces'/'JetBrains Mono' 字体名字面量 * * P0-7:补 no-restricted-imports(ARCHITECTURE.md §3.4 V3-A3 / §8.3 铁律) * - 禁 widget/页面 直接 import sonner(统一走 @/shared/lib/notify) * - 禁 widget/页面 绕过 lib/api 直接 import @apollo/client * - 禁 widget 跨目录 import 其他 widget(插件隔离) * * 关联:project_rules §3.10、portal-shell ARCHITECTURE.md §8.3、§11.7 */ import js from "@eslint/js"; import tseslint from "typescript-eslint"; import prettierConfig from "eslint-config-prettier"; export default tseslint.config( { ignores: [ "**/dist/**", "**/node_modules/**", "**/.next/**", "**/coverage/**", "**/*.config.js", "**/*.config.mjs", ], }, js.configs.recommended, ...tseslint.configs.recommended, { languageOptions: { ecmaVersion: 2024, sourceType: "module", }, rules: { "@typescript-eslint/no-explicit-any": "warn", "@typescript-eslint/no-unused-vars": [ "error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_", }, ], "no-console": "off", }, }, // 设计令牌强制规则(project_rules §3.10) { files: ["**/*.{ts,tsx,js,jsx}"], rules: { "no-restricted-syntax": [ "error", { // 禁止 #hex 颜色字面量(如 "#fff"、"#000000") selector: "Literal[value=/^#[0-9a-fA-F]{3,8}$/]", message: "禁止硬编码颜色 #hex,使用 var(--*) 或 Tailwind bg-* 类(project_rules §3.10)", }, { // 禁止字体名字面量(next/font 的 import 标识符不受影响) selector: "Literal[value=/^(Inter|Fraunces|JetBrains Mono)$/]", message: "禁止硬编码字体名字面量,使用 var(--font-family-sans/serif/mono)(project_rules §3.10)", }, ], }, }, // P0-7:no-restricted-imports 强制(ARCHITECTURE.md §8.3 / §11.7 红线) // - 禁直接 import sonner(统一走 @/shared/lib/notify 封装) // - 禁页面/widget 绕过 lib/api 直接 import @apollo/client // - 禁 widget 跨目录 import 其他 widget(插件隔离铁律) // 白名单: // - notify 封装本体(src/shared/lib/notify.ts)+ 其测试(__tests__/notify.test.ts)+ Toaster 容器(src/shared/components/ui/sonner.tsx) // - 数据层(src/lib/apollo-client.ts、useWidgetQuery.ts、useWidgetMutation.ts、config-fetcher.ts、src/lib/api/**、src/providers/ApolloProvider.tsx) // - widget 注册中心(src/shell/Registry.tsx)必须 import 各 widget 的 plugin.manifest,是唯一例外 { files: ["src/**/*.{ts,tsx}"], ignores: [ "src/shared/lib/notify.ts", "src/shared/lib/__tests__/notify.test.ts", "src/shared/components/ui/sonner.tsx", "src/lib/apollo-client.ts", "src/lib/useWidgetQuery.ts", "src/lib/useWidgetMutation.ts", "src/lib/config-fetcher.ts", "src/lib/api/**", "src/providers/ApolloProvider.tsx", "src/shell/Registry.tsx", ], rules: { "no-restricted-imports": [ "error", { paths: [ { name: "sonner", message: "禁止直接 import sonner,统一使用 @/shared/lib/notify(ARCHITECTURE.md §8.3)", }, { name: "@apollo/client", message: "禁止绕过 lib/api 直接 import @apollo/client,使用 useWidgetQuery/useWidgetMutation(ARCHITECTURE.md §11.7)", }, ], patterns: [ { group: ["@/widgets/*", "../widgets/*", "../../widgets/*"], message: "禁止 widget 跨目录 import 其他 widget(插件隔离铁律,ARCHITECTURE.md §11.7)", }, ], }, ], }, }, // 白名单:令牌原始定义、PWA manifest { files: ["**/primitive.css", "**/manifest.ts"], rules: { "no-restricted-syntax": "off", }, }, // 测试文件放宽规则 { files: [ "**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx", "**/__tests__/**", ], rules: { "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-non-null-assertion": "off", }, }, prettierConfig, );