From c481df85ed8d27c162fed3dbeb8e01a1f8544ad7 Mon Sep 17 00:00:00 2001 From: Santhi Prakash Date: Sat, 15 Aug 2026 19:27:51 +0000 Subject: [PATCH] fix(lint): deduplicate CSS transform text in gsap_css_transform_conflict message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Problem: when a single CSS transform declaration contains both translate and scale (e.g. transform: scale(1.08) translate3d(...)), both cssTranslateSelectors and cssScaleSelectors store the same transformVal for the same selector. The finding message and fixHint then concatenate the identical string, producing doubled text. - Fix: deduplicate the [cssFromTranslate, cssFromScale] parts via new Set() before joining. This is a no-op when the values differ (separate translate-only and scale-only declarations) and corrects the doubling when they are identical. - Verification: bun test packages/lint/src/rules/gsap.test.ts — 162 tests pass including a new regression test that asserts the message and fixHint do not contain doubled transform text. --- packages/lint/src/rules/gsap.test.ts | 6 ++++++ packages/lint/src/rules/gsap.ts | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) 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, };