eslint.config.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. import skipFormatting from '@vue/eslint-config-prettier/skip-formatting';
  2. import vueTsEslintConfig from '@vue/eslint-config-typescript';
  3. import unocss from '@unocss/eslint-config/flat';
  4. import eslint from '@eslint/js';
  5. import pluginVitest from '@vitest/eslint-plugin';
  6. import pluginCypress from 'eslint-plugin-cypress/flat';
  7. import pluginImport from 'eslint-plugin-import';
  8. import pluginJsonc from 'eslint-plugin-jsonc';
  9. import pluginSimpleImportSort from 'eslint-plugin-simple-import-sort';
  10. import pluginVue from 'eslint-plugin-vue';
  11. import globals from 'globals';
  12. import jsoncParser from 'jsonc-eslint-parser';
  13. import tseslint from 'typescript-eslint';
  14. export default tseslint.config(
  15. {
  16. name: 'app/files-to-lint',
  17. files: ['**/*.{ts,mts,tsx,vue}'],
  18. },
  19. {
  20. name: 'app/files-to-ignore',
  21. ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**', '**/*.json', '!src/i18n/locales/*.json'],
  22. },
  23. {
  24. languageOptions: {
  25. globals: {
  26. ...globals.browser,
  27. ...globals.commonjs,
  28. },
  29. },
  30. },
  31. eslint.configs.recommended,
  32. ...pluginJsonc.configs['flat/recommended-with-json'],
  33. ...pluginJsonc.configs['flat/prettier'],
  34. ...pluginVue.configs['flat/essential'],
  35. ...vueTsEslintConfig(),
  36. skipFormatting,
  37. unocss,
  38. {
  39. ...pluginVitest.configs.recommended,
  40. files: ['src/**/__tests__/*'],
  41. },
  42. {
  43. ...pluginCypress.configs.recommended,
  44. files: ['cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}', 'cypress/support/**/*.{js,ts,jsx,tsx}'],
  45. },
  46. // import rules
  47. {
  48. files: ['**/*.{js,mjs,cjs,jsx,ts,tsx,vue}'],
  49. extends: [pluginImport.flatConfigs.recommended, pluginImport.flatConfigs.typescript],
  50. languageOptions: {
  51. ecmaVersion: 'latest',
  52. sourceType: 'module',
  53. },
  54. rules: {
  55. 'import/no-deprecated': 'error',
  56. 'import/no-empty-named-blocks': 'error',
  57. 'import/no-mutable-exports': 'error',
  58. 'import/no-named-as-default': 'error',
  59. 'import/no-named-as-default-member': 'error',
  60. 'import/no-absolute-path': 'error',
  61. 'import/no-dynamic-require': 'error',
  62. 'import/no-self-import': 'error',
  63. 'import/no-unresolved': 'off',
  64. 'import/no-useless-path-segments': [
  65. 'error',
  66. {
  67. noUselessIndex: true,
  68. },
  69. ],
  70. 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
  71. 'import/exports-last': 'error',
  72. 'import/first': 'error',
  73. 'import/newline-after-import': [
  74. 'error',
  75. {
  76. count: 1,
  77. considerComments: true,
  78. },
  79. ],
  80. 'import/no-anonymous-default-export': 'error',
  81. 'import/no-duplicates': 'error',
  82. 'import/no-named-default': 'error',
  83. 'import/no-unassigned-import': [
  84. 'error',
  85. {
  86. allow: ['**/*.{css,scss}', '**/styles', 'element-plus/**/style/css'],
  87. },
  88. ],
  89. },
  90. },
  91. // sort rules
  92. {
  93. plugins: {
  94. 'simple-import-sort': pluginSimpleImportSort,
  95. },
  96. rules: {
  97. 'simple-import-sort/imports': [
  98. 'error',
  99. {
  100. groups: [
  101. // Packages. `vue` related packages come first.
  102. ['^vue', '^@vue', 'pinia', '^vite$', '^@vitejs', '^vite-plugin', '^unocss$', '^@unocss', '^@?\\w'],
  103. // Node.js builtins.
  104. ['^node:'],
  105. // Internal packages.
  106. [
  107. '^@/layout(/.*|$)',
  108. '^@/views(/.*|$)',
  109. '^@/components(/.*|$)',
  110. '^@/transitions(/.*|$)',
  111. '^@/hooks(/.*|$)',
  112. '^@/directives(/.*|$)',
  113. '^@/stores(/.*|$)',
  114. '^@/router(/.*|$)',
  115. '^@/i18n(/.*|$)',
  116. '^@/api(/.*|$)',
  117. '^@/utils(/.*|$)',
  118. '^@/constants(/.*|$)',
  119. '^@/styles(/.*|$)',
  120. ],
  121. // Assets
  122. ['^@/assets(/.*|$)'],
  123. // Parent imports. Put `..` last.
  124. ['^\\.\\.(?!/?$)', '^\\.\\./?$'],
  125. // Other relative imports. Put same-folder imports and `.` last.
  126. ['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'],
  127. // Type imports
  128. [
  129. '^vue.*\\u0000$',
  130. '^@vue.*\\u0000$',
  131. '^pinia.*\\u0000$',
  132. '^vite\\u0000$',
  133. '^@vitejs.*\\u0000$',
  134. '^vite-plugin.*\\u0000$',
  135. '^unocss\\u0000$',
  136. '^@unocss.*\\u0000$',
  137. '^@?\\w.*\\u0000$',
  138. '^node:.*\\u0000$',
  139. '^@/constants.*\\u0000$',
  140. '^@/types.*\\u0000$',
  141. '^.*\\u0000$',
  142. ],
  143. // Style imports.
  144. ['^.+\\.s?css$'],
  145. // Side effect imports.
  146. ['^\\u0000'],
  147. ],
  148. },
  149. ],
  150. 'simple-import-sort/exports': 'error',
  151. },
  152. },
  153. // json rules
  154. {
  155. files: ['**/*.json'],
  156. languageOptions: {
  157. parser: jsoncParser,
  158. },
  159. rules: {
  160. 'jsonc/key-name-casing': [
  161. 'error',
  162. {
  163. camelCase: true,
  164. },
  165. ],
  166. 'jsonc/sort-keys': [
  167. 'error',
  168. {
  169. pathPattern: '.*',
  170. order: { type: 'asc' },
  171. },
  172. ],
  173. },
  174. },
  175. // js rules
  176. {
  177. files: ['**/*.{js,mjs,cjs,jsx,vue}'],
  178. rules: {
  179. 'object-shorthand': ['error', 'always'],
  180. 'no-unused-vars': 'off',
  181. },
  182. },
  183. // ts rules
  184. {
  185. files: ['**/*.{ts,mts,tsx,vue}'],
  186. rules: {
  187. 'object-shorthand': ['error', 'always'],
  188. '@typescript-eslint/no-unused-vars': 'error',
  189. '@typescript-eslint/no-explicit-any': 'error',
  190. '@typescript-eslint/no-inferrable-types': 'off',
  191. '@typescript-eslint/prefer-optional-chain': 'off',
  192. },
  193. },
  194. // vue rules
  195. {
  196. files: ['**/*.vue'],
  197. rules: {
  198. 'vue/component-tags-order': [
  199. 'error',
  200. {
  201. order: ['script:not([setup])', 'script[setup]', 'template', 'style:not([scoped])', 'style[scoped]'],
  202. },
  203. ],
  204. 'vue/component-name-in-template-casing': [
  205. 'error',
  206. 'PascalCase',
  207. {
  208. registeredComponentsOnly: false,
  209. ignores: ['/^un-/'],
  210. },
  211. ],
  212. 'vue/multi-word-component-names': [
  213. 'error',
  214. {
  215. ignores: ['Login'],
  216. },
  217. ],
  218. 'vue/html-self-closing': [
  219. 'error',
  220. {
  221. html: {
  222. void: 'always',
  223. normal: 'never',
  224. component: 'always',
  225. },
  226. svg: 'always',
  227. math: 'always',
  228. },
  229. ],
  230. 'vue/prop-name-casing': ['error', 'camelCase'],
  231. 'vue/attribute-hyphenation': [
  232. 'error',
  233. 'always',
  234. {
  235. ignore: [],
  236. },
  237. ],
  238. 'vue/padding-line-between-blocks': ['error', 'always'],
  239. 'vue/block-lang': [
  240. 'error',
  241. {
  242. script: {
  243. lang: 'ts',
  244. },
  245. template: {},
  246. style: {
  247. lang: 'scss',
  248. },
  249. },
  250. ],
  251. 'vue/no-mutating-props': [
  252. 'error',
  253. {
  254. shallowOnly: true,
  255. },
  256. ],
  257. },
  258. },
  259. );