1 | # @rushstack/eslint-config
|
2 |
|
3 | A TypeScript ESLint ruleset designed for large teams and projects.
|
4 |
|
5 | ## Philosophy
|
6 |
|
7 | When you work in a small repo, you spend most of your time writing code. You know what each file does. You want lint
|
8 | rules that keep things concise and won't slow you down. That's the situation for the 99% of open source projects
|
9 | that shape popular coding conventions.
|
10 |
|
11 | But as your organization scales up, things may change. People come and go. Projects frequently get handed off between
|
12 | teams. Every day, you find yourself working with files that you've never seen before, created by strangers whom
|
13 | you may never meet. It's annoying to constantly come across inconsistent styles. It can be frustrating to decipher
|
14 | expressions that seem to require a TypeScript Ph.D. -- especially for newcomers and junior contributors. When
|
15 | refactoring in bulk, you may edit lots of files without reading them very carefully. In short, the linting needs
|
16 | reflect 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,
|
21 | there's a risk of fragmentation, duplication of efforts, and costly rewrites. (Enabling people to churn out
|
22 | lots of code really fast is still a goal of course; just not the #1 priority.)
|
23 |
|
24 | Welcome to the world of [Rush Stack](https://rushstack.io/)! The `@rushstack/eslint-config` package was specifically
|
25 | designed 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 |
|
58 | Applying the ruleset to your project is quick and easy. You install the package, then create an **.eslintrc.js** file
|
59 | and select an appropriate project profile. Optionally you can also add some "mixins" to enable additional rules.
|
60 | Let's walk through those three steps in more detail.
|
61 |
|
62 | ### 1. Install the package
|
63 |
|
64 | To 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 |
|
75 | The ruleset currently supports three different "profile" strings, which select lint rules applicable for
|
76 | your 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 |
|
93 | After choosing a profile, create an **.eslintrc.js** config file that provides the NodeJS `__dirname` context
|
94 | for 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
|
99 | require('@rushstack/eslint-config/patch/modern-module-resolution');
|
100 |
|
101 | module.exports = {
|
102 | extends: [ "@rushstack/eslint-config/profile/node" ], // <---- put your profile string here
|
103 | parserOptions: { tsconfigRootDir: __dirname }
|
104 | };
|
105 | ```
|
106 |
|
107 | The `@rushstack/eslint-config` ruleset is intended to be used with the Prettier code formatter. For general
|
108 | instructions on setting that up, please refer to the [Prettier docs](https://prettier.io/docs/en/index.html).
|
109 | For 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 |
|
115 | Optionally, you can add some "mixins" to your `extends` array to opt-in to some extra behaviors.
|
116 |
|
117 | Important: Your **.eslintrc.js** `"extends"` field must load mixins after the profile entry.
|
118 |
|
119 |
|
120 | #### `@rushstack/eslint-config/mixins/friendly-locals`
|
121 |
|
122 | Requires explicit type declarations for local variables.
|
123 |
|
124 | For the first 5 years of Rush, our lint rules required explicit types for most declarations
|
125 | such as function parameters, function return values, and exported variables. Although more verbose,
|
126 | declaring types (instead of relying on type inference) encourages engineers to create interfaces
|
127 | that inspire discussions about data structure design. It also makes source files easier
|
128 | to understand for code reviewers who may be unfamiliar with a particular project. Once developers get
|
129 | used to the extra work of declaring types, it turns out to be a surprisingly popular practice.
|
130 |
|
131 | However in 2020, to make adoption easier for existing projects, this rule was relaxed. Explicit
|
132 | type declarations are now optional for local variables (although still required in other contexts).
|
133 | See [GitHub #2206](https://github.com/microsoft/rushstack/issues/2206) for background.
|
134 |
|
135 | If you are onboarding a large existing code base, this new default will make adoption easier:
|
136 |
|
137 | Example source file without `mixins/friendly-locals`:
|
138 | ```ts
|
139 | export 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 |
|
151 | On the other hand, if your priority is make source files more friendly for other people to read, you can enable
|
152 | the `"@rushstack/eslint-config/mixins/friendly-locals"` mixin. This restores the requirement that local variables
|
153 | should have explicit type declarations.
|
154 |
|
155 | Example source file with `mixins/friendly-locals`:
|
156 | ```ts
|
157 | export 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 |
|
170 | Add 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
|
175 | require('@rushstack/eslint-config/patch/modern-module-resolution');
|
176 |
|
177 | module.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 |
|
189 | Packlets provide a lightweight alternative to NPM packages for organizing source files within a single project.
|
190 | This system is described in the [@rushstack/eslint-plugin-packlets](https://www.npmjs.com/package/@rushstack/eslint-plugin-packlets)
|
191 | documentation.
|
192 |
|
193 | To 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
|
198 | require('@rushstack/eslint-config/patch/modern-module-resolution');
|
199 |
|
200 | module.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 |
|
212 | If your project is using [API Extractor](https://api-extractor.com/) or another tool that uses
|
213 | the [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 |
|
217 | Add 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
|
222 | require('@rushstack/eslint-config/patch/modern-module-resolution');
|
223 |
|
224 | module.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 |
|
236 | For projects using the [React](https://reactjs.org/) library, the `"@rushstack/eslint-config/mixins/react"` mixin
|
237 | enables 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 |
|
245 | Add 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
|
250 | require('@rushstack/eslint-config/patch/modern-module-resolution');
|
251 |
|
252 | module.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.
|