website-builde / lib /project-writer.ts
Daviddolor's picture
Single-process Chromium, fixed visual-polish prompt, Docker config for HF Space
ec675f2
Raw
History Blame Contribute Delete
1.77 kB
import * as fs from "fs";
import * as path from "path";
import { GeneratedFile } from "./file-parser";
const TEMPLATE_DIR = path.join(process.cwd(), "templates", "nextjs-base");
function copyTemplate(destDir: string) {
fs.mkdirSync(destDir, { recursive: true });
fs.cpSync(TEMPLATE_DIR, destDir, { recursive: true });
}
// Framer Motion (and React hooks) only work in Client Components.
// If the model forgot the "use client" directive, add it automatically
// rather than relying on prompt compliance alone.
function needsClientDirective(content: string): boolean {
return (
/from\s+["']framer-motion["']/.test(content) ||
/\buse(State|Effect|Ref|Context|Reducer|Callback|Memo)\b/.test(content)
);
}
function ensureClientDirective(content: string): string {
const trimmed = content.trimStart();
if (trimmed.startsWith('"use client"') || trimmed.startsWith("'use client'")) {
return content;
}
return needsClientDirective(content) ? '"use client";\n\n' + content : content;
}
export function writeProjectFiles(
files: GeneratedFile[],
projectSlug: string
): { projectDir: string; written: string[] } {
const safeSlug = projectSlug.replace(/[^a-z0-9-]/gi, "-").toLowerCase().slice(0, 40);
const projectDir = path.join(
process.cwd(),
"generated_projects",
`${safeSlug}-${Date.now()}`
);
copyTemplate(projectDir);
const written: string[] = [];
for (const file of files) {
const fullPath = path.join(projectDir, file.path);
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
const content = file.path.endsWith(".tsx") ? ensureClientDirective(file.content) : file.content;
fs.writeFileSync(fullPath, content, "utf-8");
written.push(file.path);
}
return { projectDir, written };
}