@rushstack/eslint-config
A TypeScript ESLint ruleset designed for large teams and projects.Philosophy
When you work in a small repo, you spend most of your time writing code. You know what each file does. You want lint rules that keep things concise and won't slow you down. That's the situation for the 99% of open source projects that shape popular coding conventions.But as your organization scales up, things may change. People come and go. Projects frequently get handed off between teams. Every day, you find yourself working with files that you've never seen before, created by strangers whom you may never meet. It's annoying to constantly come across inconsistent styles. It can be frustrating to decipher expressions that seem to require a TypeScript Ph.D. -- especially for newcomers and junior contributors. When refactoring in bulk, you may edit lots of files without reading them very carefully. In short, the linting needs reflect different priorities:
Small scale: We can assume developers are familiar with the project. We want code to be easy to write.
Large scale: Developers are generally unfamiliar with projects. Code must be easy to read. If not, there's a risk of fragmentation, duplication of efforts, and costly rewrites. (Enabling people to churn out lots of code really fast is still a goal of course; just not the #1 priority.)
Welcome to the world of Rush Stack! The
@rushstack/eslint-config
package was specifically
designed around the the requirements of large teams and projects.Implementation
- Monorepo friendly: The
@rushstack/eslint-config
package has direct dependencies on all the ESLint plugins
- Battle tested: The
@rushstack/eslint-config
rules have been vetted on large production monorepos, across
- Designed for Prettier: The
@rushstack/eslint-config
ruleset is designed to be used together with
- Explicit: The ruleset does not import any "recommended" templates from other ESLint packages. This avoids
- Minimal configuration: To use this ruleset, your .eslintrc.js will need to choose one "profile"
Getting started in 3 steps
Applying the ruleset to your project is quick and easy. You install the package, then create an .eslintrc.js file and select an appropriate project profile. Optionally you can also add some "mixins" to enable additional rules. Let's walk through those three steps in more detail.- Install the package
To install the package, do this:$ cd your-project-folder
$ npm install --save-dev eslint
$ npm install --save-dev typescript
$ npm install --save-dev @rushstack/eslint-config
- Choose one profile
The ruleset currently supports three different "profile" strings, which select lint rules applicable for
your project:@rushstack/eslint-config/profile/node
- This profile enables lint rules intended for a general Node.js project,
@rushstack/eslint-config/profile/node-trusted-tool
- This profile enables lint rules intended for a Node.js project
@rushstack/eslint-config/profile/web-app
- This profile enables lint rules intended for a web application, for
After choosing a profile, create an .eslintrc.js config file that provides the NodeJS
__dirname
context
for TypeScript. Add your profile string in the extends
field, as shown below:.eslintrc.js
// This is a workaround for https://github.com/eslint/eslint/issues/3458
require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = {
extends: [ "@rushstack/eslint-config/profile/node" ], // <---- put your profile string here
parserOptions: { tsconfigRootDir: __dirname }
};
The
@rushstack/eslint-config
ruleset is intended to be used with the Prettier code formatter. For general
instructions on setting that up, please refer to the Prettier docs.
For Rush-specific settings, see the article
Rush: Enabling Prettier.- Add any relevant mixins
Optionally, you can add some "mixins" to your extends
array to opt-in to some extra behaviors.Important: Your .eslintrc.js
"extends"
field must load mixins after the profile entry.@rushstack/eslint-config/mixins/friendly-locals
Requires explicit type declarations for local variables.For the first 5 years of Rush, our lint rules required explicit types for most declarations such as function parameters, function return values, and exported variables. Although more verbose, declaring types (instead of relying on type inference) encourages engineers to create interfaces that inspire discussions about data structure design. It also makes source files easier to understand for code reviewers who may be unfamiliar with a particular project. Once developers get used to the extra work of declaring types, it turns out to be a surprisingly popular practice.
However in 2020, to make adoption easier for existing projects, this rule was relaxed. Explicit type declarations are now optional for local variables (although still required in other contexts). See GitHub #2206 for background.
If you are onboarding a large existing code base, this new default will make adoption easier:
Example source file without
mixins/friendly-locals
:
export class MyDataService {
. . .
public queryResult(provider: IProvider): IResult {
// Type inference is concise, but what are "item", "index", and "data"?
const item = provider.getItem(provider.title);
const index = item.fetchIndex();
const data = index.get(provider.state);
return data.results.filter(x => x.title === provider.title);
}
}
On the other hand, if your priority is make source files more friendly for other people to read, you can enable the
"@rushstack/eslint-config/mixins/friendly-locals"
mixin. This restores the requirement that local variables
should have explicit type declarations.Example source file with
mixins/friendly-locals
:
export class MyDataService {
. . .
public queryResult(provider: IProvider): IResult {
// This is more work for the person writing the code... but definitely easier to understand
// for a code reviewer if they are unfamiliar with your project
const item: ISalesReport = provider.getItem(provider.title);
const index: Map<string, IGeographicData> = item.fetchIndex();
const data: IGeographicData | undefined = index.get(provider.state);
return data.results.filter(x => x.title === provider.title);
}
}
Add the mixin to your
"extends"
field like this:.eslintrc.js
// This is a workaround for https://github.com/eslint/eslint/issues/3458
require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = {
extends: [
"@rushstack/eslint-config/profile/node",
"@rushstack/eslint-config/mixins/friendly-locals" // <----
],
parserOptions: { tsconfigRootDir: __dirname }
};
@rushstack/eslint-config/mixins/packlets
Packlets provide a lightweight alternative to NPM packages for organizing source files within a single project.
This system is described in the @rushstack/eslint-plugin-packlets
documentation.To use packlets, add the mixin to your
"extends"
field like this:.eslintrc.js
// This is a workaround for https://github.com/eslint/eslint/issues/3458
require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = {
extends: [
"@rushstack/eslint-config/profile/node",
"@rushstack/eslint-config/mixins/packlets" // <----
],
parserOptions: { tsconfigRootDir: __dirname }
};
@rushstack/eslint-config/mixins/tsdoc
If your project is using API Extractor or another tool that uses
the TSDoc standard for doc comments, it's recommended to use the
"@rushstack/eslint-config/mixins/tsdoc"
mixin. It will enable
eslint-plugin-tsdoc validation for TypeScript doc comments.Add the mixin to your
"extends"
field like this:.eslintrc.js
// This is a workaround for https://github.com/eslint/eslint/issues/3458
require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = {
extends: [
"@rushstack/eslint-config/profile/node",
"@rushstack/eslint-config/mixins/tsdoc" // <----
],
parserOptions: { tsconfigRootDir: __dirname }
};
@rushstack/eslint-config/mixins/react
For projects using the React library, the "@rushstack/eslint-config/mixins/react"
mixin
enables some recommended additional rules. These rules are selected via a mixin because they require you to:- Add
"jsx": "react"
to your tsconfig.json - Configure your
settings.react.version
as shown below. This determines which React APIs will be considered
Add the mixin to your
"extends"
field like this:.eslintrc.js
// This is a workaround for https://github.com/eslint/eslint/issues/3458
require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = {
extends: [
"@rushstack/eslint-config/profile/web-app",
"@rushstack/eslint-config/mixins/react" // <----
],
parserOptions: { tsconfigRootDir: __dirname },
settings: {
react: {
"version": "16.9" // <----
}
}
};
Links
https://github.com/microsoft/rushstack/blob/main/stack/eslint-config/CHANGELOG.md) - Find out what's new in the latest version@rushstack/eslint-config
is part of the Rush Stack family of projects.