eslint-plugin-ts-data-forge 0.6.0 → 0.7.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/README.md +1 -0
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/plugin.d.mts +2 -0
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +45 -41
- package/dist/plugin.mjs.map +1 -1
- package/dist/rule-types.d.mts +15 -0
- package/dist/rule-types.d.mts.map +1 -1
- package/dist/rule-types.mjs +6 -2
- package/dist/rule-types.mjs.map +1 -1
- package/dist/rules/ast-utils.mjs +107 -50
- package/dist/rules/ast-utils.mjs.map +1 -1
- package/dist/rules/branded-number-types.mjs +41 -37
- package/dist/rules/branded-number-types.mjs.map +1 -1
- package/dist/rules/constants.mjs +2 -4
- package/dist/rules/constants.mjs.map +1 -1
- package/dist/rules/import-utils.mjs +80 -56
- package/dist/rules/import-utils.mjs.map +1 -1
- package/dist/rules/index.mjs +2 -2
- package/dist/rules/index.mjs.map +1 -1
- package/dist/rules/no-side-effect-import.mjs +46 -34
- package/dist/rules/no-side-effect-import.mjs.map +1 -1
- package/dist/rules/no-unnecessary-type-guard.mjs +376 -265
- package/dist/rules/no-unnecessary-type-guard.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-array.mjs +64 -52
- package/dist/rules/prefer-arr-is-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-bounded-length-array.mjs +91 -85
- package/dist/rules/prefer-arr-is-bounded-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-fixed-length-array.mjs +92 -83
- package/dist/rules/prefer-arr-is-fixed-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-max-length-array.mjs +92 -80
- package/dist/rules/prefer-arr-is-max-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-min-length-array.mjs +92 -80
- package/dist/rules/prefer-arr-is-min-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-non-empty.mjs +85 -77
- package/dist/rules/prefer-arr-is-non-empty.mjs.map +1 -1
- package/dist/rules/prefer-arr-scan.d.mts +96 -0
- package/dist/rules/prefer-arr-scan.d.mts.map +1 -0
- package/dist/rules/prefer-arr-scan.mjs +482 -0
- package/dist/rules/prefer-arr-scan.mjs.map +1 -0
- package/dist/rules/prefer-arr-sum.mjs +176 -132
- package/dist/rules/prefer-arr-sum.mjs.map +1 -1
- package/dist/rules/prefer-as-int.mjs +77 -71
- package/dist/rules/prefer-as-int.mjs.map +1 -1
- package/dist/rules/prefer-canonical-array-slicing.mjs +503 -343
- package/dist/rules/prefer-canonical-array-slicing.mjs.map +1 -1
- package/dist/rules/prefer-canonical-length-cast.mjs +217 -144
- package/dist/rules/prefer-canonical-length-cast.mjs.map +1 -1
- package/dist/rules/prefer-canonical-length-guard.mjs +160 -109
- package/dist/rules/prefer-canonical-length-guard.mjs.map +1 -1
- package/dist/rules/prefer-comparison-over-nullish-guard.mjs +89 -71
- package/dist/rules/prefer-comparison-over-nullish-guard.mjs.map +1 -1
- package/dist/rules/prefer-is-non-null-object.mjs +91 -72
- package/dist/rules/prefer-is-non-null-object.mjs.map +1 -1
- package/dist/rules/prefer-is-record-and-has-key.mjs +148 -105
- package/dist/rules/prefer-is-record-and-has-key.mjs.map +1 -1
- package/dist/rules/prefer-num-safe-parse-float.mjs +101 -77
- package/dist/rules/prefer-num-safe-parse-float.mjs.map +1 -1
- package/dist/rules/prefer-num-safe-parse-int.mjs +103 -81
- package/dist/rules/prefer-num-safe-parse-int.mjs.map +1 -1
- package/dist/rules/prefer-obj-over-entries-round-trip.mjs +299 -194
- package/dist/rules/prefer-obj-over-entries-round-trip.mjs.map +1 -1
- package/dist/rules/prefer-range-for-loop.mjs +121 -99
- package/dist/rules/prefer-range-for-loop.mjs.map +1 -1
- package/dist/rules/rules.d.mts +1 -0
- package/dist/rules/rules.d.mts.map +1 -1
- package/dist/rules/rules.mjs +19 -20
- package/dist/rules/rules.mjs.map +1 -1
- package/dist/types.mjs +2 -2
- package/dist/types.mjs.map +1 -1
- package/package.json +6 -7
- package/src/plugin.mts +1 -0
- package/src/rule-types.mts +16 -0
- package/src/rules/prefer-arr-scan.mts +693 -0
- package/src/rules/prefer-arr-scan.test.mts +295 -0
- package/src/rules/rules.mts +2 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
1
|
+
import { AST_NODE_TYPES, } from '@typescript-eslint/utils';
|
|
2
2
|
import { getImportedLocalName, getTsDataForgeImport } from './import-utils.mjs';
|
|
3
3
|
import { preferArrIsBoundedLengthArray } from './prefer-arr-is-bounded-length-array.mjs';
|
|
4
4
|
import { preferArrIsFixedLengthArray } from './prefer-arr-is-fixed-length-array.mjs';
|
|
@@ -6,124 +6,175 @@ import { preferArrIsMaxLengthArray } from './prefer-arr-is-max-length-array.mjs'
|
|
|
6
6
|
import { preferArrIsMinLengthArray } from './prefer-arr-is-min-length-array.mjs';
|
|
7
7
|
import { preferArrIsNonEmpty } from './prefer-arr-is-non-empty.mjs';
|
|
8
8
|
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
9
17
|
const COMPARISON_RULES = [
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
preferArrIsNonEmpty,
|
|
19
|
+
preferArrIsMinLengthArray,
|
|
20
|
+
preferArrIsMaxLengthArray,
|
|
21
|
+
preferArrIsBoundedLengthArray,
|
|
22
|
+
preferArrIsFixedLengthArray,
|
|
15
23
|
];
|
|
16
24
|
const MESSAGES = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
useCanonicalGuard: 'Replace `{{arrName}}.{{guard}}({{boundsText}})` with `{{arrName}}.{{replacement}}(...)`: the bound makes the two guards equivalent.',
|
|
26
|
+
useIsNonEmpty: preferArrIsNonEmpty.meta.messages.useIsNonEmpty,
|
|
27
|
+
useIsMinLengthArray: preferArrIsMinLengthArray.meta.messages.useIsMinLengthArray,
|
|
28
|
+
useIsMaxLengthArray: preferArrIsMaxLengthArray.meta.messages.useIsMaxLengthArray,
|
|
29
|
+
useIsBoundedLengthArray: preferArrIsBoundedLengthArray.meta.messages.useIsBoundedLengthArray,
|
|
30
|
+
useIsFixedLengthArray: preferArrIsFixedLengthArray.meta.messages.useIsFixedLengthArray,
|
|
23
31
|
};
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
24
58
|
const GUARD_REWRITES = [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
59
|
+
{ guard: 'isFixedLengthArray', bounds: [0], replacement: 'isEmpty' },
|
|
60
|
+
{ guard: 'isMaxLengthArray', bounds: [0], replacement: 'isEmpty' },
|
|
61
|
+
{ guard: 'isBoundedLengthArray', bounds: [0, 0], replacement: 'isEmpty' },
|
|
62
|
+
{ guard: 'isMinLengthArray', bounds: [1], replacement: 'isNonEmpty' },
|
|
63
|
+
{ guard: 'isFixedLengthTuple', bounds: [0], replacement: 'isEmptyTuple' },
|
|
64
|
+
{ guard: 'isMaxLengthTuple', bounds: [0], replacement: 'isEmptyTuple' },
|
|
65
|
+
{
|
|
66
|
+
guard: 'isBoundedLengthTuple',
|
|
67
|
+
bounds: [0, 0],
|
|
68
|
+
replacement: 'isEmptyTuple',
|
|
69
|
+
},
|
|
70
|
+
{ guard: 'isMinLengthTuple', bounds: [1], replacement: 'isNonEmptyTuple' },
|
|
37
71
|
];
|
|
38
|
-
const preferCanonicalLengthGuard = {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
72
|
+
export const preferCanonicalLengthGuard = {
|
|
73
|
+
meta: {
|
|
74
|
+
type: 'suggestion',
|
|
75
|
+
docs: {
|
|
76
|
+
description: 'Normalize array-length checks to their canonical `Arr` guard: `xs.length <op> n` becomes the matching `Arr.is*` guard, and a degenerate guard becomes the named one for its family (`Arr.isFixedLengthArray(0, xs)` → `Arr.isEmpty`, `Arr.isFixedLengthTuple(0, xs)` → `Arr.isEmptyTuple`).',
|
|
77
|
+
},
|
|
78
|
+
fixable: 'code',
|
|
79
|
+
schema: [],
|
|
80
|
+
messages: MESSAGES,
|
|
43
81
|
},
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
guard: guardName,
|
|
79
|
-
boundsText: [...rewrite.bounds.map(String), "\u2026"].join(", "),
|
|
80
|
-
replacement: rewrite.replacement
|
|
82
|
+
create: (context) => {
|
|
83
|
+
const sourceCode = context.sourceCode;
|
|
84
|
+
const arrLocalName = getImportedLocalName(getTsDataForgeImport(sourceCode.ast), 'Arr');
|
|
85
|
+
const comparisonVisitors = COMPARISON_RULES.map((rule) => rule.create(context));
|
|
86
|
+
if (arrLocalName === undefined)
|
|
87
|
+
return mergeVisitors(comparisonVisitors);
|
|
88
|
+
return mergeVisitors([
|
|
89
|
+
...comparisonVisitors,
|
|
90
|
+
{
|
|
91
|
+
CallExpression: (node) => {
|
|
92
|
+
const guardName = getGuardName(node, arrLocalName);
|
|
93
|
+
if (guardName === undefined)
|
|
94
|
+
return;
|
|
95
|
+
const rewrite = GUARD_REWRITES.find((entry) => entry.guard === guardName);
|
|
96
|
+
if (rewrite === undefined || !matchesBounds(node, rewrite.bounds)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const array = node.arguments[rewrite.bounds.length];
|
|
101
|
+
if (array === undefined)
|
|
102
|
+
return;
|
|
103
|
+
const arrayText = sourceCode.getText(array);
|
|
104
|
+
context.report({
|
|
105
|
+
node,
|
|
106
|
+
messageId: 'useCanonicalGuard',
|
|
107
|
+
data: {
|
|
108
|
+
arrName: arrLocalName,
|
|
109
|
+
guard: guardName,
|
|
110
|
+
boundsText: [...rewrite.bounds.map(String), '…'].join(', '),
|
|
111
|
+
replacement: rewrite.replacement,
|
|
112
|
+
},
|
|
113
|
+
fix: (fixer) => fixer.replaceText(node, `${arrLocalName}.${rewrite.replacement}(${arrayText})`),
|
|
114
|
+
});
|
|
115
|
+
},
|
|
81
116
|
},
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
)
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
]);
|
|
90
|
-
},
|
|
91
|
-
defaultOptions: []
|
|
117
|
+
]);
|
|
118
|
+
},
|
|
119
|
+
defaultOptions: [],
|
|
92
120
|
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
const mergeVisitors = (
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
visitors) => {
|
|
130
|
+
const mut_merged = {};
|
|
131
|
+
for (const visitor of visitors) {
|
|
132
|
+
for (const [selector, handler] of Object.entries(visitor)) {
|
|
133
|
+
if (typeof handler !== 'function')
|
|
134
|
+
continue;
|
|
135
|
+
const mut_handlers = mut_merged[selector] ?? [];
|
|
136
|
+
mut_handlers.push(handler);
|
|
137
|
+
mut_merged[selector] = mut_handlers;
|
|
109
138
|
}
|
|
110
|
-
|
|
111
|
-
])
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
139
|
+
}
|
|
140
|
+
return Object.fromEntries(Object.entries(mut_merged).map(([selector, handlers]) => [
|
|
141
|
+
selector,
|
|
142
|
+
(node) => {
|
|
143
|
+
for (const handler of handlers) {
|
|
144
|
+
handler(node);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
]));
|
|
117
148
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
149
|
+
|
|
150
|
+
const getGuardName = (
|
|
151
|
+
|
|
152
|
+
node, arrLocalName) => {
|
|
153
|
+
const { callee } = node;
|
|
154
|
+
return callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
155
|
+
!callee.computed &&
|
|
156
|
+
callee.object.type === AST_NODE_TYPES.Identifier &&
|
|
157
|
+
callee.object.name === arrLocalName &&
|
|
158
|
+
callee.property.type === AST_NODE_TYPES.Identifier
|
|
159
|
+
? callee.property.name
|
|
160
|
+
: undefined;
|
|
126
161
|
};
|
|
127
162
|
|
|
128
|
-
|
|
129
|
-
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
const matchesBounds = (
|
|
170
|
+
|
|
171
|
+
node, bounds) => {
|
|
172
|
+
const args = node.arguments;
|
|
173
|
+
if (args.length !== bounds.length + 1 || node.typeArguments !== undefined) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
return args.every((arg, index) => arg.type !== AST_NODE_TYPES.SpreadElement &&
|
|
177
|
+
(index === bounds.length ||
|
|
178
|
+
(arg.type === AST_NODE_TYPES.Literal && arg.value === bounds[index])));
|
|
179
|
+
};
|
|
180
|
+
//# sourceMappingURL=prefer-canonical-length-guard.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-canonical-length-guard.mjs","sources":["../../src/rules/prefer-canonical-length-guard.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type MutableRecord, type ReadonlyRecord } from 'ts-type-forge';\nimport { getImportedLocalName, getTsDataForgeImport } from './import-utils.mjs';\nimport { preferArrIsBoundedLengthArray } from './prefer-arr-is-bounded-length-array.mjs';\nimport { preferArrIsFixedLengthArray } from './prefer-arr-is-fixed-length-array.mjs';\nimport { preferArrIsMaxLengthArray } from './prefer-arr-is-max-length-array.mjs';\nimport { preferArrIsMinLengthArray } from './prefer-arr-is-min-length-array.mjs';\nimport { preferArrIsNonEmpty } from './prefer-arr-is-non-empty.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds =\n | 'useCanonicalGuard'\n | 'useIsBoundedLengthArray'\n | 'useIsFixedLengthArray'\n | 'useIsMaxLengthArray'\n | 'useIsMinLengthArray'\n | 'useIsNonEmpty';\n\n/**\n * The `xs.length <op> n` → `Arr.is*` rules folded into this one.\n *\n * Their implementations are reused as-is rather than rewritten: this rule\n * merges their visitors, so every case they already cover (type-aware array\n * checks, `Arr` import insertion, the `&&`-bounded-range special case) keeps\n * behaving exactly as before, and their co-located tests keep exercising them\n * directly.\n */\nconst COMPARISON_RULES = [\n preferArrIsNonEmpty,\n preferArrIsMinLengthArray,\n preferArrIsMaxLengthArray,\n preferArrIsBoundedLengthArray,\n preferArrIsFixedLengthArray,\n] as const;\n\nconst MESSAGES = {\n useCanonicalGuard:\n 'Replace `{{arrName}}.{{guard}}({{boundsText}})` with `{{arrName}}.{{replacement}}(...)`: the bound makes the two guards equivalent.',\n useIsNonEmpty: preferArrIsNonEmpty.meta.messages.useIsNonEmpty,\n useIsMinLengthArray:\n preferArrIsMinLengthArray.meta.messages.useIsMinLengthArray,\n useIsMaxLengthArray:\n preferArrIsMaxLengthArray.meta.messages.useIsMaxLengthArray,\n useIsBoundedLengthArray:\n preferArrIsBoundedLengthArray.meta.messages.useIsBoundedLengthArray,\n useIsFixedLengthArray:\n preferArrIsFixedLengthArray.meta.messages.useIsFixedLengthArray,\n} as const satisfies ReadonlyRecord<MessageIds, string>;\n\n/**\n * Length guards whose bound makes them redundant with a named degenerate\n * guard, keyed by guard name.\n *\n * Every entry stays inside its own family rather than crossing the\n * branded/structural divide:\n *\n * - the branded `*Array` guards go to `isEmpty` / `isNonEmpty`, which narrow to\n * `FixedLengthArray<0, E> & Xs` / `MinLengthArray<1, E> & Xs`;\n * - the structural `*Tuple` guards go to `isEmptyTuple` / `isNonEmptyTuple`,\n * which narrow to `readonly []` / `MinLengthTuple<1, E>`.\n *\n * Most entries are then **type-identical** — `isFixedLengthArray(0, xs)` and\n * `isMinLengthArray(1, xs)` produce exactly what `isEmpty` / `isNonEmpty`\n * narrow to, and `FixedLengthTuple<0, E>`, `MaxLengthTuple<0, E>` and\n * `BoundedLengthTuple<0, 0, E>` are all `readonly []`. Two *strengthen*:\n * `MaxLengthArray<0, E>` and `BoundedLengthArray<0, 0, E>` do not include the\n * `readonly []` structural part that `FixedLengthArray<0, E>` intersects on, so\n * rewriting them to `isEmpty` adds it. That is sound for a guard — the narrowed\n * type only flows outwards, and the stronger type is assignable everywhere the\n * old one was — and it stays within the branded family.\n *\n * Sending a `*Tuple` guard to the branded `isEmpty` would also be sound, but it\n * would silently change which family the value belongs to, which is why each\n * family keeps its own lane.\n */\nconst GUARD_REWRITES = [\n { guard: 'isFixedLengthArray', bounds: [0], replacement: 'isEmpty' },\n { guard: 'isMaxLengthArray', bounds: [0], replacement: 'isEmpty' },\n { guard: 'isBoundedLengthArray', bounds: [0, 0], replacement: 'isEmpty' },\n { guard: 'isMinLengthArray', bounds: [1], replacement: 'isNonEmpty' },\n { guard: 'isFixedLengthTuple', bounds: [0], replacement: 'isEmptyTuple' },\n { guard: 'isMaxLengthTuple', bounds: [0], replacement: 'isEmptyTuple' },\n {\n guard: 'isBoundedLengthTuple',\n bounds: [0, 0],\n replacement: 'isEmptyTuple',\n },\n { guard: 'isMinLengthTuple', bounds: [1], replacement: 'isNonEmptyTuple' },\n] as const satisfies readonly Readonly<{\n guard: string;\n bounds: readonly number[];\n replacement: string;\n}>[];\n\nexport const preferCanonicalLengthGuard: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Normalize array-length checks to their canonical `Arr` guard: `xs.length <op> n` becomes the matching `Arr.is*` guard, and a degenerate guard becomes the named one for its family (`Arr.isFixedLengthArray(0, xs)` → `Arr.isEmpty`, `Arr.isFixedLengthTuple(0, xs)` → `Arr.isEmptyTuple`).',\n },\n fixable: 'code',\n schema: [],\n messages: MESSAGES,\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const arrLocalName = getImportedLocalName(\n getTsDataForgeImport(sourceCode.ast),\n 'Arr',\n );\n\n const comparisonVisitors = COMPARISON_RULES.map((rule) =>\n rule.create(context),\n );\n\n if (arrLocalName === undefined) return mergeVisitors(comparisonVisitors);\n\n return mergeVisitors([\n ...comparisonVisitors,\n {\n CallExpression: (node) => {\n const guardName = getGuardName(node, arrLocalName);\n\n if (guardName === undefined) return;\n\n const rewrite = GUARD_REWRITES.find(\n (entry) => entry.guard === guardName,\n );\n\n if (rewrite === undefined || !matchesBounds(node, rewrite.bounds)) {\n return;\n }\n\n // The array is the last argument; the bounds precede it.\n const array = node.arguments[rewrite.bounds.length];\n\n if (array === undefined) return;\n\n const arrayText = sourceCode.getText(array);\n\n context.report({\n node,\n messageId: 'useCanonicalGuard',\n data: {\n arrName: arrLocalName,\n guard: guardName,\n boundsText: [...rewrite.bounds.map(String), '…'].join(', '),\n replacement: rewrite.replacement,\n },\n fix: (fixer) =>\n fixer.replaceText(\n node,\n `${arrLocalName}.${rewrite.replacement}(${arrayText})`,\n ),\n });\n },\n },\n ]);\n },\n defaultOptions: [],\n} as const;\n\n/**\n * Combines several rule listeners into one, calling every handler registered\n * for a given selector in order.\n */\nconst mergeVisitors = (\n // `RuleListener` holds mutable AST handler signatures, so it is not deeply\n // readonly.\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n visitors: readonly TSESLint.RuleListener[],\n): TSESLint.RuleListener => {\n const mut_merged: MutableRecord<string, ((node: never) => void)[]> = {};\n\n for (const visitor of visitors) {\n for (const [selector, handler] of Object.entries(visitor)) {\n if (typeof handler !== 'function') continue;\n\n const mut_handlers = mut_merged[selector] ?? [];\n\n mut_handlers.push(handler);\n\n mut_merged[selector] = mut_handlers;\n }\n }\n\n return Object.fromEntries(\n Object.entries(mut_merged).map(([selector, handlers]) => [\n selector,\n (node: never) => {\n for (const handler of handlers) {\n handler(node);\n }\n },\n ]),\n );\n};\n\n/** The `Arr.<guard>` method name of `node`, or `undefined` for other calls. */\nconst getGuardName = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n node: TSESTree.CallExpression,\n arrLocalName: string,\n): string | undefined => {\n const { callee } = node;\n\n return callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.object.type === AST_NODE_TYPES.Identifier &&\n callee.object.name === arrLocalName &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ? callee.property.name\n : undefined;\n};\n\n/**\n * Whether the call is `guard(...bounds, array)` with every bound written as the\n * exact numeric literal the rewrite requires.\n *\n * The arity check also excludes the curried form (`guard(...bounds)`): the\n * degenerate guards take only the array, so there is nothing to rewrite it to.\n */\nconst matchesBounds = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n node: TSESTree.CallExpression,\n bounds: readonly number[],\n): boolean => {\n const args = node.arguments;\n\n if (args.length !== bounds.length + 1 || node.typeArguments !== undefined) {\n return false;\n }\n\n return args.every(\n (arg, index) =>\n arg.type !== AST_NODE_TYPES.SpreadElement &&\n (index === bounds.length ||\n (arg.type === AST_NODE_TYPES.Literal && arg.value === bounds[index])),\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"prefer-canonical-length-guard.mjs","sourceRoot":"","sources":["../../src/rules/prefer-canonical-length-guard.mts"],"names":[],"mappings":"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"}
|
|
@@ -1,80 +1,98 @@
|
|
|
1
|
-
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
1
|
+
import { AST_NODE_TYPES, } from '@typescript-eslint/utils';
|
|
2
2
|
import { buildCalleeResolver, getTsDataForgeImport } from './import-utils.mjs';
|
|
3
3
|
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
4
8
|
const COMPARISON_GUARDS = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
isUndefined: { op: '===', literal: 'undefined' },
|
|
10
|
+
isNotUndefined: { op: '!==', literal: 'undefined' },
|
|
11
|
+
isNull: { op: '===', literal: 'null' },
|
|
12
|
+
isNotNull: { op: '!==', literal: 'null' },
|
|
9
13
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
const ARG_NEEDS_PARENS = new Set([
|
|
19
|
+
AST_NODE_TYPES.LogicalExpression,
|
|
20
|
+
AST_NODE_TYPES.ConditionalExpression,
|
|
21
|
+
AST_NODE_TYPES.AssignmentExpression,
|
|
22
|
+
AST_NODE_TYPES.SequenceExpression,
|
|
23
|
+
AST_NODE_TYPES.ArrowFunctionExpression,
|
|
24
|
+
AST_NODE_TYPES.YieldExpression,
|
|
17
25
|
]);
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
18
30
|
const needsWrappingInParent = (node) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
const { parent } = node;
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
switch (parent.type) {
|
|
35
|
+
case AST_NODE_TYPES.UnaryExpression:
|
|
36
|
+
case AST_NODE_TYPES.AwaitExpression:
|
|
37
|
+
case AST_NODE_TYPES.BinaryExpression:
|
|
38
|
+
case AST_NODE_TYPES.TSNonNullExpression:
|
|
39
|
+
return true;
|
|
40
|
+
case AST_NODE_TYPES.MemberExpression:
|
|
41
|
+
return parent.object === node;
|
|
42
|
+
case AST_NODE_TYPES.CallExpression:
|
|
43
|
+
case AST_NODE_TYPES.NewExpression:
|
|
44
|
+
return parent.callee === node;
|
|
45
|
+
default:
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
34
48
|
};
|
|
35
|
-
const preferComparisonOverNullishGuard = {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
export const preferComparisonOverNullishGuard = {
|
|
50
|
+
meta: {
|
|
51
|
+
type: 'suggestion',
|
|
52
|
+
docs: {
|
|
53
|
+
description: 'Prefer a direct `=== null` / `!== undefined` comparison over calling `isNull`, `isNotNull`, `isUndefined`, or `isNotUndefined` with an explicit argument (those guards are intended for point-free use such as `xs.filter(isNotUndefined)`).',
|
|
54
|
+
},
|
|
55
|
+
fixable: 'code',
|
|
56
|
+
schema: [],
|
|
57
|
+
messages: {
|
|
58
|
+
preferComparison: 'Prefer `{{comparison}}` over calling `{{name}}` with an explicit argument.',
|
|
59
|
+
},
|
|
40
60
|
},
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
61
|
+
create: (context) => {
|
|
62
|
+
const sourceCode = context.sourceCode;
|
|
63
|
+
const resolveGuard = buildCalleeResolver(getTsDataForgeImport(sourceCode.ast));
|
|
64
|
+
return {
|
|
65
|
+
CallExpression: (node) => {
|
|
66
|
+
if (node.arguments.length !== 1)
|
|
67
|
+
return;
|
|
68
|
+
const argument = node.arguments[0];
|
|
69
|
+
if (argument === undefined ||
|
|
70
|
+
argument.type === AST_NODE_TYPES.SpreadElement) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const resolved = resolveGuard(node.callee);
|
|
74
|
+
if (resolved === undefined)
|
|
75
|
+
return;
|
|
76
|
+
const spec = COMPARISON_GUARDS[resolved.canonicalName];
|
|
77
|
+
if (spec === undefined)
|
|
78
|
+
return;
|
|
79
|
+
const argRaw = sourceCode.getText(argument);
|
|
80
|
+
const argText = ARG_NEEDS_PARENS.has(argument.type)
|
|
81
|
+
? `(${argRaw})`
|
|
82
|
+
: argRaw;
|
|
83
|
+
const comparison = `${argText} ${spec.op} ${spec.literal}`;
|
|
84
|
+
const replacement = needsWrappingInParent(node)
|
|
85
|
+
? `(${comparison})`
|
|
86
|
+
: comparison;
|
|
87
|
+
context.report({
|
|
88
|
+
node,
|
|
89
|
+
messageId: 'preferComparison',
|
|
90
|
+
data: { comparison, name: resolved.canonicalName },
|
|
91
|
+
fix: (fixer) => fixer.replaceText(node, replacement),
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
defaultOptions: [],
|
|
77
97
|
};
|
|
78
|
-
|
|
79
|
-
export { preferComparisonOverNullishGuard };
|
|
80
|
-
//# sourceMappingURL=prefer-comparison-over-nullish-guard.mjs.map
|
|
98
|
+
//# sourceMappingURL=prefer-comparison-over-nullish-guard.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-comparison-over-nullish-guard.mjs","sources":["../../src/rules/prefer-comparison-over-nullish-guard.mts"],"
|
|
1
|
+
{"version":3,"file":"prefer-comparison-over-nullish-guard.mjs","sourceRoot":"","sources":["../../src/rules/prefer-comparison-over-nullish-guard.mts"],"names":[],"mappings":"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"}
|