Skip to content

Commit

Permalink
feat(eslint): add more rules for testing etc
Browse files Browse the repository at this point in the history
Also add a config named export
  • Loading branch information
kentcdodds committed May 29, 2024
1 parent 82f1e5f commit 0281759
Show file tree
Hide file tree
Showing 10 changed files with 3,934 additions and 3,560 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Create a `eslint.config.js` file in your project root with the following
content:

```js
import defaultConfig from '@epic-web/config/eslint'
import { config as defaultConfig } from '@epic-web/config/eslint'

/** @type {import("eslint").Linter.Config} */
export default [...defaultConfig]
Expand Down
148 changes: 124 additions & 24 deletions eslint.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import globals from 'globals'

const hasTypeScript = await import('typescript').then(
() => true,
() => false,
)
const ERROR = 'error'
const WARN = 'warn'

export default [
const has = pkg =>
import(pkg).then(
() => true,
() => false,
)

const hasTypeScript = await has('typescript')
const hasReact = await has('react')
const hasTestingLibrary = await has('@testing-library/dom')
const hasJestDom = await has('@testing-library/jest-dom')
const hasVitest = await has('vitest')
const vitestFiles = ['**/__tests__/**/*', '**/*.test.*']
const testFiles = ['**/tests/**', '**/#tests/**', ...vitestFiles]
const playwrightFiles = ['**/e2e/**']

export const config = [
{
ignores: [
'**/.cache/**',
Expand All @@ -21,8 +34,6 @@ export default [
// all files
{
plugins: {
'react-hooks': (await import('eslint-plugin-react-hooks')).default,
react: (await import('eslint-plugin-react')).default,
import: (await import('eslint-plugin-import-x')).default,
},
languageOptions: {
Expand All @@ -32,11 +43,9 @@ export default [
},
},
rules: {
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'import/no-duplicates': ['warn', { 'prefer-inline': true }],
'import/no-duplicates': [WARN, { 'prefer-inline': true }],
'import/order': [
'warn',
WARN,
{
alphabetize: { order: 'asc', caseInsensitive: true },
pathGroups: [{ pattern: '#*/**', group: 'internal' }],
Expand All @@ -53,16 +62,49 @@ export default [
},
},

// JSX/TSX files
hasReact
? {
files: ['**/*.tsx', '**/*.jsx'].filter(Boolean),
plugins: {
react: (await import('eslint-plugin-react')).default,
},
languageOptions: {
parserOptions: {
jsx: true,
},
},
rules: {
'react/jsx-key': WARN,
},
}
: null,

// react-hook rules are applicable in ts/js/tsx/jsx, but only with React as a
// dep
hasReact
? {
files: ['**/*.ts?(x)', '**/*.js?(x)'],
plugins: {
'react-hooks': (await import('eslint-plugin-react-hooks')).default,
},
rules: {
'react-hooks/rules-of-hooks': ERROR,
'react-hooks/exhaustive-deps': WARN,
},
}
: null,

// JS and JSX files
{
files: ['**/*.js?(x)'],
rules: {
// most of these rules are useful for JS but not TS because TS handles these better
// if it weren't for https://github.com/import-js/eslint-plugin-import/issues/2132
// we could enable this :(
// 'import/no-unresolved': 'error',
// 'import/no-unresolved': ERROR,
'no-unused-vars': [
'warn',
WARN,
{
args: 'after-used',
argsIgnorePattern: '^_',
Expand Down Expand Up @@ -90,17 +132,17 @@ export default [
},
rules: {
'@typescript-eslint/no-unused-vars': [
'warn',
WARN,
{
args: 'after-used',
argsIgnorePattern: '^_',
ignoreRestSiblings: true,
varsIgnorePattern: '^ignored',
},
],
'import/consistent-type-specifier-style': ['warn', 'prefer-inline'],
'import/consistent-type-specifier-style': [WARN, 'prefer-inline'],
'@typescript-eslint/consistent-type-imports': [
'warn',
WARN,
{
prefer: 'type-imports',
disallowTypeAnnotations: true,
Expand All @@ -111,16 +153,74 @@ export default [
}
: null,

// JSX/TSX files
// This assumes test files are those which are in the test directory or have
// *.test.* in the filename. If a file doesn't match this assumption, then it
// will not be allowed to import test files.
{
files: [hasTypeScript ? '**/*.tsx' : null, '**/*.jsx'].filter(Boolean),
languageOptions: {
parserOptions: {
jsx: true,
},
},
files: ['**/*.ts?(x)', '**/*.js?(x)'],
ignores: testFiles,
rules: {
'react/jsx-key': 'warn',
'no-restricted-imports': [
ERROR,
{
patterns: [
{
group: testFiles,
message: 'Do not import test files in source files',
},
],
},
],
},
},

hasTestingLibrary
? {
files: testFiles,
ignores: [playwrightFiles],
plugins: {
'testing-library': (await import('eslint-plugin-testing-library'))
.default,
},
rules: {
'testing-library/no-unnecessary-act': [ERROR, { isStrict: false }],
'testing-library/no-wait-for-side-effects': ERROR,
'testing-library/prefer-find-by': ERROR,
},
}
: null,

hasJestDom
? {
files: testFiles,
ignores: [playwrightFiles],
plugins: {
'jest-dom': (await import('eslint-plugin-jest-dom')).default,
},
rules: {
'jest-dom/prefer-checked': ERROR,
'jest-dom/prefer-enabled-disabled': ERROR,
'jest-dom/prefer-focus': ERROR,
'jest-dom/prefer-required': ERROR,
},
}
: null,

hasVitest
? {
files: testFiles,
ignores: [playwrightFiles],
plugins: {
vitest: (await import('eslint-plugin-vitest')).default,
},
rules: {
// you don't want the editor to autofix this, but we do want to be
// made aware of it
'vitest/no-focused-tests': [WARN, { fixable: false }],
},
}
: null,
].filter(Boolean)

// this is for backward compatibility
export default config
4 changes: 4 additions & 0 deletions fixture/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Fixture

This is just a place to dump code and play around to check that the config works
as expected.
1 change: 1 addition & 0 deletions fixture/app/components/__tests__/accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const MockAccordion = () => <div>Accordion</div>
3 changes: 3 additions & 0 deletions fixture/app/components/accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { MockAccordion } from './__tests__/accordion.tsx'

console.log(MockAccordion)
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 0281759

Please sign in to comment.