UNPKG

17.8 kBMarkdownView Raw
1# eslint-config-peerigon
2
3**[Peerigon](https://peerigon.com/) coding rules as [ESLint](http://eslint.org/) config.**
4
5[![](https://img.shields.io/npm/v/eslint-config-peerigon.svg)](https://www.npmjs.com/package/eslint-config-peerigon)
6[![](https://img.shields.io/npm/dm/eslint-config-peerigon.svg)](https://www.npmjs.com/package/eslint-config-peerigon)
7[![Dependency Status](https://david-dm.org/peerigon/eslint-config-peerigon.svg)](https://david-dm.org/peerigon/eslint-config-peerigon?branch=master)
8[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
9
10## Motivation
11
12Linting and formatting rules are always a balance between
13
14- ease of reading
15- ease of refactoring
16- ease of writing.
17
18We think that
19
20- code is read more often than refactored
21- and refactored more often than written from scratch.
22
23Our linting rules have been designed with these assumptions in mind.
24
25## Features
26
27### Atomic changes
28
29Our formatting rules have been chosen carefully so that a change of a file is as atomic as possible. This makes it easier to review pull requests because there are no meaningless changes anymore.
30
31**Example:** I want to change a variable from `let` to `const`.
32
33```diff
34// Bad coding style because useless whitespace changes were necessary
35-let a = 1,
36+let a = 1,
37- bbb = 2,
38+ cc = 3;
39- cc = 3;
40+const bbb = 3;
41```
42
43```diff
44// Good coding style because only the relevant parts need to be changed
45let a = 1;
46-let bb = 2;
47+const bb = 2;
48let ccc = 3;
49```
50
51This is also the reason why we prefer [dangling commas](https://eslint.org/docs/rules/comma-dangle) for multiline arrays, objects and arguments although they look very unfamiliar on first sight (see [discussion](https://github.com/peerigon/eslint-config-peerigon/issues/10)).
52
53### Consistent formatting
54
55For the purpose of atomic changes, our rules are intentionally strict about formatting which are usually autofixable. You should use an editor configuration where you can apply these autofixes on demand (for instance when saving the file).
56
57We recommend combining these linting rules with [Prettier](https://prettier.io/) (see [below](#prettier)). There's also a [recommended configuration for VSCode](#vscode).
58
59### Code smells as a warning
60
61Developers take shortcuts. And that's ok because at the end of the day we have to deliver software within fixed time frames and budgets. Sometimes it's also because we don't know of a better alternative. We call these shortcuts "code smells" and our linting rules will complain about them with a warning.
62
63This means that this code is potentially problematic, but you don't have to fix it right away. You should keep the warning and come back later to refactor it (e.g. during a refactoring sprint). The amount of warnings is also a good indicator for technical debt.
64
65If you think that there is a good reason for deviating from the standard path, disable the warning and put an explanation above that comment why it's ok to disable the rule in that case, like:
66
67```js
68// The API returns snakecase properties
69// eslint-disable-next-line babel/camelcase
70function fetchUsers() {
71 // ...
72}
73```
74
75### Disabling rules
76
77Try to disable as less rules as possible. In most cases it's best to just write
78
79```js
80// eslint-disable-next-line [rule-code]
81```
82
83where `[rule-code]` is the code that is displayed along the error message. Disabling the next line is usually better because it resists [Prettier](https://prettier.io/) reformatting.
84
85Sometimes it makes sense to disable a rule within a specifc file. In that case you can put the following snippet at the beginning of the file:
86
87```js
88/* eslint-disable [rule-code] */
89```
90
91If you don't agree with a rule, please do not just disable the rule. Often there are good reasons and the current setting is the result of years of experience. It's better to create an issue here to start a discussion about the pros and cons of a rule.
92
93### Different styles
94
95We acknowledge that there are certain rules where there are no actual pros and cons or where there is no clear winner. You just have to decide for one style and stick with it. We also know that some rules make sense in one project, but don't make sense in another project. That's why we also provide a list of [accepted custom styles](#styles) (see also [this discussion](https://github.com/peerigon/eslint-config-peerigon/issues/11)) which you can pick.
96
97### Naming conventions for properties
98
99Sometimes we're not in full control over the naming conventions in our codebase, for instance if data is coming from a foreign API. While it often is preferable to transform property names into camelCase, it might not be practical. In these situations you can disable the check for properties like this:
100
101```js
102const options = require("eslint-config-peerigon/options.js");
103
104module.exports = {
105 /* ... */
106 rules: {
107 // The API uses snake_case as properties
108 "babel/camelcase": ["warn", {
109 ...options["camelcase"],
110 properties: "never"
111 }]
112 },
113};
114```
115
116**In TypeScript projects:**
117
118```js
119const options = require("eslint-config-peerigon/options.js");
120
121module.exports = {
122 /* ... */
123 rules: {
124 // The API uses snake_case as properties
125 "@typescript-eslint/naming-convention": [
126 "warn",
127 options["@typescript-eslint/naming-convention"].ignoreProperties,
128 ...options["@typescript-eslint/naming-convention"].defaultRules,
129 ],
130 },
131};
132```
133
134## Provided configs
135
136### [`peerigon`](base.js)
137
138**Base rules for every project. You should always add these rules.**
139
140```
141npm i eslint eslint-config-peerigon --save-dev
142```
143
144These rules assume a modern project with full ES2015 support, including ES modules. For specific environments like Node.js or old JS engines, see below. The base rules do not define an `env`, so you might want to do that for yourself to enable specific globals.
145
146Add an `.eslintrc.json` to the project's root folder:
147
148```js
149{
150 "extends": [
151 // Base rules for every project
152 "peerigon",
153 "prettier" // add this at the end of 'extends' if you're using Prettier
154 ],
155 // Do not search for further eslint configs in upper directories
156 "root": true
157}
158```
159
160In your `package.json`, add a `test:lint` script and run it as `posttest`:
161
162```js
163{
164 "scripts": {
165 "test:lint": "eslint --cache ./src ./test",
166 "posttest": "npm run test:lint"
167 }
168}
169```
170
171The base rules use the `eslint-plugin-import` to resolve imports. Although it's possible to define [custom resolvers](https://github.com/benmosher/eslint-plugin-import#resolvers), it's highly discouraged to deviate from the common Node/webpack resolving algorithm. Other tools like linters and intellisense don't work reliably when you change the resolver.
172
173### [`peerigon/node`](node.js)
174
175Special rules for Node.js >= 8.0.0 environments:
176
177```js
178{
179 "extends": [
180 // Base rules with full ES2015 support
181 "peerigon",
182 // Rules for node
183 "peerigon/node",
184 "prettier" // add this if you're using Prettier
185 ]
186 // Setting env.node = true is not necessary, this is already done by peerigon/node
187}
188```
189
190These rules assume that you're using CommonJS modules. In case you're using ECMAScript modules, you should set [`parserOptions.sourceType: "module"`](https://eslint.org/docs/user-guide/configuring#specifying-parser-options). We will change that once a LTS Node.js version has official support for ECMAScript modules.
191
192### [`peerigon/react`](react.js)
193
194**Important: Requires [`eslint-plugin-react`](https://github.com/yannickcr/eslint-plugin-react), [`eslint-plugin-jsx-a11y`](https://github.com/evcohen/eslint-plugin-jsx-a11y) and [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks) as project dependency.**
195
196```
197npm i eslint-plugin-react eslint-plugin-jsx-a11y eslint-plugin-react-hooks --save-dev
198```
199
200Rules for [React](https://facebook.github.io/react/) development, including accessibility rules.
201These rules are also applicable in other JSX environments, like [Preact](https://github.com/developit/preact):
202
203```js
204{
205 "extends": [
206 "peerigon",
207 "peerigon/react",
208 "prettier", // add this and...
209 "prettier/react" // ...this if you're using Prettier
210 ],
211 "root": true
212}
213```
214
215*We recommend using [`peerigon/styles/react-jsx-no-literals`](#peerigonstylesreact-jsx-no-literals) if you're using i18n in your project.*
216*You can use [`peerigon/styles/react-jsx-no-bind`](#peerigonstylesreact-jsx-no-bind) if you're using `memo` and `shouldComponentUpdate` a lot.*
217
218### [`peerigon/typescript`](typescript.js)
219
220**Important: Requires [`@typescript-eslint/eslint-plugin`](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin) and [`@typescript-eslint/parser`](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser) as project dependency.**
221
222```
223npm i @typescript-eslint/eslint-plugin @typescript-eslint/parser --save-dev
224```
225
226Rules for [TypeScript](https://www.typescriptlang.org/).
227
228**⚠️ Attention:** These rules require your `tsconfig.json`. Specify the path in `parserOptions.project` (see also [here](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/README.md#parseroptionsproject) for more information). *The path should be relative to the folder where `eslint` is executed.*
229
230```js
231{
232 "extends": [
233 "peerigon",
234 "peerigon/typescript",
235 // Arrow functions are preferred with TypeScript
236 // See https://github.com/peerigon/eslint-config-peerigon/issues/23#issuecomment-472614432
237 "peerigon/styles/prefer-arrow",
238 "prettier", // add this and...
239 "prettier/@typescript-eslint" // ...this if you're using Prettier
240 ],
241 "parserOptions": {
242 // Relative to the folder where eslint is executed
243 // See https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/README.md#parseroptionsproject
244 "project": "./tsconfig.json"
245 },
246 "root": true,
247}
248```
249
250You need to add `--ext js,ts,tsx` to the `test:lint` script:
251
252```js
253{
254 "scripts": {
255 "test:lint": "eslint --cache --ext js,jsx,ts,tsx ./src ./test"
256 }
257}
258```
259
260*We recommend using [`peerigon/styles/prefer-arrow`](#peerigonstylesprefer-arrow) because arrow functions (or function expressions in general) can leverage [TypeScript's contextual typing](https://www.typescriptlang.org/docs/handbook/type-inference.html#contextual-typing).*
261
262Do you see an error that looks like this?
263
264```
265Parsing error: "parserOptions.project" has been set for @typescript-eslint/parser.
266The file does not match your project config: ...
267The file must be included in at least one of the projects provided
268```
269
270This is a sign that ESLint is trying to lint a file that is not included by your `tsconfig.json`. You need to adjust either `parserOptions.project` or `include` of the referenced `tsconfig.json`.
271
272### [`peerigon/flowtype`](flowtype.js)
273
274**Important: Requires [`babel-eslint`](https://github.com/babel/babel-eslint) and [`eslint-plugin-flowtype`](https://github.com/gajus/eslint-plugin-flowtype) as project dependency.**
275
276```
277npm i babel-eslint eslint-plugin-flowtype --save-dev
278```
279
280Rules for [Flowtype](https://flowtype.org/).
281
282```js
283{
284 "extends": [
285 "peerigon",
286 "peerigon/flowtype",
287 "prettier", // add this and...
288 "prettier/flowtype" // ...this if you're using Prettier
289 ],
290 "root": true
291}
292```
293
294### [`peerigon/es5`](es5.js)
295
296Special rules for older projects:
297
298```js
299{
300 "extends": [
301 // Base rules with full ES2015 support
302 "peerigon",
303 // Legacy rules for older projects
304 "peerigon/es5"
305 ],
306 "root": true
307}
308```
309
310## Styles
311
312The following rules enable specific writing styles. Use them as you prefer.
313
314### [`peerigon/styles/prefer-arrow`](styles/prefer-arrow.js)
315
316**Important: Requires [`eslint-plugin-prefer-arrow`](https://github.com/TristonJ/eslint-plugin-prefer-arrow) as project dependency.**
317
318```
319npm i eslint-plugin-prefer-arrow --save-dev
320```
321
322Enforces arrow function expressions instead of function declarations (see [#23](https://github.com/peerigon/eslint-config-peerigon/issues/23)).
323Regular functions are still allowed as methods in objects or classes.
324
325```js
326 "extends": [
327 "peerigon",
328 "peerigon/styles/prefer-arrow"
329 ],
330```
331
332### [`peerigon/styles/no-default-export`](styles/no-default-export.js)
333
334Forbids usage of `export default`. When using default exports, it becomes harder to name classes or functions consistently throughout the codebase since every module can pick its own name for the imported thing. Nicholas C. Zakas, the creator of ESLint, wrote [an article with more compelling arguments why he stopped using `export default`](https://humanwhocodes.com/blog/2019/01/stop-using-default-exports-javascript-module/).
335
336```js
337 "extends": [
338 "peerigon",
339 "peerigon/styles/no-default-export"
340 ],
341```
342
343**Please note:** This rule is disabled in `.jsx` and `.tsx` files because React components are usually exported via `export default`. [`React.lazy`](https://reactjs.org/docs/code-splitting.html#reactlazy) even expects the lazy loaded component to be exported as `default`.
344
345### [`peerigon/styles/no-null`](styles/no-null.js)
346
347**Important: Requires [`eslint-plugin-no-null`](https://github.com/nene/eslint-plugin-no-null) as project dependency.**
348
349```
350npm i eslint-plugin-no-null --save-dev
351```
352
353Forbids the usage of `null`. In a codebase it's often better to use a single non-value to represent *the absence of a value*. With the rise of default parameters and destructuring defaults, JavaScript developed a clear tendency towards `undefined`. [This issue](https://github.com/peerigon/eslint-config-peerigon/issues/71) summarizes the arguments (and trade-offs) of **null vs. undefined**.
354
355```js
356 "extends": [
357 "peerigon",
358 "peerigon/styles/no-null"
359 ],
360```
361
362**Please note:** If you use this rule, you will probably still need a single `null` value which you can refer to whenever you need to use `null` because of third-party code:
363
364```js
365// eslint-disable-next-line no-null/no-null
366export const NULL = null;
367```
368
369### [`peerigon/styles/prefer-interface`](styles/prefer-interface.js)
370
371**Important: Use it in combination with [`peerigon/typescript`](typescript.js).**
372
373[Prefer `interface` over `type`](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-interface.md).
374
375```js
376 "extends": [
377 "peerigon",
378 "peerigon/typescript",
379 "peerigon/styles/prefer-interface"
380 ],
381```
382
383### [`peerigon/styles/react-jsx-no-bind`](styles/react-jsx-no-bind.js)
384
385**Important: Use it in combination with [`peerigon/react`](react.js).**
386
387Depending on the way you write your components, it might be not ok to create functions during `render()`. Use it if you're using things like `React.memo()` or `shouldComponentUpdate` a lot.
388
389```js
390 "extends": [
391 "peerigon",
392 "peerigon/react",
393 "peerigon/styles/react-jsx-no-bind"
394 ],
395```
396
397### [`peerigon/styles/react-jsx-no-literals`](styles/react-jsx-no-literals.js)
398
399**Important: Use it in combination with [`peerigon/react`](react.js).**
400
401Use this style if you're using i18n. It prevents people from putting raw strings in components.
402
403```js
404 "extends": [
405 "peerigon",
406 "peerigon/react",
407 "peerigon/styles/react-jsx-no-literals"
408 ],
409```
410
411It disallows this:
412
413```jsx
414const Hello = <div>test</div>;
415```
416
417As an escape hatch, this is still allowed:
418
419```jsx
420const Hello = <div>{'test'}</div>;
421```
422
423### [`peerigon/styles/prefer-array-shorthand`](styles/prefer-array-shorthand.js)
424
425**Important: Use it in combination with [`peerigon/typescript`](typescript.js).**
426
427Enforces typescript arrays to use the shorthand array-style instead of the generic style.
428
429```js
430 "extends": [
431 "peerigon",
432 "peerigon/typescript",
433 "peerigon/styles/prefer-array-shorthand"
434 ],
435```
436
437It enforces this:
438
439```ts
440const foo: string[] = [];
441```
442
443instead of
444
445```ts
446const foo: Array<string> = [];
447```
448
449## Prettier
450
451There is a [Prettier](https://prettier.io/) config in this repository that corresponds to our linting rules as much as possible. Add a `.prettierrc` file to your repository with the following content:
452
453```js
454"eslint-config-peerigon/prettier"
455```
456
457In order to avoid conflicts between Prettier and our rules, you should always add **prettier rules at the end of `extends`**. For example, in a TypeScript + React project you would use the following configuration:
458
459```js
460{
461 "extends": [
462 "peerigon",
463 "peerigon/typescript",
464 "peerigon/styles/prefer-arrow",
465 "peerigon/react",
466 // prettier must be at the end
467 "prettier",
468 "prettier/@typescript-eslint",
469 "prettier/react"
470 ],
471 "root": true,
472};
473```
474
475This module already lists [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) as dependency which is why you don't have to install it manually.
476
477## VSCode
478
479This is our recommended VSCode configuration using the [Prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode). Adjust it to the needs of your particular project:
480
481```json
482{
483 "editor.defaultFormatter": "esbenp.prettier-vscode",
484 "editor.formatOnSave": true,
485 "editor.codeActionsOnSave": {
486 "source.fixAll.eslint": true
487 }
488}
489```
490
491## License
492
493Unlicense
494
495## Sponsors
496
497[<img src="https://assets.peerigon.com/peerigon/logo/peerigon-logo-flat-spinat.png" width="150" />](https://peerigon.com)