Skip to content

A modern, opinionated ESLint configuration that automatically adapts to your project's dependencies with support for TypeScript, React, Jest, Vitest, and Prettier integration.

License

Notifications You must be signed in to change notification settings

codfish/eslint-config

Repository files navigation

@codfish/eslint-config

Modern ESLint configuration with TypeScript, React/Next.js, YAML, Testing Library, and testing framework support using ESLint v10+ flat config format.

version downloads MIT License semantic-release Commitizen friendly

Table of Contents

Features

  • Modern ESLint v10+ flat config: Uses the new flat configuration format
  • Dynamic feature detection: Automatically configures based on your project's dependencies
  • TypeScript support: Full TypeScript linting with modern typescript-eslint parser and rules
  • React ecosystem: React, React Hooks, and JSX accessibility rules when React is detected
  • Next.js support: Automatically configures Next.js official plugin linting rules when detected
  • Test framework agnostic: Supports Jest and Vitest with automatic detection
  • Testing Library integration: Automatically includes Testing Library rules for test files
  • Multi-format support: Built-in linting and formatting for Markdown, HTML, JSON, YAML/YML files
  • Prettier integration: Built-in Prettier configuration with conflict resolution via eslint-config-prettier
  • ESM architecture: Built with ECMAScript modules and full TypeScript typing
  • Docker support: Optional configuration for dockerized applications
  • Blockchain/dApp support: Optional configuration for decentralized applications

Installation

Install the package and required peer dependencies:

npm i -D eslint@10 @codfish/eslint-config

# Optionally, you can uninstall plugins or presets you don't need to manage anymore,
# @codfish/eslint-config includes them all.
npm uninstall typescript-eslint \
  eslint-config-prettier \
  eslint-plugin-jest \
  eslint-plugin-jsx-a11y \
  eslint-plugin-prettier \
  eslint-plugin-react \
  eslint-plugin-react-hooks \
  @tanstack/eslint-plugin-query \
  eslint-plugin-simple-import-sort \
  eslint-plugin-testing-library \
  eslint-plugin-yml \
  @next/eslint-plugin-next \
  eslint-plugin-next \
  commitlint \
  @commitlint/cli \
  @commitlint/config-conventional \
  prettier # optional, see note

Note

ESLint now handles linting and formatting for JavaScript, TypeScript, Markdown, HTML, JSON, and YAML files automatically. If you want to format additional file types (like CSS, SCSS, etc.), you can leave Prettier installed as a dev dependency in your project.

Usage

Create an eslint.config.js file in your project root:

import { defineConfig } from 'eslint/config';
import codfish from '@codfish/eslint-config';

export default defineConfig(
  codfish,

  {
    rules: {
      // Relax some rules for your project
      'react/prop-types': 'off',
      'import/prefer-default-export': 'off',
      '@typescript-eslint/explicit-function-return-type': 'warn',
    },
  },
);

[!IMPORTANT] If you get ES module errors, you may need to set the type field in your package.json to module or rename your config file to eslint.config.mjs.

Using extends and targetting all files:

import codfish from '@codfish/eslint-config';

export default defineConfig({
  extends: [codfish],
  rules: {
    // temporary
    '@typescript-eslint/no-explicit-any': 'off',
  },
});

Using extends and targetting specific files:

Warning

Not recommended. This will prevent it from using the files specified in the main config, so rules around test files, yml files, etc. will not be applied.

import codfish from '@codfish/eslint-config';

export default defineConfig({
  files: ['**/*.{js,jsx,ts,tsx}'],
  extends: [codfish],
  rules: {
    // temporary
    '@typescript-eslint/no-explicit-any': 'off',
  },
});

Not using the defineConfig function, just spread the config object:

import codfish from '@codfish/eslint-config';

export default [
  ...codfish,

  {
    // Your project-specific overrides
    rules: {
      // Override or add specific rules
      'no-console': 'warn',
      '@typescript-eslint/no-unused-vars': 'error',
    },
  },
];

Use the config without any overrides:

import codfish from '@codfish/eslint-config';

export default codfish;

Framework-specific customizations:

import { defineConfig } from 'eslint/config';
import codfish from '@codfish/eslint-config';

export default defineConfig(
  codfish,

  // Custom ignores
  {
    ignores: ['some-directory'],
  },

  {
    files: ['**/*.spec.{js,ts,jsx,tsx}'],
    rules: {
      // Allow any in test files
      '@typescript-eslint/no-explicit-any': 'off',
      // Relax Testing Library rules if needed
      'testing-library/prefer-screen-queries': 'warn',
    },
  },

  {
    files: ['**/*.config.{js,ts}'],
    rules: {
      // Allow require in config files
      '@typescript-eslint/no-require-imports': 'off',
    },
  },

  {
    files: ['**/*.{js,jsx,ts,tsx}'],
    rules: {
      // Customize Next.js rules
      '@next/next/no-img-element': 'warn',
      '@next/next/no-html-link-for-pages': 'off',
    },
  },
);

Docker Configuration:

For projects leveraging Docker containers, you may want to disable import resolution errors for node_modules if packages are only available in the container but you're running the linter locally.

import codfish from '@codfish/eslint-config';
import docker from '@codfish/eslint-config/docker';

export default defineConfig(
  codfish,
  docker,

  {
    // Your app-specific overrides
  },
);

dApps Configuration:

For decentralized applications that use build artifacts and blockchain globals, use the specialized dApp config. This config will set some globals as well as ignore missing build artifact imports. While you obviously need those to run your app, sometimes you might want to run the linter in a ci/cd environment and build artifacts might not be present.

import codfish from '@codfish/eslint-config';
import dapp from '@codfish/eslint-config/dapp';

export default defineConfig(
  codfish,
  dapp,

  {
    // Your app-specific overrides
  },
);

The dApp configuration provides:

  • Blockchain-specific globals (artifacts, contract, web3, etc.)
  • Import resolution handling for smart contract build artifacts
  • Relaxed rules for generated contract files

Opinionated Highlights

This configuration includes some opinionated settings that you might want to customize for your project:

Prettier/Formatting:

  • Semicolons: Enforces semicolons (;)
  • 120 character line width: Wider than the common 80/100 - you might prefer shorter lines
  • 2-space indentation: Uses 2 spaces for tabs
  • Single quotes: Prefers 'single' over "double" quotes
  • Trailing commas: Adds trailing commas everywhere
  • Arrow parentheses: Avoids parens around single args (x => x not (x) => x)

ESLint Rules:

  • Import sorting: Enforces automatic import organization with specific grouping rules
  • Lodash restrictions: Requires direct imports (import get from 'lodash-es/get') instead of full lodash
  • React hooks deps: Disables exhaustive-deps rule - you might want this stricter
  • Console statements: Disallows console.log in regular code (only allowed in test files) - some teams prefer warnings instead of errors
  • Next.js rules: Enforces Next.js best practices and Core Web Vitals optimization
  • Testing Library rules: Enforces Testing Library best practices in test files

File Ignores:

  • Ignores common build directories (.next, coverage, .vercel, etc.)
  • Includes .github and .vitepress folders (not ignored like most configs)

See the configuration examples below for instructions on overriding these settings to match your team's preferences.

IDE Setup

VS Code / Cursor

For the best development experience with VS Code or Cursor (or any VS Code-based IDE), install the ESLint extension and configure it to auto-fix on save:

Add these settings to your .vscode/settings.json or user settings:

{
  "editor.codeActionsOnSave": {
    "source.fixAll": "explicit"
  },
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "markdown",
    "json",
    "jsonc",
    "html",
    "yml",
    "yaml"
  ]
}

This configuration enables:

  • Automatic linting and formatting on save for all supported file types
  • ESLint validation for Markdown, JSON, YAML, and HTML files
  • A unified development experience across your entire codebase

Prettier Configuration

Prettier is included and runs automatically through ESLint for JavaScript, TypeScript, JSX, TSX, Markdown, HTML, JSON, and YAML files using the built-in configuration. You don't need to install or configure Prettier separately for these file types.

You can then override the default config by creating your own Prettier config file, or extend the built-in config:

Option 1: Extend the built-in config (Recommended)

Create a prettier.config.js file in your project root:

// prettier.config.js

import codfishConfig from '@codfish/eslint-config/prettier';

/**
 * @see https://prettier.io/docs/en/configuration.html
 * @type {import("prettier").Config}
 */
export default {
  ...codfishConfig,

  // Override specific settings
  printWidth: 80,
  singleQuote: false,
  tabWidth: 4,
  trailingComma: 'none',
};

Option 2: Completely custom config

This config will completely override the built-in config.

// prettier.config.js

/**
 * @see https://prettier.io/docs/en/configuration.html
 * @type {import("prettier").Config}
 */
export default {
  printWidth: 100,
  tabWidth: 2,
  useTabs: false,
  semi: true,
  singleQuote: true,
  trailingComma: 'all',
  bracketSpacing: true,
  bracketSameLine: false,
  arrowParens: 'avoid',
  proseWrap: 'always',
};

Use in combination with prettier-plugin-tailwindcss

npm i -D eslint@10 @codfish/eslint-config prettier-plugin-tailwindcss
// prettier.config.js

import codfish from '@codfish/eslint-config/prettier';

/** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */
export default {
  ...codfish,
  plugins: ['prettier-plugin-tailwindcss'],
  tailwindStylesheet: './src/styles/app.css',
  tailwindFunctions: ['clsx'], // optional
};

Example scripts

Optionally, you can add these scripts to your package.json for common linting workflows:

Recommended scripts:

{
  "scripts": {
    "lint": "eslint .",
    "fix": "eslint . --fix"
  },
  "lint-staged": {
    "*.{js,jsx,ts,tsx,md,json,yml,yaml,html}": ["eslint --fix"]
  }
}

Note

ESLint now handles linting and formatting for JavaScript, TypeScript, Markdown, HTML, JSON, and YAML files. You don't need to run Prettier separately for these file types.

With Prettier for other file types (CSS, SCSS, etc.):

If you want to format additional file types not covered by ESLint (like CSS, SCSS), you can install Prettier and add these scripts:

{
  "scripts": {
    "lint": "eslint .",
    "fix": "eslint . --fix",
    "format": "prettier --write \"**/*.{css,scss}\"",
    "check": "npm run lint && npm run format -- --check --no-write"
  },
  "lint-staged": {
    "*.{js,jsx,ts,tsx,md,json,yml,yaml,html}": ["eslint --fix"],
    "*.{css,scss}": ["prettier --write"]
  }
}

Commitlint Configuration

Extend from the shared codfish commitlint config.

import codfishConfig from '@codfish/eslint-config/commitlint.js';

export default Object.assign(codfishConfig, {
  // your overrides here
  rules: {
    'scope-case': [1],
  },
});

Or just reference it in your package.json:

{
  "commitlint": {
    "extends": ["./node_modules/@codfish/eslint-config/commitlint.js"]
  }
}

Run commitlint in your CI to validate your commits:

Note

If you have @codfish/eslint-config as a dev dependency, and a commitlint config in your project, you can just call npx commitlint in your CI and it will use the shared config.

  • You just need to setup node & install your dependencies before running commitlint.
  • Don't forget to set the fetch-depth to 0 to ensure commitlint can work properly.
# .github/workflows/validate.yml

on: pull_request_target

jobs:
  validate:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v5
        with:
          ref: ${{ github.event.pull_request.head.sha || github.ref }}
          fetch-depth: 0 # Important for commitlint to work

      - uses: actions/setup-node@v5
        with:
          node-version: lts/*
          registry-url: https://registry.npmjs.org

      - run: npm ci # or npm install

      - run:
          npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }}
          --verbose

Upgrading to ESLint 10

This package now requires ESLint 10.0.0 or higher and Node.js v20.19.0 or higher.

Breaking Changes in ESLint 10

  • ESLint 10 Required: Minimum ESLint version is now 10.0.0
  • Node.js v20.19.0+: Minimum Node.js version increased from v20.0.0 to v20.19.0
  • Legacy eslintrc removed: ESLint 10 completely removes the deprecated eslintrc config system (this package already uses flat config)
  • Improved JSX tracking: ESLint 10 properly tracks JSX references in scope analysis, which may surface previously hidden unused import warnings in React files
  • New recommended rules: Three new rules enabled in eslint:recommended:
    • no-unassigned-vars - Disallow variables that are assigned but never used
    • no-useless-assignment - Disallow assignments that don't change the value
    • preserve-caught-error - Enforce that caught errors are not reassigned

Migration Steps

  1. Update Node.js (if needed):

    node --version  # Ensure you're on v20.19.0+ or v22.13.0+
  2. Update ESLint and this config:

    npm install --save-dev eslint@10 @codfish/eslint-config@latest
  3. Install dependencies with legacy peer deps (required until all plugins update):

    npm install --legacy-peer-deps

    This is necessary because some ESLint plugins haven't updated their peerDependencies to include ESLint 10 yet. The plugins still work correctly with ESLint 10.

  4. Run linting and check for new violations:

    npm run lint
  5. Review React files for newly reported unused imports. ESLint 10's improved JSX tracking may flag imports that were previously ignored but are actually used in JSX.

What Changed

Since this package already uses ESLint's flat config format (the biggest change in ESLint 9), the upgrade to ESLint 10 is relatively smooth. The main changes are:

  • ✅ Flat config format (already implemented)
  • ✅ Plugin configurations updated to ESLint 10-compatible versions
  • ✅ All tests passing with ESLint 10
  • ⚠️ Some plugins show peer dependency warnings but work correctly (ecosystem is catching up)

For more details, see the ESLint 10 Migration Guide.

Migration from Legacy Config

If you're upgrading from an older version that used Airbnb presets:

  1. Update to ESLint v10+: npm install --save-dev eslint@10
  2. Switch to flat config: Replace .eslintrc.js with eslint.config.js
  3. Use import syntax: Change from require() to import statements
  4. Remove explicit React config: React support is now automatically detected
  5. Update scripts: Ensure your lint script runs eslint . (flat config auto-discovery)

About

A modern, opinionated ESLint configuration that automatically adapts to your project's dependencies with support for TypeScript, React, Jest, Vitest, and Prettier integration.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 5