@hyperframes/lint 0.8.26 → 0.8.28
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 +87 -2
- package/dist/browser.js.map +1 -1
- package/dist/index.js +87 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -571,7 +571,12 @@ var coreRules = [
|
|
|
571
571
|
let root;
|
|
572
572
|
try {
|
|
573
573
|
root = postcss.parse(style.content);
|
|
574
|
-
} catch {
|
|
574
|
+
} catch (error) {
|
|
575
|
+
findings.push({
|
|
576
|
+
code: "css_parse_error",
|
|
577
|
+
severity: "error",
|
|
578
|
+
message: `CSS parse error: ${error instanceof Error ? error.message : "unknown"}`
|
|
579
|
+
});
|
|
575
580
|
continue;
|
|
576
581
|
}
|
|
577
582
|
root.walkRules((rule) => {
|
|
@@ -712,7 +717,7 @@ var coreRules = [
|
|
|
712
717
|
{
|
|
713
718
|
pattern: /crypto\.getRandomValues\s*\(/,
|
|
714
719
|
label: "crypto.getRandomValues()",
|
|
715
|
-
hint: "
|
|
720
|
+
hint: "Use a seeded PRNG (e.g. a simple mulberry32) so renders are deterministic across frames."
|
|
716
721
|
},
|
|
717
722
|
{
|
|
718
723
|
pattern: /gsap\.utils\.random\s*\(/,
|
|
@@ -776,6 +781,84 @@ function hasAttrName(tagSource, attr) {
|
|
|
776
781
|
const attrs = tagSource.replace(/^<\s*[a-z][\w:-]*/i, "");
|
|
777
782
|
return new RegExp(`(?:^|\\s)${escaped}(?:\\s*=|\\s|/?>)`, "i").test(attrs);
|
|
778
783
|
}
|
|
784
|
+
var IMAGE_SRC_EXT = /* @__PURE__ */ new Set([
|
|
785
|
+
"jpg",
|
|
786
|
+
"jpeg",
|
|
787
|
+
"png",
|
|
788
|
+
"gif",
|
|
789
|
+
"bmp",
|
|
790
|
+
"webp",
|
|
791
|
+
"svg",
|
|
792
|
+
"heic",
|
|
793
|
+
"heif",
|
|
794
|
+
"tiff",
|
|
795
|
+
"ico"
|
|
796
|
+
]);
|
|
797
|
+
var VIDEO_SRC_EXT = /* @__PURE__ */ new Set([
|
|
798
|
+
"mp4",
|
|
799
|
+
"mov",
|
|
800
|
+
"avi",
|
|
801
|
+
"webm",
|
|
802
|
+
"mkv",
|
|
803
|
+
"flv",
|
|
804
|
+
"wmv",
|
|
805
|
+
"m4v",
|
|
806
|
+
"mpg",
|
|
807
|
+
"mpeg"
|
|
808
|
+
]);
|
|
809
|
+
function srcKind(src) {
|
|
810
|
+
const stripped = src.trim();
|
|
811
|
+
if (!stripped) return null;
|
|
812
|
+
const lower = stripped.toLowerCase();
|
|
813
|
+
if (lower.startsWith("data:")) {
|
|
814
|
+
const mime = /^data:([^;,]+)/i.exec(stripped)?.[1]?.toLowerCase();
|
|
815
|
+
if (!mime) return null;
|
|
816
|
+
if (mime.startsWith("image/")) return "image";
|
|
817
|
+
if (mime.startsWith("video/")) return "video";
|
|
818
|
+
return null;
|
|
819
|
+
}
|
|
820
|
+
if (lower.startsWith("blob:")) return null;
|
|
821
|
+
let pathname = stripped;
|
|
822
|
+
try {
|
|
823
|
+
if (/^https?:/i.test(stripped)) {
|
|
824
|
+
pathname = decodeURIComponent(new URL(stripped).pathname);
|
|
825
|
+
} else {
|
|
826
|
+
pathname = stripped.split("?")[0]?.split("#")[0] ?? stripped;
|
|
827
|
+
}
|
|
828
|
+
} catch {
|
|
829
|
+
pathname = stripped.split("?")[0]?.split("#")[0] ?? stripped;
|
|
830
|
+
}
|
|
831
|
+
const base = pathname.split("/").pop() ?? "";
|
|
832
|
+
const dot = base.lastIndexOf(".");
|
|
833
|
+
if (dot < 0) return null;
|
|
834
|
+
const ext = base.slice(dot + 1).toLowerCase();
|
|
835
|
+
if (IMAGE_SRC_EXT.has(ext)) return "image";
|
|
836
|
+
if (VIDEO_SRC_EXT.has(ext)) return "video";
|
|
837
|
+
return null;
|
|
838
|
+
}
|
|
839
|
+
function findMediaSrcKindMismatchFindings(ctx) {
|
|
840
|
+
const findings = [];
|
|
841
|
+
for (const tag of ctx.tags) {
|
|
842
|
+
if (tag.name !== "video" && tag.name !== "img") continue;
|
|
843
|
+
const src = readAttr(tag.raw, "src");
|
|
844
|
+
if (!src) continue;
|
|
845
|
+
const kind = srcKind(src);
|
|
846
|
+
if (kind === null) continue;
|
|
847
|
+
if (tag.name === "video" && kind !== "image") continue;
|
|
848
|
+
if (tag.name === "img" && kind !== "video") continue;
|
|
849
|
+
const elementId = readAttr(tag.raw, "id") || void 0;
|
|
850
|
+
const expected = tag.name === "video" ? "video" : "image";
|
|
851
|
+
findings.push({
|
|
852
|
+
code: "media_src_kind_mismatch",
|
|
853
|
+
severity: "error",
|
|
854
|
+
message: `<${tag.name}${elementId ? ` id="${elementId}"` : ""}> src is a ${kind}, not a ${expected}. The producer fail-closes when the tag and file kind disagree.`,
|
|
855
|
+
elementId,
|
|
856
|
+
fixHint: tag.name === "video" ? "Use <img> for a still, or point <video> at a video URL (mp4/webm/mov/\u2026)." : "Use <video> for a video URL, or point <img> at a still (png/jpg/webp/\u2026).",
|
|
857
|
+
snippet: truncateSnippet(tag.raw)
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
return findings;
|
|
861
|
+
}
|
|
779
862
|
function mediaHasResolvableSrc(tag, tags) {
|
|
780
863
|
if (readAttr(tag.raw, "src")) return true;
|
|
781
864
|
const end = tag.closeIndex ?? tag.endIndex;
|
|
@@ -1133,6 +1216,8 @@ var mediaRules = [
|
|
|
1133
1216
|
}
|
|
1134
1217
|
return findings;
|
|
1135
1218
|
},
|
|
1219
|
+
// media_src_kind_mismatch
|
|
1220
|
+
findMediaSrcKindMismatchFindings,
|
|
1136
1221
|
// placeholder_media_url
|
|
1137
1222
|
({ tags }) => {
|
|
1138
1223
|
const findings = [];
|