eslint.config.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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. '^@/styles(/.*|$)',
  121. ],
  122. // Assets
  123. ['^@/assets(/.*|$)'],
  124. // Parent imports. Put `..` last.
  125. ['^\\.\\.(?!/?$)', '^\\.\\./?$'],
  126. // Other relative imports. Put same-folder imports and `.` last.
  127. ['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'],
  128. // Type imports
  129. [
  130. '^vue.*\\u0000$',
  131. '^@vue.*\\u0000$',
  132. '^pinia.*\\u0000$',
  133. '^vite\\u0000$',
  134. '^@vitejs.*\\u0000$',
  135. '^vite-plugin.*\\u0000$',
  136. '^unocss\\u0000$',
  137. '^@unocss.*\\u0000$',
  138. '^@?\\w.*\\u0000$',
  139. '^node:.*\\u0000$',
  140. '^@/constants.*\\u0000$',
  141. '^@/types.*\\u0000$',
  142. '^.*\\u0000$',
  143. ],
  144. // Style imports.
  145. ['^.+\\.s?css$'],
  146. // Side effect imports.
  147. ['^\\u0000'],
  148. ],
  149. },
  150. ],
  151. 'simple-import-sort/exports': 'error',
  152. },
  153. },
  154. // json rules
  155. {
  156. files: ['**/*.json'],
  157. languageOptions: {
  158. parser: jsoncParser,
  159. },
  160. rules: {
  161. 'jsonc/key-name-casing': [
  162. 'error',
  163. {
  164. camelCase: true,
  165. },
  166. ],
  167. 'jsonc/sort-keys': [
  168. 'error',
  169. {
  170. pathPattern: '.*',
  171. order: { type: 'asc' },
  172. },
  173. ],
  174. },
  175. },
  176. // js rules
  177. {
  178. files: ['**/*.{js,mjs,cjs,jsx,vue}'],
  179. rules: {
  180. 'object-shorthand': ['error', 'always'],
  181. 'no-unused-vars': 'off',
  182. 'no-var': 'error',
  183. },
  184. },
  185. // ts rules
  186. {
  187. files: ['**/*.{ts,mts,tsx,vue}'],
  188. rules: {
  189. 'object-shorthand': ['error', 'always'],
  190. '@typescript-eslint/no-unused-vars': 'error',
  191. '@typescript-eslint/no-explicit-any': 'error',
  192. '@typescript-eslint/no-inferrable-types': 'off',
  193. '@typescript-eslint/prefer-optional-chain': 'off',
  194. },
  195. },
  196. // vue rules
  197. {
  198. files: ['**/*.vue'],
  199. rules: {
  200. 'vue/component-tags-order': [
  201. 'error',
  202. {
  203. order: ['script:not([setup])', 'script[setup]', 'template', 'style:not([scoped])', 'style[scoped]'],
  204. },
  205. ],
  206. 'vue/component-name-in-template-casing': [
  207. 'error',
  208. 'PascalCase',
  209. {
  210. registeredComponentsOnly: false,
  211. ignores: ['/^un-/'],
  212. },
  213. ],
  214. 'vue/multi-word-component-names': [
  215. 'error',
  216. {
  217. ignores: ['Login'],
  218. },
  219. ],
  220. 'vue/html-self-closing': [
  221. 'error',
  222. {
  223. html: {
  224. void: 'always',
  225. normal: 'never',
  226. component: 'always',
  227. },
  228. svg: 'always',
  229. math: 'always',
  230. },
  231. ],
  232. 'vue/prop-name-casing': ['error', 'camelCase'],
  233. 'vue/attribute-hyphenation': [
  234. 'error',
  235. 'always',
  236. {
  237. ignore: [],
  238. },
  239. ],
  240. 'vue/padding-line-between-blocks': ['error', 'always'],
  241. 'vue/block-lang': [
  242. 'error',
  243. {
  244. script: {
  245. lang: 'ts',
  246. },
  247. template: {},
  248. style: {
  249. lang: 'scss',
  250. },
  251. },
  252. ],
  253. 'vue/no-mutating-props': [
  254. 'error',
  255. {
  256. shallowOnly: true,
  257. },
  258. ],
  259. },
  260. },
  261. );