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