/** * 마크다운 표 행 후보인지 확인한다. * @param {string} line - 원본 줄 * @returns {boolean} 표 행 후보 여부 */ export const isMarkdownTableRow = (line) => { const value = String(line || '').trim() return value.startsWith('|') && value.endsWith('|') && value.split('|').length >= 4 } /** * 마크다운 표 행을 셀 배열로 나눈다. * @param {string} line - 원본 줄 * @returns {string[]} 셀 목록 */ export const splitMarkdownTableRow = (line) => { const value = String(line || '').trim().replace(/^\|/, '').replace(/\|$/, '') const cells = [] let cell = '' let escaped = false for (const character of value) { if (escaped) { cell += character === '|' ? '|' : `\\${character}` escaped = false continue } if (character === '\\') { escaped = true continue } if (character === '|') { cells.push(cell.trim()) cell = '' continue } cell += character } if (escaped) { cell += '\\' } cells.push(cell.trim()) return cells } /** * 마크다운 표 구분선 행인지 확인한다. * @param {string} line - 원본 줄 * @returns {boolean} 구분선 여부 */ export const isMarkdownTableSeparator = (line) => { if (!isMarkdownTableRow(line)) { return false } return splitMarkdownTableRow(line).every((cell) => /^:?-{3,}:?$/.test(cell.trim())) } /** * 표 정렬 행에서 셀 정렬 값을 파싱한다. * @param {string} cell - 구분선 셀 * @returns {'left'|'center'|'right'} 정렬 값 */ export const parseMarkdownTableAlignment = (cell) => { const value = String(cell || '').trim() if (value.startsWith(':') && value.endsWith(':')) { return 'center' } if (value.endsWith(':')) { return 'right' } return 'left' } /** * 마크다운 표 블록을 파싱한다. * @param {string[]} lines - 전체 마크다운 줄 * @param {number} startIndex - 표 시작 후보 줄 * @returns {{headers: string[], alignments: string[], rows: string[][], endLine: number, nextIndex: number}|null} 표 메타 */ export const parseMarkdownTableBlock = (lines, startIndex) => { if (!isMarkdownTableRow(lines[startIndex]) || !isMarkdownTableSeparator(lines[startIndex + 1])) { return null } const headers = splitMarkdownTableRow(lines[startIndex]) const alignments = splitMarkdownTableRow(lines[startIndex + 1]).map(parseMarkdownTableAlignment) const rows = [] let index = startIndex + 2 while (index < lines.length && isMarkdownTableRow(lines[index]) && !isMarkdownTableSeparator(lines[index])) { rows.push(splitMarkdownTableRow(lines[index])) index += 1 } return { headers, alignments, rows, endLine: index - 1, nextIndex: index } }