diff --git a/packages/core/src/lint/hyperframeLinter.test.ts b/packages/core/src/lint/hyperframeLinter.test.ts index c6546c298..a3fe608e2 100644 --- a/packages/core/src/lint/hyperframeLinter.test.ts +++ b/packages/core/src/lint/hyperframeLinter.test.ts @@ -218,3 +218,60 @@ describe("lintScriptUrls", () => { vi.unstubAllGlobals(); }); }); + +describe("template_literal_selector rule", () => { + it("reports error when querySelector uses template literal variable", () => { + const html = ` + +
+
+
+ +`; + const result = lintHyperframeHtml(html); + const finding = result.findings.find((f) => f.code === "template_literal_selector"); + expect(finding).toBeDefined(); + expect(finding?.severity).toBe("error"); + }); + + it("reports error for querySelectorAll with template literal variable", () => { + const html = ` + +
+ +`; + const result = lintHyperframeHtml(html); + const finding = result.findings.find((f) => f.code === "template_literal_selector"); + expect(finding).toBeDefined(); + }); + + it("does not report error for hardcoded querySelector strings", () => { + const html = ` + +
+
+
+ +`; + const result = lintHyperframeHtml(html); + const finding = result.findings.find((f) => f.code === "template_literal_selector"); + expect(finding).toBeUndefined(); + }); +}); diff --git a/packages/core/src/lint/hyperframeLinter.ts b/packages/core/src/lint/hyperframeLinter.ts index 081dc67d3..f43deb70c 100644 --- a/packages/core/src/lint/hyperframeLinter.ts +++ b/packages/core/src/lint/hyperframeLinter.ts @@ -502,6 +502,26 @@ export function lintHyperframeHtml( } } + // ── Template literal variables in querySelector (breaks cheerio bundler) ── + for (const script of scripts) { + const templateLiteralSelectorPattern = + /(?:querySelector|querySelectorAll)\s*\(\s*`[^`]*\$\{[^}]+\}[^`]*`\s*\)/g; + let tlMatch: RegExpExecArray | null; + while ((tlMatch = templateLiteralSelectorPattern.exec(script.content)) !== null) { + pushFinding({ + code: "template_literal_selector", + severity: "error", + message: + "querySelector uses a template literal variable (e.g. `${compId}`). " + + "The HTML bundler's CSS parser crashes on these. Use a hardcoded string instead.", + file: filePath, + fixHint: + "Replace the template literal variable with a hardcoded string. The bundler's CSS parser cannot handle interpolated variables in script content.", + snippet: truncateSnippet(tlMatch[0]), + }); + } + } + const errorCount = findings.filter((finding) => finding.severity === "error").length; const warningCount = findings.length - errorCount;