eslint.config.js 7.0 KB

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