UNPKG

@meldscience/meld

Version:

pipeable one-shot prompt scripting toolkit

166 lines (143 loc) 4.91 kB
/** * codeImporter.ts * * Extracts code symbols (functions, classes, interfaces, etc.) from a TypeScript/JavaScript file. * Supports extracting multiple symbols and adjusting the heading level and text. */ import { readFileSync } from 'fs'; export interface CodeImportOptions { filePath: string; symbolList: string[]; headingLevelOverride?: string; headingTextOverride?: string; skipHeading?: boolean; } export interface CodeImportResult { content: string; symbolsFound: boolean; duplicateSymbols: boolean; } export function importCodeSymbols(options: CodeImportOptions): CodeImportResult { const { filePath, symbolList, headingLevelOverride, headingTextOverride } = options; try { const content = readFileSync(filePath, 'utf-8'); const lines = content.split('\n'); const result: CodeImportResult = { content: '', symbolsFound: false, duplicateSymbols: false }; // Track found symbols and their locations const foundSymbols = new Map<string, number[]>(); let inComment = false; let inString = false; let stringChar = ''; // Find all symbol locations for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); // Skip empty lines if (!line) continue; // Handle multi-line comments if (line.includes('/*')) inComment = true; if (line.includes('*/')) inComment = false; // Handle strings if (!inComment) { for (let j = 0; j < line.length; j++) { const char = line[j]; if (inString) { if (char === stringChar && line[j - 1] !== '\\') { inString = false; } } else if (char === '"' || char === "'") { inString = true; stringChar = char; } } } // Skip comments and strings if (inComment || inString) continue; // Look for symbol declarations for (const symbol of symbolList) { if (line.includes(symbol)) { const locations = foundSymbols.get(symbol) || []; locations.push(i); foundSymbols.set(symbol, locations); } } } // Check for duplicates and missing symbols result.symbolsFound = symbolList.every(symbol => foundSymbols.has(symbol)); result.duplicateSymbols = Array.from(foundSymbols.values()).some(locations => locations.length > 1); if (!result.symbolsFound || result.duplicateSymbols) { return result; } // Extract symbols const extractedContent: string[] = []; // Add heading by default unless skipHeading is true if (!options.skipHeading) { const heading = headingLevelOverride ?? '#'; const text = headingTextOverride ?? 'Code Symbols'; extractedContent.push(`${heading} ${text}`); extractedContent.push(''); } // Add code fence extractedContent.push('```typescript'); // Add each symbol's code for (const symbol of symbolList) { const locations = foundSymbols.get(symbol)!; const location = locations[0]; let start = location; let end = location; // Find the start of the symbol (include JSDoc comments) while (start > 0 && (lines[start - 1].trim().startsWith('*') || lines[start - 1].trim().startsWith('/'))) { start--; } // Find the end of the symbol (include the entire block) let braceCount = 0; let foundFirstBrace = false; for (let i = location; i < lines.length; i++) { const line = lines[i]; for (const char of line) { if (char === '{') { foundFirstBrace = true; braceCount++; } else if (char === '}') { braceCount--; } } end = i; if (foundFirstBrace && braceCount === 0) break; if (!foundFirstBrace && (line.trim().endsWith(';') || line.trim().endsWith('}'))) { break; } } // Add the symbol's code for (let i = start; i <= end; i++) { const line = lines[i]; if (i === end && symbol === symbolList[symbolList.length - 1]) { const trimmedLine = line.trimEnd(); if (trimmedLine.endsWith(';') || trimmedLine.endsWith('}')) { extractedContent.push(trimmedLine); extractedContent.push('```'); } else { extractedContent.push(trimmedLine); extractedContent.push('```'); } } else { extractedContent.push(line); } } // Add newline between symbols if (symbol !== symbolList[symbolList.length - 1]) { extractedContent.push(''); } } result.content = extractedContent.join('\n'); return result; } catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') { throw new Error('File not found'); } throw error; } }