@hyperframes/lint 0.7.46 → 0.7.48

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
@@ -1120,14 +1120,26 @@ var mediaRules = [
1120
1120
  });
1121
1121
  }
1122
1122
  if (hasDataStart && hasId && !hasSrc) {
1123
- findings.push({
1124
- code: "media_missing_src",
1125
- severity: "error",
1126
- message: `<${tag.name} id="${hasId}"> has data-start but no src attribute. The renderer cannot load this media.`,
1127
- elementId: hasId,
1128
- fixHint: `Add a src attribute to the <${tag.name}> element directly. If using <source> children, the renderer still requires src on the parent element.`,
1129
- snippet: truncateSnippet(tag.raw)
1130
- });
1123
+ const varSrc = readAttr(tag.raw, "data-var-src");
1124
+ if (varSrc) {
1125
+ findings.push({
1126
+ code: "media_variable_src_no_fallback",
1127
+ severity: "warning",
1128
+ message: `<${tag.name} id="${hasId}"> relies on data-var-src="${varSrc}" with no fallback src. Renders without a "${varSrc}" value cannot load this media, and audio extraction reads the authored src.`,
1129
+ elementId: hasId,
1130
+ fixHint: `Add a fallback src the composition can render with when the variable is not provided.`,
1131
+ snippet: truncateSnippet(tag.raw)
1132
+ });
1133
+ } else {
1134
+ findings.push({
1135
+ code: "media_missing_src",
1136
+ severity: "error",
1137
+ message: `<${tag.name} id="${hasId}"> has data-start but no src attribute. The renderer cannot load this media.`,
1138
+ elementId: hasId,
1139
+ fixHint: `Add a src attribute to the <${tag.name}> element directly. If using <source> children, the renderer still requires src on the parent element.`,
1140
+ snippet: truncateSnippet(tag.raw)
1141
+ });
1142
+ }
1131
1143
  }
1132
1144
  if (readAttr(tag.raw, "preload") === "none") {
1133
1145
  findings.push({
@@ -2431,6 +2443,40 @@ function rootClassStyledSelectors(styles, rootClasses) {
2431
2443
  }
2432
2444
  return offenders;
2433
2445
  }
2446
+ function collectDeclaredVariableIds(htmlTagRaw) {
2447
+ const declared = /* @__PURE__ */ new Set();
2448
+ const raw = readJsonAttr(htmlTagRaw, "data-composition-variables");
2449
+ if (!raw) return declared;
2450
+ let parsed;
2451
+ try {
2452
+ parsed = JSON.parse(raw);
2453
+ } catch {
2454
+ return null;
2455
+ }
2456
+ if (!Array.isArray(parsed)) return declared;
2457
+ for (const entry of parsed) {
2458
+ const id = entry?.id;
2459
+ if (typeof id === "string") declared.add(id);
2460
+ }
2461
+ return declared;
2462
+ }
2463
+ function collectAllDeclaredVariableIds(source) {
2464
+ const all = /* @__PURE__ */ new Set();
2465
+ const tagRe = /<[a-zA-Z][^>]*\bdata-composition-variables\b[^>]*>/gi;
2466
+ let match;
2467
+ while ((match = tagRe.exec(source)) !== null) {
2468
+ const ids = collectDeclaredVariableIds(match[0]);
2469
+ if (ids === null) return null;
2470
+ for (const id of ids) all.add(id);
2471
+ }
2472
+ return all;
2473
+ }
2474
+ function declaredIdsForBindingCheck(source) {
2475
+ const declared = collectAllDeclaredVariableIds(source);
2476
+ if (declared === null) return null;
2477
+ if (declared.size === 0 && !findHtmlTag(source)) return null;
2478
+ return declared;
2479
+ }
2434
2480
  var compositionRules = [
2435
2481
  // invalid_parent_traversal_in_asset_path — catches `../` traversal in src,
2436
2482
  // href, inline-style url(), and <style> url() asset references on
@@ -2826,6 +2872,32 @@ var compositionRules = [
2826
2872
  }
2827
2873
  return findings;
2828
2874
  },
2875
+ // unknown_variable_binding
2876
+ // data-var-src / data-var-text bind an element to a declared variable id;
2877
+ // the runtime silently keeps the authored fallback when the id resolves to
2878
+ // nothing, so a typo'd binding is invisible until a customer's override
2879
+ // does nothing. Skipped for fragment files (no <html>): their values come
2880
+ // from a host's data-variable-values, which this file can't see.
2881
+ ({ source, tags }) => {
2882
+ const declared = declaredIdsForBindingCheck(source);
2883
+ if (!declared) return [];
2884
+ const findings = [];
2885
+ for (const tag of tags) {
2886
+ for (const attr of ["data-var-src", "data-var-text"]) {
2887
+ const id = readAttr(tag.raw, attr)?.trim();
2888
+ if (!id || declared.has(id)) continue;
2889
+ findings.push({
2890
+ code: "unknown_variable_binding",
2891
+ severity: "warning",
2892
+ message: `<${tag.name}> binds ${attr}="${id}" but no variable "${id}" is declared in data-composition-variables \u2014 the binding will silently keep the authored fallback.`,
2893
+ fixHint: `Declare the variable on the composition root (<html>, or the [data-composition-id] root element for a template/fragment comp): data-composition-variables='[{"id":"${id}","type":"${attr === "data-var-src" ? "image" : "string"}","label":"${id}","default":"..."}]', or fix the binding id.`,
2894
+ elementId: readAttr(tag.raw, "id") || void 0,
2895
+ snippet: truncateSnippet(tag.raw)
2896
+ });
2897
+ }
2898
+ }
2899
+ return findings;
2900
+ },
2829
2901
  // invalid_composition_variables_declaration
2830
2902
  // The runtime parses `data-composition-variables` and silently returns []
2831
2903
  // on any structural problem. Surface JSON / shape failures so authors