@codelaw/rules-js 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/NOTICE +10 -0
- package/README.md +57 -2
- package/codelaw-pack.toml +6 -0
- package/corpus.toml +76 -0
- package/data/globals/browser.json +1209 -0
- package/data/globals/commonjs.json +6 -0
- package/data/globals/jest.json +15 -0
- package/data/globals/mocha.json +22 -0
- package/data/globals/node.json +83 -0
- package/data/globals/serviceworker.json +328 -0
- package/data/globals/shared-node-browser.json +73 -0
- package/data/globals/vitest.json +19 -0
- package/data/globals/worker.json +349 -0
- package/package.json +14 -4
- package/presets/js/conventions.toml +6 -0
- package/presets/js/project.toml +4 -0
- package/presets/js/recommended.toml +67 -0
- package/presets/js/typescript.toml +7 -0
- package/rules/graph/no-unused-exports.md +135 -0
- package/rules/js/constructor-super.md +77 -0
- package/rules/js/exports-at-end.md +134 -0
- package/rules/js/for-direction.md +106 -0
- package/rules/js/getter-return.md +74 -0
- package/rules/js/import-groups.md +117 -0
- package/rules/js/no-async-promise-executor.md +95 -0
- package/rules/js/no-case-declarations.md +134 -0
- package/rules/js/no-class-assign.md +61 -0
- package/rules/js/no-compare-neg-zero.md +105 -0
- package/rules/js/no-cond-assign.md +135 -0
- package/rules/js/no-const-assign.md +61 -0
- package/rules/js/no-constant-binary-expression.md +83 -0
- package/rules/js/no-constant-condition.md +150 -0
- package/rules/js/no-control-regex.md +84 -0
- package/rules/js/no-debugger.md +99 -0
- package/rules/js/no-default-export.md +88 -0
- package/rules/js/no-delete-var.md +93 -0
- package/rules/js/no-dupe-args.md +61 -0
- package/rules/js/no-dupe-class-members.md +82 -0
- package/rules/js/no-dupe-else-if.md +113 -0
- package/rules/js/no-dupe-keys.md +66 -0
- package/rules/js/no-duplicate-case.md +137 -0
- package/rules/js/no-empty-character-class.md +98 -0
- package/rules/js/no-empty-pattern.md +86 -0
- package/rules/js/no-empty-static-block.md +99 -0
- package/rules/js/no-empty.md +116 -0
- package/rules/js/no-ex-assign.md +121 -0
- package/rules/js/no-extra-boolean-cast.md +107 -0
- package/rules/js/no-fallthrough.md +178 -0
- package/rules/js/no-func-assign.md +62 -0
- package/rules/js/no-global-assign.md +85 -0
- package/rules/js/no-import-assign.md +60 -0
- package/rules/js/no-invalid-regexp.md +86 -0
- package/rules/js/no-irregular-whitespace.md +87 -0
- package/rules/js/no-loss-of-precision.md +82 -0
- package/rules/js/no-misleading-character-class.md +83 -0
- package/rules/js/no-new-native-nonconstructor.md +55 -0
- package/rules/js/no-nonoctal-decimal-escape.md +84 -0
- package/rules/js/no-obj-calls.md +61 -0
- package/rules/js/no-octal.md +84 -0
- package/rules/js/no-prototype-builtins.md +99 -0
- package/rules/js/no-redeclare.md +61 -0
- package/rules/js/no-regex-spaces.md +75 -0
- package/rules/js/no-self-assign.md +91 -0
- package/rules/js/no-setter-return.md +69 -0
- package/rules/js/no-shadow-restricted-names.md +102 -0
- package/rules/js/no-sparse-arrays.md +82 -0
- package/rules/js/no-this-before-super.md +74 -0
- package/rules/js/no-unassigned-vars.md +102 -0
- package/rules/js/no-undef.md +76 -0
- package/rules/js/no-unexpected-multiline.md +91 -0
- package/rules/js/no-unreachable.md +59 -0
- package/rules/js/no-unsafe-finally.md +145 -0
- package/rules/js/no-unsafe-negation.md +71 -0
- package/rules/js/no-unsafe-optional-chaining.md +83 -0
- package/rules/js/no-unused-labels.md +102 -0
- package/rules/js/no-unused-private-class-members.md +126 -0
- package/rules/js/no-unused-vars.md +69 -0
- package/rules/js/no-useless-assignment.md +115 -0
- package/rules/js/no-useless-backreference.md +83 -0
- package/rules/js/no-useless-catch.md +105 -0
- package/rules/js/no-useless-escape.md +81 -0
- package/rules/js/no-var.md +76 -0
- package/rules/js/no-with.md +60 -0
- package/rules/js/prefer-const.md +83 -0
- package/rules/js/prefer-rest-params.md +103 -0
- package/rules/js/prefer-spread.md +95 -0
- package/rules/js/preserve-caught-error.md +122 -0
- package/rules/js/require-yield.md +107 -0
- package/rules/js/use-isnan.md +105 -0
- package/rules/js/valid-typeof.md +111 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: js/no-this-before-super
|
|
3
|
+
title: "this is not used before super() in a derived constructor"
|
|
4
|
+
languages: [javascript, jsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "moving statements across `super()` can change what the base class sees"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint/no-this-before-super]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
In the constructor of a class that extends another, `this` and `super.x` are used only after
|
|
13
|
+
`super()` has been called.
|
|
14
|
+
|
|
15
|
+
**Why:** Before `super()` runs, `this` is not initialised and touching it throws a ReferenceError.
|
|
16
|
+
|
|
17
|
+
```codelaw
|
|
18
|
+
native: js/no-this-before-super
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## How to fix
|
|
22
|
+
|
|
23
|
+
1. Move the `super(...)` call above the first use of `this`.
|
|
24
|
+
2. If the base class needs a value computed from `this`, compute it into a local first.
|
|
25
|
+
|
|
26
|
+
## Examples
|
|
27
|
+
|
|
28
|
+
### JavaScript
|
|
29
|
+
|
|
30
|
+
#### Don't
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
class Cat extends Animal {
|
|
34
|
+
constructor() {
|
|
35
|
+
this.lives = 9;
|
|
36
|
+
super();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
#### Do
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
class Cat extends Animal {
|
|
45
|
+
constructor() {
|
|
46
|
+
super();
|
|
47
|
+
this.lives = 9;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### JSX
|
|
53
|
+
|
|
54
|
+
#### Don't
|
|
55
|
+
|
|
56
|
+
```jsx
|
|
57
|
+
class Cat extends Animal {
|
|
58
|
+
constructor() {
|
|
59
|
+
this.lives = 9;
|
|
60
|
+
super();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Do
|
|
66
|
+
|
|
67
|
+
```jsx
|
|
68
|
+
class Cat extends Animal {
|
|
69
|
+
constructor() {
|
|
70
|
+
super();
|
|
71
|
+
this.lives = 9;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: js/no-unassigned-vars
|
|
3
|
+
title: "A variable that is read is also assigned"
|
|
4
|
+
languages: [javascript, jsx, typescript, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the value the variable should hold is the author's"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint/no-unassigned-vars]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A variable declared without a value is assigned somewhere before it's read.
|
|
13
|
+
|
|
14
|
+
**Why:** A variable that is read but never assigned always holds `undefined`, which is a bug or dead
|
|
15
|
+
code.
|
|
16
|
+
|
|
17
|
+
```codelaw
|
|
18
|
+
match:
|
|
19
|
+
kind: variable_declarator
|
|
20
|
+
has: { field: name, kind: identifier, pattern: $N }
|
|
21
|
+
inside:
|
|
22
|
+
stopBy: neighbor
|
|
23
|
+
any: [{ kind: lexical_declaration }, { kind: variable_declaration }]
|
|
24
|
+
not:
|
|
25
|
+
any:
|
|
26
|
+
- has: { field: value, pattern: $VALUE }
|
|
27
|
+
- inside: { kind: ambient_declaration, stopBy: end }
|
|
28
|
+
where:
|
|
29
|
+
$N: [{ not: reassigned }, { not: unused }]
|
|
30
|
+
message: "`$N` is read but never assigned, so it's always undefined"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## How to fix
|
|
34
|
+
|
|
35
|
+
1. Assign the value the variable should hold.
|
|
36
|
+
2. If it's meant to be `undefined`, use `undefined` directly.
|
|
37
|
+
|
|
38
|
+
## Examples
|
|
39
|
+
|
|
40
|
+
### JavaScript
|
|
41
|
+
|
|
42
|
+
#### Don't
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
let status;
|
|
46
|
+
report(status);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
#### Do
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
let status = "ok";
|
|
53
|
+
report(status);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### JSX
|
|
57
|
+
|
|
58
|
+
#### Don't
|
|
59
|
+
|
|
60
|
+
```jsx
|
|
61
|
+
let status;
|
|
62
|
+
report(status);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Do
|
|
66
|
+
|
|
67
|
+
```jsx
|
|
68
|
+
let status = "ok";
|
|
69
|
+
report(status);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### TypeScript
|
|
73
|
+
|
|
74
|
+
#### Don't
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
let status;
|
|
78
|
+
report(status);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### Do
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
let status = "ok";
|
|
85
|
+
report(status);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### TSX
|
|
89
|
+
|
|
90
|
+
#### Don't
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
let status;
|
|
94
|
+
report(status);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### Do
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
let status = "ok";
|
|
101
|
+
report(status);
|
|
102
|
+
```
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: js/no-undef
|
|
3
|
+
title: "No references to undeclared names"
|
|
4
|
+
languages: [javascript, jsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the right declaration — an import, a local, or an environment — is the author's call"
|
|
7
|
+
stability: preview
|
|
8
|
+
options:
|
|
9
|
+
env: { type: "string[]", default: ["shared-node-browser"], description: "environments whose globals are declared: browser, node, commonjs, worker, serviceworker, shared-node-browser, jest, mocha, vitest" }
|
|
10
|
+
globals: { type: "string[]", default: [], description: "more names to treat as declared" }
|
|
11
|
+
typeof: { type: "boolean", default: false, description: "also report names used only as `typeof x`" }
|
|
12
|
+
ported_from: [eslint/no-undef]
|
|
13
|
+
since: 0.1.0
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
Every name the code reads or writes is declared in the file, built into JavaScript, or a global of
|
|
17
|
+
an environment the rule's `env` option names. As in ESLint, a `/* global a, b:writable */` comment
|
|
18
|
+
declares names for that file (`name:off` declares nothing), and a `/* eslint-env node */` comment
|
|
19
|
+
adds environments.
|
|
20
|
+
|
|
21
|
+
**Why:** An undeclared name is usually a typo or a missing import, and throws a ReferenceError when
|
|
22
|
+
that line runs.
|
|
23
|
+
|
|
24
|
+
```codelaw
|
|
25
|
+
native: js/no-undef
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## How to fix
|
|
29
|
+
|
|
30
|
+
1. If it's a typo, correct the name.
|
|
31
|
+
2. If it comes from another module, import it.
|
|
32
|
+
3. If it's a global of the runtime (browser, Node), add that environment to the rule's `env` option.
|
|
33
|
+
|
|
34
|
+
## Examples
|
|
35
|
+
|
|
36
|
+
### JavaScript
|
|
37
|
+
|
|
38
|
+
#### Don't
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
const total = prices.reduce(add, 0);
|
|
42
|
+
function add(a, b) {
|
|
43
|
+
return a + b;
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
#### Do
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
const prices = [1, 2];
|
|
51
|
+
const total = prices.reduce(add, 0);
|
|
52
|
+
function add(a, b) {
|
|
53
|
+
return a + b;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### JSX
|
|
58
|
+
|
|
59
|
+
#### Don't
|
|
60
|
+
|
|
61
|
+
```jsx
|
|
62
|
+
const total = prices.reduce(add, 0);
|
|
63
|
+
function add(a, b) {
|
|
64
|
+
return a + b;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### Do
|
|
69
|
+
|
|
70
|
+
```jsx
|
|
71
|
+
const prices = [1, 2];
|
|
72
|
+
const total = prices.reduce(add, 0);
|
|
73
|
+
function add(a, b) {
|
|
74
|
+
return a + b;
|
|
75
|
+
}
|
|
76
|
+
```
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: js/no-unexpected-multiline
|
|
3
|
+
title: "A line break doesn't silently join two lines into one expression"
|
|
4
|
+
languages: [javascript, jsx, typescript, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "whether the lines were meant to join can only be known from intent"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint/no-unexpected-multiline]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A line that starts with `(`, `[` or a template literal does not continue the expression on the line
|
|
13
|
+
before it without a semicolon.
|
|
14
|
+
|
|
15
|
+
**Why:** Without semicolons, JavaScript joins such lines: `a\n(b)` is the call `a(b)`, which is
|
|
16
|
+
rarely what was written.
|
|
17
|
+
|
|
18
|
+
```codelaw
|
|
19
|
+
native: js/no-unexpected-multiline
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## How to fix
|
|
23
|
+
|
|
24
|
+
1. If the lines are separate statements, end the first with a semicolon.
|
|
25
|
+
2. If they are one expression, move the `(` or `[` onto the first line.
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
### JavaScript
|
|
30
|
+
|
|
31
|
+
#### Don't
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
const total = sum
|
|
35
|
+
(items).forEach(log);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
#### Do
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
const total = sum;
|
|
42
|
+
(items).forEach(log);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### JSX
|
|
46
|
+
|
|
47
|
+
#### Don't
|
|
48
|
+
|
|
49
|
+
```jsx
|
|
50
|
+
const total = sum
|
|
51
|
+
(items).forEach(log);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### Do
|
|
55
|
+
|
|
56
|
+
```jsx
|
|
57
|
+
const total = sum;
|
|
58
|
+
(items).forEach(log);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### TypeScript
|
|
62
|
+
|
|
63
|
+
#### Don't
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
const total = sum
|
|
67
|
+
(items).forEach(log);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### Do
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
const total = sum;
|
|
74
|
+
(items).forEach(log);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### TSX
|
|
78
|
+
|
|
79
|
+
#### Don't
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
const total = sum
|
|
83
|
+
(items).forEach(log);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### Do
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
const total = sum;
|
|
90
|
+
(items).forEach(log);
|
|
91
|
+
```
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: js/no-unreachable
|
|
3
|
+
title: "No code after a return, throw, break or continue"
|
|
4
|
+
languages: [javascript, jsx]
|
|
5
|
+
fix: suggest
|
|
6
|
+
fix_reason: "deleting the code is right only when it was really dead, not misplaced"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint/no-unreachable]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Statements after an unconditional `return`, `throw`, `break` or `continue` in the same block are
|
|
13
|
+
removed or moved; hoisted function declarations and uninitialised `var`s are allowed.
|
|
14
|
+
|
|
15
|
+
**Why:** Unreachable code never runs, so it misleads every reader who assumes it does.
|
|
16
|
+
|
|
17
|
+
```codelaw
|
|
18
|
+
native: js/no-unreachable
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Examples
|
|
22
|
+
|
|
23
|
+
### JavaScript
|
|
24
|
+
|
|
25
|
+
#### Don't
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
function total(items) {
|
|
29
|
+
return items.length;
|
|
30
|
+
log(items);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Do
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
function total(items) {
|
|
38
|
+
return items.length;
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### JSX
|
|
43
|
+
|
|
44
|
+
#### Don't
|
|
45
|
+
|
|
46
|
+
```jsx
|
|
47
|
+
function total(items) {
|
|
48
|
+
return items.length;
|
|
49
|
+
log(items);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
#### Do
|
|
54
|
+
|
|
55
|
+
```jsx
|
|
56
|
+
function total(items) {
|
|
57
|
+
return items.length;
|
|
58
|
+
}
|
|
59
|
+
```
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: js/no-unsafe-finally
|
|
3
|
+
title: "No return, throw, break or continue in finally"
|
|
4
|
+
languages: [javascript, jsx, typescript, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "what should happen to the try block's result is a decision"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint/no-unsafe-finally]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A `finally` block doesn't end the function or loop with `return`, `throw`, `break` or `continue`.
|
|
13
|
+
|
|
14
|
+
**Why:** Control flow in `finally` silently replaces whatever the `try` or `catch` block returned or
|
|
15
|
+
threw.
|
|
16
|
+
|
|
17
|
+
```codelaw
|
|
18
|
+
match:
|
|
19
|
+
any:
|
|
20
|
+
- kind: return_statement
|
|
21
|
+
- kind: throw_statement
|
|
22
|
+
- kind: break_statement
|
|
23
|
+
- kind: continue_statement
|
|
24
|
+
inside:
|
|
25
|
+
kind: finally_clause
|
|
26
|
+
stopBy:
|
|
27
|
+
any: [{kind: function_declaration}, {kind: function_expression}, {kind: arrow_function}, {kind: method_definition}, {kind: generator_function_declaration}, {kind: generator_function}, { kind: class_declaration }]
|
|
28
|
+
not:
|
|
29
|
+
all:
|
|
30
|
+
- any: [{ kind: break_statement }, { kind: continue_statement }]
|
|
31
|
+
- inside:
|
|
32
|
+
stopBy: { kind: finally_clause }
|
|
33
|
+
any: [{ kind: for_statement }, { kind: for_in_statement }, { kind: while_statement }, { kind: do_statement }, { kind: switch_statement }]
|
|
34
|
+
message: "Control flow in `finally` overrides the try block's return or exception"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## How to fix
|
|
38
|
+
|
|
39
|
+
1. Move the `return` or `throw` out of `finally` into the `try` or `catch` block.
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
|
|
43
|
+
### JavaScript
|
|
44
|
+
|
|
45
|
+
#### Don't
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
function load() {
|
|
49
|
+
try {
|
|
50
|
+
return fetchAll();
|
|
51
|
+
} finally {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### Do
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
function load() {
|
|
61
|
+
try {
|
|
62
|
+
return fetchAll();
|
|
63
|
+
} finally {
|
|
64
|
+
cleanup();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### JSX
|
|
70
|
+
|
|
71
|
+
#### Don't
|
|
72
|
+
|
|
73
|
+
```jsx
|
|
74
|
+
function load() {
|
|
75
|
+
try {
|
|
76
|
+
return fetchAll();
|
|
77
|
+
} finally {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### Do
|
|
84
|
+
|
|
85
|
+
```jsx
|
|
86
|
+
function load() {
|
|
87
|
+
try {
|
|
88
|
+
return fetchAll();
|
|
89
|
+
} finally {
|
|
90
|
+
cleanup();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### TypeScript
|
|
96
|
+
|
|
97
|
+
#### Don't
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
function load() {
|
|
101
|
+
try {
|
|
102
|
+
return fetchAll();
|
|
103
|
+
} finally {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Do
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
function load() {
|
|
113
|
+
try {
|
|
114
|
+
return fetchAll();
|
|
115
|
+
} finally {
|
|
116
|
+
cleanup();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### TSX
|
|
122
|
+
|
|
123
|
+
#### Don't
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
function load() {
|
|
127
|
+
try {
|
|
128
|
+
return fetchAll();
|
|
129
|
+
} finally {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
#### Do
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
function load() {
|
|
139
|
+
try {
|
|
140
|
+
return fetchAll();
|
|
141
|
+
} finally {
|
|
142
|
+
cleanup();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: js/no-unsafe-negation
|
|
3
|
+
title: "No negating the left side of in or instanceof"
|
|
4
|
+
languages: [javascript, jsx]
|
|
5
|
+
fix: suggest
|
|
6
|
+
fix_reason: "code that really meant `(!a) in b` would change"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint/no-unsafe-negation]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
`!` before `a in b` or `a instanceof B` wraps the whole test in parentheses.
|
|
13
|
+
|
|
14
|
+
**Why:** `!a in b` means `(!a) in b`, which tests whether `true` or `false` is a key — never what
|
|
15
|
+
was meant.
|
|
16
|
+
|
|
17
|
+
```codelaw
|
|
18
|
+
match:
|
|
19
|
+
kind: binary_expression
|
|
20
|
+
all:
|
|
21
|
+
- has: { field: operator, regex: ^(in|instanceof)$ }
|
|
22
|
+
- has:
|
|
23
|
+
field: left
|
|
24
|
+
kind: unary_expression
|
|
25
|
+
regex: ^!
|
|
26
|
+
pattern: $NEG
|
|
27
|
+
has: { field: argument, pattern: $A }
|
|
28
|
+
message: "`!` applies to `$A`, not to the whole test; wrap the test in parentheses"
|
|
29
|
+
fix:
|
|
30
|
+
- insert-before: "!("
|
|
31
|
+
- replace: { target: $NEG, with: "$A" }
|
|
32
|
+
- insert-after: ")"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Examples
|
|
36
|
+
|
|
37
|
+
### JavaScript
|
|
38
|
+
|
|
39
|
+
#### Don't
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
if (!key in cache) {
|
|
43
|
+
load(key);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
#### Do
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
if (!(key in cache)) {
|
|
51
|
+
load(key);
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### JSX
|
|
56
|
+
|
|
57
|
+
#### Don't
|
|
58
|
+
|
|
59
|
+
```jsx
|
|
60
|
+
if (!key in cache) {
|
|
61
|
+
load(key);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Do
|
|
66
|
+
|
|
67
|
+
```jsx
|
|
68
|
+
if (!(key in cache)) {
|
|
69
|
+
load(key);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: js/no-unsafe-optional-chaining
|
|
3
|
+
title: "No ?. chain used where undefined throws"
|
|
4
|
+
languages: [javascript, jsx, typescript, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the right fallback value depends on the code"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint/no-unsafe-optional-chaining]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A `?.` chain, which can evaluate to `undefined`, is not called through parentheses, constructed,
|
|
13
|
+
spread into an array or call, destructured, iterated with `for … of`, used on the right of `in` or
|
|
14
|
+
`instanceof`, or extended.
|
|
15
|
+
|
|
16
|
+
**Why:** In those places `undefined` throws a TypeError, which defeats the point of `?.`.
|
|
17
|
+
|
|
18
|
+
```codelaw
|
|
19
|
+
native: js/no-unsafe-optional-chaining
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## How to fix
|
|
23
|
+
|
|
24
|
+
1. Give the chain a fallback with `??`.
|
|
25
|
+
2. Or move the `?.` so that it covers the operation that can fail.
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
### JavaScript
|
|
30
|
+
|
|
31
|
+
#### Don't
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
const { name } = user?.profile;
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
#### Do
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
const { name } = user?.profile ?? {};
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### JSX
|
|
44
|
+
|
|
45
|
+
#### Don't
|
|
46
|
+
|
|
47
|
+
```jsx
|
|
48
|
+
const { name } = user?.profile;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### Do
|
|
52
|
+
|
|
53
|
+
```jsx
|
|
54
|
+
const { name } = user?.profile ?? {};
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### TypeScript
|
|
58
|
+
|
|
59
|
+
#### Don't
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const { name } = user?.profile;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Do
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const { name } = user?.profile ?? {};
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### TSX
|
|
72
|
+
|
|
73
|
+
#### Don't
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
const { name } = user?.profile;
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### Do
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
const { name } = user?.profile ?? {};
|
|
83
|
+
```
|