UNPKG

@meldscience/meld

Version:

pipeable one-shot prompt scripting toolkit

168 lines (152 loc) 5.89 kB
import { Plugin } from 'unified'; import { Node, Parent } from 'unist'; import { Text } from 'mdast'; import { visit } from 'unist-util-visit'; import { execSync } from 'child_process'; import { existsSync } from 'fs'; import { join } from 'path'; import { importMarkdownSection } from '../markdownImporter'; import { importCodeSymbols } from '../codeImporter'; interface MeldDirectiveNode extends Node { type: 'meldDirective'; meldType: 'cmd' | 'import'; data?: { command?: string; importPath?: string; heading?: string; headingLevelOverride?: string; headingTextOverride?: string; importSymbolList?: string[]; importWholeFile?: boolean; }; } interface ProcessMeldNodesOptions { currentFileDir: string; debug?: boolean; detailedErrors?: boolean; } export const remarkProcessMeldNodes: Plugin<[ProcessMeldNodesOptions]> = function (options: ProcessMeldNodesOptions) { return async function transformer(tree: Node, file: any) { const errors: string[] = []; // Initialize meldErrors in file.data file.data = file.data || {}; file.data.meldErrors = []; visit(tree, 'meldDirective', (node: MeldDirectiveNode, index, parent: Parent) => { if (options.debug) { console.log('Visiting node:', { type: node.type, meldType: node.meldType, index, parentType: parent?.type, parentChildren: parent?.children?.length }); } if (index === null || index === undefined) { if (options.debug) console.log('Skipping node due to null/undefined index'); return; } try { if (node.meldType === 'cmd') { if (options.debug) console.log('Processing command:', node.data?.command); try { const output = execSync(node.data?.command || '', { encoding: 'utf-8', cwd: options.currentFileDir, stdio: ['pipe', 'pipe', 'pipe'] }).trim(); if (options.debug) { console.log('Command output:', output); console.log('Replacing node at index', index); } // Replace with a text node containing the output parent.children[index] = { type: 'text', value: output } as Text; if (options.debug) console.log('Node replaced, new node:', parent.children[index]); } catch (cmdError: any) { const errorMessage = options.detailedErrors ? 'Command produced error output (included below)\n' + (cmdError.stderr || cmdError.message || 'Unknown error') : 'Error: Error processing cmd directive'; parent.children[index] = { type: 'text', value: errorMessage } as Text; file.data.meldErrors.push(errorMessage); } } else { // It's an import directive const importPath = node.data?.importPath; if (options.debug) console.log('Processing import:', importPath, 'heading:', node.data?.heading); if (!importPath) { throw new Error('Invalid import directive: missing import path'); } // Logic to figure out if it's a .md or .ts if (importPath.endsWith('.md')) { const heading = node.data?.heading; // Reuse existing markdownImporter try { const resolvedPath = join(options.currentFileDir, importPath); if (!existsSync(resolvedPath)) { throw new Error('File not found'); } const result = importMarkdownSection({ filePath: resolvedPath, heading, headingLevelOverride: node.data?.headingLevelOverride, headingTextOverride: node.data?.headingTextOverride }); if (!result.headingFound && heading) { throw new Error(`Heading "${heading}" not found in ${importPath}`); } parent.children[index] = { type: 'text', value: result.content } as Text; } catch (error) { throw new Error(`Error processing import directive: ${(error as Error).message}`); } } else { // Assume it's a code file try { const resolvedPath = join(options.currentFileDir, importPath); if (!existsSync(resolvedPath)) { throw new Error('File not found'); } const result = importCodeSymbols({ filePath: resolvedPath, symbolList: node.data?.importSymbolList || [], headingLevelOverride: node.data?.headingLevelOverride, headingTextOverride: node.data?.headingTextOverride }); if (result.content.trim() === '') { const errorMessage = 'Error: Error processing import directive'; parent.children[index] = { type: 'text', value: errorMessage } as Text; file.data.meldErrors.push(errorMessage); } else { parent.children[index] = { type: 'text', value: result.content } as Text; } } catch (error) { throw new Error(`Error processing import directive: ${(error as Error).message}`); } } } } catch (error) { const errorMessage = `Error: ${(error as Error).message}\n\n`; parent.children[index] = { type: 'text', value: errorMessage } as Text; file.data.meldErrors.push(errorMessage); } }); return tree; }; };