@hyperframes/lint 0.8.13 → 0.8.14

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/index.js CHANGED
@@ -531,7 +531,7 @@ var coreRules = [
531
531
  }
532
532
  ];
533
533
  },
534
- // missing_timeline_registry + timeline_registry_missing_init
534
+ // missing_timeline_registry
535
535
  // fallow-ignore-next-line complexity
536
536
  ({ source, rawSource, rootTag, options }) => {
537
537
  if (options.isSubComposition || rawSource.trimStart().toLowerCase().startsWith("<template")) {
@@ -547,14 +547,6 @@ var coreRules = [
547
547
  fixHint: "Register each composition timeline on `window.__timelines[compositionId]`."
548
548
  });
549
549
  }
550
- if (TIMELINE_REGISTRY_ASSIGN_PATTERN.test(source) && !TIMELINE_REGISTRY_INIT_PATTERN.test(source)) {
551
- findings.push({
552
- code: "timeline_registry_missing_init",
553
- severity: "error",
554
- message: "`window.__timelines[\u2026] = \u2026` is used without initializing `window.__timelines` first.",
555
- fixHint: "Add `window.__timelines = window.__timelines || {};` before any timeline assignment."
556
- });
557
- }
558
550
  return findings;
559
551
  },
560
552
  // timeline_id_mismatch
@@ -1108,9 +1100,9 @@ var mediaRules = [
1108
1100
  findings.push({
1109
1101
  code: "video_nested_in_timed_element",
1110
1102
  severity: "error",
1111
- message: `<video> with data-start is nested inside <${parent.name}${parent.id ? ` id="${parent.id}"` : ""}> which also has data-start. The framework cannot manage playback of nested media \u2014 video will be FROZEN in renders.`,
1103
+ message: `<video> with data-start is nested inside <${parent.name}${parent.id ? ` id="${parent.id}"` : ""}> which also has data-start. The frame extractor resolves the video's start from its own data-start without the wrapper's offset, while visibility uses the wrapper's window, so the two disagree: the clip shows the wrong source frames and then disappears partway through its slot.`,
1112
1104
  elementId: readAttr(tag.raw, "id") || void 0,
1113
- fixHint: "Move the <video> to be a direct child of the stage, or remove data-start from the wrapper div (use it as a non-timed visual container).",
1105
+ fixHint: "Time the wrapper OR the video, never both: remove data-start from the wrapper (use it as a non-timed visual container), or move the <video> up to be a direct child of the stage.",
1114
1106
  snippet: truncateSnippet(tag.raw)
1115
1107
  });
1116
1108
  break;
@@ -3638,7 +3630,7 @@ var compositionRules = [
3638
3630
  severity: "error",
3639
3631
  message: `<${tag.name}${elementId ? ` id="${elementId}"` : ""}> uses data-layer instead of data-track-index.`,
3640
3632
  elementId,
3641
- fixHint: "Replace data-layer with data-track-index. The runtime reads data-track-index.",
3633
+ fixHint: "Replace data-layer with data-track-index, which is the canonical name Studio and the linter read. Neither name is read by the render.",
3642
3634
  snippet: truncateSnippet(tag.raw)
3643
3635
  });
3644
3636
  }
@@ -4036,10 +4028,13 @@ var compositionRules = [
4036
4028
  // can't leak styles into each other. A rule whose LEFTMOST selector is the ROOT
4037
4029
  // element's own class (e.g. `.frame { ... }` on the same element that carries
4038
4030
  // data-composition-id) therefore becomes a DESCENDANT selector that can never
4039
- // match the root the whole scene renders unstyled (tiny text top-left, images
4040
- // at natural size). lint/validate/inspect evaluate the file in isolation (no
4041
- // scoping) and Studio previews each scene in its own iframe (no scoping), so the
4042
- // break is invisible until the composited MP4 render. Style the root via `#root`
4031
+ // match the SCOPED element itself. NOTE on the symptom: since #1886 the producer
4032
+ // preserves the authored root as a `data-hf-inner-root` wrapper INSIDE the scoped
4033
+ // element (regression fixture packages/producer/tests/sub-comp-class-selector),
4034
+ // so the class still matches as a descendant and the scene no longer renders
4035
+ // unstyled. This rule is now a consistency constraint, not a render-bug guard:
4036
+ // `#root` is the shape the registry blocks model and the one the scoper
4037
+ // special-cases. Style the root via `#root`
4043
4038
  // (the scoper special-cases the root id) and descendants via plain selectors,
4044
4039
  // like the registry blocks — the runtime already scopes each scene by id, so a
4045
4040
  // class namespace on the root is redundant.
@@ -4056,9 +4051,9 @@ var compositionRules = [
4056
4051
  {
4057
4052
  code: "subcomposition_root_styled_by_class",
4058
4053
  severity: "error",
4059
- message: `Root element has class="${rootClasses.join(" ")}" and is styled by ${offenders.length} rule(s) keyed off that class (e.g. ${example}). At render, every sub-composition rule is scoped to [data-composition-id="${rootCompositionId}"] <selector>, so a selector whose leftmost part is the ROOT's own class becomes a descendant selector that cannot match the root \u2014 the scene renders unstyled (tiny text top-left, full-size images). lint/validate/inspect and Studio's per-frame iframe preview do not scope, so this passes every static check and looks correct in preview.`,
4054
+ message: `Root element has class="${rootClasses.join(" ")}" and is styled by ${offenders.length} rule(s) keyed off that class (e.g. ${example}). At render, every sub-composition rule is scoped to [data-composition-id="${rootCompositionId}"] <selector>, so a selector whose leftmost part is the ROOT's own class becomes a descendant selector that cannot match the scoped element itself. Since #1886 the producer preserves the authored root as an inner wrapper, so this no longer renders the scene unstyled, but #root is the shape the scoper special-cases and the registry blocks model. Use it so preview, render, and Studio agree.`,
4060
4055
  selector: example,
4061
- fixHint: `Give the root id="root" and style it with \`#root { ... }\` plus plain descendant selectors (\`.kicker\`, \`#hero\`) \u2014 the runtime already scopes each sub-composition by data-composition-id, so a class namespace on the root is redundant and breaks under scoping.`,
4056
+ fixHint: `Give the root id="root" and style it with \`#root { ... }\` plus plain descendant selectors (\`.kicker\`, \`#hero\`) \u2014 the runtime already scopes each sub-composition by data-composition-id, so a class namespace on the root is redundant.`,
4062
4057
  snippet: truncateSnippet(rootTag.raw)
4063
4058
  }
4064
4059
  ];
@@ -4804,6 +4799,7 @@ import { checkSubCompositionUsability } from "@hyperframes/parsers/sub-compositi
4804
4799
  import { parseHTML } from "linkedom";
4805
4800
  import {
4806
4801
  cleanAssetUrl as cleanAssetUrl2,
4802
+ collectSubCompositionSrcs,
4807
4803
  isRemoteOrInlineUrl as isRemoteOrInlineUrl2,
4808
4804
  isUnresolvedAssetPlaceholder as isUnresolvedAssetPlaceholder2,
4809
4805
  isWithinProjectRoot,
@@ -5306,13 +5302,7 @@ function lintMissingOrEmptySubComposition(projectDir, rootHtml) {
5306
5302
  const checked = /* @__PURE__ */ new Map();
5307
5303
  const visited = /* @__PURE__ */ new Set();
5308
5304
  const walk = (html) => {
5309
- const compositionSrcRe = /<[^>]*\bdata-composition-src\s*=\s*["']([^"']+)["'][^>]*>/gi;
5310
- const scannable = maskNonScannableRanges2(html);
5311
- let match;
5312
- while ((match = compositionSrcRe.exec(scannable)) !== null) {
5313
- const srcPath = (match[1] ?? "").trim();
5314
- if (!srcPath) continue;
5315
- if (isUnresolvedAssetPlaceholder2(srcPath)) continue;
5305
+ for (const srcPath of collectSubCompositionSrcs(html)) {
5316
5306
  const filePath = resolve(projectDir, srcPath);
5317
5307
  if (visited.has(filePath)) continue;
5318
5308
  visited.add(filePath);