Files
HKSingleParty/03_source/frontend/eslint.config.mjs
2025-05-28 09:55:51 +08:00

195 lines
5.5 KiB
JavaScript

import globals from 'globals';
import eslintJs from '@eslint/js';
import eslintTs from 'typescript-eslint';
import reactPlugin from 'eslint-plugin-react';
import importPlugin from 'eslint-plugin-import';
import reactHooksPlugin from 'eslint-plugin-react-hooks';
import perfectionistPlugin from 'eslint-plugin-perfectionist';
import unusedImportsPlugin from 'eslint-plugin-unused-imports';
// ----------------------------------------------------------------------
/**
* @rules common
* from 'react', 'eslint-plugin-react-hooks'...
*/
const commonRules = () => ({
...reactHooksPlugin.configs.recommended.rules,
'func-names': 1,
'no-bitwise': 2,
'no-unused-vars': 0,
'object-shorthand': 1,
'no-useless-rename': 1,
'default-case-last': 2,
'consistent-return': 2,
'no-constant-condition': 1,
'default-case': [2, { commentPattern: '^no default$' }],
'lines-around-directive': [2, { before: 'always', after: 'always' }],
'arrow-body-style': [2, 'as-needed', { requireReturnForObjectLiteral: false }],
// react
'react/jsx-key': 0,
'react/prop-types': 0,
'react/display-name': 0,
'react/no-children-prop': 0,
'react/jsx-boolean-value': 2,
'react/self-closing-comp': 2,
'react/react-in-jsx-scope': 0,
'react/jsx-no-useless-fragment': [1, { allowExpressions: true }],
'react/jsx-curly-brace-presence': [2, { props: 'never', children: 'never' }],
// typescript
'@typescript-eslint/no-shadow': 2,
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/no-empty-object-type': 0,
'@typescript-eslint/consistent-type-imports': 1,
'@typescript-eslint/no-unused-vars': [1, { args: 'none' }],
});
/**
* @rules import
* from 'eslint-plugin-import'.
*/
const importRules = () => ({
...importPlugin.configs.recommended.rules,
'import/named': 0,
'import/export': 0,
'import/default': 0,
'import/namespace': 0,
'import/no-named-as-default': 0,
'import/newline-after-import': 2,
'import/no-named-as-default-member': 0,
'import/no-cycle': [
0, // disabled if slow
{ maxDepth: '∞', ignoreExternal: false, allowUnsafeDynamicCyclicDependency: false },
],
});
/**
* @rules unused imports
* from 'eslint-plugin-unused-imports'.
*/
const unusedImportsRules = () => ({
'unused-imports/no-unused-imports': 1,
'unused-imports/no-unused-vars': [
0,
{ vars: 'all', varsIgnorePattern: '^_', args: 'after-used', argsIgnorePattern: '^_' },
],
});
/**
* @rules sort or imports/exports
* from 'eslint-plugin-perfectionist'.
*/
const sortImportsRules = () => {
const customGroups = {
mui: ['custom-mui'],
auth: ['custom-auth'],
hooks: ['custom-hooks'],
utils: ['custom-utils'],
types: ['custom-types'],
routes: ['custom-routes'],
sections: ['custom-sections'],
components: ['custom-components'],
};
return {
'perfectionist/sort-named-imports': [1, { type: 'line-length', order: 'asc' }],
'perfectionist/sort-named-exports': [1, { type: 'line-length', order: 'asc' }],
'perfectionist/sort-exports': [
1,
{
order: 'asc',
type: 'line-length',
groupKind: 'values-first',
},
],
'perfectionist/sort-imports': [
2,
{
order: 'asc',
ignoreCase: true,
type: 'line-length',
environment: 'node',
maxLineLength: undefined,
newlinesBetween: 'always',
internalPattern: ['^src/.+'],
groups: [
'style',
'side-effect',
'type',
['builtin', 'external'],
customGroups.mui,
customGroups.routes,
customGroups.hooks,
customGroups.utils,
'internal',
customGroups.components,
customGroups.sections,
customGroups.auth,
customGroups.types,
['parent', 'sibling', 'index'],
['parent-type', 'sibling-type', 'index-type'],
'object',
'unknown',
],
customGroups: {
value: {
[customGroups.mui]: ['^@mui/.+'],
[customGroups.auth]: ['^src/auth/.+'],
[customGroups.hooks]: ['^src/hooks/.+'],
[customGroups.utils]: ['^src/utils/.+'],
[customGroups.types]: ['^src/types/.+'],
[customGroups.routes]: ['^src/routes/.+'],
[customGroups.sections]: ['^src/sections/.+'],
[customGroups.components]: ['^src/components/.+'],
},
},
},
],
};
};
/**
* Custom ESLint configuration.
*/
export const customConfig = {
plugins: {
'react-hooks': reactHooksPlugin,
'unused-imports': unusedImportsPlugin,
perfectionist: perfectionistPlugin,
import: importPlugin,
},
settings: {
// https://www.npmjs.com/package/eslint-import-resolver-typescript
...importPlugin.configs.typescript.settings,
'import/resolver': {
...importPlugin.configs.typescript.settings['import/resolver'],
typescript: {
project: './tsconfig.json',
},
},
},
rules: {
...commonRules(),
...importRules(),
...unusedImportsRules(),
...sortImportsRules(),
},
};
// ----------------------------------------------------------------------
export default [
{ files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'] },
{ ignores: ['*', '!src/', '!eslint.config.*'] },
{
languageOptions: {
globals: { ...globals.browser, ...globals.node },
},
settings: { react: { version: 'detect' } },
},
eslintJs.configs.recommended,
...eslintTs.configs.recommended,
reactPlugin.configs.flat.recommended,
customConfig,
];