@hyperframes/lint 0.8.42 → 0.8.44

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
@@ -615,6 +615,11 @@ function buildLintContext(html, options = {}) {
615
615
  // src/rules/core.ts
616
616
  import postcss from "postcss";
617
617
  import selectorParser from "postcss-selector-parser";
618
+ import {
619
+ HTML_BODY_CSS_WIDTH_FIRST_RE,
620
+ HTML_BODY_CSS_HEIGHT_FIRST_RE,
621
+ VIEWPORT_META_SIZE_RE
622
+ } from "@hyperframes/parsers";
618
623
  function repeatedDescendantId(selector) {
619
624
  let repeated = null;
620
625
  const requiredPseudoIds = (pseudo) => {
@@ -787,6 +792,56 @@ function findVisibleMarkupCommentLeak(source) {
787
792
  }
788
793
  return null;
789
794
  }
795
+ function readHtmlBodyCssSize(source) {
796
+ const widthFirst = source.match(HTML_BODY_CSS_WIDTH_FIRST_RE);
797
+ if (widthFirst) {
798
+ const [, , width = "", , height = ""] = widthFirst;
799
+ return { width, height };
800
+ }
801
+ const heightFirst = source.match(HTML_BODY_CSS_HEIGHT_FIRST_RE);
802
+ if (heightFirst) {
803
+ const [, , height = "", , width = ""] = heightFirst;
804
+ return { width, height };
805
+ }
806
+ return null;
807
+ }
808
+ function readViewportMetaSize(source) {
809
+ const match = source.match(VIEWPORT_META_SIZE_RE);
810
+ if (!match) return null;
811
+ const [, , width = "", , height = ""] = match;
812
+ return { width, height };
813
+ }
814
+ function describeSizeMismatch(label, size, dataWidth, dataHeight) {
815
+ if (!size || size.width === dataWidth && size.height === dataHeight) return null;
816
+ return `${label} is ${size.width}x${size.height}`;
817
+ }
818
+ function describeRootDimensionsDrift(source, dataWidth, dataHeight) {
819
+ const bodyCssMismatch = describeSizeMismatch(
820
+ "html/body CSS",
821
+ readHtmlBodyCssSize(source),
822
+ dataWidth,
823
+ dataHeight
824
+ );
825
+ const viewportMismatch = describeSizeMismatch(
826
+ "the viewport meta",
827
+ readViewportMetaSize(source),
828
+ dataWidth,
829
+ dataHeight
830
+ );
831
+ if (!bodyCssMismatch && !viewportMismatch) return null;
832
+ const declared = `Root composition declares data-width="${dataWidth}" data-height="${dataHeight}"`;
833
+ if (!bodyCssMismatch) {
834
+ return {
835
+ message: `${declared}, but ${viewportMismatch}. The viewport meta has no effect on capture \u2014 the renderer sizes the viewport from the root's own data-width/data-height \u2014 so this is stale metadata, not a clipping risk.`,
836
+ fixHint: "update the meta viewport to match, or scaffold with `hyperframes init --resolution portrait`"
837
+ };
838
+ }
839
+ const mismatches = viewportMismatch ? `${bodyCssMismatch} and ${viewportMismatch}` : bodyCssMismatch;
840
+ return {
841
+ message: `${declared}, but ${mismatches}. The scaffolded body clips the composition at its old size.`,
842
+ fixHint: "update html/body CSS and the meta viewport to match, or scaffold with `hyperframes init --resolution portrait`"
843
+ };
844
+ }
790
845
  var coreRules = [
791
846
  // id_requires_css_escape
792
847
  ({ tags }) => {
@@ -831,6 +886,39 @@ var coreRules = [
831
886
  }
832
887
  return findings;
833
888
  },
889
+ // root_dimensions_mismatch
890
+ //
891
+ // Render size and the runtime's forced #root size both read the root's own
892
+ // data-width/data-height, so they stay correct. But editing only those two
893
+ // attributes — rather than scaffolding with `hyperframes init --resolution`,
894
+ // which rewrites the scaffold's other copies of the resolution too — leaves
895
+ // the `html, body` CSS and the `<meta viewport>` at the old value, and a
896
+ // stale body with `overflow: hidden` visually clips the correctly-sized
897
+ // root. `hyperframes check`'s layout audits can't see it: they measure
898
+ // against the root's own (already-correct) rect, not the body's.
899
+ //
900
+ // Sub-compositions are exempt: loadExternalCompositions (packages/core/src/
901
+ // runtime/compositionLoader.ts) mounts only the matched <template>/<body>
902
+ // subtree, so a sub-comp's own <html>/<head>/<meta viewport> never reach
903
+ // the rendering document, even when it's a full standalone document.
904
+ ({ rootTag, source, options }) => {
905
+ if (!rootTag || options.isSubComposition) return [];
906
+ const dataWidth = readAttr(rootTag.raw, "data-width");
907
+ const dataHeight = readAttr(rootTag.raw, "data-height");
908
+ if (!dataWidth || !dataHeight) return [];
909
+ const drift = describeRootDimensionsDrift(source, dataWidth, dataHeight);
910
+ if (!drift) return [];
911
+ return [
912
+ {
913
+ code: "root_dimensions_mismatch",
914
+ severity: "warning",
915
+ message: drift.message,
916
+ elementId: readAttr(rootTag.raw, "id") || void 0,
917
+ fixHint: drift.fixHint,
918
+ snippet: truncateSnippet(rootTag.raw)
919
+ }
920
+ ];
921
+ },
834
922
  // unbalanced_style_tags
835
923
  ({ source }) => {
836
924
  let opens = 0;