UNPKG

meld

Version:

Meld: A template language for LLM prompts

108 lines (94 loc) 2.88 kB
import { MemfsTestFileSystem } from './MemfsTestFileSystem.js'; import * as path from 'path'; /** * Structure representing a test project */ export interface ProjectStructure { /** * Map of file paths to their contents */ files: Record<string, string>; /** * Optional list of directories to create */ dirs?: string[]; /** * Optional project root path. Defaults to 'project' */ projectRoot?: string; /** * Optional home path. Defaults to 'home' */ homePath?: string; } /** * Builds test project structures in the in-memory filesystem */ export class ProjectBuilder { constructor(private fs: MemfsTestFileSystem) {} /** * Create a project structure in the filesystem */ async create(struct: ProjectStructure): Promise<void> { const projectRoot = struct.projectRoot || 'project'; const homePath = struct.homePath || 'home'; // Create standard directories await this.fs.mkdir('/' + projectRoot); await this.fs.mkdir('/' + homePath); // Create any additional directories for (const dir of struct.dirs || []) { const fullPath = this.resolvePath(dir, projectRoot); if (!(await this.fs.exists(fullPath))) { await this.fs.mkdir(fullPath); } } // Create all files for (const [filePath, content] of Object.entries(struct.files)) { const fullPath = this.resolvePath(filePath, projectRoot); const resolvedContent = this.resolveContent(content, { $PROJECTPATH: projectRoot, $HOMEPATH: homePath }); // Ensure parent directory exists const dirPath = path.dirname(fullPath); if (!(await this.fs.exists(dirPath))) { await this.fs.mkdir(dirPath); } await this.fs.writeFile(fullPath, resolvedContent); } } /** * Create a basic project with common defaults */ async createBasicProject(): Promise<void> { await this.create({ dirs: ['src', 'tests', '../home/.config'], files: { 'README.md': '# Test Project', 'src/main.meld': '@text greeting = "Hello World"', '../home/.config/settings.json': '{}' } }); } /** * Resolve a path relative to the project root */ private resolvePath(filePath: string, projectRoot: string): string { // If path starts with $PROJECTPATH or $HOMEPATH, leave it as is if (filePath.startsWith('$')) { return filePath; } // Otherwise resolve relative to project root return path.isAbsolute(filePath) ? filePath : '/' + path.join(projectRoot, filePath); } /** * Replace variables in content */ private resolveContent(content: string, vars: Record<string, string>): string { let result = content; for (const [key, value] of Object.entries(vars)) { result = result.replace(new RegExp(key, 'g'), value); } return result; } }