diff --git a/docs/docs.json b/docs/docs.json index dd4703b7c6..85b47012ff 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -1009,7 +1009,8 @@ "reference/html-schema", "reference/color-grading", "reference/audio-effects", - "reference/speed-ramps" + "reference/speed-ramps", + "reference/lint-rules" ] }, { diff --git a/docs/packages/lint.mdx b/docs/packages/lint.mdx index 414f9b3fd9..39cdc3ee9e 100644 --- a/docs/packages/lint.mdx +++ b/docs/packages/lint.mdx @@ -100,6 +100,8 @@ The browser entry exposes `lintHyperframeHtml`, `lintMediaUrls`, and `shouldBloc ## What the Linter Catches +Every finding code — its severity, what it catches, and how to fix it — is documented on the [Lint rules](/reference/lint-rules) reference page. + Detected issues include: - Missing timeline registration (`window.__timelines`) diff --git a/docs/reference/lint-rules.mdx b/docs/reference/lint-rules.mdx new file mode 100644 index 0000000000..4a099df1a4 --- /dev/null +++ b/docs/reference/lint-rules.mdx @@ -0,0 +1,877 @@ +--- +title: "Lint rules" +sidebarTitle: "Lint rules" +description: "Every lint finding code with its severity, what it catches, and how to fix it." +--- + +`hyperframes lint`, `hyperframes check` and the `@hyperframes/lint` package report findings with a stable code. This page lists every code with its severity, what it catches, and how to fix it. The codes come straight from the rule set in `packages/lint/src/rules/` plus the project-level probes that run as part of a project lint. + +Severities: + +- **Error** — the composition is broken or the render will fail or visibly diverge. Fails `hyperframes check` by default. +- **Warning** — a quality, determinism or maintainability hazard. Does not fail `hyperframes check` unless you pass `--strict`; in `render`, warnings abort only under `--strict-all`. +- **Info** — a heads-up that needs no action. Shown by `hyperframes lint --verbose`. + + + Looking for a code from a render or check log? Codes appear in the output of `npx hyperframes lint [dir]` — add `--json` to script over them. `check` additionally runs runtime, layout and motion audits beyond these lint rules; see [Troubleshooting](/guides/troubleshooting). + + +## Structure and timeline + +Source: `packages/lint/src/rules/structure.ts`. + +The four codes marked **Warning or error** are warnings when the CLI lints a project, and errors when Studio lints the file it is editing. + +### `nested_structure_needs_subcomposition` + +**Warning or error.** A top-level timeline element contains another nested structure instead of a sub-composition mount. The timeline draws one row per top-level element, and the root composition is built only from sub-compositions. + +**Fix:** Move the element and its contents into a sub-composition file and mount it with `data-composition-src`. + +### `timeline_element_missing_timing` + +**Warning or error.** A timeline element has no `data-duration`, so the timeline cannot draw where it ends. + +**Fix:** Add `data-duration` (in seconds) to the element. + +### `caption_track_kind_missing` + +**Warning or error.** An element looks like a caption row but is not marked as one, so the timeline cannot group it on the caption track. + +**Fix:** Add `data-track-kind="captions"` to the element. + +### `multiple_caption_tracks` + +**Warning or error.** Captions are spread across several timeline tracks instead of one caption lane. + +**Fix:** Give every caption element the same `data-track-index`. + +### `clip_ends_past_root_duration` + +**Warning.** A clip runs past the root composition's `data-duration`, so it is cut off in previews, posters and renders. + +**Fix:** Extend the root `data-duration`, or make the clip end at or before it. + +## Composition contract + +Source: `packages/lint/src/rules/composition.ts`. + +### `invalid_composition_variables_declaration` + +**Error.** `data-composition-variables` is not valid JSON or not an array of variable declarations. + +**Fix:** Provide a JSON array such as `data-composition-variables='[{"id":"title","type":"string","label":"Title","default":"Hello"}]'` — single-quote the attribute, double-quote the JSON. + +### `invalid_variable_values_json` + +**Error.** `data-variable-values` is not valid JSON. + +**Fix:** Single-quote the attribute and double-quote the JSON keys and values, e.g. `data-variable-values='{"title":"Hello"}'`. + +### `unknown_variable_binding` + +**Warning.** An element binds a variable that is not declared in `data-composition-variables`, so the binding silently keeps the authored fallback. + +**Fix:** Declare the variable on the composition root (``, or the `data-composition-id` root of a template or fragment composition). + +### `unloadable_media_variable_default` + +**Error.** A variable's default is a URL the runtime refuses to load, so bound elements render their authored fallback src while the render still exits 0. + +**Fix:** Use a relative path, `http(s)`, `blob:`, or a `data:image/*` URI. Copy the file into the project and reference it relatively. + +### `deprecated_data_end` + +**Error.** A legacy `data-end` is used without `data-duration`, or a stale `data-end` disagrees with the `data-duration` next to it. + +**Fix:** Replace `data-end` with `data-duration`. The compiler generates `data-end` from `data-duration` automatically. + +### `deprecated_data_layer` + +**Error.** The legacy `data-layer` attribute is used. + +**Fix:** Replace it with `data-track-index`, the canonical name Studio and the linter read. + +### `duplicate_composition_id` + +**Error.** The same `data-composition-id` appears on several elements in one file — often a `` tag carrying the same id as the root `
`. + +**Fix:** Keep `data-composition-id` on exactly one element: the composition root. Remove it from metadata or duplicate hosts. + +### `html_dir_attribute_breaks_render` + +**Error.** `` or `` renders correctly in preview and snapshot but produces a fully blank or black video from render — a confirmed, silent failure. `dir="ltr"` is not flagged. + +**Fix:** Remove `dir` from ``, keep `lang`, and scope the direction to individual text-containing elements instead — `direction: rtl` in CSS, or `dir="auto"` on the element. + +### `invalid_parent_traversal_in_asset_path` + +**Error.** Asset paths use `../` to traverse above the project root; render and preview clamp or misresolve them. + +**Fix:** Point the path at a file inside the project — from `compositions/scene.html`, both `../assets/x.png` and `assets/x.png` reach the project's `assets` folder. + +### `composition_exceeds_inspection_viewport_cap` + +**Warning.** The composition is larger than the 4096px viewport cap that `check`, `validate`, `snapshot`, `compare` and layout audits clamp to. `render` is unaffected, but a clean inspection result no longer covers the whole canvas. + +**Fix:** Keep the authored size if the output needs it, and verify the region past the cap from a render rather than from inspection commands. + +### `composition_file_too_large` + +**Warning.** The HTML composition file has grown past a size where it stays easy to read, iterate on, and diff. + +**Fix:** Split it into sub-compositions and mount them from the parent with `data-composition-src`. + +### `composition_heavy_overlay_count_high` + +**Warning.** Many elements carry heavy overlay CSS (`filter: blur`, `radial-gradient`, `clip-path`), which can cap capture performance — including hidden ones. + +**Fix:** Split the composition and concat the pieces so each capture sees a small subset of heavy overlays; remove overlays that are not visible anyway. + +### `missing_data_no_timeline` + +**Warning.** A `data-composition-id` element (or the root) has neither a matching `window.__timelines` registration nor `data-no-timeline`, so the producer polls up to 45 seconds before timing out. + +**Fix:** Register the timeline, mount the composition with `data-composition-src`, or add `data-no-timeline` for genuinely static sections. + +### `negative_z_index` + +**Warning.** `z-index` below 0 paints the element behind its nearest stacking context's content; with no stacking-context ancestor that is the composition root, so an opaque background there hides the element entirely. + +**Fix:** Give the element's parent a stacking context (`isolation: isolate` is the cheapest; `transform`, `filter`, `opacity` below 1, `contain: paint`, `will-change` also work). + +### `requestanimationframe_in_composition` + +**Error.** `requestAnimationFrame` runs on wall-clock time, not the GSAP timeline, so it desyncs from frame capture and can flicker or drop frames in renders. + +**Fix:** Use GSAP tweens or timeline `onUpdate` callbacks for animation logic instead. + +### `root_composition_duration_derived` + +**Warning.** The root has no `data-duration` and no GSAP timeline, so its length is inferred from its clips. + +**Fix:** Add `data-duration=""` to the root element to set the length yourself. + +### `root_composition_missing_duration_source` + +**Error.** The root has no `data-duration`, no GSAP timeline, and no CSS, WAAPI, Lottie or Three.js animation to infer a duration from, so the render engine cannot determine how long to capture and fails. + +**Fix:** Add `data-duration=""` to the root, or add a paused GSAP timeline registered on `window.__timelines`. + +### `root_composition_missing_html_wrapper` + +**Error.** An `index.html` carrying `data-composition-id` is a bare fragment without ``, `` or ``, so browsers quirks-mode it and the preview server mis-serves it. + +**Fix:** Wrap the composition in `...`. + +### `root_zoom_rescales_a_fixed_canvas` + +**Error.** CSS `zoom` on the canvas rescales the painted composition inside a capture frame that does not rescale with it — the frame is sized from the root's `data-width` and `data-height` in layout pixels. + +**Fix:** Author at the real size: set `data-width` and `data-height` (and the root's width and height) and drop the canvas-level `zoom`. To scale the output without changing layout, pass `--output-resolution` to render. `zoom` on descendants is fine. + +### `split_data_attribute_selector` + +**Error.** A CSS selector combines two attributes inside one `[...]` block, which browsers reject — GSAP timelines or `querySelector` calls fail before they register. + +**Fix:** Use separate attribute selectors: `[data-composition-id="x"][data-track-index="1"]`. + +### `standalone_composition_wrapped_in_template` + +**Error.** The root `index.html` is wrapped in a `