refactor: improve blockquoteCompiler logic (#2624)

This commit is contained in:
Luffy
2025-11-15 10:29:57 +08:00
committed by GitHub
parent 8016f8f35c
commit a8d144de00
2 changed files with 53 additions and 39 deletions
+33 -24
View File
@@ -1,36 +1,45 @@
export const blockquoteCompiler = ({ renderer }) =>
(renderer.blockquote = function ({ tokens }) {
const calloutData =
tokens[0].type === 'paragraph' &&
// 0: Match "[!TIP] My Title"
// 1: Mark "[!TIP]"
// 2: Type "TIP"
tokens[0].raw.match(/^(\[!(\w+)\])/);
let openTag = '<blockquote>';
let closeTag = '</blockquote>';
if (calloutData) {
const calloutMark = calloutData[1]; // "[!TIP]"
const calloutType = calloutData[2].toLowerCase(); // "tip"
const token = tokens[0].tokens[0];
// Find the first paragraph token in the blockquote
const firstParagraphIndex = tokens.findIndex(t => t.type === 'paragraph');
const firstParagraph = tokens[firstParagraphIndex];
// Remove callout mark from tokens
['raw', 'text'].forEach(key => {
token[key] = token[key].replace(calloutMark, '').trimStart();
});
if (firstParagraph) {
// Check if the paragraph starts with a callout like [!TIP] or [!NOTE]
const calloutData = firstParagraph.raw.match(/^(\[!(\w+)\])/);
// Remove empty paragraph
if (tokens.length > 1 && !token.raw.trim()) {
tokens = tokens.slice(1);
if (calloutData) {
const calloutMark = calloutData[1]; // "[!TIP]"
const calloutType = calloutData[2].toLowerCase(); // "tip"
// Remove the callout mark from the paragraph raw text
firstParagraph.raw = firstParagraph.raw
.replace(calloutMark, '')
.trimStart();
if (firstParagraph.tokens && firstParagraph.tokens.length > 0) {
firstParagraph.tokens.forEach(t => {
if (t.raw) {
t.raw = t.raw.replace(calloutMark, '');
}
if (t.text) {
t.text = t.text.replace(calloutMark, '');
}
});
}
// If the first paragraph is now empty after removing [!TIP], remove it
if (!firstParagraph.raw.trim()) {
tokens.splice(firstParagraphIndex, 1);
}
openTag = `<div class="callout ${calloutType}">`;
closeTag = `</div>`;
}
openTag = `<div class="callout ${calloutType}">`;
closeTag = `</div>`;
}
const body = this.parser.parse(tokens);
const html = `${openTag}${body}${closeTag}`;
return html;
return `${openTag}${body}${closeTag}`;
});
+20 -15
View File
@@ -15,41 +15,46 @@ describe('render', function () {
test('caution', () => {
const output = window.marked('> [!CAUTION]\n> Text');
expect(output).toMatchInlineSnapshot(
`"<div class="callout caution"><p>Text</p></div>"`,
);
expect(output).toMatchInlineSnapshot(`
"<div class="callout caution"><p>
Text</p></div>"
`);
});
test('important', () => {
const output = window.marked('> [!IMPORTANT]\n> Text');
expect(output).toMatchInlineSnapshot(
`"<div class="callout important"><p>Text</p></div>"`,
);
expect(output).toMatchInlineSnapshot(`
"<div class="callout important"><p>
Text</p></div>"
`);
});
test('note', () => {
const output = window.marked('> [!NOTE]\n> Text');
expect(output).toMatchInlineSnapshot(
`"<div class="callout note"><p>Text</p></div>"`,
);
expect(output).toMatchInlineSnapshot(`
"<div class="callout note"><p>
Text</p></div>"
`);
});
test('tip', () => {
const output = window.marked('> [!TIP]\n> Text');
expect(output).toMatchInlineSnapshot(
`"<div class="callout tip"><p>Text</p></div>"`,
);
expect(output).toMatchInlineSnapshot(`
"<div class="callout tip"><p>
Text</p></div>"
`);
});
test('warning', () => {
const output = window.marked('> [!WARNING]\n> Text');
expect(output).toMatchInlineSnapshot(
`"<div class="callout warning"><p>Text</p></div>"`,
);
expect(output).toMatchInlineSnapshot(`
"<div class="callout warning"><p>
Text</p></div>"
`);
});
test('important (legacy)', () => {