const fs = require('fs'); const path = require('path'); /** * Phase 9: Terraform Extraction * Multi-pass regex parser for .tf files. * Extracts: modules, resources, variables, outputs, data sources, locals, providers. * Detects cross-references: var.X, module.X, local.X, data.X, resource refs. */ // Block types we care about const BLOCK_TYPES = ['resource', 'data', 'module', 'variable', 'output', 'provider', 'locals', 'terraform']; /** * Find matching closing brace for a block starting at `startLine`. * Returns the line index of the closing brace. */ function findBlockEnd(lines, startLine) { let depth = 0; for (let i = startLine; i < lines.length; i++) { const line = lines[i]; // Count braces outside of strings (simplified — good enough for HCL) for (let j = 0; j < line.length; j++) { if (line[j] === '{') depth++; else if (line[j] === '}') { depth--; if (depth === 0) return i; } } } return lines.length - 1; // fallback } /** * Extract the body text of a block (between opening { and closing }). */ function extractBlockBody(lines, startLine, endLine) { const bodyLines = lines.slice(startLine, endLine + 1); return bodyLines.join('\n'); } /** * Parse a variable block for type and default. */ function parseVariableBlock(body) { const result = { type: null, default: null, description: null }; const typeMatch = body.match(/^\s*type\s*=\s*(.+)/m); if (typeMatch) result.type = typeMatch[1].trim(); const defaultMatch = body.match(/^\s*default\s*=\s*(.+)/m); if (defaultMatch) result.default = defaultMatch[1].trim().replace(/^"(.*)"$/, '$1'); const descMatch = body.match(/^\s*description\s*=\s*"([^"]+)"/m); if (descMatch) result.description = descMatch[1]; return result; } /** * Parse a module block for source and version. */ function parseModuleBlock(body) { const result = { source: null, version: null }; const sourceMatch = body.match(/^\s*source\s*=\s*"([^"]+)"/m); if (sourceMatch) result.source = sourceMatch[1]; const versionMatch = body.match(/^\s*version\s*=\s*"([^"]+)"/m); if (versionMatch) result.version = versionMatch[1]; return result; } /** * Parse an output block for value and description. */ function parseOutputBlock(body) { const result = { value: null, description: null }; const valueMatch = body.match(/^\s*value\s*=\s*(.+)/m); if (valueMatch) result.value = valueMatch[1].trim(); const descMatch = body.match(/^\s*description\s*=\s*"([^"]+)"/m); if (descMatch) result.description = descMatch[1]; return result; } /** * Extract all cross-references from a block body. * Returns array of { type, name } objects. */ function extractReferences(body) { const refs = []; const seen = new Set(); const patterns = [ { regex: /var\.([a-zA-Z0-9_-]+)/g, type: 'variable' }, { regex: /local\.([a-zA-Z0-9_-]+)/g, type: 'local' }, { regex: /module\.([a-zA-Z0-9_-]+)/g, type: 'module' }, { regex: /data\.([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)/g, type: 'data' }, // Resource references: aws_instance.web.id → resource ref { regex: /(? [repoRoot]'); process.exit(1); } const result = extractTerraform(filePath, repoRoot); console.log(JSON.stringify(result, null, 2)); } module.exports = { extractTerraform, resolveReferences };