UNPKG

@ideascol/cli-maker

Version:
137 lines (136 loc) 6.27 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.renderMarkdown = renderMarkdown; const colors_1 = require("../colors"); /** * Render a markdown string to terminal-formatted output. * Supports: headers, bold, italic, inline code, fenced code blocks, lists, and horizontal rules. */ function renderMarkdown(md) { const lines = md.split('\n'); const output = []; let inCodeBlock = false; let codeBlockLang = ''; let codeLines = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; // Fenced code block toggle if (line.trimStart().startsWith('```')) { if (!inCodeBlock) { inCodeBlock = true; codeBlockLang = line.trimStart().slice(3).trim(); codeLines = []; continue; } else { // End of code block — render it const header = codeBlockLang ? `${colors_1.Colors.FgGray}┌─ ${codeBlockLang} ${'─'.repeat(Math.max(0, 40 - codeBlockLang.length))}${colors_1.Colors.Reset}` : `${colors_1.Colors.FgGray}${'─'.repeat(44)}${colors_1.Colors.Reset}`; output.push(header); for (const cl of codeLines) { output.push(`${colors_1.Colors.FgGray}${colors_1.Colors.Reset} ${colors_1.Colors.FgCyan}${cl}${colors_1.Colors.Reset}`); } const footer = `${colors_1.Colors.FgGray}${'─'.repeat(44)}${colors_1.Colors.Reset}`; output.push(footer); inCodeBlock = false; codeBlockLang = ''; codeLines = []; continue; } } if (inCodeBlock) { codeLines.push(line); continue; } // Horizontal rule if (/^(\s*[-*_]\s*){3,}$/.test(line)) { output.push(`${colors_1.Colors.FgGray}${'─'.repeat(46)}${colors_1.Colors.Reset}`); continue; } // Headers const headerMatch = line.match(/^(#{1,6})\s+(.*)$/); if (headerMatch) { const level = headerMatch[1].length; const text = formatInline(headerMatch[2]); if (level === 1) { output.push(`\n${colors_1.Colors.Bright}${colors_1.Colors.FgCyan}${text}${colors_1.Colors.Reset}`); output.push(`${colors_1.Colors.FgGray}${'═'.repeat(stripAnsi(text).length)}${colors_1.Colors.Reset}`); } else if (level === 2) { output.push(`\n${colors_1.Colors.Bright}${colors_1.Colors.FgGreen}${text}${colors_1.Colors.Reset}`); output.push(`${colors_1.Colors.FgGray}${'─'.repeat(stripAnsi(text).length)}${colors_1.Colors.Reset}`); } else { output.push(`\n${colors_1.Colors.Bright}${colors_1.Colors.FgYellow}${text}${colors_1.Colors.Reset}`); } continue; } // Unordered list const ulMatch = line.match(/^(\s*)([-*+])\s+(.*)$/); if (ulMatch) { const indent = ulMatch[1]; const text = formatInline(ulMatch[3]); output.push(`${indent}${colors_1.Colors.FgCyan}${colors_1.Colors.Reset} ${text}`); continue; } // Ordered list const olMatch = line.match(/^(\s*)(\d+)\.\s+(.*)$/); if (olMatch) { const indent = olMatch[1]; const num = olMatch[2]; const text = formatInline(olMatch[3]); output.push(`${indent}${colors_1.Colors.FgCyan}${num}.${colors_1.Colors.Reset} ${text}`); continue; } // Blockquote const bqMatch = line.match(/^>\s?(.*)$/); if (bqMatch) { const text = formatInline(bqMatch[1]); output.push(`${colors_1.Colors.FgGray}${colors_1.Colors.Reset} ${colors_1.Colors.Dim}${text}${colors_1.Colors.Reset}`); continue; } // Regular paragraph line output.push(formatInline(line)); } // If we ended while still inside a code block, flush it if (inCodeBlock && codeLines.length > 0) { const header = codeBlockLang ? `${colors_1.Colors.FgGray}┌─ ${codeBlockLang} ${'─'.repeat(Math.max(0, 40 - codeBlockLang.length))}${colors_1.Colors.Reset}` : `${colors_1.Colors.FgGray}${'─'.repeat(44)}${colors_1.Colors.Reset}`; output.push(header); for (const cl of codeLines) { output.push(`${colors_1.Colors.FgGray}${colors_1.Colors.Reset} ${colors_1.Colors.FgCyan}${cl}${colors_1.Colors.Reset}`); } const footer = `${colors_1.Colors.FgGray}${'─'.repeat(44)}${colors_1.Colors.Reset}`; output.push(footer); } return output.join('\n'); } /** * Format inline markdown: bold, italic, inline code, strikethrough, links. */ function formatInline(text) { // Inline code (must come before bold/italic to avoid conflicts) text = text.replace(/`([^`]+)`/g, `${colors_1.Colors.FgCyan}$1${colors_1.Colors.Reset}`); // Bold + italic text = text.replace(/\*\*\*(.+?)\*\*\*/g, `${colors_1.Colors.Bright}${colors_1.Colors.Dim}$1${colors_1.Colors.Reset}`); // Bold text = text.replace(/\*\*(.+?)\*\*/g, `${colors_1.Colors.Bright}$1${colors_1.Colors.Reset}`); text = text.replace(/__(.+?)__/g, `${colors_1.Colors.Bright}$1${colors_1.Colors.Reset}`); // Italic text = text.replace(/\*(.+?)\*/g, `${colors_1.Colors.Dim}$1${colors_1.Colors.Reset}`); text = text.replace(/_(.+?)_/g, `${colors_1.Colors.Dim}$1${colors_1.Colors.Reset}`); // Strikethrough text = text.replace(/~~(.+?)~~/g, `${colors_1.Colors.FgGray}$1${colors_1.Colors.Reset}`); // Links [text](url) text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, `${colors_1.Colors.FgBlue}${colors_1.Colors.Underscore}$1${colors_1.Colors.Reset} ${colors_1.Colors.FgGray}($2)${colors_1.Colors.Reset}`); return text; } /** * Strip ANSI escape codes from a string (for length calculations). */ function stripAnsi(str) { // biome-ignore lint: safe regex for ANSI codes return str.replace(/\u001b\[[0-9;]*[mG]/g, ''); }