eslint-plugin-ts-data-forge 0.8.6 → 0.9.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 +20 -19
- package/dist/plugin.d.mts +2 -0
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +1 -0
- package/dist/plugin.mjs.map +1 -1
- package/dist/rule-types.d.mts +16 -1
- package/dist/rule-types.d.mts.map +1 -1
- package/dist/rule-types.mjs.map +1 -1
- package/dist/rules/ast-utils.d.mts +21 -2
- package/dist/rules/ast-utils.d.mts.map +1 -1
- package/dist/rules/ast-utils.mjs +51 -4
- package/dist/rules/ast-utils.mjs.map +1 -1
- package/dist/rules/constants.d.mts +2 -0
- package/dist/rules/constants.d.mts.map +1 -1
- package/dist/rules/constants.mjs +9 -0
- package/dist/rules/constants.mjs.map +1 -1
- package/dist/rules/mutable-result.d.mts +10 -0
- package/dist/rules/mutable-result.d.mts.map +1 -0
- package/dist/rules/mutable-result.mjs +145 -0
- package/dist/rules/mutable-result.mjs.map +1 -0
- package/dist/rules/prefer-arr-generate.d.mts +57 -0
- package/dist/rules/prefer-arr-generate.d.mts.map +1 -0
- package/dist/rules/prefer-arr-generate.mjs +588 -0
- package/dist/rules/prefer-arr-generate.mjs.map +1 -0
- package/dist/rules/prefer-arr-uniq.d.mts.map +1 -1
- package/dist/rules/prefer-arr-uniq.mjs +2 -149
- package/dist/rules/prefer-arr-uniq.mjs.map +1 -1
- package/dist/rules/prefer-canonical-array-slicing.d.mts.map +1 -1
- package/dist/rules/prefer-canonical-array-slicing.mjs +47 -28
- package/dist/rules/prefer-canonical-array-slicing.mjs.map +1 -1
- package/dist/rules/prefer-is-non-null-object.d.mts.map +1 -1
- package/dist/rules/prefer-is-non-null-object.mjs +110 -40
- package/dist/rules/prefer-is-non-null-object.mjs.map +1 -1
- package/dist/rules/prefer-is-record-and-has-key.d.mts.map +1 -1
- package/dist/rules/prefer-is-record-and-has-key.mjs +102 -45
- package/dist/rules/prefer-is-record-and-has-key.mjs.map +1 -1
- package/dist/rules/prefer-range-for-loop.d.mts.map +1 -1
- package/dist/rules/prefer-range-for-loop.mjs +97 -16
- 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 +2 -0
- package/dist/rules/rules.mjs.map +1 -1
- package/package.json +11 -11
- package/src/plugin.mts +1 -0
- package/src/rule-types.mts +17 -1
- package/src/rules/ast-utils.mts +72 -5
- package/src/rules/constants.mts +12 -0
- package/src/rules/mutable-result.mts +204 -0
- package/src/rules/prefer-arr-generate.mts +918 -0
- package/src/rules/prefer-arr-generate.test.mts +967 -0
- package/src/rules/prefer-arr-is-bounded-length-array.test.mts +19 -0
- package/src/rules/prefer-arr-is-max-length-array.test.mts +8 -0
- package/src/rules/prefer-arr-is-min-length-array.test.mts +8 -0
- package/src/rules/prefer-arr-uniq.mts +2 -207
- package/src/rules/prefer-canonical-array-slicing.mts +57 -40
- package/src/rules/prefer-canonical-array-slicing.test.mts +55 -0
- package/src/rules/prefer-is-non-null-object.mts +160 -57
- package/src/rules/prefer-is-non-null-object.test.mts +138 -0
- package/src/rules/prefer-is-record-and-has-key.mts +148 -67
- package/src/rules/prefer-is-record-and-has-key.test.mts +118 -23
- package/src/rules/prefer-range-for-loop.mts +142 -21
- package/src/rules/prefer-range-for-loop.test.mts +127 -5
- 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, ASTUtils, } from '@typescript-eslint/utils';
|
|
2
2
|
import { buildNamedImportFix, getArgumentText, getImportDeclarations, getNamedImports, skipTypeWrappers, } from 'eslint-rule-forge';
|
|
3
3
|
import { Arr } from 'ts-data-forge';
|
|
4
4
|
import { TS_DATA_FORGE_MODULE } from './constants.mjs';
|
|
@@ -18,6 +18,27 @@ export const preferRangeForLoop = {
|
|
|
18
18
|
const sourceCode = context.sourceCode;
|
|
19
19
|
const program = sourceCode.ast;
|
|
20
20
|
const tsDataForgeImports = getImportDeclarations(program, TS_DATA_FORGE_MODULE);
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
const isStableBound = (
|
|
28
|
+
|
|
29
|
+
wrapped) => {
|
|
30
|
+
const node = skipTypeWrappers(wrapped);
|
|
31
|
+
if (node.type === AST_NODE_TYPES.Literal) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
if (node.type !== AST_NODE_TYPES.Identifier) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
const variable = ASTUtils.findVariable(sourceCode.getScope(node), node.name);
|
|
38
|
+
return (variable !== null &&
|
|
39
|
+
variable.defs.length > 0 &&
|
|
40
|
+
variable.references.every((reference) => !reference.isWrite() || reference.init === true));
|
|
41
|
+
};
|
|
21
42
|
const mut_nodesToFix = [];
|
|
22
43
|
return {
|
|
23
44
|
ForStatement: (node) => {
|
|
@@ -35,6 +56,10 @@ export const preferRangeForLoop = {
|
|
|
35
56
|
const varName = declaration.id.name;
|
|
36
57
|
const beginExpr = declaration.init;
|
|
37
58
|
|
|
59
|
+
if (isNonIntegerNumberLiteral(beginExpr)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
38
63
|
|
|
39
64
|
|
|
40
65
|
if (node.test?.type !== AST_NODE_TYPES.BinaryExpression ||
|
|
@@ -72,21 +97,35 @@ export const preferRangeForLoop = {
|
|
|
72
97
|
if (stepValue.type === AST_NODE_TYPES.Literal) {
|
|
73
98
|
const stepNum = stepValue.value;
|
|
74
99
|
|
|
75
|
-
if (typeof stepNum === 'number' &&
|
|
100
|
+
if (typeof stepNum === 'number' &&
|
|
101
|
+
Number.isSafeInteger(stepNum) &&
|
|
102
|
+
stepNum > 0) {
|
|
76
103
|
mut_step = stepNum === 1 ? undefined : stepNum.toString();
|
|
77
104
|
}
|
|
78
105
|
else {
|
|
79
106
|
return;
|
|
80
107
|
}
|
|
81
108
|
}
|
|
109
|
+
else if (isNegatedLiteral(stepValue)) {
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
82
115
|
else {
|
|
83
116
|
|
|
117
|
+
|
|
118
|
+
|
|
84
119
|
mut_step = getArgumentText(sourceCode, node.update.right);
|
|
85
120
|
}
|
|
86
121
|
}
|
|
87
122
|
else {
|
|
88
123
|
return;
|
|
89
124
|
}
|
|
125
|
+
|
|
126
|
+
if (isAssignedOutsideUpdate(sourceCode, declaration, node.update)) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
90
129
|
const beginText = getArgumentText(sourceCode, beginExpr);
|
|
91
130
|
const endText = getArgumentText(sourceCode, endExpr);
|
|
92
131
|
mut_nodesToFix.push({
|
|
@@ -95,12 +134,17 @@ export const preferRangeForLoop = {
|
|
|
95
134
|
begin: beginText,
|
|
96
135
|
end: endText,
|
|
97
136
|
step: mut_step,
|
|
137
|
+
fixable: isStableBound(endExpr) &&
|
|
138
|
+
(node.update.type !== AST_NODE_TYPES.AssignmentExpression ||
|
|
139
|
+
isStableBound(node.update.right)),
|
|
98
140
|
});
|
|
99
141
|
},
|
|
100
142
|
'Program:exit': () => {
|
|
101
143
|
const namedImports = getNamedImports(tsDataForgeImports);
|
|
102
144
|
const hasRangeImport = namedImports.includes('range');
|
|
103
|
-
|
|
145
|
+
const importCarrier = mut_nodesToFix.find(({ fixable }) => fixable);
|
|
146
|
+
for (const entry of mut_nodesToFix) {
|
|
147
|
+
const { node, varName, begin, end, step } = entry;
|
|
104
148
|
context.report({
|
|
105
149
|
node: node.init ?? node,
|
|
106
150
|
messageId: 'useRangeForLoop',
|
|
@@ -109,19 +153,22 @@ export const preferRangeForLoop = {
|
|
|
109
153
|
begin,
|
|
110
154
|
end,
|
|
111
155
|
},
|
|
112
|
-
fix:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
156
|
+
fix: entry.fixable
|
|
157
|
+
? (fixer) => {
|
|
158
|
+
const bodyText = sourceCode.getText(node.body);
|
|
159
|
+
const rangeArgs = step === undefined
|
|
160
|
+
? `${begin}, ${end}`
|
|
161
|
+
: `${begin}, ${end}, ${step}`;
|
|
162
|
+
const replacement = `for (const ${varName} of range(${rangeArgs})) ${bodyText}`;
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
const importFixes = entry === importCarrier && !hasRangeImport
|
|
167
|
+
? buildNamedImportFix(fixer, program, TS_DATA_FORGE_MODULE, ['range'])
|
|
168
|
+
: [];
|
|
169
|
+
return [...importFixes, fixer.replaceText(node, replacement)];
|
|
170
|
+
}
|
|
171
|
+
: undefined,
|
|
125
172
|
});
|
|
126
173
|
}
|
|
127
174
|
},
|
|
@@ -129,4 +176,38 @@ export const preferRangeForLoop = {
|
|
|
129
176
|
},
|
|
130
177
|
defaultOptions: [],
|
|
131
178
|
};
|
|
179
|
+
const isNegatedLiteral = (
|
|
180
|
+
|
|
181
|
+
node) => node.type === AST_NODE_TYPES.UnaryExpression &&
|
|
182
|
+
node.operator === '-' &&
|
|
183
|
+
skipTypeWrappers(node.argument).type === AST_NODE_TYPES.Literal;
|
|
184
|
+
const isNonIntegerNumberLiteral = (
|
|
185
|
+
|
|
186
|
+
wrapped) => {
|
|
187
|
+
const node = skipTypeWrappers(wrapped);
|
|
188
|
+
return (node.type === AST_NODE_TYPES.Literal &&
|
|
189
|
+
typeof node.value === 'number' &&
|
|
190
|
+
!Number.isSafeInteger(node.value));
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
const isAssignedOutsideUpdate = (
|
|
197
|
+
|
|
198
|
+
sourceCode,
|
|
199
|
+
|
|
200
|
+
declaration,
|
|
201
|
+
|
|
202
|
+
update) => sourceCode
|
|
203
|
+
.getDeclaredVariables(declaration)
|
|
204
|
+
.flatMap((variable) => variable.references)
|
|
205
|
+
.some((reference) => reference.isWrite() &&
|
|
206
|
+
reference.init !== true &&
|
|
207
|
+
!isWithin(reference.identifier, update));
|
|
208
|
+
const isWithin = (
|
|
209
|
+
|
|
210
|
+
node,
|
|
211
|
+
|
|
212
|
+
container) => container.range[0] <= node.range[0] && node.range[1] <= container.range[1];
|
|
132
213
|
//# sourceMappingURL=prefer-range-for-loop.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-range-for-loop.mjs","sourceRoot":"","sources":["../../src/rules/prefer-range-for-loop.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,
|
|
1
|
+
{"version":3,"file":"prefer-range-for-loop.mjs","sourceRoot":"","sources":["../../src/rules/prefer-range-for-loop.mts"],"names":[],"mappings":"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"}
|
package/dist/rules/rules.d.mts
CHANGED
|
@@ -2,6 +2,7 @@ export declare const tsDataForgeRules: {
|
|
|
2
2
|
readonly 'prefer-canonical-array-slicing': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferCanonicalArraySlicing", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
3
3
|
readonly 'prefer-canonical-length-guard': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useCanonicalGuard" | "useIsBoundedLengthArray" | "useIsFixedLengthArray" | "useIsMaxLengthArray" | "useIsMinLengthArray" | "useIsNonEmpty", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
4
4
|
readonly 'prefer-canonical-length-cast': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useCanonicalCast", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
5
|
+
readonly 'prefer-arr-generate': import("@typescript-eslint/utils/ts-eslint").RuleModule<"suggestArrGenerate" | "useArrGenerate", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
5
6
|
readonly 'prefer-arr-is-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useArrIsArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
6
7
|
readonly 'prefer-arr-scan': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferArrScanAccumulate" | "preferArrScanPrefix", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
7
8
|
readonly 'prefer-arr-sum': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useArrSum" | "useArrSumBy", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../../src/rules/rules.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../../src/rules/rules.mts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;CAmBa,CAAC"}
|
package/dist/rules/rules.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { noSideEffectImport } from './no-side-effect-import.mjs';
|
|
2
2
|
import { noUnnecessaryTypeGuard } from './no-unnecessary-type-guard.mjs';
|
|
3
|
+
import { preferArrGenerate } from './prefer-arr-generate.mjs';
|
|
3
4
|
import { preferArrIsArray } from './prefer-arr-is-array.mjs';
|
|
4
5
|
import { preferArrScan } from './prefer-arr-scan.mjs';
|
|
5
6
|
import { preferArrSum } from './prefer-arr-sum.mjs';
|
|
@@ -19,6 +20,7 @@ export const tsDataForgeRules = {
|
|
|
19
20
|
'prefer-canonical-array-slicing': preferCanonicalArraySlicing,
|
|
20
21
|
'prefer-canonical-length-guard': preferCanonicalLengthGuard,
|
|
21
22
|
'prefer-canonical-length-cast': preferCanonicalLengthCast,
|
|
23
|
+
'prefer-arr-generate': preferArrGenerate,
|
|
22
24
|
'prefer-arr-is-array': preferArrIsArray,
|
|
23
25
|
'prefer-arr-scan': preferArrScan,
|
|
24
26
|
'prefer-arr-sum': preferArrSum,
|
package/dist/rules/rules.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.mjs","sourceRoot":"","sources":["../../src/rules/rules.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,gCAAgC,EAAE,MAAM,4CAA4C,CAAC;AAC9F,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,6BAA6B,EAAE,MAAM,0CAA0C,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,gCAAgC,EAAE,2BAA2B;IAC7D,+BAA+B,EAAE,0BAA0B;IAC3D,8BAA8B,EAAE,yBAAyB;IACzD,qBAAqB,EAAE,gBAAgB;IACvC,iBAAiB,EAAE,aAAa;IAChC,gBAAgB,EAAE,YAAY;IAC9B,iBAAiB,EAAE,aAAa;IAChC,eAAe,EAAE,WAAW;IAC5B,2BAA2B,EAAE,qBAAqB;IAClD,uBAAuB,EAAE,kBAAkB;IAC3C,8BAA8B,EAAE,uBAAuB;IACvD,2BAA2B,EAAE,qBAAqB;IAClD,6BAA6B,EAAE,uBAAuB;IACtD,uBAAuB,EAAE,kBAAkB;IAC3C,2BAA2B,EAAE,sBAAsB;IACnD,sCAAsC,EAAE,gCAAgC;IACxE,oCAAoC,EAAE,6BAA6B;CAC3B,CAAC"}
|
|
1
|
+
{"version":3,"file":"rules.mjs","sourceRoot":"","sources":["../../src/rules/rules.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,gCAAgC,EAAE,MAAM,4CAA4C,CAAC;AAC9F,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,6BAA6B,EAAE,MAAM,0CAA0C,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,gCAAgC,EAAE,2BAA2B;IAC7D,+BAA+B,EAAE,0BAA0B;IAC3D,8BAA8B,EAAE,yBAAyB;IACzD,qBAAqB,EAAE,iBAAiB;IACxC,qBAAqB,EAAE,gBAAgB;IACvC,iBAAiB,EAAE,aAAa;IAChC,gBAAgB,EAAE,YAAY;IAC9B,iBAAiB,EAAE,aAAa;IAChC,eAAe,EAAE,WAAW;IAC5B,2BAA2B,EAAE,qBAAqB;IAClD,uBAAuB,EAAE,kBAAkB;IAC3C,8BAA8B,EAAE,uBAAuB;IACvD,2BAA2B,EAAE,qBAAqB;IAClD,6BAA6B,EAAE,uBAAuB;IACtD,uBAAuB,EAAE,kBAAkB;IAC3C,2BAA2B,EAAE,sBAAsB;IACnD,sCAAsC,EAAE,gCAAgC;IACxE,oCAAoC,EAAE,6BAA6B;CAC3B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-ts-data-forge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "ESLint rules that steer code toward ts-data-forge idioms.",
|
|
6
6
|
"keywords": [
|
|
@@ -35,17 +35,17 @@
|
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@typescript-eslint/utils": "^8.71.1",
|
|
38
|
-
"eslint-rule-forge": "^0.
|
|
39
|
-
"ts-data-forge": "14.
|
|
38
|
+
"eslint-rule-forge": "^0.2.0",
|
|
39
|
+
"ts-data-forge": "14.8.0",
|
|
40
40
|
"ts-type-forge": "~9.2.2"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@typescript-eslint/parser": "8.71.1",
|
|
44
44
|
"@typescript-eslint/rule-tester": "8.71.1",
|
|
45
45
|
"dedent": "1.7.2",
|
|
46
|
-
"eslint-config-typed": "5.
|
|
47
|
-
"eslint-plugin-ts-fortress": "1.3.
|
|
48
|
-
"eslint-plugin-ts-type-forge": "0.8.
|
|
46
|
+
"eslint-config-typed": "5.19.0",
|
|
47
|
+
"eslint-plugin-ts-fortress": "1.3.4",
|
|
48
|
+
"eslint-plugin-ts-type-forge": "0.8.9",
|
|
49
49
|
"prettier": "3.9.9",
|
|
50
50
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
51
51
|
"prettier-plugin-packagejson": "3.0.2",
|
|
@@ -67,15 +67,15 @@
|
|
|
67
67
|
"build": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/build.mts",
|
|
68
68
|
"check:dist:branded-number-types": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/check-branded-number-types.mts",
|
|
69
69
|
"check:ext": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/check-ext.mts",
|
|
70
|
-
"check:lint": "oxlint . && eslint .",
|
|
71
|
-
"check:test": "pnpm run z:vitest run",
|
|
72
|
-
"check:test:cov": "pnpm run z:vitest run --coverage",
|
|
73
|
-
"check:types": "node ../../node_modules/typescript-native/bin/tsc --noEmit",
|
|
70
|
+
"check:lint": "node ../../tools/scripts/cmd/assert-deps-built.mts && oxlint . && eslint .",
|
|
71
|
+
"check:test": "node ../../tools/scripts/cmd/assert-deps-built.mts && pnpm run z:vitest run",
|
|
72
|
+
"check:test:cov": "node ../../tools/scripts/cmd/assert-deps-built.mts && pnpm run z:vitest run --coverage",
|
|
73
|
+
"check:types": "node ../../tools/scripts/cmd/assert-deps-built.mts && node ../../node_modules/typescript-native/bin/tsc --noEmit",
|
|
74
74
|
"doc": "pnpm run gen:readme",
|
|
75
75
|
"fix:fmt": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/format-uncommitted.mts --cwd .",
|
|
76
76
|
"fix:fmt:diff": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/format-diff-from.mts origin/main --cwd .",
|
|
77
77
|
"fix:fmt:full": "pnpm run z:prettier .",
|
|
78
|
-
"fix:lint": "oxlint . --fix && eslint . --fix",
|
|
78
|
+
"fix:lint": "node ../../tools/scripts/cmd/assert-deps-built.mts && oxlint . --fix && eslint . --fix",
|
|
79
79
|
"fmt": "pnpm run fix:fmt",
|
|
80
80
|
"gen": "run-s gen:rule-types gen:readme",
|
|
81
81
|
"gen:readme": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/embed-examples.mts",
|
package/src/plugin.mts
CHANGED
|
@@ -13,6 +13,7 @@ const recommendedRules = {
|
|
|
13
13
|
'ts-data-forge/prefer-canonical-array-slicing': 'error',
|
|
14
14
|
'ts-data-forge/prefer-canonical-length-cast': 'error',
|
|
15
15
|
'ts-data-forge/prefer-canonical-length-guard': 'error',
|
|
16
|
+
'ts-data-forge/prefer-arr-generate': 'error',
|
|
16
17
|
'ts-data-forge/prefer-arr-is-array': 'error',
|
|
17
18
|
'ts-data-forge/prefer-arr-scan': 'error',
|
|
18
19
|
'ts-data-forge/prefer-arr-sum': 'error',
|
package/src/rule-types.mts
CHANGED
|
@@ -72,6 +72,21 @@ namespace PreferCanonicalLengthCast {
|
|
|
72
72
|
export type RuleEntry = Linter.StringSeverity;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* @description Replace the filter-and-map idiom `xs.flatMap((x) => (cond ? [f(x)] : []))` with a loop in `Arr.generate` from ts-data-forge.
|
|
77
|
+
*
|
|
78
|
+
* ```md
|
|
79
|
+
* | key | value |
|
|
80
|
+
* | :--------- | :--------- |
|
|
81
|
+
* | type | suggestion |
|
|
82
|
+
* | deprecated | false |
|
|
83
|
+
* | fixable | code |
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
namespace PreferArrGenerate {
|
|
87
|
+
export type RuleEntry = Linter.StringSeverity;
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
/**
|
|
76
91
|
* @description Replace `Array.isArray` with `Arr.isArray` from ts-data-forge.
|
|
77
92
|
*
|
|
@@ -148,7 +163,7 @@ namespace PreferAsInt {
|
|
|
148
163
|
}
|
|
149
164
|
|
|
150
165
|
/**
|
|
151
|
-
* @description Replace `typeof u === "object" && u !== null
|
|
166
|
+
* @description Replace `typeof u === "object" && u !== null`, in either order and anywhere in an `&&` chain, with `isNonNullObject(u)` from ts-data-forge.
|
|
152
167
|
*
|
|
153
168
|
* ```md
|
|
154
169
|
* | key | value |
|
|
@@ -314,6 +329,7 @@ export type EslintTsDataForgeRules = Readonly<{
|
|
|
314
329
|
'ts-data-forge/prefer-canonical-array-slicing': PreferCanonicalArraySlicing.RuleEntry;
|
|
315
330
|
'ts-data-forge/prefer-canonical-length-guard': PreferCanonicalLengthGuard.RuleEntry;
|
|
316
331
|
'ts-data-forge/prefer-canonical-length-cast': PreferCanonicalLengthCast.RuleEntry;
|
|
332
|
+
'ts-data-forge/prefer-arr-generate': PreferArrGenerate.RuleEntry;
|
|
317
333
|
'ts-data-forge/prefer-arr-is-array': PreferArrIsArray.RuleEntry;
|
|
318
334
|
'ts-data-forge/prefer-arr-scan': PreferArrScan.RuleEntry;
|
|
319
335
|
'ts-data-forge/prefer-arr-sum': PreferArrSum.RuleEntry;
|
package/src/rules/ast-utils.mts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
typeWrapperLayers,
|
|
11
11
|
} from 'eslint-rule-forge';
|
|
12
12
|
import type { DeepReadonly } from 'ts-type-forge';
|
|
13
|
+
import type * as ts from 'typescript';
|
|
13
14
|
|
|
14
15
|
const isIntegerLiteral = (node: DeepReadonly<TSESTree.Expression>): boolean =>
|
|
15
16
|
node.type === AST_NODE_TYPES.Literal &&
|
|
@@ -144,7 +145,8 @@ type LengthComparison = Readonly<{
|
|
|
144
145
|
* Parses a `<array>.length <op> <bound>` comparison (in either operand order)
|
|
145
146
|
* into its array expression, integer-literal/`const` `bound`, and whether it is
|
|
146
147
|
* a lower (`min`) or upper (`max`) length bound. Returns `undefined` for
|
|
147
|
-
* anything else. The length and the bound are read through
|
|
148
|
+
* anything else. The comparison, the length and the bound are read through
|
|
149
|
+
* type wrappers; the
|
|
148
150
|
* `bound` returned is the literal or constant underneath, since a wrapper such
|
|
149
151
|
* as `as number` would only widen the length a guard narrows to.
|
|
150
152
|
*
|
|
@@ -153,10 +155,12 @@ type LengthComparison = Readonly<{
|
|
|
153
155
|
*/
|
|
154
156
|
export const parseLengthComparison = (
|
|
155
157
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
156
|
-
|
|
158
|
+
wrapped: TSESTree.Expression,
|
|
157
159
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
158
160
|
sourceCode: TSESLint.SourceCode,
|
|
159
161
|
): LengthComparison | undefined => {
|
|
162
|
+
const node = skipTypeWrappers(wrapped);
|
|
163
|
+
|
|
160
164
|
if (
|
|
161
165
|
node.type !== AST_NODE_TYPES.BinaryExpression ||
|
|
162
166
|
(node.operator !== '>=' && node.operator !== '<=')
|
|
@@ -201,6 +205,8 @@ export const parseLengthComparison = (
|
|
|
201
205
|
* together form the `Arr.isBoundedLengthArray` pattern handled by
|
|
202
206
|
* `prefer-arr-is-bounded-length-array`. The single-bound rules skip such
|
|
203
207
|
* operands so the bounded rule rewrites the whole `&&` instead of the parts.
|
|
208
|
+
* A comparison wrapped as a whole (`(xs.length >= 1 satisfies boolean) && …`)
|
|
209
|
+
* is still the operand.
|
|
204
210
|
*/
|
|
205
211
|
export const isPartOfBoundedLengthCheck = (
|
|
206
212
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
@@ -208,10 +214,12 @@ export const isPartOfBoundedLengthCheck = (
|
|
|
208
214
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
209
215
|
sourceCode: TSESLint.SourceCode,
|
|
210
216
|
): boolean => {
|
|
211
|
-
const
|
|
217
|
+
const operand = outermostTypeWrapper(node);
|
|
218
|
+
|
|
219
|
+
const { parent } = operand;
|
|
212
220
|
|
|
213
221
|
if (
|
|
214
|
-
parent
|
|
222
|
+
parent?.type !== AST_NODE_TYPES.LogicalExpression ||
|
|
215
223
|
parent.operator !== '&&'
|
|
216
224
|
) {
|
|
217
225
|
return false;
|
|
@@ -223,7 +231,7 @@ export const isPartOfBoundedLengthCheck = (
|
|
|
223
231
|
return false;
|
|
224
232
|
}
|
|
225
233
|
|
|
226
|
-
const sibling = parent.left ===
|
|
234
|
+
const sibling = parent.left === operand ? parent.right : parent.left;
|
|
227
235
|
|
|
228
236
|
const other = parseLengthComparison(sibling, sourceCode);
|
|
229
237
|
|
|
@@ -237,6 +245,15 @@ export const isPartOfBoundedLengthCheck = (
|
|
|
237
245
|
);
|
|
238
246
|
};
|
|
239
247
|
|
|
248
|
+
/** The outermost of the type wrappers around `node`, or `node` itself. */
|
|
249
|
+
const outermostTypeWrapper = (
|
|
250
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
251
|
+
node: TSESTree.Node,
|
|
252
|
+
): TSESTree.Node =>
|
|
253
|
+
node.parent !== undefined && isTypeWrapper(node.parent)
|
|
254
|
+
? outermostTypeWrapper(node.parent)
|
|
255
|
+
: node;
|
|
256
|
+
|
|
240
257
|
type VariableDefinition = Readonly<{
|
|
241
258
|
type: string;
|
|
242
259
|
parent: TSESTree.VariableDeclaration;
|
|
@@ -248,3 +265,53 @@ const isVariableDefinition = (
|
|
|
248
265
|
def: TSESLint.Scope.Definition,
|
|
249
266
|
): def is TSESLint.Scope.Definition & VariableDefinition =>
|
|
250
267
|
(def.type as string) === 'Variable';
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Whether `node`, read through its type wrappers, is the identifier `name`:
|
|
271
|
+
* `Number`, `(Number as NumberConstructor)` and `Number!` are the same object.
|
|
272
|
+
*/
|
|
273
|
+
export const isIdentifierNamed = (
|
|
274
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
275
|
+
node: TSESTree.Node,
|
|
276
|
+
name: string,
|
|
277
|
+
): boolean => {
|
|
278
|
+
const unwrapped = skipTypeWrappers(node);
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
unwrapped.type === AST_NODE_TYPES.Identifier && unwrapped.name === name
|
|
282
|
+
);
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Whether `holds` is true of the type of every layer of type wrappers around
|
|
287
|
+
* `node` (see {@link typeWrapperLayers}), outermost to innermost; `false`
|
|
288
|
+
* without type information. A widening cast (`s as string | number`) is the
|
|
289
|
+
* author saying the value may be more than it is, and is respected; a
|
|
290
|
+
* narrowing one (`s!`, `v as unknown as string`) cannot vouch for what the
|
|
291
|
+
* value's own type does not, and the fix, which keeps the wrapper, has to
|
|
292
|
+
* type-check at every layer too.
|
|
293
|
+
*/
|
|
294
|
+
export const typeHoldsAtEveryLayer = (
|
|
295
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
296
|
+
sourceCode: TSESLint.SourceCode,
|
|
297
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
298
|
+
node: TSESTree.Node,
|
|
299
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
300
|
+
holds: (type: ts.Type, typeChecker: ts.TypeChecker) => boolean,
|
|
301
|
+
): boolean => {
|
|
302
|
+
const services = sourceCode.parserServices;
|
|
303
|
+
|
|
304
|
+
const checker = services?.program?.getTypeChecker();
|
|
305
|
+
|
|
306
|
+
if (checker === undefined) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return typeWrapperLayers(node).every((layer) => {
|
|
311
|
+
const tsNode = services?.esTreeNodeToTSNodeMap?.get(layer);
|
|
312
|
+
|
|
313
|
+
return (
|
|
314
|
+
tsNode !== undefined && holds(checker.getTypeAtLocation(tsNode), checker)
|
|
315
|
+
);
|
|
316
|
+
});
|
|
317
|
+
};
|
package/src/rules/constants.mts
CHANGED
|
@@ -1 +1,13 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
|
|
1
3
|
export const TS_DATA_FORGE_MODULE = 'ts-data-forge';
|
|
4
|
+
|
|
5
|
+
/** The type flags of every primitive type but `void`. */
|
|
6
|
+
export const primitiveTypeFlags =
|
|
7
|
+
ts.TypeFlags.StringLike |
|
|
8
|
+
ts.TypeFlags.NumberLike |
|
|
9
|
+
ts.TypeFlags.BigIntLike |
|
|
10
|
+
ts.TypeFlags.BooleanLike |
|
|
11
|
+
ts.TypeFlags.ESSymbolLike |
|
|
12
|
+
ts.TypeFlags.Null |
|
|
13
|
+
ts.TypeFlags.Undefined;
|