Skip to content

Commit 656c3e4

Browse files
authored
fix: fix link with angle brackets around href (#1851)
1 parent 654d83d commit 656c3e4

6 files changed

Lines changed: 52 additions & 32 deletions

File tree

src/Tokenizer.js

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -457,34 +457,56 @@ module.exports = class Tokenizer {
457457
link(src) {
458458
const cap = this.rules.inline.link.exec(src);
459459
if (cap) {
460-
const lastParenIndex = findClosingBracket(cap[2], '()');
461-
if (lastParenIndex > -1) {
462-
const start = cap[0].indexOf('!') === 0 ? 5 : 4;
463-
const linkLen = start + cap[1].length + lastParenIndex;
464-
cap[2] = cap[2].substring(0, lastParenIndex);
465-
cap[0] = cap[0].substring(0, linkLen).trim();
466-
cap[3] = '';
460+
const trimmedUrl = cap[2].trim();
461+
if (!this.options.pedantic && trimmedUrl.startsWith('<')) {
462+
// commonmark requires matching angle brackets
463+
if (!trimmedUrl.endsWith('>')) {
464+
return;
465+
}
466+
467+
// ending angle bracket cannot be escaped
468+
const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\');
469+
if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
470+
return;
471+
}
472+
} else {
473+
// find closing parenthesis
474+
const lastParenIndex = findClosingBracket(cap[2], '()');
475+
if (lastParenIndex > -1) {
476+
const start = cap[0].indexOf('!') === 0 ? 5 : 4;
477+
const linkLen = start + cap[1].length + lastParenIndex;
478+
cap[2] = cap[2].substring(0, lastParenIndex);
479+
cap[0] = cap[0].substring(0, linkLen).trim();
480+
cap[3] = '';
481+
}
467482
}
468483
let href = cap[2];
469484
let title = '';
470485
if (this.options.pedantic) {
486+
// split pedantic href and title
471487
const link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
472488

473489
if (link) {
474490
href = link[1];
475491
title = link[3];
476-
} else {
477-
title = '';
478492
}
479493
} else {
480494
title = cap[3] ? cap[3].slice(1, -1) : '';
481495
}
482-
href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
483-
const token = outputLink(cap, {
496+
497+
href = href.trim();
498+
if (href.startsWith('<')) {
499+
if (this.options.pedantic && !trimmedUrl.endsWith('>')) {
500+
// pedantic allows starting angle bracket without ending angle bracket
501+
href = href.slice(1);
502+
} else {
503+
href = href.slice(1, -1);
504+
}
505+
}
506+
return outputLink(cap, {
484507
href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
485508
title: title ? title.replace(this.rules.inline._escapes, '$1') : title
486509
}, cap[0]);
487-
return token;
488510
}
489511
}
490512

@@ -502,8 +524,7 @@ module.exports = class Tokenizer {
502524
text
503525
};
504526
}
505-
const token = outputLink(cap, link, cap[0]);
506-
return token;
527+
return outputLink(cap, link, cap[0]);
507528
}
508529
}
509530

src/rules.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ inline.tag = edit(inline.tag)
260260
.getRegex();
261261

262262
inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
263-
inline._href = /<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/;
263+
inline._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/;
264264
inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
265265

266266
inline.link = edit(inline.link)

test/specs/commonmark/commonmark.0.29.json

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3939,8 +3939,7 @@
39393939
"example": 486,
39403940
"start_line": 7543,
39413941
"end_line": 7547,
3942-
"section": "Links",
3943-
"shouldFail": true
3942+
"section": "Links"
39443943
},
39453944
{
39463945
"markdown": "[link](foo\nbar)\n",
@@ -3964,26 +3963,23 @@
39643963
"example": 489,
39653964
"start_line": 7571,
39663965
"end_line": 7575,
3967-
"section": "Links",
3968-
"shouldFail": true
3966+
"section": "Links"
39693967
},
39703968
{
39713969
"markdown": "[link](<foo\\>)\n",
39723970
"html": "<p>[link](&lt;foo&gt;)</p>\n",
39733971
"example": 490,
39743972
"start_line": 7579,
39753973
"end_line": 7583,
3976-
"section": "Links",
3977-
"shouldFail": true
3974+
"section": "Links"
39783975
},
39793976
{
39803977
"markdown": "[a](<b)c\n[a](<b)c>\n[a](<b>c)\n",
39813978
"html": "<p>[a](&lt;b)c\n[a](&lt;b)c&gt;\n[a](<b>c)</p>\n",
39823979
"example": 491,
39833980
"start_line": 7588,
39843981
"end_line": 7596,
3985-
"section": "Links",
3986-
"shouldFail": true
3982+
"section": "Links"
39873983
},
39883984
{
39893985
"markdown": "[link](\\(foo\\))\n",

test/specs/gfm/commonmark.0.29.json

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3939,8 +3939,7 @@
39393939
"example": 486,
39403940
"start_line": 7543,
39413941
"end_line": 7547,
3942-
"section": "Links",
3943-
"shouldFail": true
3942+
"section": "Links"
39443943
},
39453944
{
39463945
"markdown": "[link](foo\nbar)\n",
@@ -3964,26 +3963,23 @@
39643963
"example": 489,
39653964
"start_line": 7571,
39663965
"end_line": 7575,
3967-
"section": "Links",
3968-
"shouldFail": true
3966+
"section": "Links"
39693967
},
39703968
{
39713969
"markdown": "[link](<foo\\>)\n",
39723970
"html": "<p>[link](&lt;foo&gt;)</p>\n",
39733971
"example": 490,
39743972
"start_line": 7579,
39753973
"end_line": 7583,
3976-
"section": "Links",
3977-
"shouldFail": true
3974+
"section": "Links"
39783975
},
39793976
{
39803977
"markdown": "[a](<b)c\n[a](<b)c>\n[a](<b>c)\n",
39813978
"html": "<p>[a](&lt;b)c\n[a](&lt;b)c&gt;\n[a](<b>c)</p>\n",
39823979
"example": 491,
39833980
"start_line": 7588,
39843981
"end_line": 7596,
3985-
"section": "Links",
3986-
"shouldFail": true
3982+
"section": "Links"
39873983
},
39883984
{
39893985
"markdown": "[link](\\(foo\\))\n",

test/specs/new/link_lt.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
<p><a href="%3Ctest">URL</a></p>
1+
<p><a href="test">URL</a></p>
2+
3+
<p><a href="test%5C">URL</a></p>

test/specs/new/link_lt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1+
---
2+
pedantic: true
3+
---
14
[URL](<test)
5+
6+
[URL](<test\>)

0 commit comments

Comments
 (0)