Skip to content

Commit 7bc9a13

Browse files
Vibe Botclaude
andcommitted
Address SonarQube issues in S6775 decorator
Fix code quality issues in the S6775 decorator.ts file: - Remove 4 unnecessary type assertions (S4325) by using proper type predicates for array.find() and removing redundant casts where TypeScript already infers the correct types - Reduce cognitive complexity (S3776) and nesting depth (S134) in findPropTypesDeclaration by extracting helper functions: findPropTypesForAncestor, findPropTypesFromClassBody, findPropTypesFromAssignment, getDefaultPropsComponentName, and findPropTypesFromVariableDeclarator - Merge 3 nested if statements (S1066) by combining conditions with optional chaining operators Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 23f80e1 commit 7bc9a13

File tree

1 file changed

+117
-57
lines changed

1 file changed

+117
-57
lines changed

packages/jsts/src/rules/S6775/decorator.ts

Lines changed: 117 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export function decorate(rule: Rule.RuleModule): Rule.RuleModule {
6262
return;
6363
}
6464

65-
const node = descriptor.node as estree.Node;
65+
const node = descriptor.node;
6666

6767
// Find the component's propTypes declaration
6868
const propTypesValue = findPropTypesDeclaration(node, context);
@@ -93,7 +93,7 @@ export function decorate(rule: Rule.RuleModule): Rule.RuleModule {
9393
*/
9494
function extractPropertyName(descriptor: Rule.ReportDescriptor): string | null {
9595
if ('data' in descriptor && descriptor.data && typeof descriptor.data === 'object') {
96-
const data = descriptor.data as Record<string, unknown>;
96+
const data = descriptor.data;
9797
if ('name' in data && typeof data.name === 'string') {
9898
return data.name;
9999
}
@@ -113,44 +113,105 @@ function findPropTypesDeclaration(
113113

114114
for (let i = ancestors.length - 1; i >= 0; i--) {
115115
const ancestor = ancestors[i];
116-
117-
// Pattern 1: Class component with static propTypes
118-
if (ancestor.type === 'ClassBody') {
119-
const propTypes = findStaticPropTypes(ancestor);
120-
if (propTypes) {
121-
return getUniqueWriteUsageOrNode(context, propTypes, true);
122-
}
116+
const propTypes = findPropTypesForAncestor(ancestor, ancestors, context);
117+
if (propTypes) {
118+
return propTypes;
123119
}
120+
}
124121

125-
// Pattern 2: External assignment - Component.defaultProps = {...}
126-
// We need to find Component.propTypes
127-
if (ancestor.type === 'AssignmentExpression') {
128-
const left = ancestor.left;
129-
if (
130-
left.type === 'MemberExpression' &&
131-
left.object.type === 'Identifier' &&
132-
left.property.type === 'Identifier' &&
133-
left.property.name === 'defaultProps'
134-
) {
135-
const componentName = left.object.name;
136-
const propTypes = findExternalPropTypes(componentName, ancestors);
137-
if (propTypes) {
138-
return getUniqueWriteUsageOrNode(context, propTypes, true);
139-
}
140-
}
141-
}
122+
return null;
123+
}
142124

143-
// Pattern 3: Reported node is inside a constant used as defaultProps
144-
// e.g., const DefaultProps = { prop: value }; class uses static defaultProps = DefaultProps;
145-
if (ancestor.type === 'VariableDeclarator' && ancestor.id.type === 'Identifier') {
146-
const constantName = ancestor.id.name;
147-
const propTypes = findPropTypesForDefaultPropsConstant(constantName, ancestors);
148-
if (propTypes) {
149-
return getUniqueWriteUsageOrNode(context, propTypes, true);
150-
}
151-
}
125+
/**
126+
* Try to find propTypes based on the ancestor node type.
127+
*/
128+
function findPropTypesForAncestor(
129+
ancestor: estree.Node,
130+
ancestors: estree.Node[],
131+
context: Rule.RuleContext,
132+
): estree.Node | null {
133+
// Pattern 1: Class component with static propTypes
134+
if (ancestor.type === 'ClassBody') {
135+
return findPropTypesFromClassBody(ancestor, context);
136+
}
137+
138+
// Pattern 2: External assignment - Component.defaultProps = {...}
139+
if (ancestor.type === 'AssignmentExpression') {
140+
return findPropTypesFromAssignment(ancestor, ancestors, context);
141+
}
142+
143+
// Pattern 3: Reported node is inside a constant used as defaultProps
144+
if (ancestor.type === 'VariableDeclarator' && ancestor.id.type === 'Identifier') {
145+
return findPropTypesFromVariableDeclarator(ancestor, ancestors, context);
146+
}
147+
148+
return null;
149+
}
150+
151+
/**
152+
* Find propTypes from a class body.
153+
*/
154+
function findPropTypesFromClassBody(
155+
classBody: estree.ClassBody,
156+
context: Rule.RuleContext,
157+
): estree.Node | null {
158+
const propTypes = findStaticPropTypes(classBody);
159+
if (propTypes) {
160+
return getUniqueWriteUsageOrNode(context, propTypes, true);
161+
}
162+
return null;
163+
}
164+
165+
/**
166+
* Find propTypes from an assignment expression (Component.defaultProps = {...}).
167+
*/
168+
function findPropTypesFromAssignment(
169+
assignment: estree.AssignmentExpression,
170+
ancestors: estree.Node[],
171+
context: Rule.RuleContext,
172+
): estree.Node | null {
173+
const componentName = getDefaultPropsComponentName(assignment.left);
174+
if (!componentName) {
175+
return null;
176+
}
177+
const propTypes = findExternalPropTypes(componentName, ancestors);
178+
if (propTypes) {
179+
return getUniqueWriteUsageOrNode(context, propTypes, true);
180+
}
181+
return null;
182+
}
183+
184+
/**
185+
* Extract component name from a defaultProps assignment target.
186+
*/
187+
function getDefaultPropsComponentName(left: estree.Pattern): string | null {
188+
if (
189+
left.type === 'MemberExpression' &&
190+
left.object.type === 'Identifier' &&
191+
left.property.type === 'Identifier' &&
192+
left.property.name === 'defaultProps'
193+
) {
194+
return left.object.name;
152195
}
196+
return null;
197+
}
153198

199+
/**
200+
* Find propTypes from a variable declarator (constant used as defaultProps).
201+
*/
202+
function findPropTypesFromVariableDeclarator(
203+
declarator: estree.VariableDeclarator,
204+
ancestors: estree.Node[],
205+
context: Rule.RuleContext,
206+
): estree.Node | null {
207+
if (declarator.id.type !== 'Identifier') {
208+
return null;
209+
}
210+
const constantName = declarator.id.name;
211+
const propTypes = findPropTypesForDefaultPropsConstant(constantName, ancestors);
212+
if (propTypes) {
213+
return getUniqueWriteUsageOrNode(context, propTypes, true);
214+
}
154215
return null;
155216
}
156217

@@ -179,7 +240,7 @@ function findExternalPropTypes(
179240
componentName: string,
180241
ancestors: estree.Node[],
181242
): estree.Node | null {
182-
const program = ancestors.find(n => n.type === 'Program') as estree.Program | undefined;
243+
const program = ancestors.find((n): n is estree.Program => n.type === 'Program');
183244
if (!program) {
184245
return null;
185246
}
@@ -247,7 +308,7 @@ function findPropTypesForDefaultPropsConstant(
247308
constantName: string,
248309
ancestors: estree.Node[],
249310
): estree.Node | null {
250-
const program = ancestors.find(n => n.type === 'Program') as estree.Program | undefined;
311+
const program = ancestors.find((n): n is estree.Program => n.type === 'Program');
251312
if (!program) {
252313
return null;
253314
}
@@ -271,10 +332,8 @@ function findPropTypesInStatement(
271332
): estree.Node | null {
272333
// Check class declarations
273334
const classInfo = extractClassDeclaration(statement);
274-
if (classInfo) {
275-
if (classUsesDefaultPropsConstant(classInfo.body, defaultPropsConstantName)) {
276-
return findStaticPropTypes(classInfo.body);
277-
}
335+
if (classInfo && classUsesDefaultPropsConstant(classInfo.body, defaultPropsConstantName)) {
336+
return findStaticPropTypes(classInfo.body);
278337
}
279338

280339
// Check for function declarations that return classes (factory pattern)
@@ -286,15 +345,17 @@ function findPropTypesInStatement(
286345
}
287346

288347
// Check for exported functions
289-
if (statement.type === 'ExportNamedDeclaration' && statement.declaration) {
290-
if (statement.declaration.type === 'FunctionDeclaration' && statement.declaration.body) {
291-
const propTypes = findPropTypesInFunctionBody(
292-
statement.declaration.body,
293-
defaultPropsConstantName,
294-
);
295-
if (propTypes) {
296-
return propTypes;
297-
}
348+
if (
349+
statement.type === 'ExportNamedDeclaration' &&
350+
statement.declaration?.type === 'FunctionDeclaration' &&
351+
statement.declaration.body
352+
) {
353+
const propTypes = findPropTypesInFunctionBody(
354+
statement.declaration.body,
355+
defaultPropsConstantName,
356+
);
357+
if (propTypes) {
358+
return propTypes;
298359
}
299360
}
300361

@@ -328,13 +389,12 @@ function findPropTypesInFunctionBody(
328389
defaultPropsConstantName: string,
329390
): estree.Node | null {
330391
for (const stmt of body.body) {
331-
if (stmt.type === 'ReturnStatement' && stmt.argument) {
332-
if (
333-
stmt.argument.type === 'ClassExpression' &&
334-
classUsesDefaultPropsConstant(stmt.argument.body, defaultPropsConstantName)
335-
) {
336-
return findStaticPropTypes(stmt.argument.body);
337-
}
392+
if (
393+
stmt.type === 'ReturnStatement' &&
394+
stmt.argument?.type === 'ClassExpression' &&
395+
classUsesDefaultPropsConstant(stmt.argument.body, defaultPropsConstantName)
396+
) {
397+
return findStaticPropTypes(stmt.argument.body);
338398
}
339399
}
340400
return null;

0 commit comments

Comments
 (0)