Skip to content

Commit 09dd13d

Browse files
hendrikheilclaudefarnabaz
authored
fix(renderer): escape [ in markdown text nodes to prevent link re-injection (#240)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Farnabaz <farnabaz@gmail.com>
1 parent fea3b66 commit 09dd13d

5 files changed

Lines changed: 120 additions & 2 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
## Input
2+
3+
```md
4+
[foo\] bar](/url)
5+
```
6+
7+
## AST
8+
9+
```json
10+
{
11+
"frontmatter": {},
12+
"meta": {},
13+
"nodes": [
14+
[
15+
"p",
16+
{},
17+
[
18+
"a",
19+
{
20+
"href": "/url"
21+
},
22+
"foo] bar"
23+
]
24+
]
25+
]
26+
}
27+
```
28+
29+
## HTML
30+
31+
```html
32+
<p><a href="/url">foo] bar</a></p>
33+
```
34+
35+
## Markdown
36+
37+
```md
38+
[foo\] bar](/url)
39+
```
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
## Input
2+
3+
```md
4+
Escaped \[text\] in a sentence.
5+
```
6+
7+
## AST
8+
9+
```json
10+
{
11+
"frontmatter": {},
12+
"meta": {},
13+
"nodes": [
14+
[
15+
"p",
16+
{},
17+
"Escaped [text] in a sentence."
18+
]
19+
]
20+
}
21+
```
22+
23+
## HTML
24+
25+
```html
26+
<p>Escaped [text] in a sentence.</p>
27+
```
28+
29+
## Markdown
30+
31+
```md
32+
Escaped \[text\] in a sentence.
33+
```
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
## Input
2+
3+
```md
4+
\[Animation Bug\]\(/bugs#section\)
5+
```
6+
7+
## AST
8+
9+
```json
10+
{
11+
"frontmatter": {},
12+
"meta": {},
13+
"nodes": [
14+
[
15+
"p",
16+
{},
17+
"[Animation Bug](/bugs#section)"
18+
]
19+
]
20+
}
21+
```
22+
23+
## HTML
24+
25+
```html
26+
<p>[Animation Bug](/bugs#section)</p>
27+
```
28+
29+
## Markdown
30+
31+
```md
32+
\[Animation Bug\](/bugs#section)
33+
```

packages/comark/src/internal/stringify/state.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export async function one(node: ComarkNode, state: State, parent?: ComarkElement
3232
if (state.context.html) {
3333
return escapeHtml(node)
3434
}
35-
return node
35+
return escapeMarkdownText(node)
3636
}
3737

3838
if (node[0] === null) {
@@ -201,3 +201,16 @@ function escapeHtml(text: string): string {
201201
}
202202
return text.replace(/[<>]/g, (char) => map[char])
203203
}
204+
205+
/**
206+
* Escape characters in a markdown text node that would otherwise be
207+
* misinterpreted as markdown syntax on a subsequent parse.
208+
*
209+
* `[` opens link/image syntax; `]` closes it. Both must be escaped so that
210+
* a text node like `[foo](bar)` round-trips as plain text, and a text node
211+
* containing `]` inside a link (e.g. `dsd]dsd`) doesn't prematurely close
212+
* the surrounding `[…]` brackets.
213+
*/
214+
function escapeMarkdownText(text: string): string {
215+
return text.replace(/[[\]]/g, (ch) => `\\${ch}`)
216+
}

test/bundle.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ describe.skipIf(stubbed || process.env.SKIP_BUNDLE_SIZE === 'true')('package bun
7676
"@comark/react": "37.2k (56 files)",
7777
"@comark/svelte": "39.2k (66 files)",
7878
"@comark/vue": "54.8k (62 files)",
79-
"comark": "344k (132 files)",
79+
"comark": "345k (132 files)",
8080
}
8181
`)
8282
})

0 commit comments

Comments
 (0)