expo-popcore-app/eslint.config.mjs

340 lines
10 KiB
JavaScript
Raw Permalink 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'
// 由于 eslint-plugin-unicorn 在解析时出现语法错误,暂时移除该插件
// const unicornPlugin = await import('eslint-plugin-unicorn').catch(() => null)
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: 100,
tabWidth: 4,
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', 500],
// 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': [
'error',
{
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': [
'error',
{
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: 1, // 每行最多一个属性
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',
// 限制导入规则 - 强制使用自定义 Text 组件
'no-restricted-imports': [
'error',
{
paths: [
{
name: 'react-native',
importNames: ['Text'],
message: 'please use @/components/ui instead of react-native Text',
},
],
patterns: [
{
group: ['react-native'],
importNames: ['Text'],
message: 'please use @/components/ui instead of react-native Text',
},
],
},
],
}
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': 'error',
// 禁止将 any 类型作为参数传递
'@typescript-eslint/no-unsafe-argument': 'error',
// 禁止将 any 类型赋值给其他变量
'@typescript-eslint/no-unsafe-assignment': 'error',
// 禁止调用 any 类型的函数
'@typescript-eslint/no-unsafe-call': 'error',
// 禁止访问 any 类型的成员
'@typescript-eslint/no-unsafe-member-access': 'error',
// 禁止从函数返回 any 类型
'@typescript-eslint/no-unsafe-return': 'error',
// React Compiler rules
'react-compiler/react-compiler': 'error',
// 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'),
},
],
},
},
])