Download scripts/sync-generated-kit.mjs from firebird-technologies/blog2video-cpu: direct link, hf CLI and curl.
- Browser
- Download file 7.75 kB
-
https://huggingface.co/spaces/firebird-technologies/blog2video-cpu/resolve/main/scripts/sync-generated-kit.mjs
- Command line
-
hf download hf://spaces/firebird-technologies/blog2video-cpu/scripts/sync-generated-kit.mjs
-
curl -L -o sync-generated-kit.mjs https://huggingface.co/spaces/firebird-technologies/blog2video-cpu/resolve/main/scripts/sync-generated-kit.mjs
7.75 kB
| /** | |
| * Sync the generated-template craft kit from its canonical source to every | |
| * consuming tree, and emit the export manifest both injection points read. | |
| * | |
| * WHY THIS EXISTS | |
| * --------------- | |
| * The kit source used to live in three byte-identical trees kept in sync BY | |
| * HAND, and the list of kit exports was maintained by hand in TWO more places | |
| * (the backend's _wrap_generated_code import block and the frontend's | |
| * KIT_EXPORTS array). That seam had already drifted in production: | |
| * | |
| * - `CustomTable` was exported from the kit and present in the frontend's | |
| * KIT_EXPORTS, but MISSING from the backend wrapper's import list β so a | |
| * scene using it compiled in the browser preview and failed at render. | |
| * - `types.ts` had diverged: the remotion-video copy carried hasVideo, | |
| * caption fields and stock-footage video fields that neither frontend had. | |
| * | |
| * Adding kit primitives makes that worse, so the export list is now GENERATED | |
| * from the canonical `kit/index.ts` and the trees are copied mechanically. | |
| * | |
| * USAGE | |
| * node scripts/sync-generated-kit.mjs # write | |
| * node scripts/sync-generated-kit.mjs --check # verify only (CI); non-zero on drift | |
| */ | |
| import { readdirSync, readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs"; | |
| import { join, dirname, relative } from "node:path"; | |
| import { fileURLToPath } from "node:url"; | |
| const ROOT = join(dirname(fileURLToPath(import.meta.url)), ".."); | |
| /** Canonical source: the render-truth tree. Its types.ts is the superset. */ | |
| const SOURCE_KIT = join(ROOT, "remotion-video/src/templates/generated/kit"); | |
| const SOURCE_TYPES = join(ROOT, "remotion-video/src/templates/generated/types.ts"); | |
| /** Every tree that consumes a copy of the kit. */ | |
| const DEST_ROOTS = [ | |
| join(ROOT, "frontend/src/components/remotion/generated"), | |
| join(ROOT, "frontend-pdf2video/src/components/remotion/generated"), | |
| ]; | |
| const MANIFEST_BASENAME = "exportManifest.generated.ts"; | |
| /** | |
| * Kit exports that are deliberately NOT injected into generated scene code. | |
| * | |
| * The manifest is the *injection* set, not simply "everything the kit exports". | |
| * Two categories are excluded: | |
| * | |
| * - Composition-level scenes (DataChartScene / DataTableScene) β GeneratedVideo | |
| * renders these itself for dedicated data-viz scenes. A scene component must | |
| * never nest one inside itself. | |
| * - Provider/plumbing (KitProvider) β SceneFrame wires the context; a scene | |
| * mounting its own provider would detach the brand palette. | |
| * | |
| * Low-level colour/easing helpers (hexToRgb, easeOutQuint, typeScale, ...) are | |
| * intentionally left OUT of this list: they are pure functions, harmless to | |
| * expose, and previously unavailable only because two hand-maintained lists had | |
| * drifted. Injecting them is a deliberate widening, not an accident. | |
| */ | |
| const NOT_INJECTED = new Set(["DataChartScene", "DataTableScene", "KitProvider"]); | |
| const checkOnly = process.argv.includes("--check"); | |
| const problems = []; | |
| let wrote = 0; | |
| /** | |
| * Parse the runtime (value) export names out of the canonical kit/index.ts. | |
| * | |
| * Handles the two forms the file uses: | |
| * export { a, b as c, type D } from "./x"; | |
| * export * from "./y"; <- reported, cannot be resolved statically | |
| * | |
| * `type X` entries are dropped: they exist only at compile time and cannot be | |
| * injected as runtime globals into generated scene code. | |
| */ | |
| function parseKitExports(indexSource) { | |
| const names = new Set(); | |
| for (const m of indexSource.matchAll(/export\s*\{([^}]*)\}\s*from\s*['"][^'"]+['"]/g)) { | |
| for (const raw of m[1].split(",")) { | |
| let name = raw.trim(); | |
| if (!name) continue; | |
| // `export { type Foo }` and `export { Foo as Bar }` | |
| if (/^type\s/.test(name)) continue; | |
| if (/\sas\s/.test(name)) name = name.split(/\sas\s/)[1].trim(); | |
| if (/^[A-Za-z_$][\w$]*$/.test(name) && !NOT_INJECTED.has(name)) names.add(name); | |
| } | |
| } | |
| // A star re-export would silently hide exports from the manifest. | |
| if (/export\s+\*\s+from/.test(indexSource)) { | |
| problems.push( | |
| "kit/index.ts uses `export * from ...`, which this script cannot resolve statically. " + | |
| "Replace it with an explicit `export { ... } from ...` so the manifest stays complete.", | |
| ); | |
| } | |
| return [...names].sort(); | |
| } | |
| /** Write a file, or in --check mode record a mismatch instead. */ | |
| function emit(destPath, content, label) { | |
| const exists = existsSync(destPath); | |
| const current = exists ? readFileSync(destPath, "utf8") : null; | |
| if (current === content) return; | |
| if (checkOnly) { | |
| problems.push(exists ? `out of date: ${label}` : `missing: ${label}`); | |
| return; | |
| } | |
| mkdirSync(dirname(destPath), { recursive: true }); | |
| writeFileSync(destPath, content, "utf8"); | |
| wrote += 1; | |
| console.log(` wrote ${label}`); | |
| } | |
| // βββ 1. Build the export manifest ββββββββββββββββββββββββββββββββββββββββββββ | |
| const indexPath = join(SOURCE_KIT, "index.ts"); | |
| if (!existsSync(indexPath)) { | |
| console.error(`FATAL: canonical kit index not found at ${indexPath}`); | |
| process.exit(2); | |
| } | |
| const exportNames = parseKitExports(readFileSync(indexPath, "utf8")); | |
| if (exportNames.length === 0) { | |
| console.error("FATAL: parsed zero kit exports β refusing to emit an empty manifest."); | |
| process.exit(2); | |
| } | |
| const manifest = | |
| `// @generated by scripts/sync-generated-kit.mjs β DO NOT EDIT BY HAND.\n` + | |
| `// Run \`node scripts/sync-generated-kit.mjs\` after changing kit/index.ts.\n` + | |
| `//\n` + | |
| `// The single source of truth for which kit values are injected into\n` + | |
| `// AI-generated scene code. Read by the frontend preview compiler and by the\n` + | |
| `// backend render wrapper, so preview and render can never disagree.\n` + | |
| `export const KIT_EXPORT_NAMES = [\n` + | |
| exportNames.map((n) => ` "${n}",`).join("\n") + | |
| `\n] as const;\n`; | |
| // βββ 2. Fan out ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| console.log(checkOnly ? "Checking generated-kit sync..." : "Syncing generated kit..."); | |
| emit(join(SOURCE_KIT, MANIFEST_BASENAME), manifest, `kit/${MANIFEST_BASENAME} (canonical)`); | |
| const kitFiles = readdirSync(SOURCE_KIT).filter((f) => /\.(ts|tsx)$/.test(f)); | |
| for (const destRoot of DEST_ROOTS) { | |
| if (!existsSync(destRoot)) { | |
| console.log(` skip (tree absent): ${relative(ROOT, destRoot)}`); | |
| continue; | |
| } | |
| for (const file of kitFiles) { | |
| emit( | |
| join(destRoot, "kit", file), | |
| readFileSync(join(SOURCE_KIT, file), "utf8"), | |
| `${relative(ROOT, join(destRoot, "kit", file))}`, | |
| ); | |
| } | |
| // types.ts lives beside kit/, not inside it. Copying the canonical superset | |
| // closes the existing divergence; every extra field is optional, so the | |
| // frontends keep compiling. | |
| if (existsSync(SOURCE_TYPES)) { | |
| emit( | |
| join(destRoot, "types.ts"), | |
| readFileSync(SOURCE_TYPES, "utf8"), | |
| `${relative(ROOT, join(destRoot, "types.ts"))}`, | |
| ); | |
| } | |
| } | |
| // βββ 3. Report βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if (problems.length > 0) { | |
| console.error(`\n${problems.length} problem(s):`); | |
| for (const p of problems) console.error(` - ${p}`); | |
| if (checkOnly) console.error("\nRun: node scripts/sync-generated-kit.mjs"); | |
| process.exit(1); | |
| } | |
| console.log( | |
| checkOnly | |
| ? `OK β all trees in sync (${exportNames.length} kit exports).` | |
| : `Done β ${wrote} file(s) written, ${exportNames.length} kit exports.`, | |
| ); | |