expo-duooomi-app/eslint.config.mjs

328 lines
8.5 KiB
JavaScript

import eslintJs from '@eslint/js'
import typescriptEslint from '@typescript-eslint/eslint-plugin'
import typescriptParser from '@typescript-eslint/parser'
import { defineConfig } from 'eslint/config'
import expoConfig from 'eslint-config-expo/flat.js'
import i18nJsonPlugin from 'eslint-plugin-i18n-json'
import prettierPlugin from 'eslint-plugin-prettier'
import reactCompilerPlugin from 'eslint-plugin-react-compiler'
import reactNativePlugin from 'eslint-plugin-react-native'
import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort'
import tailwindcssPlugin from 'eslint-plugin-tailwindcss'
import unicornPlugin from 'eslint-plugin-unicorn'
import unusedImportsPlugin from 'eslint-plugin-unused-imports'
import path from 'path'
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
// 共享的插件配置 (移除可能与 expo 冲突的插件)
const sharedPlugins = {
prettier: prettierPlugin,
'unused-imports': unusedImportsPlugin,
'simple-import-sort': simpleImportSortPlugin,
tailwindcss: tailwindcssPlugin,
unicorn: unicornPlugin,
'react-native': reactNativePlugin,
}
// 共享的规则配置
const sharedRules = {
// Prettier 配置
'prettier/prettier': [
'error',
{
singleQuote: true,
endOfLine: 'auto',
trailingComma: 'all',
printWidth: 120,
tabWidth: 2,
semi: false,
arrowParens: 'always',
bracketSameLine: false,
bracketSpacing: true,
jsxSingleQuote: false,
useTabs: false,
quoteProps: 'as-needed',
plugins: ['prettier-plugin-tailwindcss'],
},
{ usePrettierrc: false },
],
// 通用规则
'max-params': ['warn', 3],
// 'max-lines-per-function': ['error', 800],
// Import 规则
'import/prefer-default-export': 'off',
'import/no-cycle': ['error', { maxDepth: '∞' }],
// Import 排序
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
// 未使用的导入
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
// Tailwind CSS 规则
'tailwindcss/classnames-order': 'off',
'tailwindcss/enforces-negative-arbitrary-values': 'error',
'tailwindcss/enforces-shorthand': 'error',
'tailwindcss/migration-from-tailwind-2': 'error',
'tailwindcss/no-arbitrary-value': 'off',
'tailwindcss/no-contradicting-classname': 'error',
'tailwindcss/no-custom-classname': 'off',
'tailwindcss/no-unnecessary-arbitrary-value': 'error',
// React 规则
'react/display-name': 'off',
'react/destructuring-assignment': 'off',
'react/require-default-props': 'off',
'react/no-unescaped-entities': 'off',
'react-native/no-raw-text': 'off',
'no-undef': 'off',
// JSX 属性换行规则
'react/jsx-max-props-per-line': [
'error',
{
maximum: 10, // 每行最多一个属性
when: 'multiline', // 仅在多行时生效
},
],
// JSX 第一个属性换行规则
'react/jsx-first-prop-new-line': ['error', 'multiline-multiprop'],
// React Native 规则
'react-native/no-unused-styles': 'off',
'react-native/split-platform-components': 'off',
'react-native/no-inline-styles': 'off',
'react-native/no-color-literals': 'off',
'react-native/no-single-element-style-arrays': 'off',
'react-hooks/rules-of-hooks': 'warn',
}
export default defineConfig([
// Base recommended config
eslintJs.configs.recommended,
// Expo config - 添加 expo 配置
expoConfig,
// Global ignores
{
ignores: [
'node_modules/**',
'ios/',
'android/',
'.expo/',
'assets/',
'**/.vscode',
'**/.idea',
'dist/',
'**/app.json',
'**/eas.json',
],
},
// Base configuration for JavaScript files
{
files: ['**/*.js', '**/*.jsx', '**/*.mjs'],
plugins: sharedPlugins,
rules: {
...sharedRules,
},
languageOptions: {
globals: {
__DEV__: 'readonly',
__dirname: 'readonly',
__filename: 'readonly',
console: 'readonly',
global: 'readonly',
process: 'readonly',
Buffer: 'readonly',
},
ecmaVersion: 'latest',
sourceType: 'module',
},
settings: {
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
moduleDirectory: ['node_modules', './'],
},
},
},
},
// TypeScript files configuration
{
files: ['**/*.ts', '**/*.tsx'],
languageOptions: {
parser: typescriptParser,
parserOptions: {
project: './tsconfig.json',
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 'latest',
sourceType: 'module',
tsconfigRootDir: __dirname,
},
globals: {
__DEV__: 'readonly',
__dirname: 'readonly',
__filename: 'readonly',
console: 'readonly',
global: 'readonly',
process: 'readonly',
Buffer: 'readonly',
},
},
plugins: {
...sharedPlugins,
'@typescript-eslint': typescriptEslint,
'react-compiler': reactCompilerPlugin,
},
rules: {
...sharedRules,
// TypeScript 特有规则
'@typescript-eslint/comma-dangle': 'off',
'@typescript-eslint/consistent-type-imports': [
'warn',
{
prefer: 'type-imports',
fixStyle: 'inline-type-imports',
disallowTypeAnnotations: true,
},
],
'@typescript-eslint/no-unused-vars': 'off',
// 禁止使用 any 类型的规则
'@typescript-eslint/no-explicit-any': 'warn',
// 禁止将 any 类型作为参数传递
'@typescript-eslint/no-unsafe-argument': 'warn',
// 禁止将 any 类型赋值给其他变量
'@typescript-eslint/no-unsafe-assignment': 'warn',
// 禁止调用 any 类型的函数
'@typescript-eslint/no-unsafe-call': 'warn',
// 禁止访问 any 类型的成员
'@typescript-eslint/no-unsafe-member-access': 'warn',
// 禁止从函数返回 any 类型
'@typescript-eslint/no-unsafe-return': 'warn',
// React Compiler rules
'react-compiler/react-compiler': 'error',
// React Hooks rules - 禁用在回调中使用 hooks 的检查
'react-hooks/rules-of-hooks': 'warn',
// Unicorn 特有配置 (覆盖共享配置)
'unicorn/prefer-module': 'off',
// Expo-specific rules
'no-undef': 'off', // TypeScript handles this
},
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: path.resolve(__dirname, './tsconfig.json'),
},
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
moduleDirectory: ['node_modules', './'],
},
},
},
},
// Translation files configuration
{
files: ['translations/*.json'],
plugins: {
'i18n-json': i18nJsonPlugin,
},
processor: {
meta: { name: '.json' },
...i18nJsonPlugin.processors['.json'],
},
rules: {
...i18nJsonPlugin.configs.recommended.rules,
// 消息值的语法是否符合特定的国际化格式要求
'i18n-json/valid-message-syntax': [
2,
{
syntax: path.resolve('./scripts/i18next-syntax-validation.js'),
},
],
// 是否包含有效的 JSON 语法
'i18n-json/valid-json': 2,
// 按照特定的顺序排列
// 关闭解决新加字段覆盖问题
'i18n-json/sorted-keys': [
2,
{
order: 'asc',
indentSpaces: 4,
},
],
// 检查是否有缺失的键或者多出来的键
'i18n-json/identical-keys': [
2,
{
filePath: path.resolve('./translations/en-US.json'),
},
],
},
},
// 组件文件必须使用 PascalCase
{
files: ['**/components/**/*.tsx', '**/components/**/*.jsx', '**/@share/components/**/*.tsx'],
rules: {
'unicorn/filename-case': [
'warn',
{
case: 'pascalCase',
ignore: ['\\.native\\.', '\\.web\\.', 'index\\.'],
},
],
},
},
// hooks、utils、lib 等使用 camelCase
{
files: ['**/hooks/**/*.ts', '**/utils/**/*.ts', '**/lib/**/*.ts', '**/constants/**/*.ts'],
rules: {
'unicorn/filename-case': [
'warn',
{
case: 'camelCase',
ignore: ['\\.native\\.', '\\.web\\.', 'index\\.'],
},
],
},
},
])