All files / lib/templates stack-compiler.js

90.9% Statements 40/44
87.5% Branches 21/24
100% Functions 5/5
90.9% Lines 40/44

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85        1x 1x     5x 5x 5x 5x 5x   5x 50x     50x 3x 3x       47x 3x 3x 3x       44x 6x 3x 3x   3x 3x 3x 3x   3x         38x 34x         5x 6x     5x       1x   1x 1x                     2x   2x 1x     1x 1x 2x 2x  
import fs from 'fs-extra';
import path from 'path';
import { injectVariables } from './variable-injector.js';
 
const IDE_BLOCK_OPEN = /{{#IDE\s+(\w+)}}/;
const IDE_BLOCK_CLOSE = '{{/IDE}}';
 
export function extractIDESection(template, targetIDE) {
  const lines = template.split('\n');
  const result = [];
  let inTargetBlock = false;
  let inOtherBlock = false;
  let blockDepth = 0;
 
  for (const line of lines) {
    const openMatch = line.match(IDE_BLOCK_OPEN);
 
    // Check for target IDE open tag
    if (openMatch && openMatch[1] === targetIDE) {
      inTargetBlock = true;
      continue;
    }
 
    // Check for other IDE open tag
    if (openMatch && openMatch[1] !== targetIDE) {
      inOtherBlock = true;
      blockDepth++;
      continue;
    }
 
    // Check for close tag
    if (line.includes(IDE_BLOCK_CLOSE)) {
      if (inTargetBlock) {
        inTargetBlock = false;
        continue;
      }
      Eif (inOtherBlock) {
        blockDepth--;
        Eif (blockDepth === 0) {
          inOtherBlock = false;
        }
        continue;
      }
    }
 
    // Include line if not in other IDE block
    if (!inOtherBlock) {
      result.push(line);
    }
  }
 
  // Trim trailing empty lines
  while (result.length > 0 && result[result.length - 1].trim() === '') {
    result.pop();
  }
 
  return result.join('\n');
}
 
export async function compileStackTemplate(stack, ide, context) {
  const templatePath = path.resolve('templates/stacks', `${stack}.md`);
 
  Eif (!(await fs.pathExists(templatePath))) {
    throw new Error(`Template not found: ${templatePath}`);
  }
 
  const template = await fs.readFile(templatePath, 'utf-8');
  const ideSpecific = extractIDESection(template, ide);
  const compiled = injectVariables(ideSpecific, context);
 
  return compiled;
}
 
export async function listAvailableStacks() {
  const templatesDir = path.resolve('templates/stacks');
 
  if (!(await fs.pathExists(templatesDir))) {
    return [];
  }
 
  const files = await fs.readdir(templatesDir);
  return files
    .filter(f => f.endsWith('.md'))
    .map(f => f.replace('.md', ''));
}