Files
lingxi/eslint.config.mjs
T
Stev_Wang 04f49690f4
ci / 质量门禁 (push) Canceled after 0s
feat(a0): 完成工程骨架与契约基线
- 建立 pnpm workspace + Turborepo 单仓、ESLint 9/Prettier/commitlint/husky 门禁
- NestJS + Fastify API:统一错误体、trace_id、Zod 管道、/health 真实依赖探活、OpenAPI 3.1 契约
- Prisma 基线:outbox_events/idempotency_keys/system_configs 与首版迁移、幂等种子
- shared 契约包:错误码、权限点、ULID、游标分页、命名/时间工具
- ui 包与 Web 壳层:Semi + Remix 图标统一出口、Cloud Blue 视觉、登录/工作台/错误页
- worker 骨架、Playwright 冒烟、Gitea CI、OpenAPI 快照与 oasdiff 回退脚本
- 补充 A0 实施报告与本地开发说明
2026-11-02 17:01:50 +08:00

227 lines
7.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 灵犀 ESLint 9 flat config
// 依据:docs/工程规范/03-格式化与Lint.md
// 分层:根规则(全仓库)→ apps/web(React)→ packages/ui(图标统一出口)→ scripts(允许 console)
import js from '@eslint/js';
import importX from 'eslint-plugin-import-x';
import jsxA11y from 'eslint-plugin-jsx-a11y';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import simpleImportSort from 'eslint-plugin-simple-import-sort';
import globals from 'globals';
import tseslint from 'typescript-eslint';
const UI_LIB_MESSAGE = 'UI 组件库只能用 @douyinfe/semi-ui-19(工程规范 03 §3.2);禁装其他组件库。';
const ICON_MESSAGE =
'图标只能从 @lingxi/ui/icons 按语义名导入;禁止直接引用图标库或内联 SVG(AGENTS §3.3)。';
const COLOR_MESSAGE =
'业务代码禁止颜色字面量;颜色只能定义在 packages/ui/src/theme/ 的 token 中(工程规范 03 §3.2)。';
export default tseslint.config(
{
ignores: [
'**/node_modules/**',
'**/dist/**',
'**/coverage/**',
'**/.turbo/**',
'.tools/**',
'design/**',
'docs/**',
'openapi/**',
'prisma/migrations/**',
'项目参考/**',
'playwright-report/**',
'test-results/**',
'**/*.d.ts',
],
},
js.configs.recommended,
...tseslint.configs.strictTypeChecked,
...tseslint.configs.stylisticTypeChecked,
{
name: 'lingxi/base',
languageOptions: {
globals: { ...globals.node, ...globals.browser, ...globals.es2022 },
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
plugins: {
'import-x': importX,
'simple-import-sort': simpleImportSort,
},
settings: {
'import-x/resolver': {
typescript: {
project: ['./tsconfig.json', './apps/*/tsconfig.json', './packages/*/tsconfig.json'],
},
},
},
linterOptions: { reportUnusedDisableDirectives: 'error' },
rules: {
// ---- TypeScript 硬约束(AGENTS §3.2)----
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/consistent-type-imports': [
'error',
{ prefer: 'type-imports', fixStyle: 'separate-type-imports' },
],
'@typescript-eslint/no-unnecessary-condition': 'error',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/no-non-null-assertion': 'warn',
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' },
],
'@typescript-eslint/restrict-template-expressions': [
'error',
{ allowNumber: true, allowBoolean: true },
],
'@typescript-eslint/naming-convention': [
'error',
{ selector: 'typeLike', format: ['PascalCase'] },
{ selector: 'enumMember', format: ['UPPER_CASE'] },
],
'@typescript-eslint/consistent-type-definitions': ['error', 'interface'],
// ---- 导入与依赖方向 ----
'import-x/no-cycle': ['error', { maxDepth: 8, ignoreExternal: false }],
'import-x/no-self-import': 'error',
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
// ---- 通用 ----
'no-console': 'error',
eqeqeq: ['error', 'always'],
'no-var': 'error',
'prefer-const': 'error',
complexity: ['warn', { max: 15 }],
'max-lines-per-function': [
'warn',
{ max: 50, skipBlankLines: true, skipComments: true, IIFEs: true },
],
},
},
// 纯 JS 配置文件不做类型检查
{
name: 'lingxi/js-config',
files: ['**/*.{js,mjs,cjs}'],
extends: [tseslint.configs.disableTypeChecked],
rules: {
'no-console': 'off',
'import-x/no-cycle': 'off',
},
},
// 应用层:Web(React 19)
{
name: 'lingxi/apps-web',
files: ['apps/web/**/*.{ts,tsx}'],
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
'jsx-a11y': jsxA11y,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
'jsx-a11y/alt-text': 'error',
'jsx-a11y/no-autofocus': 'warn',
'@typescript-eslint/no-restricted-imports': [
'error',
{
patterns: [
{ group: ['@douyinfe/semi-ui', '@douyinfe/semi-ui/*'], message: UI_LIB_MESSAGE },
{
group: [
'@remixicon/react',
'react-icons',
'react-icons/*',
'lucide-react',
'lucide',
'semi-icons',
'@douyinfe/semi-icons',
'@heroicons/*',
'@ant-design/icons',
],
message: ICON_MESSAGE,
},
{
group: [
'antd',
'@arco-design/web-react',
'element-plus',
'naive-ui',
'@mui/*',
'@chakra-ui/*',
],
message: UI_LIB_MESSAGE,
},
],
},
],
'no-restricted-syntax': [
'error',
{
selector: 'Literal[value=/^#(?:[0-9a-fA-F]{3,8})$/]',
message: COLOR_MESSAGE,
},
{
selector: 'Literal[value=/^rgba?\\(/]',
message: COLOR_MESSAGE,
},
{
selector: "JSXOpeningElement[name.name='svg']",
message: ICON_MESSAGE,
},
],
},
},
// packages/ui:图标统一出口(只有 icons.ts 可直接引用 Remix Icon)
{
name: 'lingxi/ui-icons',
files: ['packages/ui/src/**/*.{ts,tsx}'],
rules: {
'@typescript-eslint/no-restricted-imports': [
'error',
{ patterns: [{ group: ['@remixicon/react'], message: ICON_MESSAGE }] },
],
},
},
{
name: 'lingxi/ui-icons-exit',
files: ['packages/ui/src/icons.ts'],
rules: {
'@typescript-eslint/no-restricted-imports': ['error', { patterns: [] }],
},
},
// Nest 模块装饰器类按框架约定是空类
{
name: 'lingxi/nest-modules',
files: ['apps/api/**/*.module.ts'],
rules: {
'@typescript-eslint/no-extraneous-class': 'off',
},
},
// 脚本层:允许 console(CLI 输出)
{
name: 'lingxi/scripts',
files: ['scripts/**/*.{ts,mjs,js}', '**/scripts/**/*.ts', 'prisma/seed.ts', 'e2e/**/*.ts'],
rules: {
'no-console': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
},
},
// 测试文件:允许更松的表达
{
name: 'lingxi/tests',
files: ['**/*.spec.ts', '**/*.spec.tsx', '**/*.int.spec.ts', 'e2e/**/*.ts'],
rules: {
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
'max-lines-per-function': 'off',
complexity: 'off',
},
},
);