From 5f6d950721f93dd9b723213fc4933bc10e7a459c Mon Sep 17 00:00:00 2001 From: zenn Date: Fri, 24 Jul 2026 15:20:47 +0900 Subject: [PATCH] =?UTF-8?q?fenced=20=EB=B8=94=EB=A1=9D=20=ED=83=90?= =?UTF-8?q?=EC=83=89=20helper=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/map.md | 4 +- docs/update.md | 6 +++ lib/markdown-block-context.js | 59 +----------------------------- lib/markdown-fenced-meta.js | 58 +++++++++++++++++++++++++++++ package-lock.json | 4 +- package.json | 2 +- tests/markdown-fenced-meta.test.js | 37 +++++++++++++++++++ 7 files changed, 107 insertions(+), 63 deletions(-) diff --git a/docs/map.md b/docs/map.md index 46ef909..81b7c00 100644 --- a/docs/map.md +++ b/docs/map.md @@ -48,7 +48,7 @@ | lib/markdown-iframe-embed.js | 게시물 본문 iframe 블록의 코드 판별과 안전 속성 추출 | | lib/markdown-html-block.js | 게시물 본문 HTML 블록의 parser 기반 허용 태그·속성 sanitizer, 실행 URL·인라인 style 제거와 새 창 링크 rel 보정 | | lib/markdown-callout.js | 인용 막대 색상·콜아웃 배경색 옵션, 선언 줄 파싱·직렬화 | -| lib/markdown-fenced-meta.js | 공개 Markdown fenced 블록 본문 수집과 북마크·회원가입·미디어 메타 파싱 | +| lib/markdown-fenced-meta.js | Markdown fenced 블록 시작·종료 줄 탐색, 본문 수집, 북마크·회원가입·미디어 메타 파싱 | | lib/markdown-quote.js | 공개 Markdown 인용 줄 판별·옵션 파싱·라이브 편집 줄 변환 | | lib/brand-color.js | 사이트 브랜드 컬러 기본값·hex 검증·정규화 | | lib/markdown-image.js | 이미지 마크다운 직렬화·파싱, 단독 이미지 URL 판별 | @@ -110,7 +110,7 @@ | tests/markdown-html-block.test.js | HTML sanitizer 단위 회귀 테스트 | | tests/content-in-article-ad.test.js | 공개 본문 인아티클 광고 삽입 위치 계산 단위 회귀 테스트 | | tests/markdown-block-context.test.js | 관리자 블록 설정 패널의 활성 블록 컨텍스트 단위 회귀 테스트 | -| tests/markdown-fenced-meta.test.js | Markdown fenced 블록 본문 수집과 메타 파싱 단위 회귀 테스트 | +| tests/markdown-fenced-meta.test.js | Markdown fenced 블록 시작·종료 줄 탐색, 본문 수집, 메타 파싱 단위 회귀 테스트 | | tests/markdown-quote.test.js | Markdown 인용 옵션 파싱과 편집 줄 변환 단위 회귀 테스트 | | tests/multipart-file-upload.test.js | multipart 스트리밍·제한 단위 회귀 테스트 | | tests/csp-report.test.js | CSP URL 최소화·payload 형식 단위 회귀 테스트 | diff --git a/docs/update.md b/docs/update.md index adb8ea0..c381b10 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,5 +1,11 @@ # 업데이트 이력 +## v1.11.53 + +- 관리자 블록 컨텍스트의 fenced 블록 시작·종료 줄 탐색 helper를 `lib/markdown-fenced-meta.js`로 통합. +- fenced 블록 시작·종료 탐색과 닫힘 표식 경계 단위 테스트를 추가. +- `markdown-block-context`는 활성 블록 종류별 컨텍스트 조립에 집중하도록 정리. + ## v1.11.52 - 관리자 블록 컨텍스트의 인용 줄 판별·옵션 파싱 중복을 `lib/markdown-quote.js` 공통 helper로 통합. diff --git a/lib/markdown-block-context.js b/lib/markdown-block-context.js index 5dc64c9..1f1f6bf 100644 --- a/lib/markdown-block-context.js +++ b/lib/markdown-block-context.js @@ -3,64 +3,7 @@ import { CALLOUT_BACKGROUND_OPTIONS, parseCalloutOptions } from './markdown-call import { parseCodeFenceLine } from './markdown-code-block.js' import { parseToggleOpenerLine } from './markdown-toggle.js' import { getQuoteLineBody, isQuoteMarkerLine, parseQuoteOptions } from './markdown-quote.js' - -/** - * fenced 블록 시작 줄 인덱스를 찾는다. - * @param {string[]} lines - 본문 줄 목록 - * @param {number} currentLine - 현재 줄 - * @param {string} opener - 시작 토큰 - * @returns {number} 시작 줄 또는 -1 - */ -const findFencedBlockStart = (lines, currentLine, opener) => { - for (let index = currentLine; index >= 0; index -= 1) { - if ((lines[index] || '').trim() === opener) { - return index - } - - if ((lines[index] || '').trim() === ':::') { - break - } - } - - return -1 -} - -/** - * 조건에 맞는 fenced 블록 시작 줄 인덱스를 찾는다. - * @param {string[]} lines - 본문 줄 목록 - * @param {number} currentLine - 현재 줄 - * @param {(line: string) => boolean} predicate - 시작 줄 판별 함수 - * @returns {number} 시작 줄 또는 -1 - */ -const findFencedBlockStartBy = (lines, currentLine, predicate) => { - for (let index = currentLine; index >= 0; index -= 1) { - if (predicate((lines[index] || '').trim())) { - return index - } - - if ((lines[index] || '').trim() === ':::') { - break - } - } - - return -1 -} - -/** - * fenced 블록 종료 줄 인덱스를 찾는다. - * @param {string[]} lines - 본문 줄 목록 - * @param {number} startLine - 시작 줄 - * @returns {number} 종료 줄 또는 -1 - */ -const findFencedBlockEnd = (lines, startLine) => { - for (let index = startLine + 1; index < lines.length; index += 1) { - if ((lines[index] || '').trim() === ':::') { - return index - } - } - - return -1 -} +import { findFencedBlockEnd, findFencedBlockStart, findFencedBlockStartBy } from './markdown-fenced-meta.js' /** * 단독 URL 줄인지 확인한다. diff --git a/lib/markdown-fenced-meta.js b/lib/markdown-fenced-meta.js index c278498..0c74fa3 100644 --- a/lib/markdown-fenced-meta.js +++ b/lib/markdown-fenced-meta.js @@ -1,3 +1,61 @@ +/** + * fenced 블록 시작 줄 인덱스를 찾는다. + * @param {string[]} lines - 본문 줄 목록 + * @param {number} currentLine - 현재 줄 + * @param {string} opener - 시작 토큰 + * @returns {number} 시작 줄 또는 -1 + */ +export const findFencedBlockStart = (lines, currentLine, opener) => { + for (let index = currentLine; index >= 0; index -= 1) { + if ((lines[index] || '').trim() === opener) { + return index + } + + if ((lines[index] || '').trim() === ':::') { + break + } + } + + return -1 +} + +/** + * 조건에 맞는 fenced 블록 시작 줄 인덱스를 찾는다. + * @param {string[]} lines - 본문 줄 목록 + * @param {number} currentLine - 현재 줄 + * @param {(line: string) => boolean} predicate - 시작 줄 판별 함수 + * @returns {number} 시작 줄 또는 -1 + */ +export const findFencedBlockStartBy = (lines, currentLine, predicate) => { + for (let index = currentLine; index >= 0; index -= 1) { + if (predicate((lines[index] || '').trim())) { + return index + } + + if ((lines[index] || '').trim() === ':::') { + break + } + } + + return -1 +} + +/** + * fenced 블록 종료 줄 인덱스를 찾는다. + * @param {string[]} lines - 본문 줄 목록 + * @param {number} startLine - 시작 줄 + * @returns {number} 종료 줄 또는 -1 + */ +export const findFencedBlockEnd = (lines, startLine) => { + for (let index = startLine + 1; index < lines.length; index += 1) { + if ((lines[index] || '').trim() === ':::') { + return index + } + } + + return -1 +} + /** * 닫힘 표식까지의 행 목록을 반환한다. * @param {Array} lines - 전체 마크다운 행 diff --git a/package-lock.json b/package-lock.json index 89c9a0a..4a1967e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sori.studio", - "version": "1.11.52", + "version": "1.11.53", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sori.studio", - "version": "1.11.52", + "version": "1.11.53", "hasInstallScript": true, "dependencies": { "@nuxtjs/tailwindcss": "^6.14.0", diff --git a/package.json b/package.json index 53d582a..5dfa510 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sori.studio", - "version": "1.11.52", + "version": "1.11.53", "private": true, "type": "module", "imports": { diff --git a/tests/markdown-fenced-meta.test.js b/tests/markdown-fenced-meta.test.js index 6f68880..312b317 100644 --- a/tests/markdown-fenced-meta.test.js +++ b/tests/markdown-fenced-meta.test.js @@ -2,11 +2,48 @@ import assert from 'node:assert/strict' import test from 'node:test' import { collectFencedLines, + findFencedBlockEnd, + findFencedBlockStart, + findFencedBlockStartBy, parseBookmarkMeta, parseMediaMeta, parseSignupMeta } from '../lib/markdown-fenced-meta.js' +test('fenced 블록 시작과 종료 줄을 찾는다', () => { + const lines = [ + '이전', + ':::gallery', + '![a](/a.webp)', + ':::', + '다음' + ] + + assert.equal(findFencedBlockStart(lines, 2, ':::gallery'), 1) + assert.equal(findFencedBlockEnd(lines, 1), 3) +}) + +test('닫힘 표식 너머의 fenced 블록 시작은 현재 블록으로 보지 않는다', () => { + const lines = [ + ':::gallery', + '![a](/a.webp)', + ':::', + '본문' + ] + + assert.equal(findFencedBlockStart(lines, 3, ':::gallery'), -1) +}) + +test('조건 기반 fenced 블록 시작 줄을 찾는다', () => { + const lines = [ + ':::callout emoji=none bg=blue', + '본문', + ':::' + ] + + assert.equal(findFencedBlockStartBy(lines, 1, (line) => line.startsWith(':::callout')), 0) +}) + test('fenced 블록 닫힘 표식까지의 본문과 다음 줄을 수집한다', () => { assert.deepEqual(collectFencedLines([ ':::bookmark',