diff --git a/packages/lint/src/rules/gsap.test.ts b/packages/lint/src/rules/gsap.test.ts index 1ef53fa7b8..639d35721e 100644 --- a/packages/lint/src/rules/gsap.test.ts +++ b/packages/lint/src/rules/gsap.test.ts @@ -584,6 +584,12 @@ describe("GSAP rules", () => { const conflicts = result.findings.filter((f) => f.code === "gsap_css_transform_conflict"); expect(conflicts).toHaveLength(1); expect(conflicts[0]?.message).toMatch(/x\/scale|scale\/x/); + // Regression: a single CSS declaration with both translate and scale must + // not produce doubled transform text in the message or fixHint (#3263). + const msg = conflicts[0]?.message ?? ""; + const hint = conflicts[0]?.fixHint ?? ""; + expect(msg).not.toContain("translateX(-50%) scale(0.8) translateX(-50%) scale(0.8)"); + expect(hint).not.toContain("translateX(-50%) scale(0.8) translateX(-50%) scale(0.8)"); }); // --- Inline style transform detection tests --- diff --git a/packages/lint/src/rules/gsap.ts b/packages/lint/src/rules/gsap.ts index 92290d9cf8..d0bb22d30c 100644 --- a/packages/lint/src/rules/gsap.ts +++ b/packages/lint/src/rules/gsap.ts @@ -1352,8 +1352,13 @@ export const gsapRules: LintRule[] = [ const cssFromScale = scaleProps.length > 0 ? matchCssTransform(sel, cssScaleSelectors) : undefined; if (!cssFromTranslate && !cssFromScale) continue; + // When a single CSS declaration contains both translate and scale, + // both maps store the same transformVal for the same selector. + // Deduplicate to prevent doubled text in the finding message/fixHint. + const parts = [cssFromTranslate, cssFromScale].filter(Boolean); + const cssTransform = [...new Set(parts)].join(" "); const existing = conflicts.get(sel) ?? { - cssTransform: [cssFromTranslate, cssFromScale].filter(Boolean).join(" "), + cssTransform, props: new Set(), raw: call.raw, };