fix: shorten update notes and contain entry content in cards

This commit is contained in:
2026-09-07 14:15:39 +09:00
parent 98ef813cb5
commit 8f97683271
4 changed files with 48 additions and 13 deletions
+22
View File
@@ -0,0 +1,22 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { renderReleaseMarkdown } from '../release-notes/markdown.ts';
test('업데이트의 모든 문단과 목록을 하나의 카드에 묶는다', () => {
const html = renderReleaseMarkdown('## 날짜\n\n### 기능\n\n첫 문단\n\n추가 문단\n\n- 목록\n\n### 다음 기능\n\n설명');
const cards = [...html.matchAll(/<section class="update-entry">([\s\S]*?)<\/section>/g)];
assert.equal(cards.length, 2);
assert.match(cards[0][1], /첫 문단[\s\S]*추가 문단[\s\S]*<ul>/);
assert.doesNotMatch(cards[0][1], /다음 기능/);
assert.match(cards[1][1], /다음 기능[\s\S]*설명/);
assert.ok(html.indexOf('<h2>') < html.indexOf('<section'));
});
test('구분선과 날짜는 카드 밖에 두고 원시 HTML은 계속 이스케이프한다', () => {
const html = renderReleaseMarkdown('### 기능\n\n<script>alert(1)</script>\n\n---\n\n## 다음 날짜\n\n### 새 기능\n\n[문서](https://example.com)');
assert.match(html, /<\/section>\s*<hr>/);
assert.match(html, /<hr>\s*<h2>다음 날짜<\/h2>\s*<section/);
assert.doesNotMatch(html, /<script>/);
assert.match(html, /&lt;script&gt;/);
assert.match(html, /href="https:\/\/example.com\/"/);
});
+18 -2
View File
@@ -39,10 +39,26 @@ renderer.image = ({ href, title, text }) => {
};
export function renderReleaseMarkdown(markdown: string): string {
return marked.parse(markdown, {
const options = {
async: false,
breaks: true,
gfm: true,
renderer
}) as string;
} as const;
const tokens = marked.lexer(markdown, options);
let html = '';
let entryOpen = false;
for (const token of tokens) {
const boundary = token.type === 'hr' || (token.type === 'heading' && token.depth <= 3);
if (boundary && entryOpen) {
html += '</section>\n';
entryOpen = false;
}
if (token.type === 'heading' && token.depth === 3) {
html += '<section class="update-entry">\n';
entryOpen = true;
}
html += marked.parser([token], options);
}
return html + (entryOpen ? '</section>\n' : '');
}
+7 -4
View File
@@ -42,8 +42,12 @@
:global(.markdown-body h2) { position: relative; margin: 0 0 14px; padding-top: 2px; color: #cad0d9; font-size: 15px; font-weight: 850; }
:global(.markdown-body h2:not(:first-child)) { margin-top: 38px; }
:global(.markdown-body h2::before) { position: absolute; top: 6px; left: -34px; width: 13px; height: 13px; border: 3px solid #101319; border-radius: 50%; background: #ed8bb5; box-shadow: 0 0 0 1px rgb(237 139 181 / 45%); content: ''; }
:global(.markdown-body h3) { margin: 10px 0 0; padding: 19px 20px 0; border: 1px solid #2b3441; border-bottom: 0; border-radius: 12px 12px 0 0; color: #edf0f4; background: #151a22; font-size: 17px; letter-spacing: -.02em; }
:global(.markdown-body h3 + p) { margin: 0 0 10px; padding: 8px 20px 19px; border: 1px solid #2b3441; border-top: 0; border-radius: 0 0 12px 12px; color: #9ca6b4; background: #151a22; font-size: 14px; line-height: 1.65; }
:global(.markdown-body .update-entry) { min-width: 0; margin: 10px 0; padding: 19px 20px; border: 1px solid #2b3441; border-radius: 12px; background: #151a22; overflow-wrap: anywhere; }
:global(.markdown-body h3) { margin: 0; color: #edf0f4; font-size: 17px; letter-spacing: -.02em; }
:global(.markdown-body .update-entry p) { margin: 8px 0 0; color: #9ca6b4; font-size: 14px; line-height: 1.65; }
:global(.markdown-body .update-entry > :last-child) { margin-bottom: 0; }
:global(.markdown-body img) { max-width: 100%; height: auto; }
:global(.markdown-body pre) { max-width: 100%; overflow-x: auto; }
:global(.markdown-body code) { margin-right: 7px; padding: 3px 7px; border: 1px solid rgb(237 139 181 / 32%); border-radius: 999px; color: #efa0c1; background: rgb(237 139 181 / 9%); font-family: inherit; font-size: 10px; font-weight: 900; }
:global(.markdown-body hr) { height: 1px; margin: 38px 0; border: 0; background: #2c3440; }
:global(.markdown-body ul), :global(.markdown-body ol) { color: #9ca6b4; line-height: 1.7; }
@@ -54,7 +58,6 @@
.delivery-note { margin-bottom: 30px; }
.markdown-body { padding-left: 27px; }
:global(.markdown-body h2::before) { left: -27px; }
:global(.markdown-body h3) { padding: 17px 17px 0; }
:global(.markdown-body h3 + p) { padding: 8px 17px 17px; }
:global(.markdown-body .update-entry) { padding: 17px; }
}
</style>