expo-duooomi-app/eslint.config.mjs

323 lines
8.4 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.

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': ['error', 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',
// Unicorn 规则
'unicorn/filename-case': [
'warn',
{
case: 'kebabCase',
ignore: ['/android', '/ios'],
},
],
// React 规则
'react/display-name': 'off',
'react/destructuring-assignment': 'off',
'react/require-default-props': 'off',
'react/no-unescaped-entities': 'off',
// React/JSX 属性排序规则
'react/jsx-sort-props': [
'error',
{
callbacksLast: true, // 回调函数属性放在最后
shorthandFirst: true, // 简写属性放在前面
shorthandLast: false,
multiline: 'last', // 多行属性放在最后
ignoreCase: true, // 忽略大小写
noSortAlphabetically: false,
reservedFirst: true, // 保留属性(如 key, ref放在最前面
},
],
// 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-raw-text': 'error',
'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,
'no-undef': 'off',
},
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'),
},
],
},
},
])