ESLint configurations used across API3 projects.
The modules consists of multiple ESLint configurations supporting wide variety of targets:
universal- Linting rules for universal (both FE and BE) JS/TS code (with the emphasis on TS).react- Linting rules for React code, including JSX accessibility rules.nextJs- Next.js specific rules only. It carries no React or accessibility rules of its own, so spread it alongsidereact.jest- Linting rules for Jest tests. Note, that these rules are only applied for JS/TS files with*.test.*extensions.
Requires ESLint v10 and Node.js ^22.22.2 || ^24.15.0 || >=26.
- Create an
eslint.config.jsconfiguration file in the repo root. - Import this plugin and spread the desired configuration(s).
- Point
languageOptions.parserOptionsat thetsconfig.jsonfile(s). The configuration enables type aware rules, so this step is required. - Install
eslint(which is a peer dependency of this module) as a dev dependency.
For example:
const commons = require('@api3/eslint-plugin-commons');
module.exports = [
...commons.configs.universal,
...commons.configs.jest,
{
languageOptions: {
parserOptions: {
// We focus primarily on TS and for that we need to specify the TS configs which is project specific. The following
// is a common monorepo setup (root config and a config for each package).
project: ['./tsconfig.json', './packages/*/tsconfig.json'],
},
},
},
];configs.jest and configs.vitest both apply to the same test file names, so a repo spreads whichever matches its test
runner, never both.
The configurations are plain CommonJS, so they can also be imported from an ESM eslint.config.js:
import commons from '@api3/eslint-plugin-commons';
export default [...commons.configs.universal];If you are using TS, it's possible that ESLint will complain about .js files not being present in the project. This
can likely be fixed by adding "allowJs": true to the tsconfig.json file.
We recommend using the following linting commands inside package.json scripts:
{
"eslint:check": "eslint --report-unused-disable-directives --cache . --max-warnings 0",
"eslint:fix": "pnpm run eslint:check --fix"
}The --cache parameter makes ESLint create a .eslintcache file in the root of the project. This file should be put to
.gitignore.
The configurations are a collection of various rulesets and the config is quite strict. In general there are rules that:
- Have a fixer (import ordering)
- Simplify code (combine two nested ifs)
- Make code more consistent (make
return voidpattern be split on two lines) - Fix outdated stuff (avoid
!ts operator when not necessary) - Avoid vulnerabilities and errors (Number.parseInt without radix)
Tip: Some rules do have fixer with multiple variants of the fixes. You need to use the IDE to prompt the fixes and choose the one you want.
To override a rule, add a config object with a rules key after the shared configs in your eslint.config.js file. For
example:
module.exports = [
...commons.configs.universal,
{
rules: {
'unicorn/filename-case': 'off', // Turns of the kebab-case convention for filenames.
'import-x/no-default-export': 'off', // Turns off the rule that disallows default exports.
'import-x/prefer-default-export': 'error', // Turns on the rule that prefers default exports.
},
},
];To scope an override to a subset of files, give the config object a files key:
module.exports = [
...commons.configs.universal,
{
files: ['packages/frontend/**/*'],
rules: {
'unicorn/prefer-global-this': 'off',
},
},
];v4 requires ESLint v10 and flat configuration. To migrate a repo:
- Bump
eslintto^10.4.0and@api3/eslint-plugin-commonsto^4.0.0. - Remove
@typescript-eslint/eslint-pluginand@typescript-eslint/parserfrom the repo's owndevDependencies. The configurations ship typescript-eslint v8 themselves. If the repo's own config needs typescript-eslint, depend ontypescript-eslint^8instead. - Replace
.eslintrc.*with aneslint.config.jsas shown above. Move the contents of.eslintignoreinto an{ ignores: [...] }config object, and moveparserOptionsunderlanguageOptions. - Drop
--ext js,ts,tsx,jsxfrom the lint script. Flat config decides which files to lint, and these configurations already covercjs,cts,js,jsx,mjs,mts,tsandtsx. - Rename
import/*rules andeslint-disablecomments toimport-x/*.eslint-plugin-importdoes not support ESLint v10, so it was replaced by the maintainedeslint-plugin-import-xfork. The rules and their options are unchanged. - Rename
deprecation/deprecationcomments to@typescript-eslint/no-deprecated. Theeslint-plugin-deprecationplugin only supports ESLint v8 and was removed. - Rename
@shopify/prefer-early-returncomments tounicorn/prefer-early-returnand@shopify/prefer-module-scope-constantsones to@typescript-eslint/naming-convention. The@shopify/eslint-plugindependency was dropped andprefer-early-returnnow comes fromeslint-plugin-unicorn. - Scope any rule overrides of your own with a
fileskey. v4 lintspackage.jsonas well as source, so a config object withrulesbut nofilesnow applies topackage.jsontoo and will fail with "could not find plugin". - If the repo sorts imports with a Prettier plugin such as
prettier-plugin-organize-imports, remove it.import-x/ordernow sorts both the import statements and the names inside their braces, and the two tools disagree on a few cases, soprettier --writeandeslint --fixkeep undoing each other there. - Run
eslint --fixand then clean up whatever is left. Expect some staleeslint-disabledirectives to be reported, becauseeslint-plugin-unicornrenamed a number of rules.
This sections is intended for developers of this repo.
- Run
pnpm version [major|minor|patch]by choosing the appropriate version bump. - Push the changes to the
main, either directly or via a pull request. - The CI will register a new version and handle the release process.