UNPKG

meld

Version:

Meld: A template language for LLM prompts

146 lines (123 loc) 4.37 kB
/** * Simple API for processing meld content directly */ import { createDefaultServices } from './index.js'; import { NodeFileSystem } from '@services/fs/FileSystemService/NodeFileSystem.js'; import { ProcessOptions } from '@core/types/index.js'; import { validateServicePipeline } from '@core/utils/serviceValidation.js'; import { MemoryFileSystem } from '@tests/utils/MemoryFileSystem.js'; // Export the MemoryFileSystem for users who want to use it export { MemoryFileSystem }; /** * Process meld content directly from a string * * @param content - The meld content to process * @param options - Optional processing options * @returns Processed content as a string * * @example * ```typescript * import { runMeld } from 'meld'; * * const meldContent = ` * @text greeting = "Hello" * @text name = "World" * * ${greeting}, ${name}! * `; * * const result = await runMeld(meldContent); * console.log(result); // "Hello, World!" * ``` */ export async function runMeld( content: string, options: Partial<ProcessOptions> = {} ): Promise<string> { // Create a virtual file path const virtualFilePath = '/virtual-file.mld'; // Create an in-memory file system const memoryFS = new MemoryFileSystem(); // Store the content in the virtual file await memoryFS.writeFile(virtualFilePath, content); // Default options const defaultOptions: ProcessOptions = { format: 'markdown', transformation: true, fs: memoryFS }; // Merge options const mergedOptions: ProcessOptions = { ...defaultOptions, ...options }; // Always use the memory filesystem mergedOptions.fs = memoryFS; // Create services const services = createDefaultServices(mergedOptions); // Validate services validateServicePipeline(services); try { // Read the file (from memory) const content = await services.filesystem.readFile(virtualFilePath); // Parse the content const ast = await services.parser.parse(content, virtualFilePath); // Enable transformation if requested if (mergedOptions.transformation) { // If transformation is a boolean, use the legacy all-or-nothing approach // If it's an object with options, use selective transformation services.state.enableTransformation(mergedOptions.transformation); } // Interpret the AST const resultState = await services.interpreter.interpret(ast, { filePath: virtualFilePath, initialState: services.state, strict: true }); // Get nodes to process (transformed if transformation is enabled) const nodesToProcess = resultState.isTransformationEnabled() && resultState.getTransformedNodes() ? resultState.getTransformedNodes() : ast; // Make sure format is properly set (normalize 'md' to 'markdown', etc.) const outputFormat = normalizeFormat(mergedOptions.format || 'markdown'); // Convert to desired format let converted = await services.output.convert(nodesToProcess, resultState, outputFormat); // Post-process the output in transformation mode if (resultState.isTransformationEnabled()) { // Fix newlines in variable output converted = converted // Replace multiple newlines with a single newline .replace(/\n{2,}/g, '\n') // Common pattern fixes from the main function .replace(/(\w+):\n(\w+)/g, '$1: $2') .replace(/(\w+),\n(\w+)/g, '$1, $2') .replace(/(\w+):\n{/g, '$1: {') .replace(/},\n(\w+):/g, '}, $1:'); } return converted; } catch (error) { // Rethrow with a clearer message for API usage if (error instanceof Error) { throw new Error(`Error processing meld content: ${error.message}`); } // For non-Error objects, convert to string throw new Error(`Error processing meld content: ${String(error)}`); } } /** * Normalize format string to supported format */ function normalizeFormat(format: string): 'markdown' | 'xml' { // Normalize format aliases if (format === 'md') { return 'markdown'; } if (format === 'llmxml') { return 'xml'; } // Ensure 'xml' is properly handled if (format === 'xml') { return 'xml'; } // Default to markdown for unsupported formats return 'markdown'; } // Default export for ease of use export default runMeld;