@blumintinc/eslint-plugin-blumint 1.5.3 → 1.5.5
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/lib/index.js
CHANGED
|
@@ -4581,29 +4581,77 @@ exports.enforceVerbNounNaming = (0, createRule_1.createRule)({
|
|
|
4581
4581
|
}
|
|
4582
4582
|
return isVerb;
|
|
4583
4583
|
}
|
|
4584
|
+
function isPascalCase(name) {
|
|
4585
|
+
return /^[A-Z][a-zA-Z0-9]*$/.test(name);
|
|
4586
|
+
}
|
|
4587
|
+
function hasPropsParameter(node) {
|
|
4588
|
+
if (!node.params.length)
|
|
4589
|
+
return false;
|
|
4590
|
+
const firstParam = node.params[0];
|
|
4591
|
+
if (firstParam.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
|
|
4592
|
+
return true; // Destructured props
|
|
4593
|
+
}
|
|
4594
|
+
if (firstParam.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
4595
|
+
const paramName = firstParam.name.toLowerCase();
|
|
4596
|
+
return paramName === 'props';
|
|
4597
|
+
}
|
|
4598
|
+
return false;
|
|
4599
|
+
}
|
|
4600
|
+
function hasJsxReturn(node) {
|
|
4601
|
+
const sourceCode = context.getSourceCode();
|
|
4602
|
+
const text = sourceCode.getText(node);
|
|
4603
|
+
// Check for direct JSX returns
|
|
4604
|
+
if (text.includes('return <') || text.includes('=> <')) {
|
|
4605
|
+
return true;
|
|
4606
|
+
}
|
|
4607
|
+
// Check for JSX assigned to variables then returned
|
|
4608
|
+
if (text.includes('const ') && text.includes('<') && text.includes('return')) {
|
|
4609
|
+
return true;
|
|
4610
|
+
}
|
|
4611
|
+
return false;
|
|
4612
|
+
}
|
|
4584
4613
|
function isReactComponent(node) {
|
|
4585
4614
|
if (node.type !== utils_1.AST_NODE_TYPES.FunctionDeclaration &&
|
|
4586
4615
|
node.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
4587
4616
|
node.type !== utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
4588
4617
|
return false;
|
|
4589
4618
|
}
|
|
4590
|
-
//
|
|
4591
|
-
|
|
4619
|
+
// Get the function name
|
|
4620
|
+
let functionName = '';
|
|
4621
|
+
if (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration && node.id) {
|
|
4622
|
+
functionName = node.id.name;
|
|
4623
|
+
}
|
|
4624
|
+
else if (node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression || node.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
4592
4625
|
const parent = node.parent;
|
|
4593
|
-
if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
|
|
4594
|
-
|
|
4595
|
-
if (id.type === utils_1.AST_NODE_TYPES.Identifier && id.typeAnnotation?.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
|
|
4596
|
-
const typeText = context.getSourceCode().getText(id.typeAnnotation.typeAnnotation);
|
|
4597
|
-
if (typeText.includes('React.') || typeText.includes('FC') || typeText.includes('FunctionComponent')) {
|
|
4598
|
-
return true;
|
|
4599
|
-
}
|
|
4600
|
-
}
|
|
4626
|
+
if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator && parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
4627
|
+
functionName = parent.id.name;
|
|
4601
4628
|
}
|
|
4602
4629
|
}
|
|
4603
|
-
// Check
|
|
4604
|
-
const
|
|
4605
|
-
|
|
4606
|
-
|
|
4630
|
+
// Check for React component indicators
|
|
4631
|
+
const indicators = [
|
|
4632
|
+
// 1. PascalCase naming
|
|
4633
|
+
functionName && isPascalCase(functionName),
|
|
4634
|
+
// 2. Props parameter
|
|
4635
|
+
hasPropsParameter(node),
|
|
4636
|
+
// 3. JSX return
|
|
4637
|
+
hasJsxReturn(node),
|
|
4638
|
+
// 4. React type annotations
|
|
4639
|
+
(() => {
|
|
4640
|
+
if (node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
4641
|
+
const parent = node.parent;
|
|
4642
|
+
if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
|
|
4643
|
+
const id = parent.id;
|
|
4644
|
+
if (id.type === utils_1.AST_NODE_TYPES.Identifier && id.typeAnnotation?.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
|
|
4645
|
+
const typeText = context.getSourceCode().getText(id.typeAnnotation.typeAnnotation);
|
|
4646
|
+
return typeText.includes('React.') || typeText.includes('FC') || typeText.includes('FunctionComponent');
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
return false;
|
|
4651
|
+
})()
|
|
4652
|
+
];
|
|
4653
|
+
// Consider it a React component if it matches at least 2 indicators
|
|
4654
|
+
return indicators.filter(Boolean).length >= 2;
|
|
4607
4655
|
}
|
|
4608
4656
|
return {
|
|
4609
4657
|
FunctionDeclaration(node) {
|
|
@@ -87,12 +87,23 @@ function isInterfaceOrAbstractMethodSignature(node) {
|
|
|
87
87
|
}
|
|
88
88
|
return false;
|
|
89
89
|
}
|
|
90
|
+
function isTypeGuardFunction(node) {
|
|
91
|
+
if (!('returnType' in node) || !node.returnType)
|
|
92
|
+
return false;
|
|
93
|
+
const returnType = node.returnType;
|
|
94
|
+
if (returnType.type !== utils_1.AST_NODE_TYPES.TSTypeAnnotation)
|
|
95
|
+
return false;
|
|
96
|
+
const typeAnnotation = returnType.typeAnnotation;
|
|
97
|
+
if (typeAnnotation.type !== utils_1.AST_NODE_TYPES.TSTypePredicate)
|
|
98
|
+
return false;
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
90
101
|
exports.noExplicitReturnType = (0, createRule_1.createRule)({
|
|
91
102
|
name: 'no-explicit-return-type',
|
|
92
103
|
meta: {
|
|
93
104
|
type: 'suggestion',
|
|
94
105
|
docs: {
|
|
95
|
-
description: 'Disallow explicit return type annotations on functions when TypeScript can infer them. This reduces code verbosity and maintenance burden while leveraging TypeScript\'s powerful type inference. Exceptions are made for recursive functions, overloaded functions, interface methods, and abstract methods where explicit types improve clarity.',
|
|
106
|
+
description: 'Disallow explicit return type annotations on functions when TypeScript can infer them. This reduces code verbosity and maintenance burden while leveraging TypeScript\'s powerful type inference. Exceptions are made for type guard functions (using the `is` keyword), recursive functions, overloaded functions, interface methods, and abstract methods where explicit types improve clarity.',
|
|
96
107
|
recommended: 'error',
|
|
97
108
|
requiresTypeChecking: false,
|
|
98
109
|
extendsBaseRule: false,
|
|
@@ -136,8 +147,8 @@ exports.noExplicitReturnType = (0, createRule_1.createRule)({
|
|
|
136
147
|
FunctionDeclaration(node) {
|
|
137
148
|
if (!node.returnType)
|
|
138
149
|
return;
|
|
139
|
-
if (
|
|
140
|
-
isRecursiveFunction(node)) {
|
|
150
|
+
if (isTypeGuardFunction(node) ||
|
|
151
|
+
(mergedOptions.allowRecursiveFunctions && isRecursiveFunction(node))) {
|
|
141
152
|
return;
|
|
142
153
|
}
|
|
143
154
|
context.report({
|
|
@@ -149,8 +160,8 @@ exports.noExplicitReturnType = (0, createRule_1.createRule)({
|
|
|
149
160
|
FunctionExpression(node) {
|
|
150
161
|
if (!node.returnType)
|
|
151
162
|
return;
|
|
152
|
-
if (
|
|
153
|
-
isRecursiveFunction(node)) {
|
|
163
|
+
if (isTypeGuardFunction(node) ||
|
|
164
|
+
(mergedOptions.allowRecursiveFunctions && isRecursiveFunction(node))) {
|
|
154
165
|
return;
|
|
155
166
|
}
|
|
156
167
|
context.report({
|
|
@@ -162,6 +173,9 @@ exports.noExplicitReturnType = (0, createRule_1.createRule)({
|
|
|
162
173
|
ArrowFunctionExpression(node) {
|
|
163
174
|
if (!node.returnType)
|
|
164
175
|
return;
|
|
176
|
+
if (isTypeGuardFunction(node)) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
165
179
|
context.report({
|
|
166
180
|
node: node.returnType,
|
|
167
181
|
messageId: 'noExplicitReturnType',
|
|
@@ -39,9 +39,35 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
39
39
|
typeNode.types.forEach(extractProps);
|
|
40
40
|
}
|
|
41
41
|
else if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
|
|
42
|
-
// For referenced types like FormControlLabelProps, we need to track that these props should be forwarded
|
|
43
42
|
if (typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
44
|
-
|
|
43
|
+
if (typeNode.typeName.name === 'Pick' && typeNode.typeParameters) {
|
|
44
|
+
// Handle Pick utility type
|
|
45
|
+
const [baseType, pickedProps] = typeNode.typeParameters.params;
|
|
46
|
+
if (baseType.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
47
|
+
baseType.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
48
|
+
// Extract the picked properties from the union type
|
|
49
|
+
if (pickedProps.type === utils_1.AST_NODE_TYPES.TSUnionType) {
|
|
50
|
+
pickedProps.types.forEach((type) => {
|
|
51
|
+
if (type.type === utils_1.AST_NODE_TYPES.TSLiteralType &&
|
|
52
|
+
type.literal.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
53
|
+
typeof type.literal.value === 'string') {
|
|
54
|
+
// Add each picked property as a regular prop
|
|
55
|
+
props[type.literal.value] = type.literal;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else if (pickedProps.type === utils_1.AST_NODE_TYPES.TSLiteralType &&
|
|
60
|
+
pickedProps.literal.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
61
|
+
typeof pickedProps.literal.value === 'string') {
|
|
62
|
+
// Single property pick
|
|
63
|
+
props[pickedProps.literal.value] = pickedProps.literal;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// For referenced types like FormControlLabelProps, we need to track that these props should be forwarded
|
|
69
|
+
props[`...${typeNode.typeName.name}`] = typeNode.typeName;
|
|
70
|
+
}
|
|
45
71
|
}
|
|
46
72
|
}
|
|
47
73
|
}
|