Files
lingxi/eslint.config.mjs
T
Stev_Wang 4825626bdf test(e2e): A6 任务中心端到端用例与本地 Playwright 通道
背景:A6-QA-01 要求验证幂等 / 取消 / 重试 / 断线续推 / 权限三态;同时本地无 ~/Library/Caches 写权限,需要可复现的浏览器安装方式。
改动:
- 新增 e2e/a6-tasks.spec.ts(A6_E2E=1 门控):6 条用例覆盖提交 → 列表 → 详情 SSE → 部分失败 → 仅重试失败项、立即取消、同 Idempotency-Key 复用与 40902、Last-Event-ID 断点续推、只读不渲染写操作、无 material:read → 403;beforeAll 用 Prisma 建临时超管 / 只读 / 无权限账号与角色,afterAll 清理任务、幂等键、outbox 与账号角色(审计 append-only 保留)。
- playwright.config.ts 支持 PLAYWRIGHT_CHANNEL:未设置时本地用系统 Chrome,显式空串(CI)用 Playwright 自带 Chromium。
- eslint.config.mjs 忽略本地 .dev(Playwright 浏览器与临时脚本)。
- e2e/README.md 补充 A6 用例与运行方式。
影响面:默认(无 A6_E2E=1)仍 skip,CI pnpm test:e2e 行为不变;仅本地 e2e 配置增加环境变量支持。
回滚:撤销提交,删除 spec 与 channel 逻辑即可。
验证:A6_E2E=1 pnpm exec playwright test e2e/a6-tasks.spec.ts 6/6(约 39s,连跑多次稳定);pnpm test:e2e 默认套件不受影响。
2026-11-05 11:41:14 +08:00

228 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/**',
'.dev/**',
'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',
},
},
);