@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,134 @@
1
+ ---
2
+ id: js/exports-at-end
3
+ title: "Exports are gathered at the end of the file"
4
+ languages: [javascript, jsx, typescript, tsx]
5
+ fix: auto
6
+ stability: preview
7
+ invariant: exports-unchanged
8
+ since: 0.1.0
9
+ ---
10
+
11
+ Declarations are written without `export`, and the module's exports are listed in one `export { … }`
12
+ (and `export type { … }`) block at the end of the file.
13
+
14
+ **Why:** A reader or an agent sees the module's whole public API in one place.
15
+
16
+ ```codelaw
17
+ match:
18
+ kind: export_statement
19
+ has: { field: declaration, pattern: $DECL }
20
+ not:
21
+ any:
22
+ - has: { regex: ^default$ }
23
+ - has: { field: declaration, kind: internal_module }
24
+ - has: { field: declaration, kind: ambient_declaration }
25
+ - has: { field: declaration, kind: module }
26
+ message: "Export at the end of the file, in one `export { … }` block"
27
+ fix:
28
+ - strip-export-keyword: $DECL
29
+ - collect-into-block:
30
+ target: $DECL
31
+ block: "export { $$$NAMES };"
32
+ type_block: "export type { $$$TYPE_NAMES };"
33
+ position: end-of-file
34
+ ```
35
+
36
+ ## Examples
37
+
38
+ ### JavaScript
39
+
40
+ #### Don't
41
+
42
+ ```js
43
+ export function add(a, b) {
44
+ return a + b;
45
+ }
46
+ export const zero = 0;
47
+ ```
48
+
49
+ #### Do
50
+
51
+ ```js
52
+ function add(a, b) {
53
+ return a + b;
54
+ }
55
+ const zero = 0;
56
+ export { add, zero };
57
+ ```
58
+
59
+ ### JSX
60
+
61
+ #### Don't
62
+
63
+ ```jsx
64
+ export function add(a, b) {
65
+ return a + b;
66
+ }
67
+ export const zero = 0;
68
+ ```
69
+
70
+ #### Do
71
+
72
+ ```jsx
73
+ function add(a, b) {
74
+ return a + b;
75
+ }
76
+ const zero = 0;
77
+ export { add, zero };
78
+ ```
79
+
80
+ ### TypeScript
81
+
82
+ #### Don't
83
+
84
+ ```ts
85
+ export function add(a: number, b: number) {
86
+ return a + b;
87
+ }
88
+ export interface Point {
89
+ x: number;
90
+ }
91
+ ```
92
+
93
+ #### Do
94
+
95
+ ```ts
96
+ function add(a: number, b: number) {
97
+ return a + b;
98
+ }
99
+ interface Point {
100
+ x: number;
101
+ }
102
+ export { add };
103
+ export type { Point };
104
+ ```
105
+
106
+ ### TSX
107
+
108
+ #### Don't
109
+
110
+ ```tsx
111
+ export function add(a: number, b: number) {
112
+ return a + b;
113
+ }
114
+ export interface Point {
115
+ x: number;
116
+ }
117
+ ```
118
+
119
+ #### Do
120
+
121
+ ```tsx
122
+ function add(a: number, b: number) {
123
+ return a + b;
124
+ }
125
+ interface Point {
126
+ x: number;
127
+ }
128
+ export { add };
129
+ export type { Point };
130
+ ```
131
+
132
+ ## Differences from the source
133
+
134
+ `export default`, namespaces and `declare` blocks are left alone.
@@ -0,0 +1,106 @@
1
+ ---
2
+ id: js/for-direction
3
+ title: "A for loop's counter moves toward its end condition"
4
+ languages: [javascript, jsx, typescript, tsx]
5
+ fix: manual
6
+ fix_reason: "whether the condition or the update is wrong needs the author"
7
+ stability: preview
8
+ ported_from: [eslint/for-direction]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ A `for` loop whose condition tests `i < n` counts up, and one that tests `i > n` counts down.
13
+
14
+ **Why:** A counter that moves the wrong way never meets its end condition, so the loop never ends.
15
+
16
+ ```codelaw
17
+ match:
18
+ kind: for_statement
19
+ any:
20
+ - all:
21
+ - has: { field: condition, any: [{ pattern: $I < $END }, { pattern: $I <= $END }] }
22
+ - has: { field: increment, any: [{ pattern: $I-- }, { pattern: --$I }] }
23
+ - all:
24
+ - has: { field: condition, any: [{ pattern: $I > $END }, { pattern: $I >= $END }] }
25
+ - has: { field: increment, any: [{ pattern: $I++ }, { pattern: ++$I }] }
26
+ message: "The counter `$I` moves away from the condition that ends this loop"
27
+ ```
28
+
29
+ ## How to fix
30
+
31
+ 1. Decide which way the loop should count.
32
+ 2. Fix the update (`i++` or `i--`) or the comparison to match.
33
+
34
+ ## Examples
35
+
36
+ ### JavaScript
37
+
38
+ #### Don't
39
+
40
+ ```js
41
+ for (let i = 0; i < 10; i--) {
42
+ draw(i);
43
+ }
44
+ ```
45
+
46
+ #### Do
47
+
48
+ ```js
49
+ for (let i = 0; i < 10; i++) {
50
+ draw(i);
51
+ }
52
+ ```
53
+
54
+ ### JSX
55
+
56
+ #### Don't
57
+
58
+ ```jsx
59
+ for (let i = 0; i < 10; i--) {
60
+ draw(i);
61
+ }
62
+ ```
63
+
64
+ #### Do
65
+
66
+ ```jsx
67
+ for (let i = 0; i < 10; i++) {
68
+ draw(i);
69
+ }
70
+ ```
71
+
72
+ ### TypeScript
73
+
74
+ #### Don't
75
+
76
+ ```ts
77
+ for (let i = 0; i < 10; i--) {
78
+ draw(i);
79
+ }
80
+ ```
81
+
82
+ #### Do
83
+
84
+ ```ts
85
+ for (let i = 0; i < 10; i++) {
86
+ draw(i);
87
+ }
88
+ ```
89
+
90
+ ### TSX
91
+
92
+ #### Don't
93
+
94
+ ```tsx
95
+ for (let i = 0; i < 10; i--) {
96
+ draw(i);
97
+ }
98
+ ```
99
+
100
+ #### Do
101
+
102
+ ```tsx
103
+ for (let i = 0; i < 10; i++) {
104
+ draw(i);
105
+ }
106
+ ```
@@ -0,0 +1,74 @@
1
+ ---
2
+ id: js/getter-return
3
+ title: "A getter returns a value on every path"
4
+ languages: [javascript, jsx]
5
+ fix: manual
6
+ fix_reason: "the value a getter should return is the author's decision"
7
+ stability: preview
8
+ ported_from: [eslint/getter-return]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ Every path through a getter ends in `return <value>` or a `throw`.
13
+
14
+ **Why:** A getter that falls off its end returns `undefined`, which is almost always a forgotten
15
+ `return`.
16
+
17
+ ```codelaw
18
+ native: js/getter-return
19
+ ```
20
+
21
+ ## How to fix
22
+
23
+ 1. Find the path that ends without a `return`.
24
+ 2. Return the value the property should have there, or throw.
25
+
26
+ ## Examples
27
+
28
+ ### JavaScript
29
+
30
+ #### Don't
31
+
32
+ ```js
33
+ const box = {
34
+ get size() {
35
+ if (this.items) {
36
+ return this.items.length;
37
+ }
38
+ },
39
+ };
40
+ ```
41
+
42
+ #### Do
43
+
44
+ ```js
45
+ const box = {
46
+ get size() {
47
+ return this.items ? this.items.length : 0;
48
+ },
49
+ };
50
+ ```
51
+
52
+ ### JSX
53
+
54
+ #### Don't
55
+
56
+ ```jsx
57
+ const box = {
58
+ get size() {
59
+ if (this.items) {
60
+ return this.items.length;
61
+ }
62
+ },
63
+ };
64
+ ```
65
+
66
+ #### Do
67
+
68
+ ```jsx
69
+ const box = {
70
+ get size() {
71
+ return this.items ? this.items.length : 0;
72
+ },
73
+ };
74
+ ```
@@ -0,0 +1,117 @@
1
+ ---
2
+ id: js/import-groups
3
+ title: "Imports are grouped builtin, external, internal, relative"
4
+ languages: [javascript, jsx, typescript, tsx]
5
+ fix: auto
6
+ stability: preview
7
+ options:
8
+ internal: { type: "string[]", default: ["@/", "~/"], description: "prefixes of internal imports" }
9
+ since: 0.1.0
10
+ ---
11
+
12
+ The file's leading imports come in four groups, in order — Node.js built-ins, packages, internal
13
+ aliases (the `internal` option, `@/` and `~/` by default), relative paths — with a blank line
14
+ between groups.
15
+
16
+ **Why:** Grouped imports show at a glance what a module depends on, and keep diffs of the import
17
+ block small.
18
+
19
+ ```codelaw
20
+ native: js/import-groups
21
+ ```
22
+
23
+ ## Examples
24
+
25
+ ### JavaScript
26
+
27
+ #### Don't
28
+
29
+ ```js
30
+ import { helper } from "./helper";
31
+ import fs from "node:fs";
32
+ import React from "react";
33
+ helper(fs, React);
34
+ ```
35
+
36
+ #### Do
37
+
38
+ ```js
39
+ import fs from "node:fs";
40
+
41
+ import React from "react";
42
+
43
+ import { helper } from "./helper";
44
+ helper(fs, React);
45
+ ```
46
+
47
+ ### JSX
48
+
49
+ #### Don't
50
+
51
+ ```jsx
52
+ import { helper } from "./helper";
53
+ import fs from "node:fs";
54
+ import React from "react";
55
+ helper(fs, React);
56
+ ```
57
+
58
+ #### Do
59
+
60
+ ```jsx
61
+ import fs from "node:fs";
62
+
63
+ import React from "react";
64
+
65
+ import { helper } from "./helper";
66
+ helper(fs, React);
67
+ ```
68
+
69
+ ### TypeScript
70
+
71
+ #### Don't
72
+
73
+ ```ts
74
+ import { helper } from "./helper";
75
+ import fs from "node:fs";
76
+ import React from "react";
77
+ helper(fs, React);
78
+ ```
79
+
80
+ #### Do
81
+
82
+ ```ts
83
+ import fs from "node:fs";
84
+
85
+ import React from "react";
86
+
87
+ import { helper } from "./helper";
88
+ helper(fs, React);
89
+ ```
90
+
91
+ ### TSX
92
+
93
+ #### Don't
94
+
95
+ ```tsx
96
+ import { helper } from "./helper";
97
+ import fs from "node:fs";
98
+ import React from "react";
99
+ helper(fs, React);
100
+ ```
101
+
102
+ #### Do
103
+
104
+ ```tsx
105
+ import fs from "node:fs";
106
+
107
+ import React from "react";
108
+
109
+ import { helper } from "./helper";
110
+ helper(fs, React);
111
+ ```
112
+
113
+ ## Differences from the source
114
+
115
+ A block of imports with a comment inside is left alone, so no comment is moved away from the import
116
+ it describes. A side-effect import (`import './polyfills'`, a stylesheet) stays where it is, since moving it
117
+ changes what runs first; only the imports between two of them are ordered.
@@ -0,0 +1,95 @@
1
+ ---
2
+ id: js/no-async-promise-executor
3
+ title: "A Promise executor is not an async function"
4
+ languages: [javascript, jsx, typescript, tsx]
5
+ fix: manual
6
+ fix_reason: "the rewrite depends on what the executor does"
7
+ stability: preview
8
+ ported_from: [eslint/no-async-promise-executor]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ The function passed to `new Promise(...)` is not `async`.
13
+
14
+ **Why:** An error thrown in an async executor doesn't reject the promise, and an executor that needs
15
+ `await` doesn't need the `Promise` wrapper.
16
+
17
+ ```codelaw
18
+ match:
19
+ any:
20
+ - pattern: new Promise(async ($$$P) => $$$B)
21
+ - pattern: new Promise(async function ($$$P) { $$$B })
22
+ - pattern: new Promise(async $P => $$$B)
23
+ message: "An async Promise executor loses its errors; await inside a plain async function instead"
24
+ ```
25
+
26
+ ## How to fix
27
+
28
+ 1. If the executor only awaits and resolves, use the awaited promise directly.
29
+ 2. Otherwise make the executor a plain function and handle errors with `reject`.
30
+
31
+ ## Examples
32
+
33
+ ### JavaScript
34
+
35
+ #### Don't
36
+
37
+ ```js
38
+ const data = new Promise(async (resolve) => {
39
+ resolve(await load());
40
+ });
41
+ ```
42
+
43
+ #### Do
44
+
45
+ ```js
46
+ const data = load();
47
+ ```
48
+
49
+ ### JSX
50
+
51
+ #### Don't
52
+
53
+ ```jsx
54
+ const data = new Promise(async (resolve) => {
55
+ resolve(await load());
56
+ });
57
+ ```
58
+
59
+ #### Do
60
+
61
+ ```jsx
62
+ const data = load();
63
+ ```
64
+
65
+ ### TypeScript
66
+
67
+ #### Don't
68
+
69
+ ```ts
70
+ const data = new Promise(async (resolve) => {
71
+ resolve(await load());
72
+ });
73
+ ```
74
+
75
+ #### Do
76
+
77
+ ```ts
78
+ const data = load();
79
+ ```
80
+
81
+ ### TSX
82
+
83
+ #### Don't
84
+
85
+ ```tsx
86
+ const data = new Promise(async (resolve) => {
87
+ resolve(await load());
88
+ });
89
+ ```
90
+
91
+ #### Do
92
+
93
+ ```tsx
94
+ const data = load();
95
+ ```
@@ -0,0 +1,134 @@
1
+ ---
2
+ id: js/no-case-declarations
3
+ title: "No let, const, function or class directly in a case"
4
+ languages: [javascript, jsx, typescript, tsx]
5
+ fix: manual
6
+ fix_reason: "another case may rely on the shared declaration"
7
+ stability: preview
8
+ ported_from: [eslint/no-case-declarations]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ A `let`, `const`, `function` or `class` inside a `case` sits in a block `{ … }` of its own.
13
+
14
+ **Why:** Declarations in a case are scoped to the whole `switch`, so another case can see them
15
+ uninitialised.
16
+
17
+ ```codelaw
18
+ match:
19
+ any:
20
+ - kind: lexical_declaration
21
+ - kind: class_declaration
22
+ - kind: function_declaration
23
+ inside:
24
+ stopBy: neighbor
25
+ any: [{ kind: switch_case }, { kind: switch_default }]
26
+ message: "A declaration in a case is visible to the whole switch; wrap the case in braces"
27
+ ```
28
+
29
+ ## How to fix
30
+
31
+ 1. Wrap the case's statements in `{ … }`.
32
+ 2. If another case uses the declaration, move it above the `switch`.
33
+
34
+ ## Examples
35
+
36
+ ### JavaScript
37
+
38
+ #### Don't
39
+
40
+ ```js
41
+ switch (kind) {
42
+ case "a":
43
+ const n = 1;
44
+ use(n);
45
+ break;
46
+ }
47
+ ```
48
+
49
+ #### Do
50
+
51
+ ```js
52
+ switch (kind) {
53
+ case "a": {
54
+ const n = 1;
55
+ use(n);
56
+ break;
57
+ }
58
+ }
59
+ ```
60
+
61
+ ### JSX
62
+
63
+ #### Don't
64
+
65
+ ```jsx
66
+ switch (kind) {
67
+ case "a":
68
+ const n = 1;
69
+ use(n);
70
+ break;
71
+ }
72
+ ```
73
+
74
+ #### Do
75
+
76
+ ```jsx
77
+ switch (kind) {
78
+ case "a": {
79
+ const n = 1;
80
+ use(n);
81
+ break;
82
+ }
83
+ }
84
+ ```
85
+
86
+ ### TypeScript
87
+
88
+ #### Don't
89
+
90
+ ```ts
91
+ switch (kind) {
92
+ case "a":
93
+ const n = 1;
94
+ use(n);
95
+ break;
96
+ }
97
+ ```
98
+
99
+ #### Do
100
+
101
+ ```ts
102
+ switch (kind) {
103
+ case "a": {
104
+ const n = 1;
105
+ use(n);
106
+ break;
107
+ }
108
+ }
109
+ ```
110
+
111
+ ### TSX
112
+
113
+ #### Don't
114
+
115
+ ```tsx
116
+ switch (kind) {
117
+ case "a":
118
+ const n = 1;
119
+ use(n);
120
+ break;
121
+ }
122
+ ```
123
+
124
+ #### Do
125
+
126
+ ```tsx
127
+ switch (kind) {
128
+ case "a": {
129
+ const n = 1;
130
+ use(n);
131
+ break;
132
+ }
133
+ }
134
+ ```
@@ -0,0 +1,61 @@
1
+ ---
2
+ id: js/no-class-assign
3
+ title: "A class name is not reassigned"
4
+ languages: [javascript, jsx]
5
+ fix: manual
6
+ fix_reason: "what the assignment was for is the author's call"
7
+ stability: preview
8
+ ported_from: [eslint/no-class-assign]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ Code doesn't assign a new value to the name a class declaration binds.
13
+
14
+ **Why:** Reassigning a class name is almost always a mistake, and makes every later use mean
15
+ something else.
16
+
17
+ ```codelaw
18
+ match:
19
+ kind: identifier
20
+ where:
21
+ $$MATCH: [write, { binding: class }]
22
+ message: "`$$MATCH` is a class; don't assign to it"
23
+ ```
24
+
25
+ ## How to fix
26
+
27
+ 1. Assign to a new variable instead of the class name.
28
+
29
+ ## Examples
30
+
31
+ ### JavaScript
32
+
33
+ #### Don't
34
+
35
+ ```js
36
+ class Point {}
37
+ Point = null;
38
+ ```
39
+
40
+ #### Do
41
+
42
+ ```js
43
+ class Point {}
44
+ const origin = null;
45
+ ```
46
+
47
+ ### JSX
48
+
49
+ #### Don't
50
+
51
+ ```jsx
52
+ class Point {}
53
+ Point = null;
54
+ ```
55
+
56
+ #### Do
57
+
58
+ ```jsx
59
+ class Point {}
60
+ const origin = null;
61
+ ```