UNPKG

12.5 kBMarkdownView Raw
1# @rushstack/eslint-config
2
3A TypeScript ESLint ruleset designed for large teams and projects.
4
5## Philosophy
6
7When you work in a small repo, you spend most of your time writing code. You know what each file does. You want lint
8rules that keep things concise and won't slow you down. That's the situation for the 99% of open source projects
9that shape popular coding conventions.
10
11But as your organization scales up, things may change. People come and go. Projects frequently get handed off between
12teams. Every day, you find yourself working with files that you've never seen before, created by strangers whom
13you may never meet. It's annoying to constantly come across inconsistent styles. It can be frustrating to decipher
14expressions that seem to require a TypeScript Ph.D. -- especially for newcomers and junior contributors. When
15refactoring in bulk, you may edit lots of files without reading them very carefully. In short, the linting needs
16reflect different priorities:
17
18**Small scale:** We can assume developers are _familiar_ with the project. We want code to be _easy to write_.
19
20**Large scale:** Developers are generally _unfamiliar_ with projects. Code must be _easy to read_. If not,
21there's a risk of fragmentation, duplication of efforts, and costly rewrites. (Enabling people to churn out
22lots of code really fast is still a goal of course; just not the #1 priority.)
23
24Welcome to the world of [Rush Stack](https://rushstack.io/)! The `@rushstack/eslint-config` package was specifically
25designed around the the requirements of large teams and projects.
26
27
28## Implementation
29
30- **Monorepo friendly:** The `@rushstack/eslint-config` package has direct dependencies on all the ESLint plugins
31 that it needs. This avoids encumbering each consuming project with the obligation to satisfy a peer dependencies.
32 It also ensures that the installed plugin versions were tested for compatibility together.
33
34- **Battle tested:** The `@rushstack/eslint-config` rules have been vetted on large production monorepos, across
35 a broad set of projects, teams, and requirements. These rules embody a way of working that scales. Quite
36 a lot of discussion and evolution went into them.
37
38- **Designed for Prettier:** The `@rushstack/eslint-config` ruleset is designed to be used together with
39 the [Prettier](https://prettier.io/) code formatter. This separation of workflows avoids hassling developers with
40 lint "errors" for frivolous issues like spaces and commas. Instead, those issues get fixed automatically whenever
41 you save or commit a file. Prettier also avoids frivolous debates: its defaults have already been debated
42 at length and adopted by a sizeable community. No need to reinvent the wheel!
43
44- **Explicit:** The ruleset does not import any "recommended" templates from other ESLint packages. This avoids
45 worrying about precedence issues due to import order. It also eliminates confusion caused by files
46 overriding/undoing settings from another file. Each rule is configured once, in one
47 [easy-to-read file](https://github.com/microsoft/rushstack/blob/main/stack/eslint-config/profile/_common.js).
48
49- **Minimal configuration:** To use this ruleset, your **.eslintrc.js** will need to choose one **"profile"**
50 and possibly one or two **"mixins"** that cover special cases. Beyond that, our goal is to reduce monorepo
51 maintenance by providing a small set of **.eslintrc.js** recipes that can be reused across many different projects.
52 (This sometimes means that rules will be included which have no effect for a particular project, however in practice
53 the installation/execution cost for unused rules turns out to be negligible.)
54
55
56## Getting started in 3 steps
57
58Applying the ruleset to your project is quick and easy. You install the package, then create an **.eslintrc.js** file
59and select an appropriate project profile. Optionally you can also add some "mixins" to enable additional rules.
60Let's walk through those three steps in more detail.
61
62### 1. Install the package
63
64To install the package, do this:
65
66```sh
67$ cd your-project-folder
68$ npm install --save-dev eslint
69$ npm install --save-dev typescript
70$ npm install --save-dev @rushstack/eslint-config
71```
72
73### 2. Choose one profile
74
75The ruleset currently supports three different "profile" strings, which select lint rules applicable for
76your project:
77
78- `@rushstack/eslint-config/profile/node` - This profile enables lint rules intended for a general Node.js project,
79 typically a web service. It enables security rules that assume the service could receive malicious inputs from an
80 untrusted user.
81
82- `@rushstack/eslint-config/profile/node-trusted-tool` - This profile enables lint rules intended for a Node.js project
83 whose inputs will always come from a developer or other trusted source. Most build system tasks are like this,
84 since they operate exclusively on files prepared by a developer. This profile disables certain security rules that
85 would otherwise prohibit APIs that could cause a denial-of-service by consuming too many resources, or which might
86 interact with the filesystem in unsafe ways. Such activities are safe and commonplace for a trusted tool.
87 **DO NOT use this profile for a library project that might also be loaded by a Node.js service.**
88
89- `@rushstack/eslint-config/profile/web-app` - This profile enables lint rules intended for a web application, for
90 example security rules that are relevant to web browser APIs such as DOM.
91 _Also use this profile if you are creating a library that can be consumed by both Node.js and web applications._
92
93After choosing a profile, create an **.eslintrc.js** config file that provides the NodeJS `__dirname` context
94for TypeScript. Add your profile string in the `extends` field, as shown below:
95
96**.eslintrc.js**
97```ts
98// This is a workaround for https://github.com/eslint/eslint/issues/3458
99require('@rushstack/eslint-config/patch/modern-module-resolution');
100
101module.exports = {
102 extends: [ "@rushstack/eslint-config/profile/node" ], // <---- put your profile string here
103 parserOptions: { tsconfigRootDir: __dirname }
104};
105```
106
107The `@rushstack/eslint-config` ruleset is intended to be used with the Prettier code formatter. For general
108instructions on setting that up, please refer to the [Prettier docs](https://prettier.io/docs/en/index.html).
109For Rush-specific settings, see the article
110[Rush: Enabling Prettier](https://rushjs.io/pages/maintainer/enabling_prettier/).
111
112
113### 3. Add any relevant mixins
114
115Optionally, you can add some "mixins" to your `extends` array to opt-in to some extra behaviors.
116
117Important: Your **.eslintrc.js** `"extends"` field must load mixins after the profile entry.
118
119
120#### `@rushstack/eslint-config/mixins/friendly-locals`
121
122Requires explicit type declarations for local variables.
123
124For the first 5 years of Rush, our lint rules required explicit types for most declarations
125such as function parameters, function return values, and exported variables. Although more verbose,
126declaring types (instead of relying on type inference) encourages engineers to create interfaces
127that inspire discussions about data structure design. It also makes source files easier
128to understand for code reviewers who may be unfamiliar with a particular project. Once developers get
129used to the extra work of declaring types, it turns out to be a surprisingly popular practice.
130
131However in 2020, to make adoption easier for existing projects, this rule was relaxed. Explicit
132type declarations are now optional for local variables (although still required in other contexts).
133See [GitHub #2206](https://github.com/microsoft/rushstack/issues/2206) for background.
134
135If you are onboarding a large existing code base, this new default will make adoption easier:
136
137Example source file without `mixins/friendly-locals`:
138```ts
139export class MyDataService {
140 . . .
141 public queryResult(provider: IProvider): IResult {
142 // Type inference is concise, but what are "item", "index", and "data"?
143 const item = provider.getItem(provider.title);
144 const index = item.fetchIndex();
145 const data = index.get(provider.state);
146 return data.results.filter(x => x.title === provider.title);
147 }
148}
149```
150
151On the other hand, if your priority is make source files more friendly for other people to read, you can enable
152the `"@rushstack/eslint-config/mixins/friendly-locals"` mixin. This restores the requirement that local variables
153should have explicit type declarations.
154
155Example source file with `mixins/friendly-locals`:
156```ts
157export class MyDataService {
158 . . .
159 public queryResult(provider: IProvider): IResult {
160 // This is more work for the person writing the code... but definitely easier to understand
161 // for a code reviewer if they are unfamiliar with your project
162 const item: ISalesReport = provider.getItem(provider.title);
163 const index: Map<string, IGeographicData> = item.fetchIndex();
164 const data: IGeographicData | undefined = index.get(provider.state);
165 return data.results.filter(x => x.title === provider.title);
166 }
167}
168```
169
170Add the mixin to your `"extends"` field like this:
171
172**.eslintrc.js**
173```ts
174// This is a workaround for https://github.com/eslint/eslint/issues/3458
175require('@rushstack/eslint-config/patch/modern-module-resolution');
176
177module.exports = {
178 extends: [
179 "@rushstack/eslint-config/profile/node",
180 "@rushstack/eslint-config/mixins/friendly-locals" // <----
181 ],
182 parserOptions: { tsconfigRootDir: __dirname }
183};
184```
185
186
187#### `@rushstack/eslint-config/mixins/packlets`
188
189Packlets provide a lightweight alternative to NPM packages for organizing source files within a single project.
190This system is described in the [@rushstack/eslint-plugin-packlets](https://www.npmjs.com/package/@rushstack/eslint-plugin-packlets)
191documentation.
192
193To use packlets, add the mixin to your `"extends"` field like this:
194
195**.eslintrc.js**
196```ts
197// This is a workaround for https://github.com/eslint/eslint/issues/3458
198require('@rushstack/eslint-config/patch/modern-module-resolution');
199
200module.exports = {
201 extends: [
202 "@rushstack/eslint-config/profile/node",
203 "@rushstack/eslint-config/mixins/packlets" // <----
204 ],
205 parserOptions: { tsconfigRootDir: __dirname }
206};
207```
208
209
210#### `@rushstack/eslint-config/mixins/tsdoc`
211
212If your project is using [API Extractor](https://api-extractor.com/) or another tool that uses
213the [TSDoc](https://github.com/Microsoft/tsdoc) standard for doc comments, it's recommended to use the
214`"@rushstack/eslint-config/mixins/tsdoc"` mixin. It will enable
215[eslint-plugin-tsdoc](https://www.npmjs.com/package/eslint-plugin-tsdoc) validation for TypeScript doc comments.
216
217Add the mixin to your `"extends"` field like this:
218
219**.eslintrc.js**
220```ts
221// This is a workaround for https://github.com/eslint/eslint/issues/3458
222require('@rushstack/eslint-config/patch/modern-module-resolution');
223
224module.exports = {
225 extends: [
226 "@rushstack/eslint-config/profile/node",
227 "@rushstack/eslint-config/mixins/tsdoc" // <----
228 ],
229 parserOptions: { tsconfigRootDir: __dirname }
230};
231```
232
233
234#### `@rushstack/eslint-config/mixins/react`
235
236For projects using the [React](https://reactjs.org/) library, the `"@rushstack/eslint-config/mixins/react"` mixin
237enables some recommended additional rules. These rules are selected via a mixin because they require you to:
238
239- Add `"jsx": "react"` to your **tsconfig.json**
240- Configure your `settings.react.version` as shown below. This determines which React APIs will be considered
241 to be deprecated. (If you omit this, the React version will be detected automatically by
242 [loading the entire React library](https://github.com/yannickcr/eslint-plugin-react/blob/4da74518bd78f11c9c6875a159ffbae7d26be693/lib/util/version.js#L23)
243 into the linter's process, which is costly.)
244
245Add the mixin to your `"extends"` field like this:
246
247**.eslintrc.js**
248```ts
249// This is a workaround for https://github.com/eslint/eslint/issues/3458
250require('@rushstack/eslint-config/patch/modern-module-resolution');
251
252module.exports = {
253 extends: [
254 "@rushstack/eslint-config/profile/web-app",
255 "@rushstack/eslint-config/mixins/react" // <----
256 ],
257 parserOptions: { tsconfigRootDir: __dirname },
258
259 settings: {
260 react: {
261 "version": "16.9" // <----
262 }
263 }
264};
265```
266
267
268## Links
269
270- [CHANGELOG.md](
271 https://github.com/microsoft/rushstack/blob/main/stack/eslint-config/CHANGELOG.md) - Find
272 out what's new in the latest version
273
274`@rushstack/eslint-config` is part of the [Rush Stack](https://rushstack.io/) family of projects.