fix(markdown): keep CJK punctuation out of bare-URL autolinks
marked's GFM url tokenizer swallows any non-space text after a bare URL,
including fullwidth punctuation and Chinese annotations written directly
after it ("https://x.com/docs(说明)了解更多"). Switch the autolink
tokenizer to marked-linkify-it, which treats Unicode punctuation as a URL
boundary, so fullwidth parens and CJK punctuation are trimmed naturally.
This commit is contained in:
@@ -187,3 +187,30 @@ describe('Markdown images', () => {
|
||||
expect(html).not.toContain('data-openchamber-markdown-image');
|
||||
});
|
||||
});
|
||||
|
||||
describe('CJK-aware link parsing', () => {
|
||||
const hrefOf = (html: string): string | null => /<a\b[^>]*href="([^"]*)"/.exec(html)?.[1] ?? null;
|
||||
|
||||
test('bare URL followed by a CJK annotation trims the annotation from the href', () => {
|
||||
const html = renderMarkdownSync('访问 https://example.com/docs(中文说明)了解更多');
|
||||
expect(hrefOf(html)).toBe('https://example.com/docs');
|
||||
});
|
||||
|
||||
test('bare URL followed by CJK punctuation trims the punctuation', () => {
|
||||
expect(hrefOf(renderMarkdownSync('地址 https://example.com/guide,详见'))).toBe(
|
||||
'https://example.com/guide',
|
||||
);
|
||||
expect(hrefOf(renderMarkdownSync('官网 https://example.com。'))).toBe('https://example.com');
|
||||
});
|
||||
|
||||
test('correct links are unaffected', () => {
|
||||
expect(hrefOf(renderMarkdownSync('官方文档见 [这里](https://docs.example.com)(中文说明)'))).toBe(
|
||||
'https://docs.example.com',
|
||||
);
|
||||
expect(hrefOf(renderMarkdownSync('[下载](https://dl.example.com/安装包(正式版))'))).toBe(
|
||||
'https://dl.example.com/安装包(正式版)',
|
||||
);
|
||||
expect(hrefOf(renderMarkdownSync('[a](url(1))'))).toBe('url(1)');
|
||||
expect(hrefOf(renderMarkdownSync('[a](url "title")'))).toBe('url');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Marked, marked, type Tokens } from 'marked';
|
||||
import markedLinkifyIt from 'marked-linkify-it';
|
||||
import remend from 'remend';
|
||||
import katex from 'katex';
|
||||
import DOMPurify from 'dompurify';
|
||||
@@ -321,10 +322,15 @@ const blockMathExtension = {
|
||||
},
|
||||
};
|
||||
|
||||
const createParser = (imageMode: MarkdownImageMode) => new Marked().use({
|
||||
gfm: true,
|
||||
breaks: false,
|
||||
extensions: [inlineMathExtension, blockMathExtension],
|
||||
// marked's GFM autolink swallows CJK punctuation after a bare URL, so switch
|
||||
// to marked-linkify-it, which treats Unicode punctuation as a URL boundary.
|
||||
// Plain CJK characters right after a URL are still consumed, matching GitHub.
|
||||
const createParser = (imageMode: MarkdownImageMode) => new Marked().use(
|
||||
markedLinkifyIt({ fuzzyLink: false }),
|
||||
{
|
||||
gfm: true,
|
||||
breaks: false,
|
||||
extensions: [inlineMathExtension, blockMathExtension],
|
||||
renderer: {
|
||||
// Assistant output is untrusted. Markdown constructs still render as HTML,
|
||||
// but raw HTML must remain visible text so it cannot introduce active DOM
|
||||
|
||||
Reference in New Issue
Block a user