mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-02 03:36:52 +00:00
* feat(cli): move media-use engine into the CLI * fix(cli): replace media-use compatibility asset * fix(cli): replace moved media-use tree * fix(cli): copy moved media-use files cleanly * fix(cli): copy media-use engine files individually * fix(cli): keep audio helper compatibility import * fix(cli): clear copied skill destinations * test(cli): preserve moved media-use test paths * fix(cli): preserve media-use script compatibility imports * fix(cli): preserve media-use telemetry shim * fix(cli): resolve packaged media-use LUT assets * fix(cli): resolve packaged bundled SFX assets * chore(skills): regenerate media-use manifest * fix(cli): preserve media-use parity coverage * fix(cli): cover relocated media-use tests * chore(skills): regenerate media-use manifest * fix(cli): format bundled media provider * fix(ci): run relocated media-use tests with node * fix(ci): preserve fallow script exemptions * fix(ci): scope media-use fallow exemption * refactor(cli): trim media-use essay comments * feat(cli): index media-use assets for catalog search * feat(catalog): commit media-use search vectors * chore(catalog): track media vector metadata * fix(catalog): validate media vector row order * fix(catalog): validate media artifact freshness * fix(catalog): cover media metadata and credits * fix(cli): reject malformed media rows * fix(catalog): compare complete media rows * feat(cli): route media search through media-use * refactor(cli): trim relocated media-use comments * fix(cli): make media search runtime-safe * fix(cli): preserve sfx fallback diagnostics * fix(cli): probe sfx fallback provider explicitly * chore(skills): regenerate manifest * refactor(cli): keep typed media ranker source * fix(cli): align media search checks with local-first flow * fix(cli): scope vector audit exceptions * fix(cli): ship resolver registry beside skill * fix(cli): keep media-use skill scripts standalone * test(cli): cover local vector validation * test(cli): correct media vector fixtures * fix(cli): prefer exact media keys * fix(cli): stem exact media keys * test(cli): skip media lint for snippets without media * fix(cli): align skills test discovery Keep the CI skills job's bash discovery as the single source of truth so its empty-list guard covers the exact files Node executes. Make hasMediaVectors private because it has no external callers and needs no Fallow suppression. * fix(cli): close media-use review gaps Prioritize literal ids before stemmed ids and cover the collision alongside the plural regression. Add command seam tests for engine discovery, actionable failure, and argv passthrough. Add a deterministic parity check for standalone media-use copies, with documented allowlists for the standalone media-fetch shim and self-contained npx resolver. Keep the bash-discovered skills test list as the CI source of truth. * fix(cli): lower parity check complexity Split missing-copy and unallowlisted-drift checks so the parity guard keeps its behavior without a threshold CRAP score. * fix(cli): close media-use review seams Run the parity test in the skills job so skills-only edits exercise the same check. Export the verb mapping and inject command dispatch for direct coverage. Keep missing-copy diagnostics separate from drift reads.
157 lines
6.1 KiB
JavaScript
157 lines
6.1 KiB
JavaScript
// Cross-platform replacement for the previous `mkdir -p … && cp -r …` shell
|
|
// chain, which failed on Windows because `cp` doesn't accept `-r` there.
|
|
|
|
import { cpSync, existsSync, mkdirSync, readdirSync, rmSync } from "node:fs";
|
|
import { dirname, join, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
|
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
const CLI_ROOT = resolve(HERE, "..");
|
|
const REPO_ROOT = resolve(CLI_ROOT, "..", "..");
|
|
const DIST = join(CLI_ROOT, "dist");
|
|
|
|
// Studio's vite build clears its dist before rewriting it; don't start the
|
|
// copy until both sentinels are present so we never observe a partial tree.
|
|
const STUDIO_WAIT_TIMEOUT_MS = 30_000;
|
|
const STUDIO_POLL_INTERVAL_MS = 250;
|
|
|
|
// fallow-ignore-next-line complexity
|
|
async function waitForStudioDist(dir) {
|
|
const deadline = Date.now() + STUDIO_WAIT_TIMEOUT_MS;
|
|
while (Date.now() < deadline) {
|
|
try {
|
|
const entries = new Set(readdirSync(dir));
|
|
// vite emits `assets/` before rewriting `index.html` at the end of the
|
|
// build — so once both are present, the tree is complete.
|
|
if (entries.has("index.html") && entries.has("assets")) return;
|
|
} catch {
|
|
// dir doesn't exist yet — vite will create it
|
|
}
|
|
await sleep(STUDIO_POLL_INTERVAL_MS);
|
|
}
|
|
throw new Error(`[build-copy] timed out waiting for studio dist at ${dir}`);
|
|
}
|
|
|
|
function copyDir(src, dest) {
|
|
cpSync(src, dest, { recursive: true, force: true });
|
|
}
|
|
|
|
function copyDirContents(src, dest) {
|
|
for (const entry of readdirSync(src)) {
|
|
cpSync(join(src, entry), join(dest, entry), {
|
|
recursive: true,
|
|
force: true,
|
|
});
|
|
}
|
|
}
|
|
|
|
function copyMdFiles(srcDir, destDir) {
|
|
if (!existsSync(srcDir)) return;
|
|
for (const name of readdirSync(srcDir)) {
|
|
if (name.endsWith(".md")) {
|
|
cpSync(join(srcDir, name), join(destDir, name));
|
|
}
|
|
}
|
|
}
|
|
|
|
// fallow-ignore-next-line complexity
|
|
async function main() {
|
|
for (const sub of ["studio", "docs", "templates", "skills", "docker"]) {
|
|
mkdirSync(join(DIST, sub), { recursive: true });
|
|
}
|
|
mkdirSync(join(DIST, "commands"), { recursive: true });
|
|
|
|
const studioDist = resolve(CLI_ROOT, "..", "studio", "dist");
|
|
await waitForStudioDist(studioDist);
|
|
copyDirContents(studioDist, join(DIST, "studio"));
|
|
|
|
for (const tmpl of ["blank", "from-file", "_shared"]) {
|
|
copyDir(join(CLI_ROOT, "src", "templates", tmpl), join(DIST, "templates", tmpl));
|
|
}
|
|
|
|
// Bundle warm-grain from the repo registry so the built CLI can scaffold it
|
|
// offline and CI smoke tests pick up PR-branch changes before merge to main.
|
|
const warmGrainSrc = join(REPO_ROOT, "registry", "examples", "warm-grain");
|
|
if (existsSync(warmGrainSrc)) {
|
|
copyDir(warmGrainSrc, join(DIST, "templates", "warm-grain"));
|
|
}
|
|
|
|
// Skills bundled into the published CLI. Branches don't all carry the same
|
|
// skills/ tree (it gets restructured), so each entry is existsSync-guarded:
|
|
// a missing skill dir warns + skips instead of crashing the build.
|
|
for (const skill of ["hyperframes", "hyperframes-cli", "gsap", "media-use"]) {
|
|
const src = join(REPO_ROOT, "skills", skill);
|
|
if (!existsSync(src)) {
|
|
console.warn(`[build-copy] skill not found, skipping: skills/${skill}`);
|
|
continue;
|
|
}
|
|
const destination = join(DIST, "skills", skill);
|
|
rmSync(destination, { recursive: true, force: true });
|
|
copyDir(src, destination);
|
|
}
|
|
|
|
// The media-use engine lives with the CLI source, but keeps its published
|
|
// skill-relative layout so the moved .mjs tree can run without a rewrite.
|
|
const mediaEngine = join(CLI_ROOT, "src", "media-use");
|
|
const publishedMediaLib = join(DIST, "skills", "media-use", "scripts", "lib");
|
|
rmSync(publishedMediaLib, { recursive: true, force: true });
|
|
mkdirSync(publishedMediaLib, { recursive: true });
|
|
copyDirContents(join(mediaEngine, "lib"), publishedMediaLib);
|
|
cpSync(
|
|
join(mediaEngine, "resolve.mjs"),
|
|
join(DIST, "skills", "media-use", "scripts", "resolve.mjs"),
|
|
);
|
|
mkdirSync(join(DIST, "skills", "registry"), { recursive: true });
|
|
copyDirContents(join(DIST, "registry"), join(DIST, "skills", "registry"));
|
|
mkdirSync(join(DIST, "skills", "media-use", "registry"), { recursive: true });
|
|
copyDirContents(join(DIST, "registry"), join(DIST, "skills", "media-use", "registry"));
|
|
|
|
const dockerfile = join(CLI_ROOT, "src", "docker", "Dockerfile.render");
|
|
if (existsSync(dockerfile)) {
|
|
cpSync(dockerfile, join(DIST, "docker", "Dockerfile.render"));
|
|
}
|
|
|
|
const layoutAuditScript = join(CLI_ROOT, "src", "commands", "layout-audit.browser.js");
|
|
if (existsSync(layoutAuditScript)) {
|
|
cpSync(layoutAuditScript, join(DIST, "commands", "layout-audit.browser.js"));
|
|
}
|
|
|
|
const contrastAuditScript = join(CLI_ROOT, "src", "commands", "contrast-audit.browser.js");
|
|
if (existsSync(contrastAuditScript)) {
|
|
cpSync(contrastAuditScript, join(DIST, "commands", "contrast-audit.browser.js"));
|
|
}
|
|
|
|
const motionSampleScript = join(CLI_ROOT, "src", "commands", "motion-sample.browser.js");
|
|
if (existsSync(motionSampleScript)) {
|
|
cpSync(motionSampleScript, join(DIST, "commands", "motion-sample.browser.js"));
|
|
}
|
|
|
|
// Player bundles for the standalone browser player used by `present` and
|
|
// `play`. resolvePlayerPath/resolveSlideshowPath look for these alongside the
|
|
// built CLI (dist/<name>.global.js), so they must ship in the package — the
|
|
// monorepo-dev fallback paths don't exist once installed from npm. Without
|
|
// this, `npx hyperframes present` fails with "@hyperframes/player not found".
|
|
const playerDist = join(REPO_ROOT, "packages", "player", "dist");
|
|
const playerGlobals = [
|
|
[join(playerDist, "hyperframes-player.global.js"), join(DIST, "hyperframes-player.global.js")],
|
|
[
|
|
join(playerDist, "slideshow", "hyperframes-slideshow.global.js"),
|
|
join(DIST, "hyperframes-slideshow.global.js"),
|
|
],
|
|
];
|
|
for (const [src, dest] of playerGlobals) {
|
|
if (existsSync(src)) {
|
|
cpSync(src, dest);
|
|
} else {
|
|
console.warn(`[build-copy] player bundle not found, skipping: ${src}`);
|
|
}
|
|
}
|
|
|
|
copyMdFiles(join(CLI_ROOT, "src", "docs"), join(DIST, "docs"));
|
|
|
|
console.log("[build-copy] done");
|
|
}
|
|
|
|
await main();
|