
Research
/Security News
Toptal’s GitHub Organization Hijacked: 10 Malicious Packages Published
Threat actors hijacked Toptal’s GitHub org, publishing npm packages with malicious payloads that steal tokens and attempt to wipe victim systems.
@esrf/eslint-config
Advanced tools
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.
The configuration makes the following assumptions:
package.json
has "type": "module"
).src
folder target a browser environment,
except for test files.src
folder, as well as test files inside the
src
folder, target a Node environment.By "test files inside the
src
folder", we mean any file matching eithersrc/**/__tests__/**/*.{js,jsx,ts,tsx}
orsrc/**/*.test.{js,jsx,ts,tsx}
.
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.
Install the config, together with ESLint:
pnpm install @esrf/eslint-config eslint
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 theMJS
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. Thewarning
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).
tsconfig.json
for front-end project{
"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`
}
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`
}
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"
}
tsconfig.json
{
"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
}
tsconfig.json
in each project{
"extends": "../../tsconfig.json", // extend root `tsconfig.json`
"include": ["*", "src"], // project root files + source files
}
If your project was already set up for linting with ESLint<=8, start with these steps:
peerDependencies
in package.json
)..eslintignore
file (replaced with globalIgnores()
in
eslint.config.js
).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.:
eslint.config.js
.pnpm lint:eslint --fix
.pnpm lint:eslint
passes.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.
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([
// ...
{
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.
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
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
DEFAULT_OPTS
object insrc/index.js
.
createConfig(opts)
This function generates a flat config array with the given options.
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
}
};
FAQs
ESLint config
The npm package @esrf/eslint-config receives a total of 243 weekly downloads. As such, @esrf/eslint-config popularity was classified as not popular.
We found that @esrf/eslint-config demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
/Security News
Threat actors hijacked Toptal’s GitHub org, publishing npm packages with malicious payloads that steal tokens and attempt to wipe victim systems.
Research
/Security News
Socket researchers investigate 4 malicious npm and PyPI packages with 56,000+ downloads that install surveillance malware.
Security News
The ongoing npm phishing campaign escalates as attackers hijack the popular 'is' package, embedding malware in multiple versions.