(input: string)
| 19 | } |
| 20 | |
| 21 | export function extractFirstCodeBlock(input: string) { |
| 22 | // 1) We use a more general pattern for the code fence: |
| 23 | // - ^```([^\n]*) captures everything after the triple backticks up to the newline. |
| 24 | // - ([\s\S]*?) captures the *body* of the code block (non-greedy). |
| 25 | // - Then we look for a closing backticks on its own line (\n```). |
| 26 | // The 'm' (multiline) flag isn't strictly necessary here, but can help if input is multiline. |
| 27 | // The '([\s\S]*?)' is a common trick to match across multiple lines non-greedily. |
| 28 | const match = input.match(/```([^\n]*)\n([\s\S]*?)\n```/); |
| 29 | |
| 30 | if (match) { |
| 31 | const fenceTag = match[1] || ""; // e.g. "tsx{filename=Calculator.tsx}" |
| 32 | const code = match[2]; // The actual code block content |
| 33 | const fullMatch = match[0]; // Entire matched string including backticks |
| 34 | |
| 35 | // We'll parse the fenceTag to extract optional language and filename |
| 36 | let language: string | null = null; |
| 37 | let filename: { name: string; extension: string } | null = null; |
| 38 | |
| 39 | // Attempt to parse out the language, which we assume is the leading alphanumeric part |
| 40 | // Example: fenceTag = "tsx{filename=Calculator.tsx}" |
| 41 | const langMatch = fenceTag.match(/^([A-Za-z0-9]+)/); |
| 42 | if (langMatch) { |
| 43 | language = langMatch[1]; |
| 44 | } |
| 45 | |
| 46 | // Attempt to parse out a filename from braces, e.g. {filename=Calculator.tsx} |
| 47 | const fileMatch = fenceTag.match(/{\s*filename\s*=\s*([^}]+)\s*}/); |
| 48 | if (fileMatch) { |
| 49 | filename = parseFileName(fileMatch[1]); |
| 50 | } |
| 51 | |
| 52 | return { code, language, filename, fullMatch }; |
| 53 | } |
| 54 | return null; // No code block found |
| 55 | } |
| 56 | |
| 57 | export function extractAllCodeBlocks(input: string): Array<{ |
| 58 | code: string; |
no test coverage detected