Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/packages/lint.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ Every finding code — its severity, what it catches, and how to fix it — is d

Detected issues include:

- Invalid inline JavaScript syntax. GLSL data blocks with `type="x-shader/x-fragment"`,
`type="x-shader/x-vertex"`, or `type="text/glsl"` skip JavaScript syntax checks and
still receive shader warnings.
- Missing timeline registration (`window.__timelines`)
- A timed `<video>` that declares neither `muted` (silent footage) nor `data-has-audio="true"` (has sound) — `video_missing_muted`
- Missing `class="clip"` on timed visible elements
Expand Down
2 changes: 2 additions & 0 deletions packages/lint/src/rules/core.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { LintContext, HyperframeLintFinding } from "../context";
import postcss from "postcss";
import selectorParser from "postcss-selector-parser";
import { GLSL_SCRIPT_TYPES } from "../shaderDomains";
import {
HTML_BODY_CSS_WIDTH_FIRST_RE,
HTML_BODY_CSS_HEIGHT_FIRST_RE,
Expand Down Expand Up @@ -28,6 +29,7 @@ const SCRIPT_SYNTAX_EXEMPT_TYPES = new Set([
"application/hyperframes-slideshow+json",
"importmap",
"module",
...GLSL_SCRIPT_TYPES,
]);

function repeatedDescendantId(selector: string): string | null {
Expand Down
18 changes: 17 additions & 1 deletion packages/lint/src/rules/shaders.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,29 @@ describe("shader numeric domains", () => {
expect(result.map((finding) => finding.line)).toEqual([3, 8]);
});

it("checks native GLSL script blocks", async () => {
it.each(["x-shader/x-fragment", "x-shader/x-vertex", "text/glsl"])(
"accepts %s shader source without parsing it as JavaScript",
async (type) => {
const result = await lintHyperframeHtml(`<!doctype html><html><body>
<div id="root" data-composition-id="shader-demo" data-width="1920" data-height="1080"
data-duration="1" data-no-timeline></div>
<script type="${type}">precision mediump float; void main() {}</script>
<script>window.__timelines = {};</script>
</body></html>`);
expect(result.findings).toEqual([]);
},
);

it("checks numeric domains in native GLSL without JavaScript syntax errors", async () => {
const result = await lintHyperframeHtml(
`<script type="x-shader/x-fragment">${shader(unsafe)}</script>`,
);
expect(
result.findings.filter((finding) => finding.code === "shader_unbounded_cosine_pow"),
).toHaveLength(1);
expect(result.findings.map((finding) => finding.code)).not.toContain(
"invalid_inline_script_syntax",
);
});

it("checks escaped static strings without claiming unmapped source coordinates", async () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/lint/src/rules/shaders.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { LintContext, HyperframeLintFinding } from "../context";
import { shaderDomainOffsets } from "../shaderDomains";
import { GLSL_SCRIPT_TYPES, shaderDomainOffsets } from "../shaderDomains";
import { readAttr } from "../utils";

export function shaderDomainWarning(): HyperframeLintFinding {
Expand All @@ -19,7 +19,7 @@ export const shaderRules = [
const tag = `<script ${script.attrs}>`;
if (readAttr(tag, "src") !== null) return [];
const type = readAttr(tag, "type") ?? "";
const glsl = /^(?:x-shader\/x-(?:fragment|vertex)|text\/glsl)$/i.test(type);
const glsl = GLSL_SCRIPT_TYPES.has(type.toLowerCase());
if (type && !glsl && !/^(?:module|(?:text|application)\/javascript)$/i.test(type)) return [];
return shaderDomainOffsets(script.content, glsl).map((offset) => ({
...shaderDomainWarning(),
Expand Down
2 changes: 2 additions & 0 deletions packages/lint/src/shaderDomains.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { parse, parseExpressionAt, type CallExpression, type Expression } from "acorn";
import { simple } from "acorn-walk";

export const GLSL_SCRIPT_TYPES = new Set(["x-shader/x-fragment", "x-shader/x-vertex", "text/glsl"]);

interface ShaderSource {
text: string;
start: number;
Expand Down
Loading