diff --git a/packages/cli/src/registry/registryComponents.test.ts b/packages/cli/src/registry/registryComponents.test.ts new file mode 100644 index 0000000000..2716e7401c --- /dev/null +++ b/packages/cli/src/registry/registryComponents.test.ts @@ -0,0 +1,71 @@ +import { existsSync, readFileSync, readdirSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { lintHyperframeHtml } from "@hyperframes/lint"; +import { describe, expect, it } from "vitest"; + +const componentsDir = resolve( + dirname(fileURLToPath(import.meta.url)), + "../../../../registry/components", +); + +interface RegistryManifest { + files: Array<{ path: string; type: string }>; +} + +async function invalidInstallableMedia(entryName: string): Promise { + const itemDir = join(componentsDir, entryName); + const manifest = JSON.parse( + readFileSync(join(itemDir, "registry-item.json"), "utf8"), + ) as RegistryManifest; + const invalidMedia: string[] = []; + + for (const file of manifest.files) { + if (file.type !== "hyperframes:snippet" || !file.path.endsWith(".html")) continue; + const result = await lintHyperframeHtml(readFileSync(join(itemDir, file.path), "utf8"), { + isSubComposition: true, + }); + for (const finding of result.findings) { + if (finding.code !== "media_in_subcomposition" && finding.code !== "media_missing_src") { + continue; + } + invalidMedia.push(`${entryName}/${file.path}: ${finding.code}`); + } + } + + return invalidMedia; +} + +async function invalidDemoMedia(entryName: string): Promise { + const demoPath = join(componentsDir, entryName, "demo.html"); + if (!existsSync(demoPath)) return []; + + const result = await lintHyperframeHtml(readFileSync(demoPath, "utf8")); + return result.findings + .filter((finding) => finding.code === "media_missing_src") + .map((finding) => `${entryName}/demo.html: ${finding.code}`); +} + +describe("registry components", () => { + it("ships installable snippets without invalid nested media", async () => { + const invalidMedia: string[] = []; + + for (const entry of readdirSync(componentsDir, { withFileTypes: true })) { + if (!entry.isDirectory()) continue; + invalidMedia.push(...(await invalidInstallableMedia(entry.name))); + } + + expect(invalidMedia).toEqual([]); + }); + + it("ships demos without source-less media", async () => { + const invalidMedia: string[] = []; + + for (const entry of readdirSync(componentsDir, { withFileTypes: true })) { + if (!entry.isDirectory()) continue; + invalidMedia.push(...(await invalidDemoMedia(entry.name))); + } + + expect(invalidMedia).toEqual([]); + }); +}); diff --git a/registry/components/caption-clip-wipe/caption-clip-wipe.html b/registry/components/caption-clip-wipe/caption-clip-wipe.html index e48662dded..9b5d55e599 100644 --- a/registry/components/caption-clip-wipe/caption-clip-wipe.html +++ b/registry/components/caption-clip-wipe/caption-clip-wipe.html @@ -33,14 +33,6 @@ overflow: hidden; background: transparent; } - #wp-video { - position: absolute; - inset: 0; - z-index: 0; - width: 100%; - height: 100%; - object-fit: cover; - } .wp-overlay { position: absolute; inset: 0; @@ -92,15 +84,6 @@ data-width="1920" data-height="1080" > -
diff --git a/registry/components/caption-clip-wipe/demo.html b/registry/components/caption-clip-wipe/demo.html index a519dbfe88..c5d8c27552 100644 --- a/registry/components/caption-clip-wipe/demo.html +++ b/registry/components/caption-clip-wipe/demo.html @@ -32,14 +32,6 @@ overflow: hidden; background: #0a0a0a; } - #wp-video { - position: absolute; - inset: 0; - z-index: 0; - width: 100%; - height: 100%; - object-fit: cover; - } .wp-overlay { position: absolute; inset: 0; @@ -90,15 +82,6 @@ data-width="1920" data-height="1080" > -
diff --git a/registry/components/caption-editorial-emphasis/caption-editorial-emphasis.html b/registry/components/caption-editorial-emphasis/caption-editorial-emphasis.html index b90b987289..50b41a1220 100644 --- a/registry/components/caption-editorial-emphasis/caption-editorial-emphasis.html +++ b/registry/components/caption-editorial-emphasis/caption-editorial-emphasis.html @@ -37,15 +37,6 @@ background: transparent; } - #bg-video { - position: absolute; - inset: 0; - z-index: 1; - width: 100%; - height: 100%; - object-fit: cover; - } - .caption-layer { position: absolute; inset: 0; @@ -132,17 +123,6 @@ data-width="1920" data-height="1080" > - - diff --git a/registry/components/caption-editorial-emphasis/demo.html b/registry/components/caption-editorial-emphasis/demo.html index 85067766d3..d942f7ce7b 100644 --- a/registry/components/caption-editorial-emphasis/demo.html +++ b/registry/components/caption-editorial-emphasis/demo.html @@ -36,15 +36,6 @@ background: #000; } - #bg-video { - position: absolute; - inset: 0; - z-index: 1; - width: 100%; - height: 100%; - object-fit: cover; - } - .caption-layer { position: absolute; inset: 0; @@ -130,17 +121,6 @@ data-width="1920" data-height="1080" > - - diff --git a/registry/components/caption-emoji-pop/caption-emoji-pop.html b/registry/components/caption-emoji-pop/caption-emoji-pop.html index f5985d2982..f8a863d2e8 100644 --- a/registry/components/caption-emoji-pop/caption-emoji-pop.html +++ b/registry/components/caption-emoji-pop/caption-emoji-pop.html @@ -37,15 +37,6 @@ background: transparent; } - #avatar-video { - position: absolute; - inset: 0; - z-index: 1; - width: 100%; - height: 100%; - object-fit: cover; - } - .caption-layer { position: absolute; inset: 0; @@ -128,16 +119,6 @@ data-width="1920" data-height="1080" > - - diff --git a/registry/components/caption-emoji-pop/demo.html b/registry/components/caption-emoji-pop/demo.html index 64ac88ec5b..f488b6a6bf 100644 --- a/registry/components/caption-emoji-pop/demo.html +++ b/registry/components/caption-emoji-pop/demo.html @@ -36,15 +36,6 @@ background: #000000; } - #avatar-video { - position: absolute; - inset: 0; - z-index: 1; - width: 100%; - height: 100%; - object-fit: cover; - } - .caption-layer { position: absolute; inset: 0; @@ -126,16 +117,6 @@ data-width="1920" data-height="1080" > - - diff --git a/registry/components/caption-glitch-rgb/caption-glitch-rgb.html b/registry/components/caption-glitch-rgb/caption-glitch-rgb.html index d0da71dc56..e44a67674f 100644 --- a/registry/components/caption-glitch-rgb/caption-glitch-rgb.html +++ b/registry/components/caption-glitch-rgb/caption-glitch-rgb.html @@ -33,14 +33,6 @@ overflow: hidden; background: transparent; } - #gl-video { - position: absolute; - inset: 0; - z-index: 0; - width: 100%; - height: 100%; - object-fit: cover; - } .gl-overlay { position: absolute; inset: 0; @@ -112,15 +104,6 @@ data-width="1920" data-height="1080" > -
diff --git a/registry/components/caption-glitch-rgb/demo.html b/registry/components/caption-glitch-rgb/demo.html index 0cff82ec3f..c7cf5b9d77 100644 --- a/registry/components/caption-glitch-rgb/demo.html +++ b/registry/components/caption-glitch-rgb/demo.html @@ -32,14 +32,6 @@ overflow: hidden; background: #0a0a0a; } - #gl-video { - position: absolute; - inset: 0; - z-index: 0; - width: 100%; - height: 100%; - object-fit: cover; - } .gl-overlay { position: absolute; inset: 0; @@ -110,15 +102,6 @@ data-width="1920" data-height="1080" > -
diff --git a/registry/components/caption-gradient-fill/caption-gradient-fill.html b/registry/components/caption-gradient-fill/caption-gradient-fill.html index 1b4ecc900f..23f46161fe 100644 --- a/registry/components/caption-gradient-fill/caption-gradient-fill.html +++ b/registry/components/caption-gradient-fill/caption-gradient-fill.html @@ -80,15 +80,6 @@ data-width="1920" data-height="1080" > -
diff --git a/registry/components/caption-gradient-fill/demo.html b/registry/components/caption-gradient-fill/demo.html index 6ea07aa971..1e7c31e079 100644 --- a/registry/components/caption-gradient-fill/demo.html +++ b/registry/components/caption-gradient-fill/demo.html @@ -78,15 +78,6 @@ data-width="1920" data-height="1080" > -
diff --git a/registry/components/caption-kinetic-slam/caption-kinetic-slam.html b/registry/components/caption-kinetic-slam/caption-kinetic-slam.html index 38a6ce013d..7a8120ba01 100644 --- a/registry/components/caption-kinetic-slam/caption-kinetic-slam.html +++ b/registry/components/caption-kinetic-slam/caption-kinetic-slam.html @@ -30,14 +30,6 @@ overflow: hidden; background: transparent; } - #kt-video { - position: absolute; - inset: 0; - z-index: 0; - width: 100%; - height: 100%; - object-fit: cover; - } .kt-overlay { position: absolute; inset: 0; @@ -79,15 +71,6 @@ data-width="1920" data-height="1080" > -
diff --git a/registry/components/caption-kinetic-slam/demo.html b/registry/components/caption-kinetic-slam/demo.html index a3d4d73144..dfc2b42a76 100644 --- a/registry/components/caption-kinetic-slam/demo.html +++ b/registry/components/caption-kinetic-slam/demo.html @@ -29,14 +29,6 @@ overflow: hidden; background: #0a0a0a; } - #kt-video { - position: absolute; - inset: 0; - z-index: 0; - width: 100%; - height: 100%; - object-fit: cover; - } .kt-overlay { position: absolute; inset: 0; @@ -77,15 +69,6 @@ data-width="1920" data-height="1080" > -
diff --git a/registry/components/caption-matrix-decode/caption-matrix-decode.html b/registry/components/caption-matrix-decode/caption-matrix-decode.html index 7c44b778e9..e43f0f69db 100644 --- a/registry/components/caption-matrix-decode/caption-matrix-decode.html +++ b/registry/components/caption-matrix-decode/caption-matrix-decode.html @@ -33,14 +33,6 @@ overflow: hidden; background: transparent; } - #sc-video { - position: absolute; - inset: 0; - z-index: 0; - width: 100%; - height: 100%; - object-fit: cover; - } .sc-overlay { position: absolute; inset: 0; @@ -100,15 +92,6 @@ data-width="1920" data-height="1080" > -
diff --git a/registry/components/caption-matrix-decode/demo.html b/registry/components/caption-matrix-decode/demo.html index 11ac39f608..35968c32c6 100644 --- a/registry/components/caption-matrix-decode/demo.html +++ b/registry/components/caption-matrix-decode/demo.html @@ -32,14 +32,6 @@ overflow: hidden; background: #000; } - #sc-video { - position: absolute; - inset: 0; - z-index: 0; - width: 100%; - height: 100%; - object-fit: cover; - } .sc-overlay { position: absolute; inset: 0; @@ -98,15 +90,6 @@ data-width="1920" data-height="1080" > -
diff --git a/registry/components/caption-neon-accent/caption-neon-accent.html b/registry/components/caption-neon-accent/caption-neon-accent.html index 6866f20955..1313eeb8e3 100644 --- a/registry/components/caption-neon-accent/caption-neon-accent.html +++ b/registry/components/caption-neon-accent/caption-neon-accent.html @@ -37,15 +37,6 @@ background: transparent; } - #avatar-video { - position: absolute; - inset: 0; - z-index: 1; - width: 100%; - height: 100%; - object-fit: cover; - } - .caption-layer { position: absolute; inset: 0; @@ -128,16 +119,6 @@ data-width="1920" data-height="1080" > - - diff --git a/registry/components/caption-neon-accent/demo.html b/registry/components/caption-neon-accent/demo.html index 3f9e294aac..90f45018e8 100644 --- a/registry/components/caption-neon-accent/demo.html +++ b/registry/components/caption-neon-accent/demo.html @@ -36,15 +36,6 @@ background: #000000; } - #avatar-video { - position: absolute; - inset: 0; - z-index: 1; - width: 100%; - height: 100%; - object-fit: cover; - } - .caption-layer { position: absolute; inset: 0; @@ -126,16 +117,6 @@ data-width="1920" data-height="1080" > - - diff --git a/registry/components/caption-neon-glow/caption-neon-glow.html b/registry/components/caption-neon-glow/caption-neon-glow.html index 871a1f7b6c..736507e71e 100644 --- a/registry/components/caption-neon-glow/caption-neon-glow.html +++ b/registry/components/caption-neon-glow/caption-neon-glow.html @@ -33,14 +33,6 @@ overflow: hidden; background: transparent; } - #ne-video { - position: absolute; - inset: 0; - z-index: 0; - width: 100%; - height: 100%; - object-fit: cover; - } .ne-overlay { position: absolute; inset: 0; @@ -91,15 +83,6 @@ data-width="1920" data-height="1080" > -
diff --git a/registry/components/caption-neon-glow/demo.html b/registry/components/caption-neon-glow/demo.html index 0416d344f9..c1adb3775b 100644 --- a/registry/components/caption-neon-glow/demo.html +++ b/registry/components/caption-neon-glow/demo.html @@ -32,14 +32,6 @@ overflow: hidden; background: #000; } - #ne-video { - position: absolute; - inset: 0; - z-index: 0; - width: 100%; - height: 100%; - object-fit: cover; - } .ne-overlay { position: absolute; inset: 0; @@ -89,15 +81,6 @@ data-width="1920" data-height="1080" > -
diff --git a/registry/components/caption-parallax-layers/caption-parallax-layers.html b/registry/components/caption-parallax-layers/caption-parallax-layers.html index 5eba818fa4..2d2f33afe0 100644 --- a/registry/components/caption-parallax-layers/caption-parallax-layers.html +++ b/registry/components/caption-parallax-layers/caption-parallax-layers.html @@ -37,15 +37,6 @@ background: transparent; } - #bg-video { - position: absolute; - inset: 0; - z-index: 1; - width: 100%; - height: 100%; - object-fit: cover; - } - /* Behind captions: big red 3D text */ .behind-caption-layer { position: absolute; @@ -156,17 +147,6 @@ data-width="1920" data-height="1080" > - - diff --git a/registry/components/caption-parallax-layers/demo.html b/registry/components/caption-parallax-layers/demo.html index 5a18a88223..a56e9e6aa1 100644 --- a/registry/components/caption-parallax-layers/demo.html +++ b/registry/components/caption-parallax-layers/demo.html @@ -36,15 +36,6 @@ background: #000; } - #bg-video { - position: absolute; - inset: 0; - z-index: 1; - width: 100%; - height: 100%; - object-fit: cover; - } - /* Behind captions: big red 3D text */ .behind-caption-layer { position: absolute; @@ -154,17 +145,6 @@ data-width="1920" data-height="1080" > - - diff --git a/registry/components/caption-particle-burst/caption-particle-burst.html b/registry/components/caption-particle-burst/caption-particle-burst.html index 8e0beab7ff..2df4604db8 100644 --- a/registry/components/caption-particle-burst/caption-particle-burst.html +++ b/registry/components/caption-particle-burst/caption-particle-burst.html @@ -33,14 +33,6 @@ overflow: hidden; background: transparent; } - #bs-video { - position: absolute; - inset: 0; - z-index: 0; - width: 100%; - height: 100%; - object-fit: cover; - } .bs-overlay { position: absolute; inset: 0; @@ -102,15 +94,6 @@ data-width="1920" data-height="1080" > -
diff --git a/registry/components/caption-particle-burst/demo.html b/registry/components/caption-particle-burst/demo.html index b0a4306e44..ea2bef5b5a 100644 --- a/registry/components/caption-particle-burst/demo.html +++ b/registry/components/caption-particle-burst/demo.html @@ -32,14 +32,6 @@ overflow: hidden; background: #0a0a0a; } - #bs-video { - position: absolute; - inset: 0; - z-index: 0; - width: 100%; - height: 100%; - object-fit: cover; - } .bs-overlay { position: absolute; inset: 0; @@ -100,15 +92,6 @@ data-width="1920" data-height="1080" > -
diff --git a/registry/components/caption-pill-karaoke/caption-pill-karaoke.html b/registry/components/caption-pill-karaoke/caption-pill-karaoke.html index ec6c274423..06b5a4ac2c 100644 --- a/registry/components/caption-pill-karaoke/caption-pill-karaoke.html +++ b/registry/components/caption-pill-karaoke/caption-pill-karaoke.html @@ -37,15 +37,6 @@ background: transparent; } - #avatar-video { - position: absolute; - inset: 0; - z-index: 1; - width: 100%; - height: 100%; - object-fit: cover; - } - .caption-layer { position: absolute; inset: 0; @@ -134,16 +125,6 @@ data-width="1920" data-height="1080" > - - diff --git a/registry/components/caption-pill-karaoke/demo.html b/registry/components/caption-pill-karaoke/demo.html index 6648259599..ac4d10ea18 100644 --- a/registry/components/caption-pill-karaoke/demo.html +++ b/registry/components/caption-pill-karaoke/demo.html @@ -36,15 +36,6 @@ background: #000000; } - #avatar-video { - position: absolute; - inset: 0; - z-index: 1; - width: 100%; - height: 100%; - object-fit: cover; - } - .caption-layer { position: absolute; inset: 0; @@ -132,16 +123,6 @@ data-width="1920" data-height="1080" > - - diff --git a/registry/components/caption-texture/caption-texture.html b/registry/components/caption-texture/caption-texture.html index ac50240a29..1647735f76 100644 --- a/registry/components/caption-texture/caption-texture.html +++ b/registry/components/caption-texture/caption-texture.html @@ -40,14 +40,6 @@ overflow: hidden; background: transparent; } - #lv-video { - position: absolute; - inset: 0; - z-index: 0; - width: 100%; - height: 100%; - object-fit: cover; - } .lv-overlay { position: absolute; inset: 0; @@ -105,15 +97,6 @@ data-height="1080" data-composition-variables='{"texture":{"type":"string","default":"lava"}}' > -
diff --git a/registry/components/caption-texture/demo.html b/registry/components/caption-texture/demo.html index 7c082dfcfa..60e73207ae 100644 --- a/registry/components/caption-texture/demo.html +++ b/registry/components/caption-texture/demo.html @@ -40,14 +40,6 @@ overflow: hidden; background: #0a0805; } - #lv-video { - position: absolute; - inset: 0; - z-index: 0; - width: 100%; - height: 100%; - object-fit: cover; - } .lv-overlay { position: absolute; inset: 0; @@ -104,15 +96,6 @@ data-height="1080" data-composition-variables='{"texture":{"type":"string","default":"lava"}}' > -
diff --git a/registry/components/caption-weight-shift/caption-weight-shift.html b/registry/components/caption-weight-shift/caption-weight-shift.html index 2b92c10415..a6f37fe0b7 100644 --- a/registry/components/caption-weight-shift/caption-weight-shift.html +++ b/registry/components/caption-weight-shift/caption-weight-shift.html @@ -37,15 +37,6 @@ background: transparent; } - #avatar-video { - position: absolute; - inset: 0; - z-index: 1; - width: 100%; - height: 100%; - object-fit: cover; - } - .caption-layer { position: absolute; inset: 0; @@ -120,16 +111,6 @@ data-width="1920" data-height="1080" > - - diff --git a/registry/components/caption-weight-shift/demo.html b/registry/components/caption-weight-shift/demo.html index 1eb03870d8..827bbd4d60 100644 --- a/registry/components/caption-weight-shift/demo.html +++ b/registry/components/caption-weight-shift/demo.html @@ -36,15 +36,6 @@ background: #000000; } - #avatar-video { - position: absolute; - inset: 0; - z-index: 1; - width: 100%; - height: 100%; - object-fit: cover; - } - .caption-layer { position: absolute; inset: 0; @@ -118,16 +109,6 @@ data-width="1920" data-height="1080" > - -