(filePath, fileContent, error, regionOffset, region)
| 226 | // ===== Error formatting ===== |
| 227 | |
| 228 | function formatError(filePath, fileContent, error, regionOffset, region) { |
| 229 | // Map error position to absolute file position |
| 230 | const absOffset = regionOffset + (error.token?.start ?? 0); |
| 231 | const pos = offsetToLineCol(fileContent, absOffset); |
| 232 | const relPath = path.relative(process.cwd(), filePath); |
| 233 | |
| 234 | let out = `\x1b[1m${relPath}:${pos.line}:${pos.col}\x1b[0m \x1b[31merror:\x1b[0m ${error.message}`; |
| 235 | |
| 236 | // Show context line |
| 237 | const lines = fileContent.split('\n'); |
| 238 | const lineIdx = pos.line - 1; |
| 239 | if (lineIdx >= 0 && lineIdx < lines.length) { |
| 240 | const lineStr = lines[lineIdx]; |
| 241 | const gutter = String(pos.line).length; |
| 242 | out += '\n'; |
| 243 | out += ` ${String(pos.line).padStart(gutter)} | ${lineStr}\n`; |
| 244 | |
| 245 | // Underline |
| 246 | const col = pos.col - 1; |
| 247 | const len = Math.max(1, error.token?.value?.length || 1); |
| 248 | out += ` ${' '.repeat(gutter)} | ${' '.repeat(col)}${'~'.repeat(len)}`; |
| 249 | } |
| 250 | |
| 251 | return out; |
| 252 | } |
| 253 | |
| 254 | function offsetToLineCol(text, offset) { |
| 255 | let line = 1, col = 1; |
no test coverage detected