From ae7e6a73cb87cbc7a63cb6a8392babbe5afe7d3c Mon Sep 17 00:00:00 2001 From: Miguel Angel Simon Sierra Date: Thu, 29 Oct 2026 23:29:14 -0400 Subject: [PATCH] fix(lint): a descendant selector inside a scoped query hides the overlay too --- packages/lint/src/rules/gsap.test.ts | 18 ++++++++++++++++++ packages/lint/src/rules/gsap.ts | 11 +++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/lint/src/rules/gsap.test.ts b/packages/lint/src/rules/gsap.test.ts index e51353c21d..efc9d00fe6 100644 --- a/packages/lint/src/rules/gsap.test.ts +++ b/packages/lint/src/rules/gsap.test.ts @@ -3753,6 +3753,24 @@ describe("SVG draw-on rules", () => { ).toBe(false); }); + it("accepts a descendant selector inside the query", async () => { + expect( + await flagged( + scene( + 'gsap.set(document.querySelector("[data-composition-id=scene] .ov"), { opacity: 0 });', + ), + ), + ).toBe(false); + }); + + it("accepts a querySelector variable written without semicolons", async () => { + expect( + await flagged( + scene('const ov = root.querySelector(".ov")\n gsap.set(ov, { opacity: 0 })'), + ), + ).toBe(false); + }); + it("still flags an overlay nothing hides at zero", async () => { expect(await flagged(scene(""))).toBe(true); }); diff --git a/packages/lint/src/rules/gsap.ts b/packages/lint/src/rules/gsap.ts index 2471f4de36..fef7ced7b2 100644 --- a/packages/lint/src/rules/gsap.ts +++ b/packages/lint/src/rules/gsap.ts @@ -274,7 +274,7 @@ function extractStandaloneHiddenSelectors(script: string): Set { aliases.set(match[1] ?? "", match[3] ?? ""); } for (const match of source.matchAll( - /(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*[\w$.]+\.querySelector(?:All)?\(\s*(["'`])([^"'`]+)\2\s*\)\s*;/g, + /(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*[\w$.]+\.querySelector(?:All)?\(\s*(["'`])([^"'`]+)\2\s*\)/g, )) { aliases.set(match[1] ?? "", match[3] ?? ""); } @@ -1202,7 +1202,14 @@ export const gsapRules: LintRule[] = [ (win.fromPropertyValues !== undefined && isHiddenGsapState(win.fromPropertyValues))), ); if (startsHiddenAtZero) continue; - if (selectors.some((selector) => authoredHiddenSelectors.has(selector))) continue; + if ( + selectors.some((selector) => + [...authoredHiddenSelectors].some( + (hidden) => hidden === selector || targetedSelectorTokens(hidden).has(selector), + ), + ) + ) + continue; const firstVisible = visibilityWindows.find((win) => makesOverlayVisible(win)); if (!firstVisible) continue; const selector =