Skip to content

Commit 1f10da8

Browse files
committed
fix(no-unlocalized-strings): only ignore direct property values in styling constants
- Prevents false negatives for strings inside IIFEs, function calls, nested objects - Exact path check: Literal → Property → ObjectExpression → VariableDeclarator
1 parent 4d4955a commit 1f10da8

File tree

2 files changed

+44
-15
lines changed

2 files changed

+44
-15
lines changed

src/rules/no-unlocalized-strings.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ ruleTester.run("no-unlocalized-strings", noUnlocalizedStrings, {
127127
// Singular forms
128128
{ code: 'const DEFAULT_COLOR = { value: "#ff0000" }', filename: "test.tsx" },
129129
{ code: 'const MAIN_CLASS = { container: "mx-auto max-w-7xl" }', filename: "test.tsx" },
130+
// Nested objects should NOT be ignored (only direct property values)
131+
// These are in the invalid section below
130132

131133
// Technical strings (no spaces, identifiers)
132134
{ code: 'const x = "myIdentifier"', filename: "test.tsx" },
@@ -435,6 +437,24 @@ ruleTester.run("no-unlocalized-strings", noUnlocalizedStrings, {
435437
code: 'export const testAction = () => { return "Something went wrong!" }',
436438
filename: "test.tsx",
437439
errors: [{ messageId: "unlocalizedString" }]
440+
},
441+
442+
// Styling constants: only DIRECT property values are ignored
443+
// Functions, IIFEs, nested objects, etc. should still be flagged
444+
{
445+
code: 'const STATUS_COLORS = { active: (() => "Hello World")() }',
446+
filename: "test.tsx",
447+
errors: [{ messageId: "unlocalizedString" }]
448+
},
449+
{
450+
code: 'const STATUS_COLORS = { active: fn("Hello World") }',
451+
filename: "test.tsx",
452+
errors: [{ messageId: "unlocalizedString" }]
453+
},
454+
{
455+
code: 'const STATUS_COLORS = { active: { nested: "Hello World" } }',
456+
filename: "test.tsx",
457+
errors: [{ messageId: "unlocalizedString" }]
438458
}
439459
]
440460
})

src/rules/no-unlocalized-strings.ts

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -568,28 +568,37 @@ function isIgnoredProperty(node: TSESTree.Node, ignoreProperties: string[]): boo
568568
}
569569

570570
/**
571-
* Checks if a string is inside an object assigned to a styling constant.
571+
* Checks if a string is a direct property value in an object assigned to a styling constant.
572572
*
573-
* Matches patterns like:
573+
* Only matches the exact structure:
574574
* const STATUS_COLORS = { active: "bg-green-100..." }
575-
* const BUTTON_STYLES = { primary: "px-4 py-2..." }
575+
*
576+
* Does NOT match strings inside functions, IIFEs, or nested structures:
577+
* const STATUS_COLORS = { active: (() => "value")() } // ❌ not matched
578+
* const STATUS_COLORS = { active: fn("value") } // ❌ not matched
576579
*/
577580
function isInsideStylingConstant(node: TSESTree.Node): boolean {
578-
let current: TSESTree.Node | undefined = node.parent ?? undefined
581+
// Must be: Literal → Property (as value) → ObjectExpression → VariableDeclarator
582+
const property = node.parent
583+
if (property?.type !== AST_NODE_TYPES.Property || property.value !== node) {
584+
return false
585+
}
579586

580-
while (current !== undefined) {
581-
// Look for: const NAME = { ... } or let NAME = { ... }
582-
if (
583-
current.type === AST_NODE_TYPES.VariableDeclarator &&
584-
current.id.type === AST_NODE_TYPES.Identifier &&
585-
isStylingConstant(current.id.name)
586-
) {
587-
return true
588-
}
589-
current = current.parent ?? undefined
587+
const objectExpr = property.parent
588+
if (objectExpr.type !== AST_NODE_TYPES.ObjectExpression) {
589+
return false
590590
}
591591

592-
return false
592+
const declarator = objectExpr.parent
593+
if (
594+
declarator.type !== AST_NODE_TYPES.VariableDeclarator ||
595+
declarator.id.type !== AST_NODE_TYPES.Identifier ||
596+
declarator.init !== objectExpr
597+
) {
598+
return false
599+
}
600+
601+
return isStylingConstant(declarator.id.name)
593602
}
594603

595604
// ============================================================================

0 commit comments

Comments
 (0)