@hyperframes/lint 0.7.25 → 0.7.27

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
@@ -6,7 +6,7 @@ var COMPOSITION_ID_IN_CSS_PATTERN = /\[data-composition-id=["']([^"']+)["']\]/g;
6
6
  var TIMELINE_REGISTRY_INIT_PATTERN = /window\.__timelines\s*=\s*window\.__timelines\s*\|\|\s*\{\}|window\.__timelines\s*=\s*\{\}|window\.__timelines\s*\?\?=\s*\{\}/i;
7
7
  var TIMELINE_REGISTRY_OBJECT_LITERAL_PATTERN = /window\.__timelines\s*=\s*\{\s*(?:["'][^"']+["']|[A-Za-z_$][\w$]*)\s*:/i;
8
8
  var TIMELINE_REGISTRY_ASSIGN_PATTERN = /window\.__timelines(?:\[[^\]]+\]|\.[A-Za-z_$][\w$]*)\s*=/i;
9
- var WINDOW_TIMELINE_ASSIGN_PATTERN = /window\.__timelines(?:\[\s*["']([^"']+)["']\s*\]|\.\s*([A-Za-z_$][\w$]*))\s*=\s*([A-Za-z_$][\w$]*)/i;
9
+ var WINDOW_TIMELINE_ASSIGN_PATTERN = /window\.__timelines(?:\[\s*(?:["']([^"']+)["']|[A-Za-z_$][\w$.]*)\s*\]|\.\s*([A-Za-z_$][\w$]*))\s*=\s*([A-Za-z_$][\w$]*)/i;
10
10
  var INVALID_SCRIPT_CLOSE_PATTERN = /<script[^>]*>[\s\S]*?<\s*\/\s*script(?!>)/i;
11
11
  var TIMELINE_REGISTRY_KEY_PATTERN = /window\.__timelines(?:\[\s*["']([^"']+)["']\s*\]|\.\s*([A-Za-z_$][\w$]*))\s*=/g;
12
12
  var TIMELINE_REGISTRY_OBJECT_BODY_PATTERN = /window\.__timelines\s*=\s*\{([\s\S]*?)\}/i;
@@ -66,8 +66,15 @@ function findRootTag(source) {
66
66
  const bodyEnd = bodyOpenMatch && bodyCloseMatch && bodyCloseMatch.index > bodyStart ? bodyCloseMatch.index : source.length;
67
67
  const bodyContent = bodyOpenMatch ? source.slice(bodyStart, bodyEnd) : source;
68
68
  const bodyTags = extractOpenTags(bodyContent);
69
+ let skipBefore = -1;
69
70
  for (const tag of bodyTags) {
71
+ if (tag.index < skipBefore) continue;
70
72
  if (["script", "style", "meta", "link", "title"].includes(tag.name)) continue;
73
+ if (tag.name === "svg" && !readAttr(tag.raw, "data-composition-id") && !readAttr(tag.raw, "data-width") && !readAttr(tag.raw, "data-height")) {
74
+ const closeMatch = /<\/svg\s*>/i.exec(bodyContent.slice(tag.index));
75
+ skipBefore = closeMatch ? tag.index + closeMatch.index + closeMatch[0].length : Infinity;
76
+ continue;
77
+ }
71
78
  return { ...tag, index: tag.index + bodyStart };
72
79
  }
73
80
  return null;
@@ -1621,12 +1628,14 @@ ${right.raw}`)
1621
1628
  (candidate) => isHardKillSet(candidate, win.targetSelector, boundary)
1622
1629
  );
1623
1630
  if (hasHardKill) continue;
1631
+ const exitClipInfo = clipIds.get(win.targetSelector) || clipClasses.get(win.targetSelector);
1632
+ const fixHint = exitClipInfo ? `"${win.targetSelector}" is a clip element \u2014 the framework already manages its visibility. Wrap the scene's content in an inner non-clip <div>, move the exit tween and the hard kill (\`tl.set("<inner-selector>", ${hiddenStateLiteral(win.propertyValues)}, ${boundary.toFixed(2)})\`) onto that wrapper instead.` : `Add \`tl.set("${win.targetSelector}", ${hiddenStateLiteral(win.propertyValues)}, ${boundary.toFixed(2)})\` after the exit tween.`;
1624
1633
  findings.push({
1625
1634
  code: "gsap_exit_missing_hard_kill",
1626
1635
  severity: "error",
1627
1636
  message: `GSAP exit on "${win.targetSelector}" ends at the ${boundary.toFixed(2)}s clip start boundary without a matching tl.set hard kill. Non-linear seeking can land after the fade and leave stale visibility state.`,
1628
1637
  selector: win.targetSelector,
1629
- fixHint: `Add \`tl.set("${win.targetSelector}", ${hiddenStateLiteral(win.propertyValues)}, ${boundary.toFixed(2)})\` after the exit tween.`,
1638
+ fixHint,
1630
1639
  snippet: truncateSnippet(win.raw)
1631
1640
  });
1632
1641
  }
@@ -1896,12 +1905,15 @@ ${right.raw}`)
1896
1905
  const killPattern = new RegExp(`["']#${id}["'][^)]*visibility\\s*:\\s*["']hidden["']`);
1897
1906
  const hasKill = killPattern.test(content);
1898
1907
  if (!hasKill) {
1908
+ const classes = (readAttr(tag.raw, "class") || "").split(/\s+/).filter(Boolean);
1909
+ const isClip = classes.includes("clip");
1910
+ const fixHint = isClip ? `"#${id}" is a clip element \u2014 the framework already manages its visibility. Wrap the scene's content in an inner non-clip <div>, move the exit tween and the hard kill (\`tl.set("<inner-selector>", { visibility: "hidden" }, <exit-end-time>)\`) onto that wrapper instead.` : `Add \`tl.set("#${id}", { visibility: "hidden" }, <exit-end-time>)\` after the scene's exit tweens.`;
1899
1911
  findings.push({
1900
1912
  code: "scene_layer_missing_visibility_kill",
1901
1913
  severity: "error",
1902
1914
  elementId: id,
1903
1915
  message: `Scene layer "#${id}" exits via opacity tween but has no visibility: hidden hard kill. When scrubbing or when tweens conflict, the scene may remain partially visible and overlap the next scene.`,
1904
- fixHint: `Add \`tl.set("#${id}", { visibility: "hidden" }, <exit-end-time>)\` after the scene's exit tweens.`
1916
+ fixHint
1905
1917
  });
1906
1918
  }
1907
1919
  }
@@ -3293,7 +3305,7 @@ var fontRules = [
3293
3305
  code: "font_family_without_font_face",
3294
3306
  severity: "error",
3295
3307
  message: `Font ${undeclared.length === 1 ? "family" : "families"} used without @font-face declaration: ${undeclared.join(", ")}. These are not in the auto-resolved font list, so the renderer cannot supply them automatically. Text will fall back to a generic font, producing incorrect typography in the video.`,
3296
- fixHint: "Add @font-face { font-family: '...'; src: url('capture/assets/fonts/...woff2'); } for each font family, pointing to the captured .woff2 files."
3308
+ fixHint: "Add @font-face { font-family: '...'; src: url('capture/assets/fonts/...woff2'); } for each font family, pointing to the captured .woff2 files. For an OS-bundled system font (e.g. Hiragino Sans, Microsoft YaHei) that has no downloadable file, use src: local('Exact Font Name') instead \u2014 the declaration alone satisfies this check without needing a font file."
3297
3309
  });
3298
3310
  return findings;
3299
3311
  }