@hyperframes/lint 0.8.27 → 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 +81 -1
- package/dist/browser.js.map +1 -1
- package/dist/index.js +81 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -717,7 +717,7 @@ var coreRules = [
|
|
|
717
717
|
{
|
|
718
718
|
pattern: /crypto\.getRandomValues\s*\(/,
|
|
719
719
|
label: "crypto.getRandomValues()",
|
|
720
|
-
hint: "
|
|
720
|
+
hint: "Use a seeded PRNG (e.g. a simple mulberry32) so renders are deterministic across frames."
|
|
721
721
|
},
|
|
722
722
|
{
|
|
723
723
|
pattern: /gsap\.utils\.random\s*\(/,
|
|
@@ -781,6 +781,84 @@ function hasAttrName(tagSource, attr) {
|
|
|
781
781
|
const attrs = tagSource.replace(/^<\s*[a-z][\w:-]*/i, "");
|
|
782
782
|
return new RegExp(`(?:^|\\s)${escaped}(?:\\s*=|\\s|/?>)`, "i").test(attrs);
|
|
783
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
|
+
}
|
|
784
862
|
function mediaHasResolvableSrc(tag, tags) {
|
|
785
863
|
if (readAttr(tag.raw, "src")) return true;
|
|
786
864
|
const end = tag.closeIndex ?? tag.endIndex;
|
|
@@ -1138,6 +1216,8 @@ var mediaRules = [
|
|
|
1138
1216
|
}
|
|
1139
1217
|
return findings;
|
|
1140
1218
|
},
|
|
1219
|
+
// media_src_kind_mismatch
|
|
1220
|
+
findMediaSrcKindMismatchFindings,
|
|
1141
1221
|
// placeholder_media_url
|
|
1142
1222
|
({ tags }) => {
|
|
1143
1223
|
const findings = [];
|