UNPKG

meld

Version:

Meld: A template language for LLM prompts

346 lines (299 loc) 10.3 kB
import { vi, beforeEach } from 'vitest'; import type { DirectiveNode, MeldNode } from 'meld-spec'; import { InterpreterState } from '../../_old/src/interpreter/state/state.js'; import { ErrorFactory } from '../../_old/src/interpreter/errors/factory.js'; import { HandlerContext } from '../../_old/src/interpreter/directives/types.js'; import { embedDirectiveHandler, importDirectiveHandler } from './directive-handlers.js'; // Mock file system state const mockFiles: Record<string, string> = {}; // Mock file management functions function getMockFiles(): Record<string, string> { return mockFiles; } export function addMockFile(path: string, content: string) { mockFiles[path] = content; } export function clearMockFiles() { Object.keys(mockFiles).forEach(key => delete mockFiles[key]); } // Mock fs module vi.mock('fs', () => ({ default: { readFileSync: vi.fn((path: string) => { if (mockFiles[path]) { return mockFiles[path]; } throw new Error(`ENOENT: no such file or directory, open '${path}'`); }), writeFileSync: vi.fn((path: string, content: string) => { mockFiles[path] = content; }), existsSync: vi.fn((path: string) => !!mockFiles[path]), mkdirSync: vi.fn(), promises: { readFile: vi.fn(async (path: string) => { if (mockFiles[path]) { return mockFiles[path]; } throw new Error(`ENOENT: no such file or directory, open '${path}'`); }), writeFile: vi.fn(async (path: string, content: string) => { mockFiles[path] = content; }) } }, readFileSync: vi.fn((path: string) => { if (mockFiles[path]) { return mockFiles[path]; } throw new Error(`ENOENT: no such file or directory, open '${path}'`); }), writeFileSync: vi.fn((path: string, content: string) => { mockFiles[path] = content; }), existsSync: vi.fn((path: string) => !!mockFiles[path]), mkdirSync: vi.fn(), promises: { readFile: vi.fn(async (path: string) => { if (mockFiles[path]) { return mockFiles[path]; } throw new Error(`ENOENT: no such file or directory, open '${path}'`); }), writeFile: vi.fn(async (path: string, content: string) => { mockFiles[path] = content; }) } })); // Mock path module vi.mock('path', async () => { // Import the path mock factory const { createPathMock } = await import('./path'); // Create a single mock instance that will be used for both named and default exports const mockExports = await createPathMock(); // Return the mock exports directly - they already include __esModule and default return mockExports; }); // Mock InterpreterState with enhanced tracking export class MockInterpreterState extends InterpreterState { private nodeHistory: MeldNode[] = []; private textVarHistory: Map<string, string[]> = new Map(); private dataVarHistory: Map<string, any[]> = new Map(); constructor() { super(); } // Track node history override addNode(node: MeldNode): void { this.nodeHistory.push({ ...node }); super.addNode(node); } getNodeHistory(): MeldNode[] { return [...this.nodeHistory]; } // Track text var history override setTextVar(name: string, value: string): void { const history = this.textVarHistory.get(name) || []; history.push(value); this.textVarHistory.set(name, history); super.setTextVar(name, value); } getTextVarHistory(name: string): string[] { return this.textVarHistory.get(name) || []; } // Track data var history override setDataVar(name: string, value: any): void { const history = this.dataVarHistory.get(name) || []; history.push(value); this.dataVarHistory.set(name, history); super.setDataVar(name, value); } getDataVarHistory(name: string): any[] { return this.dataVarHistory.get(name) || []; } // Clear history clearHistory(): void { this.nodeHistory = []; this.textVarHistory.clear(); this.dataVarHistory.clear(); } // Existing methods with proper error handling override mergeChildState(childState: InterpreterState): void { try { super.mergeChildState(childState); } catch (error) { throw ErrorFactory.createInterpretError( `Failed to merge child state: ${error instanceof Error ? error.message : String(error)}`, 'State' ); } } } // Mock handler factory with enhanced error handling function createMockHandler(kind: string) { const handlers: Record<string, any> = { data: { canHandle: (k: string) => k === 'data', handle: async (node: DirectiveNode, state: InterpreterState, context: HandlerContext) => { const data = node.directive; if (!data.name) { throw ErrorFactory.createDirectiveError( 'Data directive requires a name', 'data', node.location?.start ); } state.setDataVar(data.name, data.value); } }, text: { canHandle: (k: string) => k === 'text', handle: async (node: DirectiveNode, state: InterpreterState, context: HandlerContext) => { const data = node.directive; if (!data.name) { throw ErrorFactory.createDirectiveError( 'Text directive requires a name', 'text', node.location?.start ); } state.setTextVar(data.name, data.value); } }, run: { canHandle: (k: string) => k === 'run', handle: async (node: DirectiveNode, state: InterpreterState, context: HandlerContext) => { const data = node.directive; if (!data.command) { throw ErrorFactory.createDirectiveError( 'Run directive requires a command', 'run', node.location?.start ); } const commandData = state.getCommand(data.command); if (commandData && typeof commandData === 'object' && 'command' in commandData) { const { command } = commandData as { command: string }; console.log(`[MOCK] Executing command: ${command}`); } } }, define: { canHandle: (k: string) => k === 'define', handle: async (node: DirectiveNode, state: InterpreterState, context: HandlerContext) => { const data = node.directive; if (!data.name) { throw ErrorFactory.createDirectiveError( 'Define directive requires a name', 'define', node.location?.start ); } state.setCommand(data.name, data.command || ''); } } }; return handlers[kind]; } // File system utilities export function mockFile(path: string, content: string): void { mockFiles[path] = content; } // Add test fixtures beforeEach(() => { clearMockFiles(); // Basic fixtures addMockFile('/Users/adam/dev/meld/src/__fixtures__/markdown/basic.md', ` # Basic Document ## Section One Some content in section one ## Section Two Some content in section two ### Nested Section This is a nested section \`\`\`typescript function test() { console.log('Hello'); } \`\`\` `); addMockFile('/Users/adam/dev/meld/src/__fixtures__/xml/expected/basic.xml', `<BasicDocument title="Basic Document"><Section title="Section One" hlevel="2">Some content in section one</Section><Section title="Section Two" hlevel="2">Some content in section two<Section title="Nested Section" hlevel="3">This is a nested section\`\`\`typescript function test() { console.log('Hello'); } \`\`\`</Section></Section></BasicDocument>`); // Complex fixtures addMockFile('/Users/adam/dev/meld/src/__fixtures__/markdown/complex.md', ` # Complex Document ## 你好,世界 Some unicode content ## 🎉 Emoji Title 🚀 こんにちは and Café ## Code Blocks \`\`\`typescript interface Test { name: string; } \`\`\` \`\`\`python def hello(): print("Hello") \`\`\` ## About the Project Project info ### About Development Dev info ## Getting Started (Quick Guide) This section has a title with parentheses `); addMockFile('/Users/adam/dev/meld/src/__fixtures__/xml/expected/complex.xml', `<ComplexDocument title="Complex Document"><Section title="你好,世界" hlevel="2">Some unicode content</Section><Section title="🎉 Emoji Title 🚀" hlevel="2">こんにちは and Café</Section><Section title="Code Blocks" hlevel="2">\`\`\`typescript interface Test { name: string; } \`\`\` \`\`\`python def hello(): print("Hello") \`\`\`</Section><Section title="About the Project" hlevel="2">Project info<Section title="About Development" hlevel="3">Dev info</Section></Section><Section title="Getting Started (Quick Guide)" hlevel="2">This section has a title with parentheses</Section></ComplexDocument>`); // Edge cases addMockFile('/Users/adam/dev/meld/src/__fixtures__/markdown/edge-cases.md', ` # Edge Cases ## Malformed Code Block \`\`\`typescript const x = { // Missing closing brace ## Incomplete Code Fence \`\`\`python def test(): print("No closing fence") ## Empty Section ## HTML in Markdown <h1>Raw HTML header</h1> <div class="test"> Some content </div> `); // Real-world examples addMockFile('/Users/adam/dev/meld/src/__fixtures__/real-world/architecture.md', ` # Architecture Documentation ## System Overview The system consists of multiple components: - Frontend - Backend - Database ## Component Details ### Frontend Built with React & TypeScript ### Backend Node.js with Express ### Database PostgreSQL for persistence ## Deployment Using Docker & Kubernetes `); addMockFile('/Users/adam/dev/meld/src/__fixtures__/xml/expected/real-world/architecture.xml', `<ArchitectureDocumentation title="Architecture Documentation"><Section title="System Overview" hlevel="2">The system consists of multiple components: - Frontend - Backend - Database</Section><Section title="Component Details" hlevel="2"><Section title="Frontend" hlevel="3">Built with React &amp; TypeScript</Section><Section title="Backend" hlevel="3">Node.js with Express</Section><Section title="Database" hlevel="3">PostgreSQL for persistence</Section></Section><Section title="Deployment" hlevel="2">Using Docker &amp; Kubernetes</Section></ArchitectureDocumentation>`); }); // Export utilities export { createMockHandler, mockFiles };