mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
feat(lint): add template_literal_selector rule
Detects querySelector/querySelectorAll calls that use template literal
variables (e.g. `${compId}`) inside script tags. The HTML bundler's
cheerio/css-what parser crashes on these during compilation, causing
silent fallback to raw HTML without runtime injection.
Severity: error (breaks bundling)
Fix: replace template literal with hardcoded composition ID string
This commit is contained in:
committed by
Vance Ingalls
parent
476c20747d
commit
a0d3efbf58
@@ -218,3 +218,60 @@ describe("lintScriptUrls", () => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
});
|
||||
|
||||
describe("template_literal_selector rule", () => {
|
||||
it("reports error when querySelector uses template literal variable", () => {
|
||||
const html = `
|
||||
<html><body>
|
||||
<div data-composition-id="main" data-width="1920" data-height="1080">
|
||||
<div class="chart"></div>
|
||||
</div>
|
||||
<script>
|
||||
window.__timelines = window.__timelines || {};
|
||||
const compId = "main";
|
||||
const el = document.querySelector(\`[data-composition-id="\${compId}"] .chart\`);
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
window.__timelines["main"] = tl;
|
||||
</script>
|
||||
</body></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 = `
|
||||
<html><body>
|
||||
<div data-composition-id="main" data-width="1920" data-height="1080"></div>
|
||||
<script>
|
||||
window.__timelines = window.__timelines || {};
|
||||
const id = "main";
|
||||
document.querySelectorAll(\`[data-composition-id="\${id}"] .item\`);
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
window.__timelines["main"] = tl;
|
||||
</script>
|
||||
</body></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 = `
|
||||
<html><body>
|
||||
<div data-composition-id="main" data-width="1920" data-height="1080">
|
||||
<div class="chart"></div>
|
||||
</div>
|
||||
<script>
|
||||
window.__timelines = window.__timelines || {};
|
||||
const el = document.querySelector('[data-composition-id="main"] .chart');
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
window.__timelines["main"] = tl;
|
||||
</script>
|
||||
</body></html>`;
|
||||
const result = lintHyperframeHtml(html);
|
||||
const finding = result.findings.find((f) => f.code === "template_literal_selector");
|
||||
expect(finding).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user