返回 html-video
validate.ts
根目录 / packages / adapter-remotion / src / validate.ts
1 import { existsSync } from 'node:fs';
2 import { createRequire } from 'node:module';
3 import type { TemplateRef, ValidationError, ValidationResult } from '@html-video/core';
4
5 /**
6 * Validate a template against the Remotion adapter. Cheap & read-only (RFC-01):
7 * no bundling, no model calls — just engine match + source existence + a soft
8 * note when the optional Remotion peer deps aren't installed yet.
9 *
10 * Phase 1: a template's `sourcePath` is an HTML frame the bridge renders. The
11 * `engine` field may be 'remotion' (native, Phase 2) or 'hyperframes' (an HTML
12 * frame the user explicitly chose to render through Remotion) — accept both so
13 * the bridge can take any HTML frame.
14 */
15 export function validate(template: TemplateRef): ValidationResult {
16 const errors: ValidationError[] = [];
17 const warnings: ValidationError[] = [];
18
19 if (template.engine !== 'remotion' && template.engine !== 'hyperframes') {
20 errors.push({
21 code: 'engine-mismatch',
22 message: `Template engine "${template.engine}" cannot be rendered by the Remotion adapter`,
23 fix: `Use the @html-video/adapter-${template.engine} adapter instead`,
24 });
25 return { ok: false, errors, warnings };
26 }
27
28 if (!template.sourcePath) {
29 errors.push({ code: 'missing-source', message: 'Template has no sourcePath' });
30 } else if (!existsSync(template.sourcePath)) {
31 errors.push({
32 code: 'source-not-found',
33 message: `Template source not found: ${template.sourcePath}`,
34 fix: 'Check that the template directory contains the file declared in source_entry',
35 });
36 }
37
38 // Soft signal — render() will fail clearly if the peer deps are truly missing,
39 // but flag it early so `doctor` / the agent can prompt an install.
40 if (!remotionInstalled()) {
41 warnings.push({
42 code: 'engine-not-installed',
43 message: 'Remotion peer dependencies are not installed',
44 fix: 'Run `pnpm add remotion @remotion/bundler @remotion/renderer react react-dom` in the workspace root',
45 });
46 }
47
48 return { ok: errors.length === 0, errors, warnings };
49 }
50
51 /** Best-effort, synchronous check that the Remotion renderer can be resolved. */
52 export function remotionInstalled(): boolean {
53 try {
54 const req = createRequire(import.meta.url);
55 req.resolve('@remotion/renderer');
56 req.resolve('@remotion/bundler');
57 return true;
58 } catch {
59 return false;
60 }
61 }
62
62 lines TYPESCRIPT