@tpzdsp/eslint-config-dsp
Version:
re-usable config for ESLint, Prettier and semantic-release
211 lines (206 loc) • 5.99 kB
JavaScript
import eslintConfigPrettier from 'eslint-config-prettier/flat';
import importPlugin from 'eslint-plugin-import';
import jestDom from 'eslint-plugin-jest-dom';
import jsxA11y from 'eslint-plugin-jsx-a11y';
import prettier from 'eslint-plugin-prettier';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import js from '@eslint/js';
import tsEslint from '@typescript-eslint/eslint-plugin';
import tsEslintParser from '@typescript-eslint/parser';
import vitest from '@vitest/eslint-plugin';
import 'eslint-import-resolver-alias';
import 'eslint-import-resolver-typescript';
export const NO_RESTRICTED_STYNAX_CONFIG = [
{
selector: 'FunctionExpression',
message: 'Use ES6 arrow functions instead.',
},
{
selector: 'FunctionDeclaration',
message: 'Use arrow functions instead of function declarations.',
},
{
selector: 'ForStatement',
message:
'Use functional iterators like .map(), .forEach(), or for...of instead of a manual for-loop.',
},
{
selector: 'ForInStatement',
message:
'Avoid for...in loops as they iterate over inherited properties. Use Object.keys() or Object.entries() instead.',
},
{
selector:
"ImportDeclaration[source.value='react'] > ImportDefaultSpecifier[local.name='React']",
message: 'Default import of React is no longer necessary, and should not be used.',
},
];
export default [
{
name: 'eslint:recommended',
rules: {
...js.configs.recommended.rules,
curly: 'error',
camelcase: 'off',
'no-var': 'error',
'no-restricted-syntax': ['error', ...NO_RESTRICTED_STYNAX_CONFIG],
'no-restricted-imports': [
'error',
{
patterns: ['../../*'], // only allow `./` and `../`
},
],
},
},
{
name: 'typescript',
files: ['**/*.ts', '**/*.tsx'],
languageOptions: {
parser: tsEslintParser,
parserOptions: {
project: true,
},
},
plugins: {
'@typescript-eslint': tsEslint,
},
rules: {
...tsEslint.configs.recommended.rules,
...tsEslint.configs['stylistic-type-checked'].rules,
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/consistent-type-definitions': ['error', 'type'],
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'default',
format: ['camelCase'],
},
{
selector: 'import',
format: ['camelCase', 'PascalCase'],
},
{
selector: 'function',
format: ['camelCase', 'PascalCase'],
},
{ selector: 'typeLike', format: ['PascalCase'] },
{ selector: 'typeProperty', format: null }, // allow any case
{ selector: 'enumMember', format: ['PascalCase'] },
{
selector: 'variable',
format: ['camelCase', 'PascalCase', 'UPPER_CASE'],
},
{
selector: 'objectLiteralProperty',
format: null, // allow any case
},
{
selector: 'objectLiteralMethod',
format: ['camelCase', 'PascalCase'],
},
],
},
},
{
name: 'react-and-a11y',
files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'],
plugins: {
react,
'jsx-a11y': jsxA11y,
import: importPlugin,
prettier,
...reactHooks.configs['recommended-latest'].plugins,
},
rules: {
...react.configs.recommended.rules,
...jsxA11y.configs.strict.rules,
...reactHooks.configs['recommended-latest'].rules,
'import/order': [
'error',
{
groups: [
'builtin',
// Split external into unscoped and scoped manually
'external',
'internal', // Absolute imports from the same project
['parent', 'sibling', 'index'], // Relative imports
'object', // TypeScript Type imports
],
'newlines-between': 'always',
alphabetize: { order: 'asc', caseInsensitive: true },
pathGroups: [
{
// Match scoped packages and move them after unscoped
pattern: '@*/**',
group: 'external',
position: 'after',
},
{
pattern: 'react',
group: 'builtin',
position: 'before',
},
{
pattern: '@/**', // Project root alias
group: 'internal',
position: 'before',
},
],
pathGroupsExcludedImportTypes: ['react', 'builtin', 'internal'],
},
],
'prettier/prettier': [
'error',
{
endOfLine: 'auto',
},
],
'react/react-in-jsx-scope': 'off',
'react/jsx-pascal-case': ['error', { allowNamespace: true, allowAllCaps: true }],
},
settings: {
react: {
version: 'detect',
},
'import/resolver': {
typescript: {},
alias: {
map: [['@', './src']], // Must have this configured in the TS config too
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
},
{
name: 'testing',
files: [
'**/*.test.js',
'**/*.test.jsx',
'**/*.test.ts',
'**/*.test.tsx',
'**/__tests__/**/*.js',
'**/__tests__/**/*.jsx',
'**/__tests__/**/*.ts',
'**/__tests__/**/*.tsx',
],
plugins: {
vitest,
'jest-dom': jestDom,
},
rules: {
...vitest.configs.recommended.rules,
'vitest/padding-around-describe-blocks': 'error',
'vitest/padding-around-test-blocks': 'error',
'jest-dom/prefer-checked': 'warn',
'jest-dom/prefer-enabled-disabled': 'warn',
'jest-dom/prefer-required': 'warn',
},
},
{
name: 'ignores',
files: ['**/*.ts', '**/*.tsx'],
ignores: ['node_modules/', 'dist/', 'build/'],
},
eslintConfigPrettier,
];