diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs index 1874d200b3..e60e50aa1a 100644 --- a/web/eslint.config.mjs +++ b/web/eslint.config.mjs @@ -1,15 +1,169 @@ -import antfu from '@antfu/eslint-config' -import js from '@eslint/js' +import { + GLOB_TESTS, combine, javascript, node, + stylistic, typescript, unicorn, +} from '@antfu/eslint-config' +import globals from 'globals' +import storybook from 'eslint-plugin-storybook' +// import { fixupConfigRules } from '@eslint/compat' import tailwind from 'eslint-plugin-tailwindcss' -import ts from 'typescript-eslint' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' -const antConfig = await antfu({ jsonc: false }) +export default combine( + stylistic({ + lessOpinionated: true, + // original @antfu/eslint-config does not support jsx + jsx: false, + semi: false, + quotes: 'single', + overrides: { + // original config + 'style/indent': ['error', 2], + 'style/quotes': ['error', 'single'], + 'curly': ['error', 'multi-or-nest', 'consistent'], + 'style/comma-spacing': ['error', { before: false, after: true }], + 'style/quote-props': ['warn', 'consistent-as-needed'], -const eslintConfig = [ - ...antConfig, - js.configs.recommended, - ...ts.configs.recommended, - ...tailwind.configs['flat/recommended'], + // these options does not exist in old version + // maybe useless + 'style/indent-binary-ops': 'off', + 'style/multiline-ternary': 'off', + 'antfu/top-level-function': 'off', + 'antfu/curly': 'off', + 'antfu/consistent-chaining': 'off', + + // copy from eslint-config-antfu 0.36.0 + 'style/brace-style': ['error', 'stroustrup', { allowSingleLine: true }], + 'style/dot-location': ['error', 'property'], + 'style/object-curly-newline': ['error', { consistent: true, multiline: true }], + 'style/object-property-newline': ['error', { allowMultiplePropertiesPerLine: true }], + 'style/template-curly-spacing': ['error', 'never'], + 'style/keyword-spacing': 'off', + + // not exist in old version, and big change + 'style/member-delimiter-style': 'off', + }, + }), + javascript({ + overrides: { + // handled by unused-imports/no-unused-vars + 'no-unused-vars': 'off', + }, + }), + typescript({ + overrides: { + // original config + 'ts/consistent-type-definitions': ['warn', 'type'], + + // useful, but big change + 'ts/no-empty-object-type': 'off', + }, + }), + unicorn(), + node(), + // use nextjs config will break @eslint/config-inspector + // use `ESLINT_CONFIG_INSPECTOR=true pnpx @eslint/config-inspector` to check the config + // ...process.env.ESLINT_CONFIG_INSPECTOR + // ? [] + // TODO: remove this when upgrade to nextjs 15 + // : fixupConfigRules(compat.extends('next')), + { + rules: { + // performance issue, and not used. + '@next/next/no-html-link-for-pages': 'off', + }, + }, + { + ignores: [ + '**/node_modules/*', + '**/dist/', + '**/build/', + '**/out/', + '**/.next/', + '**/public/*', + '**/*.json', + ], + }, + { + // orignal config + rules: { + // orignal ts/no-var-requires + 'ts/no-require-imports': 'off', + 'no-console': 'off', + 'react-hooks/exhaustive-deps': 'warn', + 'react/display-name': 'off', + 'array-callback-return': ['error', { + allowImplicit: false, + checkForEach: false, + }], + + // copy from eslint-config-antfu 0.36.0 + 'camelcase': 'off', + 'default-case-last': 'error', + + // antfu use eslint-plugin-perfectionist to replace this + // will cause big change, so keep the original sort-imports + 'sort-imports': [ + 'error', + { + ignoreCase: false, + ignoreDeclarationSort: true, + ignoreMemberSort: false, + memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'], + allowSeparatedGroups: false, + }, + ], + + // antfu migrate to eslint-plugin-unused-imports + 'unused-imports/no-unused-vars': 'warn', + 'unused-imports/no-unused-imports': 'warn', + }, + + languageOptions: { + globals: { + ...globals.browser, + ...globals.es2025, + ...globals.node, + React: 'readable', + JSX: 'readable', + }, + }, + }, + storybook.configs['flat/recommended'], + reactRefresh.configs.recommended, + { + rules: reactHooks.configs.recommended.rules, + plugins: { + 'react-hooks': reactHooks, + }, + }, + // need futher research + { + rules: { + // not exist in old version + 'antfu/consistent-list-newline': 'off', + 'node/prefer-global/process': 'off', + 'node/prefer-global/buffer': 'off', + 'node/no-callback-literal': 'off', + + // useful, but big change + 'unicorn/prefer-number-properties': 'warn', + 'unicorn/no-new-array': 'warn', + }, + }, + // suppress error for `no-undef` rule + { + files: GLOB_TESTS, + languageOptions: { + globals: { + ...globals.browser, + ...globals.es2021, + ...globals.node, + ...globals.jest, + }, + }, + }, + tailwind.configs['flat/recommended'], { settings: { tailwindcss: { @@ -24,9 +178,7 @@ const eslintConfig = [ '!**/build', '!**/.storybook', '!**/.next', - '!**/.output', - '!**/public', - '!**/out', + '!**/.public', ], cssFilesRefreshRate: 5_000, removeDuplicates: true, @@ -36,15 +188,17 @@ const eslintConfig = [ classRegex: '^class(Name)?$', // can be modified to support custom attributes. E.g. "^tw$" for `twin.macro` }, }, - }, - { rules: { - '@typescript-eslint/no-explicit-any': 'off', + // due to 1k lines of tailwind config, these rule have performance issue + 'tailwindcss/no-contradicting-classname': 'off', + 'tailwindcss/enforces-shorthand': 'off', + 'tailwindcss/no-custom-classname': 'off', + 'tailwindcss/no-unnecessary-arbitrary-value': 'off', + + 'tailwindcss/classnames-order': 'warn', + 'tailwindcss/enforces-negative-arbitrary-values': 'warn', + 'tailwindcss/no-arbitrary-value': 'warn', + 'tailwindcss/migration-from-tailwind-2': 'warn', }, }, - { - ignores: ['node_modules/**', '.next/**', '.storybook/**', 'build/**', 'dist/**', 'out/**', 'public/**'], - }, -] - -export default eslintConfig +) diff --git a/web/package.json b/web/package.json index cee67888b6..a6aa2225ba 100644 --- a/web/package.json +++ b/web/package.json @@ -153,8 +153,10 @@ "bing-translate-api": "^4.0.2", "code-inspector-plugin": "^0.18.1", "cross-env": "^7.0.3", - "eslint": "^9.19.0", + "eslint": "^9.20.1", "eslint-config-next": "15.1.6", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.19", "eslint-plugin-storybook": "^0.9.0", "eslint-plugin-tailwindcss": "^3.18.0", "husky": "^8.0.3", diff --git a/web/yarn.lock b/web/yarn.lock index f73e3925ee..92304d71b6 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -6664,11 +6664,16 @@ eslint-plugin-perfectionist@^4.7.0: "@typescript-eslint/utils" "^8.23.0" natural-orderby "^5.0.0" -eslint-plugin-react-hooks@^5.0.0: +eslint-plugin-react-hooks@^5.0.0, eslint-plugin-react-hooks@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz#3d34e37d5770866c34b87d5b499f5f0b53bf0854" integrity sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw== +eslint-plugin-react-refresh@^0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz#f15020c0caa58e33fc4efda27d328281ca74e53d" + integrity sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ== + eslint-plugin-react@^7.37.0: version "7.37.4" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz#1b6c80b6175b6ae4b26055ae4d55d04c414c7181" @@ -6825,10 +6830,10 @@ eslint-visitor-keys@^4.2.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== -eslint@^9.19.0: - version "9.20.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.20.0.tgz#6244c46c1640cd5e577a31ebc460fca87838c0b7" - integrity sha512-aL4F8167Hg4IvsW89ejnpTwx+B/UQRzJPGgbIOl+4XqffWsahVVsLEWoZvnrVuwpWmnRd7XeXmQI1zlKcFDteA== +eslint@^9.20.1: + version "9.20.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.20.1.tgz#923924c078f5226832449bac86662dd7e53c91d6" + integrity sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.12.1"