eslint.config.mjs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import eslintConfigPrettier from 'eslint-config-prettier';
  2. import importPlugin from 'eslint-plugin-import';
  3. import jest from 'eslint-plugin-jest';
  4. import perfectionist from 'eslint-plugin-perfectionist';
  5. import react from 'eslint-plugin-react';
  6. import reactHooks from 'eslint-plugin-react-hooks';
  7. import reactRefresh from 'eslint-plugin-react-refresh';
  8. import testingLibrary from 'eslint-plugin-testing-library';
  9. import unicorn from 'eslint-plugin-unicorn';
  10. const ERROR = 2;
  11. const OFF = 0;
  12. import eslint from '@eslint/js';
  13. import tseslint from 'typescript-eslint';
  14. export default tseslint.config(
  15. eslint.configs.recommended,
  16. tseslint.configs.strictTypeChecked,
  17. tseslint.configs.stylisticTypeChecked,
  18. unicorn.configs.all,
  19. perfectionist.configs['recommended-alphabetical'],
  20. importPlugin.flatConfigs.react,
  21. importPlugin.flatConfigs['react-native'],
  22. importPlugin.flatConfigs.typescript,
  23. react.configs.flat.all,
  24. react.configs.flat['jsx-runtime'],
  25. reactRefresh.configs.recommended,
  26. testingLibrary.configs['flat/react'],
  27. eslintConfigPrettier, // last
  28. {
  29. languageOptions: {
  30. globals: {
  31. __DEV__: 'readonly', // define it as a global variable
  32. },
  33. parserOptions: {
  34. projectService: true,
  35. tsconfigRootDir: import.meta.dirname,
  36. },
  37. },
  38. settings: {
  39. 'import/resolver': {
  40. node: true,
  41. typescript: true,
  42. },
  43. perfectionist: {
  44. partitionByComment: true,
  45. type: 'alphabetical',
  46. },
  47. react: {
  48. version: 'detect',
  49. },
  50. },
  51. },
  52. {
  53. ...reactHooks.configs.recommended,
  54. plugins: {
  55. 'react-hooks': reactHooks,
  56. },
  57. rules: {
  58. ...reactHooks.configs.recommended.rules,
  59. '@typescript-eslint/consistent-type-definitions': [ERROR, 'type'],
  60. '@typescript-eslint/dot-notation': [ERROR, { allowKeywords: true }],
  61. '@typescript-eslint/no-empty-function': OFF,
  62. '@typescript-eslint/restrict-template-expressions': OFF,
  63. 'import/no-unresolved': OFF, // handled by TypeScript
  64. 'no-console': [ERROR, { allow: ['warn', 'error'] }],
  65. 'no-magic-numbers': [
  66. ERROR,
  67. { ignore: [-1, 0, 1, 2, 3, 4, 5, 6], ignoreArrayIndexes: true },
  68. ],
  69. 'perfectionist/sort-imports': [
  70. 'error',
  71. {
  72. customGroups: {
  73. value: {
  74. components: '@/components(/.+)?',
  75. hooks: '@/hooks(/.+)?',
  76. navigation: '@/navigation(/.+)?',
  77. screens: '@/screens(/.+)?',
  78. test: '@/test(/.+)?',
  79. theme: '@/theme(/.+)?',
  80. translations: '@/translations(/.+)?',
  81. },
  82. },
  83. groups: [
  84. 'side-effect',
  85. ['type', 'internal-type'],
  86. ['builtin', 'external'],
  87. ['theme', 'hooks', 'navigation', 'translations'],
  88. ['components', 'screens'],
  89. ['test'],
  90. 'internal',
  91. 'unknown',
  92. ],
  93. newlinesBetween: 'always',
  94. type: 'alphabetical',
  95. },
  96. ],
  97. 'react-refresh/only-export-components': OFF,
  98. 'react/forbid-component-props': OFF,
  99. 'react/jsx-filename-extension': [ERROR, { extensions: ['.tsx', '.jsx'] }],
  100. 'react/jsx-max-depth': [ERROR, { max: 10 }],
  101. 'react/jsx-no-bind': OFF,
  102. 'react/jsx-no-literals': OFF,
  103. 'react/jsx-props-no-spreading': OFF,
  104. 'react/jsx-sort-props': OFF, // Handled by perfectionist
  105. 'react/no-multi-comp': OFF,
  106. 'react/no-unescaped-entities': OFF,
  107. 'react/require-default-props': [
  108. ERROR,
  109. {
  110. forbidDefaultForRequired: true,
  111. functions: 'defaultArguments',
  112. },
  113. ],
  114. 'unicorn/filename-case': OFF,
  115. 'unicorn/no-keyword-prefix': OFF,
  116. 'unicorn/no-useless-undefined': OFF,
  117. 'unicorn/prefer-top-level-await': 0, // not valid on RN for the moment
  118. 'unicorn/prevent-abbreviations': [
  119. ERROR,
  120. {
  121. allowList: {
  122. env: true,
  123. Param: true,
  124. props: true,
  125. Props: true,
  126. },
  127. },
  128. ],
  129. },
  130. },
  131. {
  132. files: ['**/theme/*.ts'],
  133. rules: {
  134. 'no-magic-numbers': OFF,
  135. },
  136. },
  137. {
  138. files: ['*.conf.js', '*.config.js', '*.setup.js'],
  139. rules: {
  140. '@typescript-eslint/no-require-imports': OFF,
  141. '@typescript-eslint/no-unsafe-assignment': OFF,
  142. '@typescript-eslint/no-unsafe-call': OFF,
  143. 'no-undef': OFF,
  144. 'unicorn/prefer-module': OFF,
  145. },
  146. },
  147. {
  148. files: ['**/*.spec.{js,ts,jsx,tsx}', '**/*.test.{js,ts,jsx,tsx}'],
  149. ...jest.configs['flat/recommended'],
  150. rules: {
  151. ...jest.configs['flat/recommended'].rules,
  152. },
  153. },
  154. {
  155. ignores: ['plugins/**'],
  156. },
  157. );