@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.
Files changed (91) hide show
  1. package/LICENSE +23 -0
  2. package/NOTICE +10 -0
  3. package/README.md +57 -2
  4. package/codelaw-pack.toml +6 -0
  5. package/corpus.toml +76 -0
  6. package/data/globals/browser.json +1209 -0
  7. package/data/globals/commonjs.json +6 -0
  8. package/data/globals/jest.json +15 -0
  9. package/data/globals/mocha.json +22 -0
  10. package/data/globals/node.json +83 -0
  11. package/data/globals/serviceworker.json +328 -0
  12. package/data/globals/shared-node-browser.json +73 -0
  13. package/data/globals/vitest.json +19 -0
  14. package/data/globals/worker.json +349 -0
  15. package/package.json +14 -4
  16. package/presets/js/conventions.toml +6 -0
  17. package/presets/js/project.toml +4 -0
  18. package/presets/js/recommended.toml +67 -0
  19. package/presets/js/typescript.toml +7 -0
  20. package/rules/graph/no-unused-exports.md +135 -0
  21. package/rules/js/constructor-super.md +77 -0
  22. package/rules/js/exports-at-end.md +134 -0
  23. package/rules/js/for-direction.md +106 -0
  24. package/rules/js/getter-return.md +74 -0
  25. package/rules/js/import-groups.md +117 -0
  26. package/rules/js/no-async-promise-executor.md +95 -0
  27. package/rules/js/no-case-declarations.md +134 -0
  28. package/rules/js/no-class-assign.md +61 -0
  29. package/rules/js/no-compare-neg-zero.md +105 -0
  30. package/rules/js/no-cond-assign.md +135 -0
  31. package/rules/js/no-const-assign.md +61 -0
  32. package/rules/js/no-constant-binary-expression.md +83 -0
  33. package/rules/js/no-constant-condition.md +150 -0
  34. package/rules/js/no-control-regex.md +84 -0
  35. package/rules/js/no-debugger.md +99 -0
  36. package/rules/js/no-default-export.md +88 -0
  37. package/rules/js/no-delete-var.md +93 -0
  38. package/rules/js/no-dupe-args.md +61 -0
  39. package/rules/js/no-dupe-class-members.md +82 -0
  40. package/rules/js/no-dupe-else-if.md +113 -0
  41. package/rules/js/no-dupe-keys.md +66 -0
  42. package/rules/js/no-duplicate-case.md +137 -0
  43. package/rules/js/no-empty-character-class.md +98 -0
  44. package/rules/js/no-empty-pattern.md +86 -0
  45. package/rules/js/no-empty-static-block.md +99 -0
  46. package/rules/js/no-empty.md +116 -0
  47. package/rules/js/no-ex-assign.md +121 -0
  48. package/rules/js/no-extra-boolean-cast.md +107 -0
  49. package/rules/js/no-fallthrough.md +178 -0
  50. package/rules/js/no-func-assign.md +62 -0
  51. package/rules/js/no-global-assign.md +85 -0
  52. package/rules/js/no-import-assign.md +60 -0
  53. package/rules/js/no-invalid-regexp.md +86 -0
  54. package/rules/js/no-irregular-whitespace.md +87 -0
  55. package/rules/js/no-loss-of-precision.md +82 -0
  56. package/rules/js/no-misleading-character-class.md +83 -0
  57. package/rules/js/no-new-native-nonconstructor.md +55 -0
  58. package/rules/js/no-nonoctal-decimal-escape.md +84 -0
  59. package/rules/js/no-obj-calls.md +61 -0
  60. package/rules/js/no-octal.md +84 -0
  61. package/rules/js/no-prototype-builtins.md +99 -0
  62. package/rules/js/no-redeclare.md +61 -0
  63. package/rules/js/no-regex-spaces.md +75 -0
  64. package/rules/js/no-self-assign.md +91 -0
  65. package/rules/js/no-setter-return.md +69 -0
  66. package/rules/js/no-shadow-restricted-names.md +102 -0
  67. package/rules/js/no-sparse-arrays.md +82 -0
  68. package/rules/js/no-this-before-super.md +74 -0
  69. package/rules/js/no-unassigned-vars.md +102 -0
  70. package/rules/js/no-undef.md +76 -0
  71. package/rules/js/no-unexpected-multiline.md +91 -0
  72. package/rules/js/no-unreachable.md +59 -0
  73. package/rules/js/no-unsafe-finally.md +145 -0
  74. package/rules/js/no-unsafe-negation.md +71 -0
  75. package/rules/js/no-unsafe-optional-chaining.md +83 -0
  76. package/rules/js/no-unused-labels.md +102 -0
  77. package/rules/js/no-unused-private-class-members.md +126 -0
  78. package/rules/js/no-unused-vars.md +69 -0
  79. package/rules/js/no-useless-assignment.md +115 -0
  80. package/rules/js/no-useless-backreference.md +83 -0
  81. package/rules/js/no-useless-catch.md +105 -0
  82. package/rules/js/no-useless-escape.md +81 -0
  83. package/rules/js/no-var.md +76 -0
  84. package/rules/js/no-with.md +60 -0
  85. package/rules/js/prefer-const.md +83 -0
  86. package/rules/js/prefer-rest-params.md +103 -0
  87. package/rules/js/prefer-spread.md +95 -0
  88. package/rules/js/preserve-caught-error.md +122 -0
  89. package/rules/js/require-yield.md +107 -0
  90. package/rules/js/use-isnan.md +105 -0
  91. 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
+ ```