@hyperframes/lint 0.8.75 → 0.8.76
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 +77 -78
- package/dist/browser.js.map +1 -1
- package/dist/index.js +77 -78
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2469,6 +2469,81 @@ function collectDeclaredVariableIds(htmlTagRaw) {
|
|
|
2469
2469
|
}
|
|
2470
2470
|
return declared;
|
|
2471
2471
|
}
|
|
2472
|
+
function variablesDeclarationFindings(tag, tags) {
|
|
2473
|
+
const raw = readJsonAttr(tag.raw, "data-composition-variables");
|
|
2474
|
+
if (!raw) return [];
|
|
2475
|
+
let parsed;
|
|
2476
|
+
try {
|
|
2477
|
+
parsed = JSON.parse(raw);
|
|
2478
|
+
} catch (err) {
|
|
2479
|
+
const reason = err instanceof Error ? err.message : "unknown";
|
|
2480
|
+
return [
|
|
2481
|
+
{
|
|
2482
|
+
code: "invalid_composition_variables_declaration",
|
|
2483
|
+
severity: "error",
|
|
2484
|
+
message: `data-composition-variables is not valid JSON (${reason}).`,
|
|
2485
|
+
fixHint: `Provide a JSON array of variable declarations: data-composition-variables='[{"id":"title","type":"string","label":"Title","default":"Hello"}]'.`,
|
|
2486
|
+
snippet: truncateSnippet(tag.raw)
|
|
2487
|
+
}
|
|
2488
|
+
];
|
|
2489
|
+
}
|
|
2490
|
+
if (!Array.isArray(parsed)) {
|
|
2491
|
+
return [
|
|
2492
|
+
{
|
|
2493
|
+
code: "invalid_composition_variables_declaration",
|
|
2494
|
+
severity: "error",
|
|
2495
|
+
message: "data-composition-variables must be a JSON array of variable declarations.",
|
|
2496
|
+
fixHint: `Wrap declarations in [] and give each an id, type, label, and default: '[{"id":"title","type":"string","label":"Title","default":"Hello"}]'.`,
|
|
2497
|
+
snippet: truncateSnippet(tag.raw)
|
|
2498
|
+
}
|
|
2499
|
+
];
|
|
2500
|
+
}
|
|
2501
|
+
const findings = [];
|
|
2502
|
+
const knownTypes = new Set(COMPOSITION_VARIABLE_TYPES);
|
|
2503
|
+
const varSrcIds = /* @__PURE__ */ new Set();
|
|
2504
|
+
for (const other of tags) {
|
|
2505
|
+
const bound = readAttr(other.raw, "data-var-src");
|
|
2506
|
+
if (bound) varSrcIds.add(bound);
|
|
2507
|
+
}
|
|
2508
|
+
for (let i = 0; i < parsed.length; i += 1) {
|
|
2509
|
+
const entry = parsed[i];
|
|
2510
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
2511
|
+
findings.push({
|
|
2512
|
+
code: "invalid_composition_variables_declaration",
|
|
2513
|
+
severity: "error",
|
|
2514
|
+
message: `data-composition-variables entry [${i}] must be an object with id, type, label, and default.`,
|
|
2515
|
+
snippet: truncateSnippet(tag.raw)
|
|
2516
|
+
});
|
|
2517
|
+
continue;
|
|
2518
|
+
}
|
|
2519
|
+
const e = entry;
|
|
2520
|
+
const missing = [];
|
|
2521
|
+
if (typeof e.id !== "string") missing.push("id");
|
|
2522
|
+
if (typeof e.type !== "string" || !knownTypes.has(e.type)) missing.push("type");
|
|
2523
|
+
if (typeof e.label !== "string") missing.push("label");
|
|
2524
|
+
if (!("default" in e)) missing.push("default");
|
|
2525
|
+
if (missing.length > 0) {
|
|
2526
|
+
findings.push({
|
|
2527
|
+
code: "invalid_composition_variables_declaration",
|
|
2528
|
+
severity: "error",
|
|
2529
|
+
message: `data-composition-variables entry [${i}] is missing or has invalid: ${missing.join(", ")}. Type must be one of string, number, color, boolean, enum, font, image.`,
|
|
2530
|
+
snippet: truncateSnippet(tag.raw)
|
|
2531
|
+
});
|
|
2532
|
+
continue;
|
|
2533
|
+
}
|
|
2534
|
+
const id = String(e.id);
|
|
2535
|
+
if ((e.type === "image" || varSrcIds.has(id)) && typeof e.default === "string" && e.default.length > 0 && !isSafeMediaUrl(e.default)) {
|
|
2536
|
+
findings.push({
|
|
2537
|
+
code: "unloadable_media_variable_default",
|
|
2538
|
+
severity: "error",
|
|
2539
|
+
message: `Variable "${id}" defaults to a URL the runtime will refuse to load, so any element bound to it renders its authored fallback src instead and the render still exits 0.`,
|
|
2540
|
+
fixHint: `Media URLs must be relative, http(s), blob:, or a data:image/* URI. Copy the file into the project and reference it relatively (e.g. "assets/bg.png") rather than by absolute path.`,
|
|
2541
|
+
snippet: truncateSnippet(tag.raw)
|
|
2542
|
+
});
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
return findings;
|
|
2546
|
+
}
|
|
2472
2547
|
function collectAllDeclaredVariableIds(tags) {
|
|
2473
2548
|
const all = /* @__PURE__ */ new Set();
|
|
2474
2549
|
for (const tag of tags) {
|
|
@@ -2944,84 +3019,8 @@ var compositionRules = [
|
|
|
2944
3019
|
// on any structural problem. Surface JSON / shape failures so authors
|
|
2945
3020
|
// catch them at lint time rather than wondering why their `getVariables()`
|
|
2946
3021
|
// defaults aren't applied.
|
|
2947
|
-
//
|
|
2948
|
-
({ tags }) =>
|
|
2949
|
-
const htmlTag = findHtmlTag(tags);
|
|
2950
|
-
if (!htmlTag) return [];
|
|
2951
|
-
const raw = readJsonAttr(htmlTag.raw, "data-composition-variables");
|
|
2952
|
-
if (!raw) return [];
|
|
2953
|
-
let parsed;
|
|
2954
|
-
try {
|
|
2955
|
-
parsed = JSON.parse(raw);
|
|
2956
|
-
} catch (err) {
|
|
2957
|
-
const reason = err instanceof Error ? err.message : "unknown";
|
|
2958
|
-
return [
|
|
2959
|
-
{
|
|
2960
|
-
code: "invalid_composition_variables_declaration",
|
|
2961
|
-
severity: "error",
|
|
2962
|
-
message: `data-composition-variables is not valid JSON (${reason}).`,
|
|
2963
|
-
fixHint: `Provide a JSON array of variable declarations: data-composition-variables='[{"id":"title","type":"string","label":"Title","default":"Hello"}]'.`,
|
|
2964
|
-
snippet: truncateSnippet(htmlTag.raw)
|
|
2965
|
-
}
|
|
2966
|
-
];
|
|
2967
|
-
}
|
|
2968
|
-
if (!Array.isArray(parsed)) {
|
|
2969
|
-
return [
|
|
2970
|
-
{
|
|
2971
|
-
code: "invalid_composition_variables_declaration",
|
|
2972
|
-
severity: "error",
|
|
2973
|
-
message: "data-composition-variables must be a JSON array of variable declarations.",
|
|
2974
|
-
fixHint: `Wrap declarations in [] and give each an id, type, label, and default: '[{"id":"title","type":"string","label":"Title","default":"Hello"}]'.`,
|
|
2975
|
-
snippet: truncateSnippet(htmlTag.raw)
|
|
2976
|
-
}
|
|
2977
|
-
];
|
|
2978
|
-
}
|
|
2979
|
-
const findings = [];
|
|
2980
|
-
const knownTypes = new Set(COMPOSITION_VARIABLE_TYPES);
|
|
2981
|
-
const varSrcIds = /* @__PURE__ */ new Set();
|
|
2982
|
-
for (const tag of tags) {
|
|
2983
|
-
const bound = readAttr(tag.raw, "data-var-src");
|
|
2984
|
-
if (bound) varSrcIds.add(bound);
|
|
2985
|
-
}
|
|
2986
|
-
for (let i = 0; i < parsed.length; i += 1) {
|
|
2987
|
-
const entry = parsed[i];
|
|
2988
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
2989
|
-
findings.push({
|
|
2990
|
-
code: "invalid_composition_variables_declaration",
|
|
2991
|
-
severity: "error",
|
|
2992
|
-
message: `data-composition-variables entry [${i}] must be an object with id, type, label, and default.`,
|
|
2993
|
-
snippet: truncateSnippet(htmlTag.raw)
|
|
2994
|
-
});
|
|
2995
|
-
continue;
|
|
2996
|
-
}
|
|
2997
|
-
const e = entry;
|
|
2998
|
-
const missing = [];
|
|
2999
|
-
if (typeof e.id !== "string") missing.push("id");
|
|
3000
|
-
if (typeof e.type !== "string" || !knownTypes.has(e.type)) missing.push("type");
|
|
3001
|
-
if (typeof e.label !== "string") missing.push("label");
|
|
3002
|
-
if (!("default" in e)) missing.push("default");
|
|
3003
|
-
if (missing.length > 0) {
|
|
3004
|
-
findings.push({
|
|
3005
|
-
code: "invalid_composition_variables_declaration",
|
|
3006
|
-
severity: "error",
|
|
3007
|
-
message: `data-composition-variables entry [${i}] is missing or has invalid: ${missing.join(", ")}. Type must be one of string, number, color, boolean, enum, font, image.`,
|
|
3008
|
-
snippet: truncateSnippet(htmlTag.raw)
|
|
3009
|
-
});
|
|
3010
|
-
continue;
|
|
3011
|
-
}
|
|
3012
|
-
const id = String(e.id);
|
|
3013
|
-
if ((e.type === "image" || varSrcIds.has(id)) && typeof e.default === "string" && e.default.length > 0 && !isSafeMediaUrl(e.default)) {
|
|
3014
|
-
findings.push({
|
|
3015
|
-
code: "unloadable_media_variable_default",
|
|
3016
|
-
severity: "error",
|
|
3017
|
-
message: `Variable "${id}" defaults to a URL the runtime will refuse to load, so any element bound to it renders its authored fallback src instead and the render still exits 0.`,
|
|
3018
|
-
fixHint: `Media URLs must be relative, http(s), blob:, or a data:image/* URI. Copy the file into the project and reference it relatively (e.g. "assets/bg.png") rather than by absolute path.`,
|
|
3019
|
-
snippet: truncateSnippet(htmlTag.raw)
|
|
3020
|
-
});
|
|
3021
|
-
}
|
|
3022
|
-
}
|
|
3023
|
-
return findings;
|
|
3024
|
-
},
|
|
3022
|
+
// Checked on every element that declares: <html>, or the composition root.
|
|
3023
|
+
({ tags }) => tags.filter((tag) => readJsonAttr(tag.raw, "data-composition-variables")).flatMap((tag) => variablesDeclarationFindings(tag, tags)),
|
|
3025
3024
|
// html_dir_attribute_breaks_render — valid non-LTR dir values on
|
|
3026
3025
|
// <html> renders correctly in preview/snapshot but produces a fully
|
|
3027
3026
|
// blank/black video from render, with no other lint/validate/inspect
|