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