eslint-plugin-ziloen 0.1.20 → 0.1.22
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/dist/index.d.ts +0 -3
- package/dist/index.js +96 -69
- package/package.json +12 -10
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { ASTUtils, AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
import { getConstrainedTypeAtLocation } from "@typescript-eslint/type-utils";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// src/utils.ts
|
|
5
5
|
const createEslintRule = ESLintUtils.RuleCreator((ruleName) => ruleName);
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
//#region src/rules/generic-spacing.ts
|
|
7
|
+
// src/rules/generic-spacing.ts
|
|
9
8
|
const RULE_NAME$1 = "generic-spacing";
|
|
10
9
|
const messageId = "genericSpacingMismatch";
|
|
11
10
|
function removeSpaceAround(node, context) {
|
|
@@ -85,31 +84,14 @@ const genericSpacing = createEslintRule({
|
|
|
85
84
|
}
|
|
86
85
|
});
|
|
87
86
|
|
|
88
|
-
|
|
89
|
-
//#region src/tsutils.ts
|
|
90
|
-
function isUnionType(type) {
|
|
91
|
-
return (type.flags & TypeFlags.Union) !== 0;
|
|
92
|
-
}
|
|
93
|
-
/** Returns all types of a union type or an array containing `type` itself if it's no union type. */
|
|
94
|
-
function unionTypeParts(type) {
|
|
95
|
-
return isUnionType(type) ? type.types : [type];
|
|
96
|
-
}
|
|
97
|
-
function isTypeFlagSet(type, flag) {
|
|
98
|
-
return (type.flags & flag) !== 0;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Resolves the given node's type. Will resolve to the type's generic constraint, if it has one.
|
|
102
|
-
*/
|
|
103
|
-
function getConstrainedTypeAtLocation(checker, node) {
|
|
104
|
-
const nodeType = checker.getTypeAtLocation(node);
|
|
105
|
-
return checker.getBaseConstraintOfType(nodeType) ?? nodeType;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
//#endregion
|
|
109
|
-
//#region src/rules/jsx-strict-logical-expressions.ts
|
|
87
|
+
// src/rules/jsx-strict-logical-expressions.ts
|
|
110
88
|
/**
|
|
111
89
|
* https://github.com/hluisson/eslint-plugin-jsx-expressions
|
|
112
90
|
*/
|
|
91
|
+
const messages = {
|
|
92
|
+
conditionErrorFalseyString: "Potentially falsy string in logical AND expression. Please use a boolean.",
|
|
93
|
+
conditionErrorFalseyNumber: "Potentially falsy number or bigint in logical AND expression. Please use a boolean."
|
|
94
|
+
};
|
|
113
95
|
const RULE_NAME = "jsx-strict-logical-expressions";
|
|
114
96
|
const jsxStrictLogicalExpressions = createEslintRule({
|
|
115
97
|
name: RULE_NAME,
|
|
@@ -118,9 +100,9 @@ const jsxStrictLogicalExpressions = createEslintRule({
|
|
|
118
100
|
allowNumber: false
|
|
119
101
|
}],
|
|
120
102
|
meta: {
|
|
121
|
-
docs: { description: "Forbid non-boolean falsey values in inline expressions" },
|
|
122
|
-
fixable: "code",
|
|
123
103
|
type: "problem",
|
|
104
|
+
fixable: "code",
|
|
105
|
+
docs: { description: "Forbid non-boolean falsy values in JSX logical AND expressions" },
|
|
124
106
|
schema: [{
|
|
125
107
|
type: "object",
|
|
126
108
|
properties: {
|
|
@@ -129,57 +111,102 @@ const jsxStrictLogicalExpressions = createEslintRule({
|
|
|
129
111
|
},
|
|
130
112
|
additionalProperties: false
|
|
131
113
|
}],
|
|
132
|
-
messages
|
|
133
|
-
conditionErrorFalseyString: "Potentially falsey string in logical AND expression. Please use boolean.",
|
|
134
|
-
conditionErrorFalseyNumber: "Potentially falsey number in logical AND expression. Please use boolean."
|
|
135
|
-
}
|
|
114
|
+
messages
|
|
136
115
|
},
|
|
137
|
-
create(context, [
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
function
|
|
148
|
-
|
|
149
|
-
if (errorId) context.report({
|
|
150
|
-
node,
|
|
151
|
-
messageId: errorId,
|
|
152
|
-
fix: (fixer) => fixer.insertTextBefore(fixNode, "!!")
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
function determineNode(originalNode) {
|
|
156
|
-
let nodeToEvaluate = originalNode;
|
|
157
|
-
if (nodeToEvaluate.type === TSESTree.AST_NODE_TYPES.ChainExpression) nodeToEvaluate = nodeToEvaluate.expression;
|
|
158
|
-
if (nodeToEvaluate.type === TSESTree.AST_NODE_TYPES.MemberExpression && nodeToEvaluate.property.type !== TSESTree.AST_NODE_TYPES.PrivateIdentifier) nodeToEvaluate = nodeToEvaluate.property;
|
|
159
|
-
return nodeToEvaluate;
|
|
116
|
+
create(context, [{ allowString = false, allowNumber = false }]) {
|
|
117
|
+
const services = ESLintUtils.getParserServices(context);
|
|
118
|
+
const checker = services.program.getTypeChecker();
|
|
119
|
+
const stringType = checker.getStringType();
|
|
120
|
+
const numberType = checker.getNumberType();
|
|
121
|
+
const bigintType = checker.getBigIntType();
|
|
122
|
+
/**
|
|
123
|
+
* Returns true when `value` is TypeScript's internal representation
|
|
124
|
+
* of a non-zero bigint literal.
|
|
125
|
+
*/
|
|
126
|
+
function isNonZeroBigIntLiteralValue(value) {
|
|
127
|
+
return typeof value === "object" && value !== null && "base10Value" in value && typeof value.base10Value === "string" && value.base10Value !== "0";
|
|
160
128
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Determines which diagnostic, if any, applies to a condition.
|
|
131
|
+
*/
|
|
132
|
+
function getConditionError(node) {
|
|
133
|
+
const type = getConstrainedTypeAtLocation(services, node);
|
|
134
|
+
const typeParts = type.isUnion() ? type.types : [type];
|
|
135
|
+
let hasPotentiallyFalsyString = false;
|
|
136
|
+
let hasPotentiallyFalsyNumber = false;
|
|
137
|
+
for (const typePart of typeParts) {
|
|
138
|
+
if (typePart.isStringLiteral()) {
|
|
139
|
+
hasPotentiallyFalsyString ||= typePart.value === "";
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (typePart.isNumberLiteral()) {
|
|
143
|
+
hasPotentiallyFalsyNumber ||= typePart.value === 0;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const baseType = checker.getBaseTypeOfLiteralType(typePart);
|
|
147
|
+
if (baseType === stringType) {
|
|
148
|
+
hasPotentiallyFalsyString = true;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (baseType === numberType) {
|
|
152
|
+
hasPotentiallyFalsyNumber = true;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (baseType === bigintType) {
|
|
156
|
+
const value = "value" in typePart ? typePart.value : void 0;
|
|
157
|
+
hasPotentiallyFalsyNumber ||= !isNonZeroBigIntLiteralValue(value);
|
|
158
|
+
}
|
|
168
159
|
}
|
|
160
|
+
if (!allowString && hasPotentiallyFalsyString) return "conditionErrorFalseyString";
|
|
161
|
+
if (!allowNumber && hasPotentiallyFalsyNumber) return "conditionErrorFalseyNumber";
|
|
169
162
|
}
|
|
170
|
-
|
|
171
|
-
|
|
163
|
+
/**
|
|
164
|
+
* Flattens:
|
|
165
|
+
*
|
|
166
|
+
* a && b && <Component />
|
|
167
|
+
*
|
|
168
|
+
* into:
|
|
169
|
+
*
|
|
170
|
+
* [a, b, <Component />]
|
|
171
|
+
*
|
|
172
|
+
* Every operand except the final one acts as a condition.
|
|
173
|
+
*/
|
|
174
|
+
function collectAndOperands(node, operands = []) {
|
|
175
|
+
if (node.type === AST_NODE_TYPES.LogicalExpression && node.operator === "&&") {
|
|
176
|
+
collectAndOperands(node.left, operands);
|
|
177
|
+
collectAndOperands(node.right, operands);
|
|
178
|
+
} else operands.push(node);
|
|
179
|
+
return operands;
|
|
180
|
+
}
|
|
181
|
+
function reportCondition(node) {
|
|
182
|
+
const messageId = getConditionError(node);
|
|
183
|
+
if (!messageId) return;
|
|
184
|
+
context.report({
|
|
185
|
+
node,
|
|
186
|
+
messageId,
|
|
187
|
+
fix: (fixer) => {
|
|
188
|
+
if (node.type === AST_NODE_TYPES.Identifier || node.type === AST_NODE_TYPES.Literal || node.type === AST_NODE_TYPES.MemberExpression || node.type === AST_NODE_TYPES.CallExpression || node.type === AST_NODE_TYPES.ChainExpression) return fixer.insertTextBefore(node, "!!");
|
|
189
|
+
if (ASTUtils.isParenthesized(node, context.sourceCode)) {
|
|
190
|
+
const openingParen = context.sourceCode.getTokenBefore(node);
|
|
191
|
+
if (openingParen?.value === "(") return fixer.insertTextBefore(openingParen, "!!");
|
|
192
|
+
}
|
|
193
|
+
return [fixer.insertTextBefore(node, "!!("), fixer.insertTextAfter(node, ")")];
|
|
194
|
+
}
|
|
195
|
+
});
|
|
172
196
|
}
|
|
173
|
-
return { JSXExpressionContainer
|
|
197
|
+
return { JSXExpressionContainer(node) {
|
|
198
|
+
const expression = node.expression;
|
|
199
|
+
if (expression.type !== AST_NODE_TYPES.LogicalExpression || expression.operator !== "&&") return;
|
|
200
|
+
const operands = collectAndOperands(expression);
|
|
201
|
+
for (let index = 0; index < operands.length - 1; index += 1) reportCondition(operands[index]);
|
|
202
|
+
} };
|
|
174
203
|
}
|
|
175
204
|
});
|
|
176
205
|
|
|
177
|
-
|
|
178
|
-
//#region src/index.ts
|
|
206
|
+
// src/index.ts
|
|
179
207
|
const ziloen = { rules: {
|
|
180
208
|
"generic-spacing": genericSpacing,
|
|
181
209
|
"jsx-strict-logical-expressions": jsxStrictLogicalExpressions
|
|
182
210
|
} };
|
|
183
211
|
|
|
184
|
-
//#endregion
|
|
185
212
|
export { ziloen as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-ziloen",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -14,20 +14,22 @@
|
|
|
14
14
|
"test": "vitest"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@typescript-eslint/utils": "8.
|
|
17
|
+
"@typescript-eslint/type-utils": "^8.72.2",
|
|
18
|
+
"@typescript-eslint/utils": "8.72.2"
|
|
18
19
|
},
|
|
19
20
|
"devDependencies": {
|
|
20
21
|
"@types/eslint": "^9.6.1",
|
|
21
|
-
"@types/node": "^
|
|
22
|
-
"@typescript-eslint/parser": "8.
|
|
23
|
-
"bumpp": "^
|
|
24
|
-
"eslint": "^10.
|
|
25
|
-
"eslint-vitest-rule-tester": "^3.
|
|
26
|
-
"tsdown": "^0.
|
|
22
|
+
"@types/node": "^26.6.6",
|
|
23
|
+
"@typescript-eslint/parser": "8.72.2",
|
|
24
|
+
"bumpp": "^12.3.0",
|
|
25
|
+
"eslint": "^10.14.0",
|
|
26
|
+
"eslint-vitest-rule-tester": "^3.2.0",
|
|
27
|
+
"tsdown": "^0.23.7",
|
|
27
28
|
"typescript": "6.0.2",
|
|
28
|
-
"vitest": "^
|
|
29
|
+
"vitest": "^5.1.0"
|
|
29
30
|
},
|
|
30
31
|
"peerDependencies": {
|
|
31
32
|
"typescript": ">=6.0.0"
|
|
32
|
-
}
|
|
33
|
+
},
|
|
34
|
+
"packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3"
|
|
33
35
|
}
|