@hyperframes/lint 0.7.37 → 0.7.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.js CHANGED
@@ -2625,6 +2625,29 @@ var compositionRules = [
2625
2625
  }
2626
2626
  return findings;
2627
2627
  },
2628
+ // missing_data_no_timeline
2629
+ // The producer polls window.__timelines[id] with a 45-second timeout waiting
2630
+ // for GSAP timeline registration. Compositions that never call
2631
+ // window.__timelines[id] = tl stall for 45 s every render. Adding
2632
+ // data-no-timeline to the root element tells the producer to skip the poll.
2633
+ ({ rootTag, rootCompositionId, scripts, rawSource, options }) => {
2634
+ if (options.isSubComposition) return [];
2635
+ if (!rootCompositionId || !rootTag) return [];
2636
+ const tagNoValues = rootTag.raw.replace(/"[^"]*"|'[^']*'/g, '""');
2637
+ if (/(?:^|\s)data-no-timeline(?=[\s>=/]|$)/i.test(tagNoValues)) return [];
2638
+ if (/<script\b[^>]*\bsrc\s*=/i.test(rawSource)) return [];
2639
+ const registersTimeline = scripts.some((s) => s.content.includes("window.__timelines["));
2640
+ if (registersTimeline) return [];
2641
+ return [
2642
+ {
2643
+ code: "missing_data_no_timeline",
2644
+ severity: "warning",
2645
+ message: "This composition has no `window.__timelines` registration but is missing `data-no-timeline`. The producer polls for timeline registration for up to 45 seconds before timing out, adding 45 s to every render.",
2646
+ fixHint: 'Add `data-no-timeline` to the root element to skip the poll: `<div data-composition-id="..." data-no-timeline ...>`.',
2647
+ snippet: truncateSnippet(rootTag.raw)
2648
+ }
2649
+ ];
2650
+ },
2628
2651
  // requestanimationframe_in_composition
2629
2652
  ({ scripts, rawSource, options }) => {
2630
2653
  if (isRegistrySourceFile(options.filePath) || isRegistryInstalledFile(rawSource)) return [];
@@ -2749,6 +2772,37 @@ var compositionRules = [
2749
2772
  }
2750
2773
  return findings;
2751
2774
  },
2775
+ // html_dir_attribute_breaks_render — valid non-LTR dir values on
2776
+ // <html> renders correctly in preview/snapshot but produces a fully
2777
+ // blank/black video from render, with no other lint/validate/inspect
2778
+ // check catching it (output file size, far smaller than expected, is the
2779
+ // only tell). Confirmed independently by two separate reports, both
2780
+ // diagnosing the same exact trigger and the same fix: drop dir from
2781
+ // <html>, keep lang, and scope `direction: rtl` to individual
2782
+ // text-containing elements via CSS instead (text still bidi-shapes
2783
+ // correctly). Advisory-only — this does not attempt to fix the render
2784
+ // pipeline's own root cause (suspected to be a capture step that clips a
2785
+ // fixed top-left-origin screenshot region, which RTL layout can shift the
2786
+ // actual content away from), only surfaces the already-confirmed footgun
2787
+ // before someone hits it blind.
2788
+ ({ source }) => {
2789
+ const htmlTag = findHtmlTag(source);
2790
+ if (!htmlTag) return [];
2791
+ const dir = readAttr(htmlTag.raw, "dir");
2792
+ if (!dir) return [];
2793
+ const normalizedDir = dir.toLowerCase();
2794
+ if (normalizedDir !== "rtl" && normalizedDir !== "auto") return [];
2795
+ const scopedDirection = normalizedDir === "auto" ? 'dir="auto"' : `direction: ${normalizedDir}`;
2796
+ return [
2797
+ {
2798
+ code: "html_dir_attribute_breaks_render",
2799
+ severity: "error",
2800
+ message: `<html dir="${dir}"> renders correctly in preview/snapshot but produces a fully blank/black video from render \u2014 a confirmed, silent failure.`,
2801
+ fixHint: `Remove dir="${dir}" from <html>. Keep lang, and scope ${scopedDirection} to individual text-containing elements instead \u2014 text still shapes correctly via the browser's own bidi algorithm.`,
2802
+ snippet: truncateSnippet(htmlTag.raw)
2803
+ }
2804
+ ];
2805
+ },
2752
2806
  // subcomposition_blanks_before_host
2753
2807
  // Warns when a full-bleed sub-composition slot ends before the host composition
2754
2808
  // does, leaving the slot blank for the remainder (issue #1540). Scoped narrowly to