@bdrk/eslint-config
v3.0.0
Published
ESLint shareable configuration for Bedrock projects.
Readme
ESLint Shareable Configs
This repository contains the standard ESLint shareable configuration for Bedrock projects.
It ships as flat config and targets ESLint 9+ (developed and tested against ESLint 10).
There is one entrypoint per runtime — Node.js, React, and Angular — plus a plain-JS root. All three runtime entrypoints share the same rules, so TypeScript that does not import a framework lints identically under all of them.
Choosing an entrypoint
| Entrypoint | For |
|---|---|
| @bdrk/eslint-config | Plain JavaScript. Rules only — you supply the environment |
| @bdrk/eslint-config/node | TypeScript on Node.js |
| @bdrk/eslint-config/react | TypeScript + JSX in the browser |
| @bdrk/eslint-config/angular | TypeScript in Angular |
Install the package:
npm install --save-dev @bdrk/eslint-configEach entrypoint exports an array of flat config objects. Spread it into your own config and add project-specific overrides afterwards.
Node.js:
// eslint.config.js
const bdrk = require('@bdrk/eslint-config/node');
module.exports = [
...bdrk,
{
// project-specific overrides
},
];/react and /angular are used the same way. Using ES modules? Import the default
export instead:
// eslint.config.mjs
import bdrk from '@bdrk/eslint-config/react';
export default [
...bdrk,
// project-specific overrides
];Plain JavaScript uses the root entrypoint. It declares no globals, so add the ones your code actually runs with:
// eslint.config.js
const globals = require('globals');
const bdrk = require('@bdrk/eslint-config');
module.exports = [
...bdrk,
{
languageOptions: {
globals: { ...globals.node },
},
},
];Anything defined in this configuration can be overridden by adding a later config object with the rules you want to change.
What's included
- Core:
@eslint/jsrecommended rules plus Bedrock's conventions, formatting via@stylistic/eslint-plugin, import hygiene viaeslint-plugin-import-x, and directive-comment hygiene via@eslint-community/eslint-plugin-eslint-comments. - TypeScript, in every runtime entrypoint:
typescript-eslintrecommended + type-checked rules and Bedrock's TypeScript conventions. Typed rules are scoped to.ts/.tsx/.mts/.cts, so plain.jsfiles such aseslint.config.jsare not subject to them. - Runtime layers: globals and parser options only — Node globals for
/node, browser globals for/reactand/angular, JSX parsing for.jsx/.tsx, and animport-x/firstexemption for Angular's*.module.ts.
The TypeScript config uses typescript-eslint's
projectService, so it
auto-detects the nearest tsconfig.json — no parserOptions.project override required
in most projects.
Framework plugins are not included
This package deliberately ships no framework plugins. Add them in your own config if you want them:
- React —
eslint-plugin-react,eslint-plugin-react-hooks,eslint-plugin-jsx-a11y - Angular —
angular-eslint, which also handles template linting
This keeps frontend plugins out of every Node service's dependency tree.
Contributing: the portability invariant
The point of this package is that code moves between runtimes without being rewritten. That rests on one rule:
All rules live in
index.jsorinternal/typescript.js. A runtime layer may only setlanguageOptions(globals, parser options) and addfiles-scoped overrides for file shapes that exist solely in that framework. A runtime layer never sets a rule that another layer sets differently.
If a change appears to need a rule in node/, react/, or angular/, that is a signal
the rule belongs in the core with a different value — not a signal to add it to the
layer. The single sanctioned exception is Angular's **/*.module.ts override, which is
safe because no other runtime has a file matching that pattern.
npm test enforces this: test/fixtures/portable/portable.ts is linted under all three
runtime entrypoints and the three result sets must be identical, so a rule added to
one layer fails the build. npm run lint lints this package with its own root config.
Migrating from the previous major
@bdrk/eslint-config/typescriptno longer exists. Use/node,/react, or/angular.- The root entrypoint no longer supplies Node globals. Plain-JS projects add them as shown above; TypeScript projects should move to a runtime entrypoint.
- The
Iinterface prefix is no longer enforced. ExistingIFoonames still lint clean underPascalCase, so no rename is forced.
The rule changes only widen what is accepted — func-style,
import-x/no-default-export, new-cap and the naming conventions all loosened — so the
upgrade surface is essentially the entrypoint rename.
One thing to be aware of rather than surprised by: explicit-function-return-type
applies to functions assigned to variables, so React components written as
const Foo = () => … need a return-type annotation (: ReactElement). This is the
existing Bedrock convention, unchanged by this release, and it applies identically in
every runtime.
