@esrf/eslint-config

中文文档
4.0.4发布于 2026/11/02MITES Module近 30 天下载 3244

ESLint config

原始使用文档
文档目录11 节
  1. Prerequisites
  2. CommonJS projects
  3. Getting started
  4. Monorepos
  5. Migrating an existing codebase
  6. Usage guidelines
  7. Troubleshooting
  8. API reference
  9. DEFAULT_FILES
  10. detectOpts(projectDir)
  11. createConfig(opts?, settings?)

Shared ESLint config @ ESRF

Dynamic linting configuration for JavaScript, TypeScript and Node projects, with support for React/JSX, Vitest, Storybook, Testing Library, and Cypress.

In addition to core ESLint rules, @esrf/eslint-config includes rules from the following ESLint plugins:

Written in ESLint's flat config format, available since ESLint v9, this shared configuration supports monorepos and can be easily loosened, tweaked or extended to adapt to the needs of any front-end project.

Prerequisites

The configuration makes the following assumptions:

  • All JS files are ES modules (i.e. package.json has "type": "module").
  • All JS/TS files outside the src folder target a Node environment.
  • All JS/JSX/TS/TSX files inside the src folder target a browser environment except for some test files.
  • Test files inside src folder target a Node environment except for files ending in .browser.test.{js,jsx,ts,tsx}, which target a browser environment (e.g. Vitest Browser Mode).

By "test files inside the src folder", we mean any file matching either src/**/__tests__/**/*.{js,jsx,ts,tsx} or src/**/*.test.{js,jsx,ts,tsx}.

CommonJS projects

If, for whatever reason you cannot set "type": "module" in package.json, you can still benefit from this linting configuration by switching to TypeScript, and/or by using the explicit .mjs or .cjs extensions for files outside the src folder.

We do not support MJS/CJS files inside the src folder at this time.

Getting started

Install the config, together with the required version of ESLint (cf. peerDependencies in package.json):

pnpm add --save-dev @esrf/eslint-config eslint@<x.y>

Create a file called eslint.config.js in the root of your project, with the following content:

import { createConfig, detectOpts } from '@esrf/eslint-config';
import { defineConfig, globalIgnores } from 'eslint/config';

const opts = detectOpts(import.meta.dirname);

const config = defineConfig([
  globalIgnores(['dist/', 'folder/to/ignore/']),
  ...createConfig(opts),
]);

export default config;

If your project's package.json doesn't have "type": "module" (or has "type": "commonjs"), use the MJS extension to enable ESM: eslint.config.mjs.

Adjust the list of ignored folders as needed. Don't forget folders that might be generated by your CI or your toolchain (e.g. pnpm store, test coverage report, etc.) Don't include node_modules or .git, which are already ignored by ESLint out of the box.

Add a linting script to your package.json. In a typical project with TypeScript, ESLint and Prettier, we recommend having four scripts as shown below to ensure linting, formatting and type-checking are performed in parallel:

"scripts": {
  "lint": "pnpm \"/^lint:/\"",
  "lint:eslint": "eslint --max-warnings=0",
  "lint:tsc": "tsc",
  "lint:prettier": "prettier . --cache --check"
},

Make sure to run the lint script above as part of your CI workflow.

The --max-warnings=0 option ensures that ESLint exits with a non-zero code if it finds warning-level violations. The warning level helps to make cosmetic, low-impact violations less intrusive during development, but those must still be fixed before committing.

Finally, in a TypeScript project, make sure tsconfig.json includes all TS and JS files in the codebase (including eslint.config.js itself).

Recommended tsconfig.json for front-end project with TS >= 6.x
{
  "compilerOptions": {
    "moduleResolution": "bundler",
    "lib": ["dom", "esnext"],
    "types": [],
    "jsx": "react-jsx", // if React
    "allowJs": true, // include JS files when linting
    "skipLibCheck": true,
    "isolatedModules": true,
    "resolveJsonModule": true,
    "noEmit": true, // allow running `tsc` for type-checking
    "incremental": true,
    "noImplicitOverride": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
  },
  "include": ["*", "src"], // include root files like `eslint.config.js`
}
Recommended tsconfig.json for front-end project with TS 5.x
{
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "bundler",
    "target": "esnext",
    "lib": ["dom", "dom.iterable", "esnext"],
    "jsx": "react-jsx", // if React
    "allowJs": true, // include JS files when linting
    "skipLibCheck": true,
    "esModuleInterop": true,
    "isolatedModules": true,
    "resolveJsonModule": true,
    "noEmit": true, // allow running `tsc` for type-checking
    "incremental": true,
    "strict": true,
    "noImplicitOverride": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true,
  },
  "include": ["*", "src"], // include root files like `eslint.config.js`
}
Recommended tsconfig.json for Node project
{
  "compilerOptions": {
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "target": "esnext",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true, // include JS files when linting
    "skipLibCheck": true,
    "esModuleInterop": true,
    "isolatedModules": true,
    "resolveJsonModule": true,
    "noEmit": true, // allow running `tsc` for type-checking
    "incremental": true,
    "strict": true,
    "noImplicitOverride": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true,
  },
  "include": ["*", "src"], // include root files like `eslint.config.js`
}

Monorepos

In monorepos, install and configure ESLint at the root, as well as in every project:

.
├── cypress/
│   └── tsconfig.json
├── node_modules/
├── packages/
│   ├── foo/
│   │   ├── node_modules/
│   |   ├── src/
│   │   ├── eslint.config.js
│   │   ├── package.json
│   │   └── tsconfig.json
│   └── bar/
│       ├── node_modules/
│       ├── src/
│       ├── eslint.config.js
│       ├── package.json
│       └── tsconfig.json
├── eslint.config.js
├── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── tsconfig.json

This allows each project to have a linting config tailored to its own needs (with or without React, with or without Vitest, etc.)

In the root eslint.config.js, ignore the packages folder to avoid linting the same files twice:

const config = defineConfig([
  globalIgnores(['packages/']),
  ...createConfig(opts),
]);

Here is one way of defining the linting scripts in the root package.json to ensure that the root folder is linted as well:

"scripts": {
  "lint": "pnpm \"/^lint:/\"",
  "lint:prettier": "prettier . --cache --check",
  "lint:eslint": "pnpm -r --parallel lint:eslint",
  "lint:tsc": "pnpm -r --parallel lint:tsc",
  "lint:root:eslint": "eslint --max-warnings=0",
  "lint:root:tsc": "tsc"
}
Recommended root tsconfig.json with TS >= 6.x
{
  "compilerOptions": {
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "target": "esnext",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "isolatedModules": true,
    "resolveJsonModule": true,
    "noEmit": true,
    "incremental": true,
    "strict": true,
    "noImplicitOverride": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true,
  },
  "include": ["*"], // only root files to avoid linting/type-checking project files twice
}
Recommended root tsconfig.json with TS 5.x
{
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "bundler",
    "target": "esnext",
    "lib": ["dom", "dom.iterable", "esnext"],
    "jsx": "react-jsx",
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "isolatedModules": true,
    "resolveJsonModule": true,
    "noEmit": true,
    "incremental": true,
    "strict": true,
    "noImplicitOverride": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true,
  },
  "include": ["*"], // only root files to avoid linting/type-checking project files twice
}
Recommended tsconfig.json in each project
{
  "extends": "../../tsconfig.json", // extend root `tsconfig.json`
  "include": ["*", "src"], // project root files + source files
}

Migrating an existing codebase

If your project was already set up for linting with ESLint<=8, start with these steps:

  1. Upgrade ESLint to the version required by this config (cf. peerDependencies in package.json).
  2. Remove the legacy .eslintignore file (replaced with globalIgnores() in eslint.config.js).
  3. Remove any legacy configuration (eslintConfig property in package.json, .eslintrc.js file, etc.)

You're now ready to apply the new configuration to your codebase.

Run the lint script, pnpm lint:eslint, and check the output. Depending on the size of your codebase, you may see thousands of warnings and errors. Most will be rule violations, but some might also be "Unused eslint-disable directive" warnings, meaning that the new configuration no longer reports violations everywhere your previous config used to.

The most sane way to proceed from here is to start by turning off every single rule that reports a violation in eslint.config.js:

const config = defineConfig([
  globalIgnores(['dist/', 'folder/to/ignore/']),
  ...createConfig(opts),
  {
    rules: {
      'simple-import-sort/imports': 'off',
      'import/consistent-type-specifier-style': 'off',
      // ...
    },
  },
]);

Keep turning off rules until ESLint no longer reports any violations — only "Unused eslint-disable directive" warnings. You can now automatically remove all unused directives with pnpm lint:eslint --fix. Make sure to reformat all files afterwards with pnpm lint:prettier --write.

At this point, pnpm lint:eslint should pass, so it's a good time to commit the new linting set up and open a PR. Once merged, you can start to actually fix the new violations.

Proceed one rule at a time, ideally starting with the most impactful, auto-fixable rules, like import/consistent-type-specifier-style, import/no-duplicates, simple-import-sort/imports, etc.:

  1. Turn the rule back on by removing it from eslint.config.js.
  2. Run pnpm lint:eslint --fix.
  3. If the rule is not auto-fixable, fix the violations manually until pnpm lint:eslint passes.
  4. Commit, open a PR, request a review, and merge into the main branch once approved.

Rules with few violations can of course be fixed together in the same PR as long as the diff remains reviewable. When a fix is non-trivial, or when the rationale behind a rule might not be clear to the reviewer, make sure to comment and link to the documentation of the rule in question.

Usage guidelines

If you strongly disagree with a rule, or if it goes against agreed-upon practices in your project, or if it's really not worth fixing, either disable it entirely or configure it as you see fit, making sure to explain why in a comment:

const config = defineConfig([
  // ...
  {
    files: DEFAULT_FILES,
    rules: {
      'react/prop-types': 'off', // legacy code, not worth fixing

      /* Default is "avoid", but there are lots of complicated `switch` statements,
       * notably in Redux reducers, which benefit from clear case blocks. */
      'unicorn/switch-case-braces': ['warn', 'always'],
    },
  },
  {
    /* Some rules apply only to specific files.
     * Make sure to use the same `files` array as in `src/index.js`. */
    files: ['**/*.{jsx,tsx}'],
    rules: {
      'jsx-a11y/control-has-associated-label': 'off',
    },
  },
]);

If you turn off a rule completely, beware that it will not be applied to new code. To disable a rule on existing code only, use eslint-disable directives instead (assuming the number of violations is within reason):

/* eslint-disable react/prop-types --
 * Long explanation why the rule is disabled. */

// eslint-disable-next-line react/no-multi-comp -- short explanation
export function MyComponent(props) {
  let foo; // eslint-disable-line no-unused-vars -- same-line syntax

  return (
    {/* eslint-disable-next-line jsx-a11y/anchor-is-valid -- JSX syntax */}
    <a onClick={(e) => e.stopPropagation()}>{props.value}</a>
  );
}

Of course, don't hesitate to open an issue in this repo if you think the config should be changed, if you'd like to better understand the rationale behind some of the rules and options, or if you're unsure how to fix a specific violation.

If your project uses React, you may want to configure ESLint React plugin so it knows about your custom hooks:

const config = defineConfig([
  // ...
  {
    files: DEFAULT_FILES,
    settings: {
      react: { additionalStateHooks: '/^(useDimMappingState)$/u' },
    },
  },
]);

Troubleshooting

ESLint provides a brilliant config inspector to debug configuration issues, notably when ignoring/filtering files. You can run it with:

pnpm lint:eslint --inspect-config
pnpm --filter <project-in-monorepo> lint:eslint --inspect-config

API reference

DEFAULT_FILES

Default files glob for JavaScript and TypeScript files.

Unless you're only disabling rules, make sure to always specify which files a config object applies to:

import { DEFAULT_FILES } from '@esrf/eslint-config';

const config = defineConfig([
  // ...
  {
    files: DEFAULT_FILES,
    rules: {/* ... */},
  },
]);

detectOpts(projectDir)

This function looks at the dependencies installed in <projectDir>/package.json and whether a <projectDir>/tsconfig.json file exists. It returns an object that can then be passed to createConfig() in order generate an ESLint configuration tailored to your project.

Calling this function is entirely optional; the options object can be declared manually. It's also possible to override specific options as needed:

const opts = { react: true };
createConfig(opts);

const opts = detectOpts(import.meta.dirname);
createConfig({ ...opts, typescript: false });

All options accept boolean values, except for the typescript option, which accepts either false or an object with the a tsconfigRootDir property. By default, the project dir is used: { tsconfigRootDir: <projectDir> } but you can override it as follows:

import path from 'node:path';

createConfig({
  ...detectOpts(import.meta.dirname),
  typescript: {
    tsconfigRootDir: path.join(import.meta.dirname, '..'), // `tsconfig.json` in parent directory
  },
});

For the full list of options, please refer to the Opts type definition in src/index.js.

createConfig(opts?, settings?)

This function generates a flat config array with the given options and settings.

The returned array contains ESLint config objects — you can spread it, filter it, extend it, etc. before exporting it:

const config = createConfig(opts);
const unicornConfig = config.find(c => c.name.endsWith('unicorn'));

export default {
  ...config.filter(c => c !== unicornConfig),
  {
    ...unicornConfig,
    files: ['**/*.{ts,tsx}'] // apply unicorn rules to TS/TSX files only
  }
};

For the full list of settings, please refer to the Settings type definition in src/index.js.

版本与发布记录

@esrf/eslint-config 共发布 53 个版本,首次发布于 2025/03/26;过去 12 个月发布了 25 个版本。

版本发布日期距今
4.0.4latest2026/11/0211 天前
4.0.32026/11/0211 天前
4.0.22026/11/0211 天前
4.0.12026/10/3014 天前
4.0.02026/10/3014 天前
3.2.42026/10/021 个月前

关于 @esrf/eslint-config 的常见问题

@esrf/eslint-config 是什么?

ESLint config

如何安装 @esrf/eslint-config?

在项目目录运行 npm install @esrf/eslint-config,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:eslint(>=10.4)、typescript(>=5)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@esrf/eslint-config 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/esrf__eslint-config 是否存在。

@esrf/eslint-config 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@esrf/eslint-config 可以商用吗?

@esrf/eslint-config 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@esrf/eslint-config 的下载量有多少?

根据 npm 官方下载统计,@esrf/eslint-config 在 2026/10/14 – 2026/11/12 这 30 天内被下载 3,244 次,日均约 108 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@esrf/eslint-config 还在维护吗?

最新版本 4.0.4 发布于 2026/11/02(11 天前);至今共发布 53 个版本,其中过去 12 个月发布了 25 个;首次发布于 2025/03/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。