Skip to content

Commit 7b381da

Browse files
committed
Fix to not escape some character in certain spans
Related to remarkjs/remark#885.
1 parent 5fdb18e commit 7b381da

File tree

2 files changed

+79
-17
lines changed

2 files changed

+79
-17
lines changed

lib/unsafe.js

+40-11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22
* @typedef {import('./types.js').Unsafe} Unsafe
33
*/
44

5+
/**
6+
* List of constructs that occur in phrasing (paragraphs, headings), but cannot
7+
* contain things like attention (emphasis, strong), images, or links.
8+
* So they sort of cancel each other out.
9+
* Note: could use a better name.
10+
*/
11+
const fullPhrasingSpans = [
12+
'autolink',
13+
'destinationLiteral',
14+
'destinationRaw',
15+
'reference',
16+
'titleQuote',
17+
'titleApostrophe'
18+
]
19+
520
/** @type {Array.<Unsafe>} */
621
export const unsafe = [
722
{character: '\t', after: '[\\r\\n]', inConstruct: 'phrasing'},
@@ -40,7 +55,12 @@ export const unsafe = [
4055
},
4156
// An exclamation mark can start an image, if it is followed by a link or
4257
// a link reference.
43-
{character: '!', after: '\\[', inConstruct: 'phrasing'},
58+
{
59+
character: '!',
60+
after: '\\[',
61+
inConstruct: 'phrasing',
62+
notInConstruct: fullPhrasingSpans
63+
},
4464
// A quote can break out of a title.
4565
{character: '"', inConstruct: 'titleQuote'},
4666
// A number sign could start an ATX heading if it starts a line.
@@ -53,14 +73,20 @@ export const unsafe = [
5373
{character: "'", inConstruct: 'titleApostrophe'},
5474
// A left paren could break out of a destination raw.
5575
{character: '(', inConstruct: 'destinationRaw'},
56-
{before: '\\]', character: '(', inConstruct: 'phrasing'},
76+
// A left paren followed by `]` could make something into a link or image.
77+
{
78+
before: '\\]',
79+
character: '(',
80+
inConstruct: 'phrasing',
81+
notInConstruct: fullPhrasingSpans
82+
},
5783
// A right paren could start a list item or break out of a destination
5884
// raw.
5985
{atBreak: true, before: '\\d+', character: ')'},
6086
{character: ')', inConstruct: 'destinationRaw'},
6187
// An asterisk can start thematic breaks, list items, emphasis, strong.
6288
{atBreak: true, character: '*'},
63-
{character: '*', inConstruct: 'phrasing'},
89+
{character: '*', inConstruct: 'phrasing', notInConstruct: fullPhrasingSpans},
6490
// A plus sign could start a list item.
6591
{atBreak: true, character: '+'},
6692
// A dash can start thematic breaks, list items, and setext heading
@@ -75,7 +101,12 @@ export const unsafe = [
75101
// An autolink also starts with a letter.
76102
// Finally, it could break out of a destination literal.
77103
{atBreak: true, character: '<', after: '[!/?A-Za-z]'},
78-
{character: '<', after: '[!/?A-Za-z]', inConstruct: 'phrasing'},
104+
{
105+
character: '<',
106+
after: '[!/?A-Za-z]',
107+
inConstruct: 'phrasing',
108+
notInConstruct: fullPhrasingSpans
109+
},
79110
{character: '<', inConstruct: 'destinationLiteral'},
80111
// An equals to can start setext heading underlines.
81112
{atBreak: true, character: '='},
@@ -86,7 +117,8 @@ export const unsafe = [
86117
// Question mark and at sign are not used in markdown for constructs.
87118
// A left bracket can start definitions, references, labels,
88119
{atBreak: true, character: '['},
89-
{character: '[', inConstruct: ['phrasing', 'label', 'reference']},
120+
{character: '[', inConstruct: 'phrasing', notInConstruct: fullPhrasingSpans},
121+
{character: '[', inConstruct: ['label', 'reference']},
90122
// A backslash can start an escape (when followed by punctuation) or a
91123
// hard break (when followed by an eol).
92124
// Note: typical escapes are handled in `safe`!
@@ -96,18 +128,15 @@ export const unsafe = [
96128
// Caret is not used in markdown for constructs.
97129
// An underscore can start emphasis, strong, or a thematic break.
98130
{atBreak: true, character: '_'},
99-
{character: '_', inConstruct: 'phrasing'},
131+
{character: '_', inConstruct: 'phrasing', notInConstruct: fullPhrasingSpans},
100132
// A grave accent can start code (fenced or text), or it can break out of
101133
// a grave accent code fence.
102134
{atBreak: true, character: '`'},
103135
{
104136
character: '`',
105-
inConstruct: [
106-
'codeFencedLangGraveAccent',
107-
'codeFencedMetaGraveAccent',
108-
'phrasing'
109-
]
137+
inConstruct: ['codeFencedLangGraveAccent', 'codeFencedMetaGraveAccent']
110138
},
139+
{character: '`', inConstruct: 'phrasing', notInConstruct: fullPhrasingSpans},
111140
// Left brace, vertical bar, right brace are not used in markdown for
112141
// constructs.
113142
// A tilde can start code (fenced).

test/index.js

+39-6
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,44 @@ test('code (flow)', (t) => {
790790
'should use an indent if the value is indented'
791791
)
792792

793+
t.equal(
794+
to({type: 'link', url: 'a b![c](d*e_f[g_h`i', children: []}),
795+
'[](<a b![c](d*e_f[g_h`i>)\n',
796+
'should not escape unneeded characters in a `destinationLiteral`'
797+
)
798+
799+
t.equal(
800+
to({type: 'link', url: 'a![b](c*d_e[f_g`h<i</j', children: []}),
801+
'[](a![b]\\(c*d_e[f_g`h<i</j)\n',
802+
'should not escape unneeded characters in a `destinationRaw`'
803+
)
804+
805+
t.equal(
806+
to({
807+
type: 'linkReference',
808+
identifier: 'a![b](c*d_e[f_g`h<i</j',
809+
referenceType: 'full',
810+
children: []
811+
}),
812+
'[][a!\\[b\\](c*d_e\\[f_g`h<i</j]\n',
813+
'should not escape unneeded characters in a `reference`'
814+
)
815+
816+
t.equal(
817+
to({type: 'link', url: '#', title: 'a![b](c*d_e[f_g`h<i</j', children: []}),
818+
'[](# "a![b](c*d_e[f_g`h<i</j")\n',
819+
'should not escape unneeded characters in a `title` (double quotes)'
820+
)
821+
822+
t.equal(
823+
to(
824+
{type: 'link', url: '#', title: 'a![b](c*d_e[f_g`h<i</j', children: []},
825+
{quote: "'"}
826+
),
827+
"[](# 'a![b](c*d_e[f_g`h<i</j')\n",
828+
'should not escape unneeded characters in a `title` (single quotes)'
829+
)
830+
793831
t.end()
794832
})
795833

@@ -3195,12 +3233,7 @@ test('escape', (t) => {
31953233
children: [
31963234
{
31973235
type: 'paragraph',
3198-
children: [
3199-
{
3200-
type: 'text',
3201-
value: 'bar'
3202-
}
3203-
]
3236+
children: [{type: 'text', value: 'bar'}]
32043237
}
32053238
]
32063239
}

0 commit comments

Comments
 (0)