Skip to content

Commit eb4703b

Browse files
committed
Check class names in conditions too
1 parent 165dcfa commit eb4703b

File tree

2 files changed

+80
-12
lines changed

2 files changed

+80
-12
lines changed

src/rules/no-unknown-class.ts

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@ const rule: RuleModule<'unknownClass', [PluginOptions]> = {
101101
}
102102
};
103103

104+
/** Helper to validate string literal class names */
105+
const validateStringLiteral = (value: string, node: TSESTree.Node) => {
106+
const classNames = value.split(/\s+/);
107+
classNames.forEach((className: string) => {
108+
validate(className, node);
109+
});
110+
};
111+
104112
/** Helper to validate class names in object expressions */
105113
const validateObjectExpression = (objExpr: TSESTree.ObjectExpression) => {
106114
objExpr.properties.forEach((prop: TSESTree.Property | TSESTree.SpreadElement) => {
@@ -115,6 +123,29 @@ const rule: RuleModule<'unknownClass', [PluginOptions]> = {
115123
});
116124
};
117125

126+
/** Helper to validate expressions that might contain class names */
127+
const validateExpression = (expr: TSESTree.Expression) => {
128+
switch (expr.type) {
129+
case 'Literal':
130+
if (typeof expr.value === 'string') {
131+
validateStringLiteral(expr.value, expr);
132+
}
133+
break;
134+
case 'ObjectExpression':
135+
validateObjectExpression(expr);
136+
break;
137+
case 'ConditionalExpression':
138+
// Handle ternary expressions: condition ? 'class1' : 'class2'
139+
validateExpression(expr.consequent);
140+
validateExpression(expr.alternate);
141+
break;
142+
case 'LogicalExpression':
143+
// Handle logical expressions: condition && 'class1'
144+
validateExpression(expr.right);
145+
break;
146+
}
147+
};
148+
118149
return {
119150
// Check JSX className attributes
120151
JSXAttribute(node: TSESTree.JSXAttribute) {
@@ -123,10 +154,7 @@ const rule: RuleModule<'unknownClass', [PluginOptions]> = {
123154
options.classAttributes?.includes(node.name.name)
124155
) {
125156
if (node.value?.type === 'Literal' && typeof node.value.value === 'string') {
126-
const classNames = node.value.value.split(/\s+/);
127-
classNames.forEach((className: string) => {
128-
validate(className, node);
129-
});
157+
validateStringLiteral(node.value.value, node);
130158
} else if (node.value?.type === 'JSXExpressionContainer') {
131159
const expr = node.value.expression;
132160
if (expr.type === 'ObjectExpression') {
@@ -143,14 +171,7 @@ const rule: RuleModule<'unknownClass', [PluginOptions]> = {
143171
options.classFunctions?.includes(node.callee.name)
144172
) {
145173
node.arguments.forEach((arg: TSESTree.Node) => {
146-
if (arg.type === 'Literal' && typeof arg.value === 'string') {
147-
const classNames = arg.value.split(/\s+/);
148-
classNames.forEach((className: string) => {
149-
validate(className, arg);
150-
});
151-
} else if (arg.type === 'ObjectExpression') {
152-
validateObjectExpression(arg);
153-
}
174+
validateExpression(arg as TSESTree.Expression);
154175
});
155176
}
156177
},

tests/rules/no-unknown-class.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ ruleTester.run('no-unknown-class', rule, {
3434
code: 'clsx("container flex", "p-4")',
3535
options: [{ classFunctions: ['clsx'] }],
3636
},
37+
// Test class utility function with valid classes in conditions
38+
{
39+
code: 'clsx("container", condition() ? "flex" : "p-4", otherCondition() && "btn")',
40+
options: [{ classFunctions: ['clsx'] }],
41+
},
3742
// Test object expression with valid classes
3843
{
3944
code: 'clsx({ flex: true, "p-4": condition })',
@@ -73,6 +78,48 @@ ruleTester.run('no-unknown-class', rule, {
7378
options: [{ classFunctions: ['clsx'] }],
7479
errors: [{ messageId: 'unknownClass', data: { className: 'unknown-class' } }],
7580
},
81+
// Test class utility function with invalid classes in ternary if
82+
{
83+
code: 'clsx("container", condition() ? "flex" : "unknown-class")',
84+
options: [{ classFunctions: ['clsx'] }],
85+
errors: [{ messageId: 'unknownClass', data: { className: 'unknown-class' } }],
86+
},
87+
// Test class utility function with invalid classes in ternary then
88+
{
89+
code: 'clsx("container", condition() ? "unknown-class" : "flex")',
90+
options: [{ classFunctions: ['clsx'] }],
91+
errors: [{ messageId: 'unknownClass', data: { className: 'unknown-class' } }],
92+
},
93+
// Test class utility function with invalid classes in conditional && form
94+
{
95+
code: 'clsx("container", condition() && "unknown-class")',
96+
options: [{ classFunctions: ['clsx'] }],
97+
errors: [{ messageId: 'unknownClass', data: { className: 'unknown-class' } }],
98+
},
99+
// Test class utility function with invalid classes in multiple coniditonal && form
100+
{
101+
code: 'clsx("container", condition() && otherCondition() && "unknown-class")',
102+
options: [{ classFunctions: ['clsx'] }],
103+
errors: [{ messageId: 'unknownClass', data: { className: 'unknown-class' } }],
104+
},
105+
// Test class utility function with invalid classes in conditional || form
106+
{
107+
code: 'clsx("container", condition() || "unknown-class")',
108+
options: [{ classFunctions: ['clsx'] }],
109+
errors: [{ messageId: 'unknownClass', data: { className: 'unknown-class' } }],
110+
},
111+
// Test class utility function with invalid classes in multiple coniditonal || form
112+
{
113+
code: 'clsx("container", condition() || otherCondition() || "unknown-class")',
114+
options: [{ classFunctions: ['clsx'] }],
115+
errors: [{ messageId: 'unknownClass', data: { className: 'unknown-class' } }],
116+
},
117+
// Test class utility function with invalid classes in nested ternary + conditional
118+
{
119+
code: 'clsx("container", condition() || (otherCondition() ? "unknown-class" : "flex"))',
120+
options: [{ classFunctions: ['clsx'] }],
121+
errors: [{ messageId: 'unknownClass', data: { className: 'unknown-class' } }],
122+
},
76123
// Test object expression with invalid class
77124
{
78125
code: 'clsx({ "invalid-class": true })',

0 commit comments

Comments
 (0)