/** * 인용 접두사 제거 * @param {string} value - 원본 * @returns {string} 본문 */ export const stripQuoteMarker = (value) => String(value ?? '').replace(/^(?:>\s*)+/, '').trim() /** * 목록 접두사 제거 * @param {string} value - 원본 * @param {boolean} ordered - 순서 목록 여부 * @returns {string} 본문 */ export const stripListMarker = (value, ordered = false) => { const raw = String(value ?? '').trim() if (ordered) { return raw.replace(/^\d+\.\s*/, '').trim() } return raw.replace(/^[-*+]\s+/, '').trim() } /** * 인용 마커 포함 여부 * @param {string} value - 원본 * @returns {boolean} */ export const hasQuoteMarker = (value) => /^\s*>/.test(String(value ?? '')) /** * 목록 마커 포함 여부 * @param {string} value - 원본 * @param {boolean} [ordered=false] - 순서 목록 여부 * @returns {boolean} */ export const hasListMarker = (value, ordered = false) => { const raw = String(value ?? '').trim() if (ordered) { return /^\d+\.\s*/.test(raw) } return /^[-*+]\s/.test(raw) } /** * 순서 목록 마커를 파싱한다. * @param {string} value - 원본 줄 * @returns {{ number: number, body: string }|null} */ export const parseOrderedListMarker = (value) => { const raw = String(value ?? '').trim() const match = raw.match(/^(\d+)\.\s*(.*)$/) if (!match) { return null } return { number: Number(match[1]), body: match[2].trim() } } /** * 순서 목록 다음 줄 마커를 만든다. * @param {string} line - 현재 줄 * @returns {string} 다음 줄 마커 */ export const getNextOrderedListLine = (line) => { const parsed = parseOrderedListMarker(line) if (!parsed) { return '' } return `${parsed.number + 1}. ` } /** * 마커만 있고 본문이 없는 목록 줄인지 확인한다. * @param {string} line - 마크다운 줄 * @param {boolean} ordered - 순서 목록 여부 * @returns {boolean} */ export const isEmptyListMarkerLine = (line, ordered = false) => { if (!hasListMarker(line, ordered)) { return false } if (ordered) { const parsed = parseOrderedListMarker(line) return parsed ? !parsed.body : false } return !stripListMarker(line, false).trim() } /** * 마커만 있고 본문이 없는 인용 줄인지 확인한다. * @param {string} line - 마크다운 줄 * @returns {boolean} */ export const isEmptyQuoteMarkerLine = (line) => { if (!hasQuoteMarker(line)) { return false } return !stripQuoteMarker(line).trim() } /** * 제목 접두사 제거 * @param {string} value - 원본 * @returns {{ level: number, text: string }} 레벨·본문 */ export const stripHeadingMarker = (value) => { const raw = String(value ?? '').trim() const match = raw.match(/^(#{1,6})\s+(.*)$/) if (!match) { return { level: 0, text: raw } } return { level: match[1].length, text: match[2].trim() } }