marked 14.1.4 → 15.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/marked.cjs +956 -913
- package/lib/marked.cjs.map +1 -1
- package/lib/marked.d.cts +170 -110
- package/lib/marked.d.ts +170 -110
- package/lib/marked.esm.js +956 -913
- package/lib/marked.esm.js.map +1 -1
- package/lib/marked.umd.js +956 -913
- package/lib/marked.umd.js.map +1 -1
- package/man/marked.1 +1 -1
- package/marked.min.js +2 -2
- package/package.json +5 -7
package/lib/marked.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* marked
|
|
2
|
+
* marked v15.0.1 - a markdown parser
|
|
3
3
|
* Copyright (c) 2011-2024, Christopher Jeffrey. (MIT Licensed)
|
|
4
4
|
* https://github.com/markedjs/marked
|
|
5
5
|
*/
|
|
@@ -31,42 +31,13 @@ function changeDefaults(newDefaults) {
|
|
|
31
31
|
_defaults = newDefaults;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
*/
|
|
37
|
-
const escapeTest = /[&<>"']/;
|
|
38
|
-
const escapeReplace = new RegExp(escapeTest.source, 'g');
|
|
39
|
-
const escapeTestNoEncode = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/;
|
|
40
|
-
const escapeReplaceNoEncode = new RegExp(escapeTestNoEncode.source, 'g');
|
|
41
|
-
const escapeReplacements = {
|
|
42
|
-
'&': '&',
|
|
43
|
-
'<': '<',
|
|
44
|
-
'>': '>',
|
|
45
|
-
'"': '"',
|
|
46
|
-
"'": ''',
|
|
47
|
-
};
|
|
48
|
-
const getEscapeReplacement = (ch) => escapeReplacements[ch];
|
|
49
|
-
function escape$1(html, encode) {
|
|
50
|
-
if (encode) {
|
|
51
|
-
if (escapeTest.test(html)) {
|
|
52
|
-
return html.replace(escapeReplace, getEscapeReplacement);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
if (escapeTestNoEncode.test(html)) {
|
|
57
|
-
return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return html;
|
|
61
|
-
}
|
|
62
|
-
const caret = /(^|[^\[])\^/g;
|
|
63
|
-
function edit(regex, opt) {
|
|
34
|
+
const noopTest = { exec: () => null };
|
|
35
|
+
function edit(regex, opt = '') {
|
|
64
36
|
let source = typeof regex === 'string' ? regex : regex.source;
|
|
65
|
-
opt = opt || '';
|
|
66
37
|
const obj = {
|
|
67
38
|
replace: (name, val) => {
|
|
68
39
|
let valSource = typeof val === 'string' ? val : val.source;
|
|
69
|
-
valSource = valSource.replace(caret, '$1');
|
|
40
|
+
valSource = valSource.replace(other.caret, '$1');
|
|
70
41
|
source = source.replace(name, valSource);
|
|
71
42
|
return obj;
|
|
72
43
|
},
|
|
@@ -76,230 +47,596 @@ function edit(regex, opt) {
|
|
|
76
47
|
};
|
|
77
48
|
return obj;
|
|
78
49
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
50
|
+
const other = {
|
|
51
|
+
codeRemoveIndent: /^(?: {1,4}| {0,3}\t)/gm,
|
|
52
|
+
outputLinkReplace: /\\([\[\]])/g,
|
|
53
|
+
indentCodeCompensation: /^(\s+)(?:```)/,
|
|
54
|
+
beginningSpace: /^\s+/,
|
|
55
|
+
endingHash: /#$/,
|
|
56
|
+
startingSpaceChar: /^ /,
|
|
57
|
+
endingSpaceChar: / $/,
|
|
58
|
+
nonSpaceChar: /[^ ]/,
|
|
59
|
+
newLineCharGlobal: /\n/g,
|
|
60
|
+
tabCharGlobal: /\t/g,
|
|
61
|
+
multipleSpaceGlobal: /\s+/g,
|
|
62
|
+
blankLine: /^[ \t]*$/,
|
|
63
|
+
doubleBlankLine: /\n[ \t]*\n[ \t]*$/,
|
|
64
|
+
blockquoteStart: /^ {0,3}>/,
|
|
65
|
+
blockquoteSetextReplace: /\n {0,3}((?:=+|-+) *)(?=\n|$)/g,
|
|
66
|
+
blockquoteSetextReplace2: /^ {0,3}>[ \t]?/gm,
|
|
67
|
+
listReplaceTabs: /^\t+/,
|
|
68
|
+
listReplaceNesting: /^ {1,4}(?=( {4})*[^ ])/g,
|
|
69
|
+
listIsTask: /^\[[ xX]\] /,
|
|
70
|
+
listReplaceTask: /^\[[ xX]\] +/,
|
|
71
|
+
anyLine: /\n.*\n/,
|
|
72
|
+
hrefBrackets: /^<(.*)>$/,
|
|
73
|
+
tableDelimiter: /[:|]/,
|
|
74
|
+
tableAlignChars: /^\||\| *$/g,
|
|
75
|
+
tableRowBlankLine: /\n[ \t]*$/,
|
|
76
|
+
tableAlignRight: /^ *-+: *$/,
|
|
77
|
+
tableAlignCenter: /^ *:-+: *$/,
|
|
78
|
+
tableAlignLeft: /^ *:-+ *$/,
|
|
79
|
+
startATag: /^<a /i,
|
|
80
|
+
endATag: /^<\/a>/i,
|
|
81
|
+
startPreScriptTag: /^<(pre|code|kbd|script)(\s|>)/i,
|
|
82
|
+
endPreScriptTag: /^<\/(pre|code|kbd|script)(\s|>)/i,
|
|
83
|
+
startAngleBracket: /^</,
|
|
84
|
+
endAngleBracket: />$/,
|
|
85
|
+
pedanticHrefTitle: /^([^'"]*[^\s])\s+(['"])(.*)\2/,
|
|
86
|
+
unicodeAlphaNumeric: /[\p{L}\p{N}]/u,
|
|
87
|
+
escapeTest: /[&<>"']/,
|
|
88
|
+
escapeReplace: /[&<>"']/g,
|
|
89
|
+
escapeTestNoEncode: /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,
|
|
90
|
+
escapeReplaceNoEncode: /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,
|
|
91
|
+
unescapeTest: /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig,
|
|
92
|
+
caret: /(^|[^\[])\^/g,
|
|
93
|
+
percentDecode: /%25/g,
|
|
94
|
+
findPipe: /\|/g,
|
|
95
|
+
splitPipe: / \|/,
|
|
96
|
+
slashPipe: /\\\|/g,
|
|
97
|
+
carriageReturn: /\r\n|\r/g,
|
|
98
|
+
spaceLine: /^ +$/gm,
|
|
99
|
+
notSpaceStart: /^\S*/,
|
|
100
|
+
endingNewline: /\n$/,
|
|
101
|
+
listItemRegex: (bull) => new RegExp(`^( {0,3}${bull})((?:[\t ][^\\n]*)?(?:\\n|$))`),
|
|
102
|
+
nextBulletRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`),
|
|
103
|
+
hrRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),
|
|
104
|
+
fencesBeginRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\`\`\`|~~~)`),
|
|
105
|
+
headingBeginRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`),
|
|
106
|
+
htmlBeginRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}<(?:[a-z].*>|!--)`, 'i'),
|
|
107
|
+
};
|
|
130
108
|
/**
|
|
131
|
-
*
|
|
132
|
-
* /c*$/ is vulnerable to REDOS.
|
|
133
|
-
*
|
|
134
|
-
* @param str
|
|
135
|
-
* @param c
|
|
136
|
-
* @param invert Remove suffix of non-c chars instead. Default falsey.
|
|
109
|
+
* Block-Level Grammar
|
|
137
110
|
*/
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return token;
|
|
198
|
-
}
|
|
199
|
-
return {
|
|
200
|
-
type: 'image',
|
|
201
|
-
raw,
|
|
202
|
-
href,
|
|
203
|
-
title,
|
|
204
|
-
text: escape$1(text),
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
function indentCodeCompensation(raw, text) {
|
|
208
|
-
const matchIndentToCode = raw.match(/^(\s+)(?:```)/);
|
|
209
|
-
if (matchIndentToCode === null) {
|
|
210
|
-
return text;
|
|
211
|
-
}
|
|
212
|
-
const indentToCode = matchIndentToCode[1];
|
|
213
|
-
return text
|
|
214
|
-
.split('\n')
|
|
215
|
-
.map(node => {
|
|
216
|
-
const matchIndentInNode = node.match(/^\s+/);
|
|
217
|
-
if (matchIndentInNode === null) {
|
|
218
|
-
return node;
|
|
219
|
-
}
|
|
220
|
-
const [indentInNode] = matchIndentInNode;
|
|
221
|
-
if (indentInNode.length >= indentToCode.length) {
|
|
222
|
-
return node.slice(indentToCode.length);
|
|
223
|
-
}
|
|
224
|
-
return node;
|
|
225
|
-
})
|
|
226
|
-
.join('\n');
|
|
227
|
-
}
|
|
111
|
+
const newline = /^(?:[ \t]*(?:\n|$))+/;
|
|
112
|
+
const blockCode = /^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/;
|
|
113
|
+
const fences = /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/;
|
|
114
|
+
const hr = /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/;
|
|
115
|
+
const heading = /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/;
|
|
116
|
+
const bullet = /(?:[*+-]|\d{1,9}[.)])/;
|
|
117
|
+
const lheading = edit(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html))+?)\n {0,3}(=+|-+) *(?:\n+|$)/)
|
|
118
|
+
.replace(/bull/g, bullet) // lists can interrupt
|
|
119
|
+
.replace(/blockCode/g, /(?: {4}| {0,3}\t)/) // indented code blocks can interrupt
|
|
120
|
+
.replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/) // fenced code blocks can interrupt
|
|
121
|
+
.replace(/blockquote/g, / {0,3}>/) // blockquote can interrupt
|
|
122
|
+
.replace(/heading/g, / {0,3}#{1,6}/) // ATX heading can interrupt
|
|
123
|
+
.replace(/html/g, / {0,3}<[^\n>]+>\n/) // block html can interrupt
|
|
124
|
+
.getRegex();
|
|
125
|
+
const _paragraph = /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/;
|
|
126
|
+
const blockText = /^[^\n]+/;
|
|
127
|
+
const _blockLabel = /(?!\s*\])(?:\\.|[^\[\]\\])+/;
|
|
128
|
+
const def = edit(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/)
|
|
129
|
+
.replace('label', _blockLabel)
|
|
130
|
+
.replace('title', /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/)
|
|
131
|
+
.getRegex();
|
|
132
|
+
const list = edit(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/)
|
|
133
|
+
.replace(/bull/g, bullet)
|
|
134
|
+
.getRegex();
|
|
135
|
+
const _tag = 'address|article|aside|base|basefont|blockquote|body|caption'
|
|
136
|
+
+ '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
|
|
137
|
+
+ '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
|
|
138
|
+
+ '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
|
|
139
|
+
+ '|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title'
|
|
140
|
+
+ '|tr|track|ul';
|
|
141
|
+
const _comment = /<!--(?:-?>|[\s\S]*?(?:-->|$))/;
|
|
142
|
+
const html = edit('^ {0,3}(?:' // optional indentation
|
|
143
|
+
+ '<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
|
|
144
|
+
+ '|comment[^\\n]*(\\n+|$)' // (2)
|
|
145
|
+
+ '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3)
|
|
146
|
+
+ '|<![A-Z][\\s\\S]*?(?:>\\n*|$)' // (4)
|
|
147
|
+
+ '|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)' // (5)
|
|
148
|
+
+ '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)' // (6)
|
|
149
|
+
+ '|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)' // (7) open tag
|
|
150
|
+
+ '|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)' // (7) closing tag
|
|
151
|
+
+ ')', 'i')
|
|
152
|
+
.replace('comment', _comment)
|
|
153
|
+
.replace('tag', _tag)
|
|
154
|
+
.replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
|
|
155
|
+
.getRegex();
|
|
156
|
+
const paragraph = edit(_paragraph)
|
|
157
|
+
.replace('hr', hr)
|
|
158
|
+
.replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
|
|
159
|
+
.replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs
|
|
160
|
+
.replace('|table', '')
|
|
161
|
+
.replace('blockquote', ' {0,3}>')
|
|
162
|
+
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
|
|
163
|
+
.replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
|
|
164
|
+
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
|
|
165
|
+
.replace('tag', _tag) // pars can be interrupted by type (6) html blocks
|
|
166
|
+
.getRegex();
|
|
167
|
+
const blockquote = edit(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/)
|
|
168
|
+
.replace('paragraph', paragraph)
|
|
169
|
+
.getRegex();
|
|
228
170
|
/**
|
|
229
|
-
*
|
|
171
|
+
* Normal Block Grammar
|
|
230
172
|
*/
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
173
|
+
const blockNormal = {
|
|
174
|
+
blockquote,
|
|
175
|
+
code: blockCode,
|
|
176
|
+
def,
|
|
177
|
+
fences,
|
|
178
|
+
heading,
|
|
179
|
+
hr,
|
|
180
|
+
html,
|
|
181
|
+
lheading,
|
|
182
|
+
list,
|
|
183
|
+
newline,
|
|
184
|
+
paragraph,
|
|
185
|
+
table: noopTest,
|
|
186
|
+
text: blockText,
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* GFM Block Grammar
|
|
190
|
+
*/
|
|
191
|
+
const gfmTable = edit('^ *([^\\n ].*)\\n' // Header
|
|
192
|
+
+ ' {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)' // Align
|
|
193
|
+
+ '(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)') // Cells
|
|
194
|
+
.replace('hr', hr)
|
|
195
|
+
.replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
|
|
196
|
+
.replace('blockquote', ' {0,3}>')
|
|
197
|
+
.replace('code', '(?: {4}| {0,3}\t)[^\\n]')
|
|
198
|
+
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
|
|
199
|
+
.replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
|
|
200
|
+
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
|
|
201
|
+
.replace('tag', _tag) // tables can be interrupted by type (6) html blocks
|
|
202
|
+
.getRegex();
|
|
203
|
+
const blockGfm = {
|
|
204
|
+
...blockNormal,
|
|
205
|
+
table: gfmTable,
|
|
206
|
+
paragraph: edit(_paragraph)
|
|
207
|
+
.replace('hr', hr)
|
|
208
|
+
.replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
|
|
209
|
+
.replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs
|
|
210
|
+
.replace('table', gfmTable) // interrupt paragraphs with table
|
|
211
|
+
.replace('blockquote', ' {0,3}>')
|
|
212
|
+
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
|
|
213
|
+
.replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
|
|
214
|
+
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
|
|
215
|
+
.replace('tag', _tag) // pars can be interrupted by type (6) html blocks
|
|
216
|
+
.getRegex(),
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* Pedantic grammar (original John Gruber's loose markdown specification)
|
|
220
|
+
*/
|
|
221
|
+
const blockPedantic = {
|
|
222
|
+
...blockNormal,
|
|
223
|
+
html: edit('^ *(?:comment *(?:\\n|\\s*$)'
|
|
224
|
+
+ '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
|
|
225
|
+
+ '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
|
|
226
|
+
.replace('comment', _comment)
|
|
227
|
+
.replace(/tag/g, '(?!(?:'
|
|
228
|
+
+ 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
|
|
229
|
+
+ '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
|
|
230
|
+
+ '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
|
|
231
|
+
.getRegex(),
|
|
232
|
+
def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
|
|
233
|
+
heading: /^(#{1,6})(.*)(?:\n+|$)/,
|
|
234
|
+
fences: noopTest, // fences not supported
|
|
235
|
+
lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,
|
|
236
|
+
paragraph: edit(_paragraph)
|
|
237
|
+
.replace('hr', hr)
|
|
238
|
+
.replace('heading', ' *#{1,6} *[^\n]')
|
|
239
|
+
.replace('lheading', lheading)
|
|
240
|
+
.replace('|table', '')
|
|
241
|
+
.replace('blockquote', ' {0,3}>')
|
|
242
|
+
.replace('|fences', '')
|
|
243
|
+
.replace('|list', '')
|
|
244
|
+
.replace('|html', '')
|
|
245
|
+
.replace('|tag', '')
|
|
246
|
+
.getRegex(),
|
|
247
|
+
};
|
|
248
|
+
/**
|
|
249
|
+
* Inline-Level Grammar
|
|
250
|
+
*/
|
|
251
|
+
const escape$1 = /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/;
|
|
252
|
+
const inlineCode = /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/;
|
|
253
|
+
const br = /^( {2,}|\\)\n(?!\s*$)/;
|
|
254
|
+
const inlineText = /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/;
|
|
255
|
+
// list of unicode punctuation marks, plus any missing characters from CommonMark spec
|
|
256
|
+
const _punctuation = '\\p{P}\\p{S}';
|
|
257
|
+
const punctuation = edit(/^((?![*_])[\spunctuation])/, 'u')
|
|
258
|
+
.replace(/punctuation/g, _punctuation).getRegex();
|
|
259
|
+
// sequences em should skip over [title](link), `code`, <html>
|
|
260
|
+
const blockSkip = /\[[^[\]]*?\]\((?:\\.|[^\\\(\)]|\((?:\\.|[^\\\(\)])*\))*\)|`[^`]*?`|<[^<>]*?>/g;
|
|
261
|
+
const emStrongLDelim = edit(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/, 'u')
|
|
262
|
+
.replace(/punct/g, _punctuation)
|
|
263
|
+
.getRegex();
|
|
264
|
+
const emStrongRDelimAst = edit('^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)' // Skip orphan inside strong
|
|
265
|
+
+ '|[^*]+(?=[^*])' // Consume to delim
|
|
266
|
+
+ '|(?!\\*)[punct](\\*+)(?=[\\s]|$)' // (1) #*** can only be a Right Delimiter
|
|
267
|
+
+ '|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)' // (2) a***#, a*** can only be a Right Delimiter
|
|
268
|
+
+ '|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])' // (3) #***a, ***a can only be Left Delimiter
|
|
269
|
+
+ '|[\\s](\\*+)(?!\\*)(?=[punct])' // (4) ***# can only be Left Delimiter
|
|
270
|
+
+ '|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])' // (5) #***# can be either Left or Right Delimiter
|
|
271
|
+
+ '|[^punct\\s](\\*+)(?=[^punct\\s])', 'gu') // (6) a***a can be either Left or Right Delimiter
|
|
272
|
+
.replace(/punct/g, _punctuation)
|
|
273
|
+
.getRegex();
|
|
274
|
+
// (6) Not allowed for _
|
|
275
|
+
const emStrongRDelimUnd = edit('^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)' // Skip orphan inside strong
|
|
276
|
+
+ '|[^_]+(?=[^_])' // Consume to delim
|
|
277
|
+
+ '|(?!_)[punct](_+)(?=[\\s]|$)' // (1) #___ can only be a Right Delimiter
|
|
278
|
+
+ '|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)' // (2) a___#, a___ can only be a Right Delimiter
|
|
279
|
+
+ '|(?!_)[punct\\s](_+)(?=[^punct\\s])' // (3) #___a, ___a can only be Left Delimiter
|
|
280
|
+
+ '|[\\s](_+)(?!_)(?=[punct])' // (4) ___# can only be Left Delimiter
|
|
281
|
+
+ '|(?!_)[punct](_+)(?!_)(?=[punct])', 'gu') // (5) #___# can be either Left or Right Delimiter
|
|
282
|
+
.replace(/punct/g, _punctuation)
|
|
283
|
+
.getRegex();
|
|
284
|
+
const anyPunctuation = edit(/\\([punct])/, 'gu')
|
|
285
|
+
.replace(/punct/g, _punctuation)
|
|
286
|
+
.getRegex();
|
|
287
|
+
const autolink = edit(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/)
|
|
288
|
+
.replace('scheme', /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/)
|
|
289
|
+
.replace('email', /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/)
|
|
290
|
+
.getRegex();
|
|
291
|
+
const _inlineComment = edit(_comment).replace('(?:-->|$)', '-->').getRegex();
|
|
292
|
+
const tag = edit('^comment'
|
|
293
|
+
+ '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
|
|
294
|
+
+ '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
|
|
295
|
+
+ '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
|
|
296
|
+
+ '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
|
|
297
|
+
+ '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>') // CDATA section
|
|
298
|
+
.replace('comment', _inlineComment)
|
|
299
|
+
.replace('attribute', /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/)
|
|
300
|
+
.getRegex();
|
|
301
|
+
const _inlineLabel = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
|
|
302
|
+
const link = edit(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/)
|
|
303
|
+
.replace('label', _inlineLabel)
|
|
304
|
+
.replace('href', /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/)
|
|
305
|
+
.replace('title', /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/)
|
|
306
|
+
.getRegex();
|
|
307
|
+
const reflink = edit(/^!?\[(label)\]\[(ref)\]/)
|
|
308
|
+
.replace('label', _inlineLabel)
|
|
309
|
+
.replace('ref', _blockLabel)
|
|
310
|
+
.getRegex();
|
|
311
|
+
const nolink = edit(/^!?\[(ref)\](?:\[\])?/)
|
|
312
|
+
.replace('ref', _blockLabel)
|
|
313
|
+
.getRegex();
|
|
314
|
+
const reflinkSearch = edit('reflink|nolink(?!\\()', 'g')
|
|
315
|
+
.replace('reflink', reflink)
|
|
316
|
+
.replace('nolink', nolink)
|
|
317
|
+
.getRegex();
|
|
318
|
+
/**
|
|
319
|
+
* Normal Inline Grammar
|
|
320
|
+
*/
|
|
321
|
+
const inlineNormal = {
|
|
322
|
+
_backpedal: noopTest, // only used for GFM url
|
|
323
|
+
anyPunctuation,
|
|
324
|
+
autolink,
|
|
325
|
+
blockSkip,
|
|
326
|
+
br,
|
|
327
|
+
code: inlineCode,
|
|
328
|
+
del: noopTest,
|
|
329
|
+
emStrongLDelim,
|
|
330
|
+
emStrongRDelimAst,
|
|
331
|
+
emStrongRDelimUnd,
|
|
332
|
+
escape: escape$1,
|
|
333
|
+
link,
|
|
334
|
+
nolink,
|
|
335
|
+
punctuation,
|
|
336
|
+
reflink,
|
|
337
|
+
reflinkSearch,
|
|
338
|
+
tag,
|
|
339
|
+
text: inlineText,
|
|
340
|
+
url: noopTest,
|
|
341
|
+
};
|
|
342
|
+
/**
|
|
343
|
+
* Pedantic Inline Grammar
|
|
344
|
+
*/
|
|
345
|
+
const inlinePedantic = {
|
|
346
|
+
...inlineNormal,
|
|
347
|
+
link: edit(/^!?\[(label)\]\((.*?)\)/)
|
|
348
|
+
.replace('label', _inlineLabel)
|
|
349
|
+
.getRegex(),
|
|
350
|
+
reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
|
|
351
|
+
.replace('label', _inlineLabel)
|
|
352
|
+
.getRegex(),
|
|
353
|
+
};
|
|
354
|
+
/**
|
|
355
|
+
* GFM Inline Grammar
|
|
356
|
+
*/
|
|
357
|
+
const inlineGfm = {
|
|
358
|
+
...inlineNormal,
|
|
359
|
+
escape: edit(escape$1).replace('])', '~|])').getRegex(),
|
|
360
|
+
url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, 'i')
|
|
361
|
+
.replace('email', /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/)
|
|
362
|
+
.getRegex(),
|
|
363
|
+
_backpedal: /(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,
|
|
364
|
+
del: /^(~~?)(?=[^\s~])((?:\\.|[^\\])*?(?:\\.|[^\s~\\]))\1(?=[^~]|$)/,
|
|
365
|
+
text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/,
|
|
366
|
+
};
|
|
367
|
+
/**
|
|
368
|
+
* GFM + Line Breaks Inline Grammar
|
|
369
|
+
*/
|
|
370
|
+
const inlineBreaks = {
|
|
371
|
+
...inlineGfm,
|
|
372
|
+
br: edit(br).replace('{2,}', '*').getRegex(),
|
|
373
|
+
text: edit(inlineGfm.text)
|
|
374
|
+
.replace('\\b_', '\\b_| {2,}\\n')
|
|
375
|
+
.replace(/\{2,\}/g, '*')
|
|
376
|
+
.getRegex(),
|
|
377
|
+
};
|
|
378
|
+
/**
|
|
379
|
+
* exports
|
|
380
|
+
*/
|
|
381
|
+
const block = {
|
|
382
|
+
normal: blockNormal,
|
|
383
|
+
gfm: blockGfm,
|
|
384
|
+
pedantic: blockPedantic,
|
|
385
|
+
};
|
|
386
|
+
const inline = {
|
|
387
|
+
normal: inlineNormal,
|
|
388
|
+
gfm: inlineGfm,
|
|
389
|
+
breaks: inlineBreaks,
|
|
390
|
+
pedantic: inlinePedantic,
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Helpers
|
|
395
|
+
*/
|
|
396
|
+
const escapeReplacements = {
|
|
397
|
+
'&': '&',
|
|
398
|
+
'<': '<',
|
|
399
|
+
'>': '>',
|
|
400
|
+
'"': '"',
|
|
401
|
+
"'": ''',
|
|
402
|
+
};
|
|
403
|
+
const getEscapeReplacement = (ch) => escapeReplacements[ch];
|
|
404
|
+
function escape(html, encode) {
|
|
405
|
+
if (encode) {
|
|
406
|
+
if (other.escapeTest.test(html)) {
|
|
407
|
+
return html.replace(other.escapeReplace, getEscapeReplacement);
|
|
272
408
|
}
|
|
273
409
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
let text = cap[2].trim();
|
|
278
|
-
// remove trailing #s
|
|
279
|
-
if (/#$/.test(text)) {
|
|
280
|
-
const trimmed = rtrim(text, '#');
|
|
281
|
-
if (this.options.pedantic) {
|
|
282
|
-
text = trimmed.trim();
|
|
283
|
-
}
|
|
284
|
-
else if (!trimmed || / $/.test(trimmed)) {
|
|
285
|
-
// CommonMark requires space before trailing #s
|
|
286
|
-
text = trimmed.trim();
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return {
|
|
290
|
-
type: 'heading',
|
|
291
|
-
raw: cap[0],
|
|
292
|
-
depth: cap[1].length,
|
|
293
|
-
text,
|
|
294
|
-
tokens: this.lexer.inline(text),
|
|
295
|
-
};
|
|
410
|
+
else {
|
|
411
|
+
if (other.escapeTestNoEncode.test(html)) {
|
|
412
|
+
return html.replace(other.escapeReplaceNoEncode, getEscapeReplacement);
|
|
296
413
|
}
|
|
297
414
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
415
|
+
return html;
|
|
416
|
+
}
|
|
417
|
+
function cleanUrl(href) {
|
|
418
|
+
try {
|
|
419
|
+
href = encodeURI(href).replace(other.percentDecode, '%');
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
return href;
|
|
425
|
+
}
|
|
426
|
+
function splitCells(tableRow, count) {
|
|
427
|
+
// ensure that every cell-delimiting pipe has a space
|
|
428
|
+
// before it to distinguish it from an escaped pipe
|
|
429
|
+
const row = tableRow.replace(other.findPipe, (match, offset, str) => {
|
|
430
|
+
let escaped = false;
|
|
431
|
+
let curr = offset;
|
|
432
|
+
while (--curr >= 0 && str[curr] === '\\')
|
|
433
|
+
escaped = !escaped;
|
|
434
|
+
if (escaped) {
|
|
435
|
+
// odd number of slashes means | is escaped
|
|
436
|
+
// so we leave it alone
|
|
437
|
+
return '|';
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
// add space before unescaped |
|
|
441
|
+
return ' |';
|
|
442
|
+
}
|
|
443
|
+
}), cells = row.split(other.splitPipe);
|
|
444
|
+
let i = 0;
|
|
445
|
+
// First/last cell in a row cannot be empty if it has no leading/trailing pipe
|
|
446
|
+
if (!cells[0].trim()) {
|
|
447
|
+
cells.shift();
|
|
448
|
+
}
|
|
449
|
+
if (cells.length > 0 && !cells.at(-1)?.trim()) {
|
|
450
|
+
cells.pop();
|
|
451
|
+
}
|
|
452
|
+
if (count) {
|
|
453
|
+
if (cells.length > count) {
|
|
454
|
+
cells.splice(count);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
while (cells.length < count)
|
|
458
|
+
cells.push('');
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
for (; i < cells.length; i++) {
|
|
462
|
+
// leading or trailing whitespace is ignored per the gfm spec
|
|
463
|
+
cells[i] = cells[i].trim().replace(other.slashPipe, '|');
|
|
464
|
+
}
|
|
465
|
+
return cells;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
|
|
469
|
+
* /c*$/ is vulnerable to REDOS.
|
|
470
|
+
*
|
|
471
|
+
* @param str
|
|
472
|
+
* @param c
|
|
473
|
+
* @param invert Remove suffix of non-c chars instead. Default falsey.
|
|
474
|
+
*/
|
|
475
|
+
function rtrim(str, c, invert) {
|
|
476
|
+
const l = str.length;
|
|
477
|
+
if (l === 0) {
|
|
478
|
+
return '';
|
|
479
|
+
}
|
|
480
|
+
// Length of suffix matching the invert condition.
|
|
481
|
+
let suffLen = 0;
|
|
482
|
+
// Step left until we fail to match the invert condition.
|
|
483
|
+
while (suffLen < l) {
|
|
484
|
+
const currChar = str.charAt(l - suffLen - 1);
|
|
485
|
+
if (currChar === c && !invert) {
|
|
486
|
+
suffLen++;
|
|
487
|
+
}
|
|
488
|
+
else if (currChar !== c && invert) {
|
|
489
|
+
suffLen++;
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return str.slice(0, l - suffLen);
|
|
496
|
+
}
|
|
497
|
+
function findClosingBracket(str, b) {
|
|
498
|
+
if (str.indexOf(b[1]) === -1) {
|
|
499
|
+
return -1;
|
|
500
|
+
}
|
|
501
|
+
let level = 0;
|
|
502
|
+
for (let i = 0; i < str.length; i++) {
|
|
503
|
+
if (str[i] === '\\') {
|
|
504
|
+
i++;
|
|
505
|
+
}
|
|
506
|
+
else if (str[i] === b[0]) {
|
|
507
|
+
level++;
|
|
508
|
+
}
|
|
509
|
+
else if (str[i] === b[1]) {
|
|
510
|
+
level--;
|
|
511
|
+
if (level < 0) {
|
|
512
|
+
return i;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return -1;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function outputLink(cap, link, raw, lexer, rules) {
|
|
520
|
+
const href = link.href;
|
|
521
|
+
const title = link.title || null;
|
|
522
|
+
const text = cap[1].replace(rules.other.outputLinkReplace, '$1');
|
|
523
|
+
if (cap[0].charAt(0) !== '!') {
|
|
524
|
+
lexer.state.inLink = true;
|
|
525
|
+
const token = {
|
|
526
|
+
type: 'link',
|
|
527
|
+
raw,
|
|
528
|
+
href,
|
|
529
|
+
title,
|
|
530
|
+
text,
|
|
531
|
+
tokens: lexer.inlineTokens(text),
|
|
532
|
+
};
|
|
533
|
+
lexer.state.inLink = false;
|
|
534
|
+
return token;
|
|
535
|
+
}
|
|
536
|
+
return {
|
|
537
|
+
type: 'image',
|
|
538
|
+
raw,
|
|
539
|
+
href,
|
|
540
|
+
title,
|
|
541
|
+
text,
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
function indentCodeCompensation(raw, text, rules) {
|
|
545
|
+
const matchIndentToCode = raw.match(rules.other.indentCodeCompensation);
|
|
546
|
+
if (matchIndentToCode === null) {
|
|
547
|
+
return text;
|
|
548
|
+
}
|
|
549
|
+
const indentToCode = matchIndentToCode[1];
|
|
550
|
+
return text
|
|
551
|
+
.split('\n')
|
|
552
|
+
.map(node => {
|
|
553
|
+
const matchIndentInNode = node.match(rules.other.beginningSpace);
|
|
554
|
+
if (matchIndentInNode === null) {
|
|
555
|
+
return node;
|
|
556
|
+
}
|
|
557
|
+
const [indentInNode] = matchIndentInNode;
|
|
558
|
+
if (indentInNode.length >= indentToCode.length) {
|
|
559
|
+
return node.slice(indentToCode.length);
|
|
560
|
+
}
|
|
561
|
+
return node;
|
|
562
|
+
})
|
|
563
|
+
.join('\n');
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Tokenizer
|
|
567
|
+
*/
|
|
568
|
+
class _Tokenizer {
|
|
569
|
+
options;
|
|
570
|
+
rules; // set by the lexer
|
|
571
|
+
lexer; // set by the lexer
|
|
572
|
+
constructor(options) {
|
|
573
|
+
this.options = options || _defaults;
|
|
574
|
+
}
|
|
575
|
+
space(src) {
|
|
576
|
+
const cap = this.rules.block.newline.exec(src);
|
|
577
|
+
if (cap && cap[0].length > 0) {
|
|
578
|
+
return {
|
|
579
|
+
type: 'space',
|
|
580
|
+
raw: cap[0],
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
code(src) {
|
|
585
|
+
const cap = this.rules.block.code.exec(src);
|
|
586
|
+
if (cap) {
|
|
587
|
+
const text = cap[0].replace(this.rules.other.codeRemoveIndent, '');
|
|
588
|
+
return {
|
|
589
|
+
type: 'code',
|
|
590
|
+
raw: cap[0],
|
|
591
|
+
codeBlockStyle: 'indented',
|
|
592
|
+
text: !this.options.pedantic
|
|
593
|
+
? rtrim(text, '\n')
|
|
594
|
+
: text,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
fences(src) {
|
|
599
|
+
const cap = this.rules.block.fences.exec(src);
|
|
600
|
+
if (cap) {
|
|
601
|
+
const raw = cap[0];
|
|
602
|
+
const text = indentCodeCompensation(raw, cap[3] || '', this.rules);
|
|
603
|
+
return {
|
|
604
|
+
type: 'code',
|
|
605
|
+
raw,
|
|
606
|
+
lang: cap[2] ? cap[2].trim().replace(this.rules.inline.anyPunctuation, '$1') : cap[2],
|
|
607
|
+
text,
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
heading(src) {
|
|
612
|
+
const cap = this.rules.block.heading.exec(src);
|
|
613
|
+
if (cap) {
|
|
614
|
+
let text = cap[2].trim();
|
|
615
|
+
// remove trailing #s
|
|
616
|
+
if (this.rules.other.endingHash.test(text)) {
|
|
617
|
+
const trimmed = rtrim(text, '#');
|
|
618
|
+
if (this.options.pedantic) {
|
|
619
|
+
text = trimmed.trim();
|
|
620
|
+
}
|
|
621
|
+
else if (!trimmed || this.rules.other.endingSpaceChar.test(trimmed)) {
|
|
622
|
+
// CommonMark requires space before trailing #s
|
|
623
|
+
text = trimmed.trim();
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
return {
|
|
627
|
+
type: 'heading',
|
|
628
|
+
raw: cap[0],
|
|
629
|
+
depth: cap[1].length,
|
|
630
|
+
text,
|
|
631
|
+
tokens: this.lexer.inline(text),
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
hr(src) {
|
|
636
|
+
const cap = this.rules.block.hr.exec(src);
|
|
637
|
+
if (cap) {
|
|
638
|
+
return {
|
|
639
|
+
type: 'hr',
|
|
303
640
|
raw: rtrim(cap[0], '\n'),
|
|
304
641
|
};
|
|
305
642
|
}
|
|
@@ -317,7 +654,7 @@ class _Tokenizer {
|
|
|
317
654
|
let i;
|
|
318
655
|
for (i = 0; i < lines.length; i++) {
|
|
319
656
|
// get lines up to a continuation
|
|
320
|
-
if (
|
|
657
|
+
if (this.rules.other.blockquoteStart.test(lines[i])) {
|
|
321
658
|
currentLines.push(lines[i]);
|
|
322
659
|
inBlockquote = true;
|
|
323
660
|
}
|
|
@@ -332,8 +669,8 @@ class _Tokenizer {
|
|
|
332
669
|
const currentRaw = currentLines.join('\n');
|
|
333
670
|
const currentText = currentRaw
|
|
334
671
|
// precede setext continuation with 4 spaces so it isn't a setext
|
|
335
|
-
.replace(
|
|
336
|
-
.replace(
|
|
672
|
+
.replace(this.rules.other.blockquoteSetextReplace, '\n $1')
|
|
673
|
+
.replace(this.rules.other.blockquoteSetextReplace2, '');
|
|
337
674
|
raw = raw ? `${raw}\n${currentRaw}` : currentRaw;
|
|
338
675
|
text = text ? `${text}\n${currentText}` : currentText;
|
|
339
676
|
// parse blockquote lines as top level tokens
|
|
@@ -346,7 +683,7 @@ class _Tokenizer {
|
|
|
346
683
|
if (lines.length === 0) {
|
|
347
684
|
break;
|
|
348
685
|
}
|
|
349
|
-
const lastToken = tokens
|
|
686
|
+
const lastToken = tokens.at(-1);
|
|
350
687
|
if (lastToken?.type === 'code') {
|
|
351
688
|
// blockquote continuation cannot be preceded by a code block
|
|
352
689
|
break;
|
|
@@ -369,7 +706,7 @@ class _Tokenizer {
|
|
|
369
706
|
tokens[tokens.length - 1] = newToken;
|
|
370
707
|
raw = raw.substring(0, raw.length - lastToken.raw.length) + newToken.raw;
|
|
371
708
|
text = text.substring(0, text.length - oldToken.raw.length) + newToken.raw;
|
|
372
|
-
lines = newText.substring(tokens
|
|
709
|
+
lines = newText.substring(tokens.at(-1).raw.length).split('\n');
|
|
373
710
|
continue;
|
|
374
711
|
}
|
|
375
712
|
}
|
|
@@ -399,7 +736,7 @@ class _Tokenizer {
|
|
|
399
736
|
bull = isordered ? bull : '[*+-]';
|
|
400
737
|
}
|
|
401
738
|
// Get next list item
|
|
402
|
-
const itemRegex =
|
|
739
|
+
const itemRegex = this.rules.other.listItemRegex(bull);
|
|
403
740
|
let endsWithBlankLine = false;
|
|
404
741
|
// Check if current bullet point can start a new List Item
|
|
405
742
|
while (src) {
|
|
@@ -414,7 +751,7 @@ class _Tokenizer {
|
|
|
414
751
|
}
|
|
415
752
|
raw = cap[0];
|
|
416
753
|
src = src.substring(raw.length);
|
|
417
|
-
let line = cap[2].split('\n', 1)[0].replace(
|
|
754
|
+
let line = cap[2].split('\n', 1)[0].replace(this.rules.other.listReplaceTabs, (t) => ' '.repeat(3 * t.length));
|
|
418
755
|
let nextLine = src.split('\n', 1)[0];
|
|
419
756
|
let blankLine = !line.trim();
|
|
420
757
|
let indent = 0;
|
|
@@ -426,22 +763,22 @@ class _Tokenizer {
|
|
|
426
763
|
indent = cap[1].length + 1;
|
|
427
764
|
}
|
|
428
765
|
else {
|
|
429
|
-
indent = cap[2].search(
|
|
766
|
+
indent = cap[2].search(this.rules.other.nonSpaceChar); // Find first non-space char
|
|
430
767
|
indent = indent > 4 ? 1 : indent; // Treat indented code blocks (> 4 spaces) as having only 1 indent
|
|
431
768
|
itemContents = line.slice(indent);
|
|
432
769
|
indent += cap[1].length;
|
|
433
770
|
}
|
|
434
|
-
if (blankLine &&
|
|
771
|
+
if (blankLine && this.rules.other.blankLine.test(nextLine)) { // Items begin with at most one blank line
|
|
435
772
|
raw += nextLine + '\n';
|
|
436
773
|
src = src.substring(nextLine.length + 1);
|
|
437
774
|
endEarly = true;
|
|
438
775
|
}
|
|
439
776
|
if (!endEarly) {
|
|
440
|
-
const nextBulletRegex =
|
|
441
|
-
const hrRegex =
|
|
442
|
-
const fencesBeginRegex =
|
|
443
|
-
const headingBeginRegex =
|
|
444
|
-
const htmlBeginRegex =
|
|
777
|
+
const nextBulletRegex = this.rules.other.nextBulletRegex(indent);
|
|
778
|
+
const hrRegex = this.rules.other.hrRegex(indent);
|
|
779
|
+
const fencesBeginRegex = this.rules.other.fencesBeginRegex(indent);
|
|
780
|
+
const headingBeginRegex = this.rules.other.headingBeginRegex(indent);
|
|
781
|
+
const htmlBeginRegex = this.rules.other.htmlBeginRegex(indent);
|
|
445
782
|
// Check if following lines should be included in List Item
|
|
446
783
|
while (src) {
|
|
447
784
|
const rawLine = src.split('\n', 1)[0];
|
|
@@ -449,11 +786,11 @@ class _Tokenizer {
|
|
|
449
786
|
nextLine = rawLine;
|
|
450
787
|
// Re-align to follow commonmark nesting rules
|
|
451
788
|
if (this.options.pedantic) {
|
|
452
|
-
nextLine = nextLine.replace(
|
|
789
|
+
nextLine = nextLine.replace(this.rules.other.listReplaceNesting, ' ');
|
|
453
790
|
nextLineWithoutTabs = nextLine;
|
|
454
791
|
}
|
|
455
792
|
else {
|
|
456
|
-
nextLineWithoutTabs = nextLine.replace(
|
|
793
|
+
nextLineWithoutTabs = nextLine.replace(this.rules.other.tabCharGlobal, ' ');
|
|
457
794
|
}
|
|
458
795
|
// End list item if found code fences
|
|
459
796
|
if (fencesBeginRegex.test(nextLine)) {
|
|
@@ -475,7 +812,7 @@ class _Tokenizer {
|
|
|
475
812
|
if (hrRegex.test(nextLine)) {
|
|
476
813
|
break;
|
|
477
814
|
}
|
|
478
|
-
if (nextLineWithoutTabs.search(
|
|
815
|
+
if (nextLineWithoutTabs.search(this.rules.other.nonSpaceChar) >= indent || !nextLine.trim()) { // Dedent if possible
|
|
479
816
|
itemContents += '\n' + nextLineWithoutTabs.slice(indent);
|
|
480
817
|
}
|
|
481
818
|
else {
|
|
@@ -484,7 +821,7 @@ class _Tokenizer {
|
|
|
484
821
|
break;
|
|
485
822
|
}
|
|
486
823
|
// paragraph continuation unless last line was a different block level element
|
|
487
|
-
if (line.replace(
|
|
824
|
+
if (line.replace(this.rules.other.tabCharGlobal, ' ').search(this.rules.other.nonSpaceChar) >= 4) { // indented code block
|
|
488
825
|
break;
|
|
489
826
|
}
|
|
490
827
|
if (fencesBeginRegex.test(line)) {
|
|
@@ -511,7 +848,7 @@ class _Tokenizer {
|
|
|
511
848
|
if (endsWithBlankLine) {
|
|
512
849
|
list.loose = true;
|
|
513
850
|
}
|
|
514
|
-
else if (
|
|
851
|
+
else if (this.rules.other.doubleBlankLine.test(raw)) {
|
|
515
852
|
endsWithBlankLine = true;
|
|
516
853
|
}
|
|
517
854
|
}
|
|
@@ -519,10 +856,10 @@ class _Tokenizer {
|
|
|
519
856
|
let ischecked;
|
|
520
857
|
// Check for task list items
|
|
521
858
|
if (this.options.gfm) {
|
|
522
|
-
istask =
|
|
859
|
+
istask = this.rules.other.listIsTask.exec(itemContents);
|
|
523
860
|
if (istask) {
|
|
524
861
|
ischecked = istask[0] !== '[ ] ';
|
|
525
|
-
itemContents = itemContents.replace(
|
|
862
|
+
itemContents = itemContents.replace(this.rules.other.listReplaceTask, '');
|
|
526
863
|
}
|
|
527
864
|
}
|
|
528
865
|
list.items.push({
|
|
@@ -537,8 +874,11 @@ class _Tokenizer {
|
|
|
537
874
|
list.raw += raw;
|
|
538
875
|
}
|
|
539
876
|
// Do not consume newlines at end of final item. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic
|
|
540
|
-
|
|
541
|
-
|
|
877
|
+
const lastItem = list.items.at(-1);
|
|
878
|
+
if (lastItem) {
|
|
879
|
+
lastItem.raw = lastItem.raw.trimEnd();
|
|
880
|
+
lastItem.text = lastItem.text.trimEnd();
|
|
881
|
+
}
|
|
542
882
|
list.raw = list.raw.trimEnd();
|
|
543
883
|
// Item child tokens handled here at end because we needed to have the final item to trim it first
|
|
544
884
|
for (let i = 0; i < list.items.length; i++) {
|
|
@@ -547,7 +887,7 @@ class _Tokenizer {
|
|
|
547
887
|
if (!list.loose) {
|
|
548
888
|
// Check if list should be loose
|
|
549
889
|
const spacers = list.items[i].tokens.filter(t => t.type === 'space');
|
|
550
|
-
const hasMultipleLineBreaks = spacers.length > 0 && spacers.some(t =>
|
|
890
|
+
const hasMultipleLineBreaks = spacers.length > 0 && spacers.some(t => this.rules.other.anyLine.test(t.raw));
|
|
551
891
|
list.loose = hasMultipleLineBreaks;
|
|
552
892
|
}
|
|
553
893
|
}
|
|
@@ -576,8 +916,8 @@ class _Tokenizer {
|
|
|
576
916
|
def(src) {
|
|
577
917
|
const cap = this.rules.block.def.exec(src);
|
|
578
918
|
if (cap) {
|
|
579
|
-
const tag = cap[1].toLowerCase().replace(
|
|
580
|
-
const href = cap[2] ? cap[2].replace(
|
|
919
|
+
const tag = cap[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal, ' ');
|
|
920
|
+
const href = cap[2] ? cap[2].replace(this.rules.other.hrefBrackets, '$1').replace(this.rules.inline.anyPunctuation, '$1') : '';
|
|
581
921
|
const title = cap[3] ? cap[3].substring(1, cap[3].length - 1).replace(this.rules.inline.anyPunctuation, '$1') : cap[3];
|
|
582
922
|
return {
|
|
583
923
|
type: 'def',
|
|
@@ -593,13 +933,13 @@ class _Tokenizer {
|
|
|
593
933
|
if (!cap) {
|
|
594
934
|
return;
|
|
595
935
|
}
|
|
596
|
-
if (
|
|
936
|
+
if (!this.rules.other.tableDelimiter.test(cap[2])) {
|
|
597
937
|
// delimiter row must have a pipe (|) or colon (:) otherwise it is a setext heading
|
|
598
938
|
return;
|
|
599
939
|
}
|
|
600
940
|
const headers = splitCells(cap[1]);
|
|
601
|
-
const aligns = cap[2].replace(
|
|
602
|
-
const rows = cap[3]
|
|
941
|
+
const aligns = cap[2].replace(this.rules.other.tableAlignChars, '').split('|');
|
|
942
|
+
const rows = cap[3]?.trim() ? cap[3].replace(this.rules.other.tableRowBlankLine, '').split('\n') : [];
|
|
603
943
|
const item = {
|
|
604
944
|
type: 'table',
|
|
605
945
|
raw: cap[0],
|
|
@@ -612,13 +952,13 @@ class _Tokenizer {
|
|
|
612
952
|
return;
|
|
613
953
|
}
|
|
614
954
|
for (const align of aligns) {
|
|
615
|
-
if (
|
|
955
|
+
if (this.rules.other.tableAlignRight.test(align)) {
|
|
616
956
|
item.align.push('right');
|
|
617
957
|
}
|
|
618
|
-
else if (
|
|
958
|
+
else if (this.rules.other.tableAlignCenter.test(align)) {
|
|
619
959
|
item.align.push('center');
|
|
620
960
|
}
|
|
621
|
-
else if (
|
|
961
|
+
else if (this.rules.other.tableAlignLeft.test(align)) {
|
|
622
962
|
item.align.push('left');
|
|
623
963
|
}
|
|
624
964
|
else {
|
|
@@ -688,572 +1028,281 @@ class _Tokenizer {
|
|
|
688
1028
|
return {
|
|
689
1029
|
type: 'escape',
|
|
690
1030
|
raw: cap[0],
|
|
691
|
-
text:
|
|
1031
|
+
text: cap[1],
|
|
692
1032
|
};
|
|
693
1033
|
}
|
|
694
1034
|
}
|
|
695
1035
|
tag(src) {
|
|
696
1036
|
const cap = this.rules.inline.tag.exec(src);
|
|
697
1037
|
if (cap) {
|
|
698
|
-
if (!this.lexer.state.inLink &&
|
|
1038
|
+
if (!this.lexer.state.inLink && this.rules.other.startATag.test(cap[0])) {
|
|
699
1039
|
this.lexer.state.inLink = true;
|
|
700
1040
|
}
|
|
701
|
-
else if (this.lexer.state.inLink &&
|
|
1041
|
+
else if (this.lexer.state.inLink && this.rules.other.endATag.test(cap[0])) {
|
|
702
1042
|
this.lexer.state.inLink = false;
|
|
703
1043
|
}
|
|
704
|
-
if (!this.lexer.state.inRawBlock &&
|
|
1044
|
+
if (!this.lexer.state.inRawBlock && this.rules.other.startPreScriptTag.test(cap[0])) {
|
|
705
1045
|
this.lexer.state.inRawBlock = true;
|
|
706
1046
|
}
|
|
707
|
-
else if (this.lexer.state.inRawBlock &&
|
|
708
|
-
this.lexer.state.inRawBlock = false;
|
|
709
|
-
}
|
|
710
|
-
return {
|
|
711
|
-
type: 'html',
|
|
712
|
-
raw: cap[0],
|
|
713
|
-
inLink: this.lexer.state.inLink,
|
|
714
|
-
inRawBlock: this.lexer.state.inRawBlock,
|
|
715
|
-
block: false,
|
|
716
|
-
text: cap[0],
|
|
717
|
-
};
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
link(src) {
|
|
721
|
-
const cap = this.rules.inline.link.exec(src);
|
|
722
|
-
if (cap) {
|
|
723
|
-
const trimmedUrl = cap[2].trim();
|
|
724
|
-
if (!this.options.pedantic &&
|
|
725
|
-
// commonmark requires matching angle brackets
|
|
726
|
-
if (!(
|
|
727
|
-
return;
|
|
728
|
-
}
|
|
729
|
-
// ending angle bracket cannot be escaped
|
|
730
|
-
const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\');
|
|
731
|
-
if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
else {
|
|
736
|
-
// find closing parenthesis
|
|
737
|
-
const lastParenIndex = findClosingBracket(cap[2], '()');
|
|
738
|
-
if (lastParenIndex > -1) {
|
|
739
|
-
const start = cap[0].indexOf('!') === 0 ? 5 : 4;
|
|
740
|
-
const linkLen = start + cap[1].length + lastParenIndex;
|
|
741
|
-
cap[2] = cap[2].substring(0, lastParenIndex);
|
|
742
|
-
cap[0] = cap[0].substring(0, linkLen).trim();
|
|
743
|
-
cap[3] = '';
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
let href = cap[2];
|
|
747
|
-
let title = '';
|
|
748
|
-
if (this.options.pedantic) {
|
|
749
|
-
// split pedantic href and title
|
|
750
|
-
const link =
|
|
751
|
-
if (link) {
|
|
752
|
-
href = link[1];
|
|
753
|
-
title = link[3];
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
else {
|
|
757
|
-
title = cap[3] ? cap[3].slice(1, -1) : '';
|
|
758
|
-
}
|
|
759
|
-
href = href.trim();
|
|
760
|
-
if (
|
|
761
|
-
if (this.options.pedantic && !(
|
|
762
|
-
// pedantic allows starting angle bracket without ending angle bracket
|
|
763
|
-
href = href.slice(1);
|
|
764
|
-
}
|
|
765
|
-
else {
|
|
766
|
-
href = href.slice(1, -1);
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
return outputLink(cap, {
|
|
770
|
-
href: href ? href.replace(this.rules.inline.anyPunctuation, '$1') : href,
|
|
771
|
-
title: title ? title.replace(this.rules.inline.anyPunctuation, '$1') : title,
|
|
772
|
-
}, cap[0], this.lexer);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
reflink(src, links) {
|
|
776
|
-
let cap;
|
|
777
|
-
if ((cap = this.rules.inline.reflink.exec(src))
|
|
778
|
-
|| (cap = this.rules.inline.nolink.exec(src))) {
|
|
779
|
-
const linkString = (cap[2] || cap[1]).replace(
|
|
780
|
-
const link = links[linkString.toLowerCase()];
|
|
781
|
-
if (!link) {
|
|
782
|
-
const text = cap[0].charAt(0);
|
|
783
|
-
return {
|
|
784
|
-
type: 'text',
|
|
785
|
-
raw: text,
|
|
786
|
-
text,
|
|
787
|
-
};
|
|
788
|
-
}
|
|
789
|
-
return outputLink(cap, link, cap[0], this.lexer);
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
emStrong(src, maskedSrc, prevChar = '') {
|
|
793
|
-
let match = this.rules.inline.emStrongLDelim.exec(src);
|
|
794
|
-
if (!match)
|
|
795
|
-
return;
|
|
796
|
-
// _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
|
|
797
|
-
if (match[3] && prevChar.match(
|
|
798
|
-
return;
|
|
799
|
-
const nextChar = match[1] || match[2] || '';
|
|
800
|
-
if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {
|
|
801
|
-
// unicode Regex counts emoji as 1 char; spread into array for proper count (used multiple times below)
|
|
802
|
-
const lLength = [...match[0]].length - 1;
|
|
803
|
-
let rDelim, rLength, delimTotal = lLength, midDelimTotal = 0;
|
|
804
|
-
const endReg = match[0][0] === '*' ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd;
|
|
805
|
-
endReg.lastIndex = 0;
|
|
806
|
-
// Clip maskedSrc to same section of string as src (move to lexer?)
|
|
807
|
-
maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
|
|
808
|
-
while ((match = endReg.exec(maskedSrc)) != null) {
|
|
809
|
-
rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
|
|
810
|
-
if (!rDelim)
|
|
811
|
-
continue; // skip single * in __abc*abc__
|
|
812
|
-
rLength = [...rDelim].length;
|
|
813
|
-
if (match[3] || match[4]) { // found another Left Delim
|
|
814
|
-
delimTotal += rLength;
|
|
815
|
-
continue;
|
|
816
|
-
}
|
|
817
|
-
else if (match[5] || match[6]) { // either Left or Right Delim
|
|
818
|
-
if (lLength % 3 && !((lLength + rLength) % 3)) {
|
|
819
|
-
midDelimTotal += rLength;
|
|
820
|
-
continue; // CommonMark Emphasis Rules 9-10
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
delimTotal -= rLength;
|
|
824
|
-
if (delimTotal > 0)
|
|
825
|
-
continue; // Haven't found enough closing delimiters
|
|
826
|
-
// Remove extra characters. *a*** -> *a*
|
|
827
|
-
rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
|
|
828
|
-
// char length can be >1 for unicode characters;
|
|
829
|
-
const lastCharLength = [...match[0]][0].length;
|
|
830
|
-
const raw = src.slice(0, lLength + match.index + lastCharLength + rLength);
|
|
831
|
-
// Create `em` if smallest delimiter has odd char count. *a***
|
|
832
|
-
if (Math.min(lLength, rLength) % 2) {
|
|
833
|
-
const text = raw.slice(1, -1);
|
|
834
|
-
return {
|
|
835
|
-
type: 'em',
|
|
836
|
-
raw,
|
|
837
|
-
text,
|
|
838
|
-
tokens: this.lexer.inlineTokens(text),
|
|
839
|
-
};
|
|
840
|
-
}
|
|
841
|
-
// Create 'strong' if smallest delimiter has even char count. **a***
|
|
842
|
-
const text = raw.slice(2, -2);
|
|
843
|
-
return {
|
|
844
|
-
type: 'strong',
|
|
845
|
-
raw,
|
|
846
|
-
text,
|
|
847
|
-
tokens: this.lexer.inlineTokens(text),
|
|
848
|
-
};
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
codespan(src) {
|
|
853
|
-
const cap = this.rules.inline.code.exec(src);
|
|
854
|
-
if (cap) {
|
|
855
|
-
let text = cap[2].replace(
|
|
856
|
-
const hasNonSpaceChars =
|
|
857
|
-
const hasSpaceCharsOnBothEnds =
|
|
858
|
-
if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
|
|
859
|
-
text = text.substring(1, text.length - 1);
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
raw: cap[0],
|
|
967
|
-
text,
|
|
968
|
-
};
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
/**
|
|
974
|
-
* Block-Level Grammar
|
|
975
|
-
*/
|
|
976
|
-
const newline = /^(?:[ \t]*(?:\n|$))+/;
|
|
977
|
-
const blockCode = /^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/;
|
|
978
|
-
const fences = /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/;
|
|
979
|
-
const hr = /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/;
|
|
980
|
-
const heading = /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/;
|
|
981
|
-
const bullet = /(?:[*+-]|\d{1,9}[.)])/;
|
|
982
|
-
const lheading = edit(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html))+?)\n {0,3}(=+|-+) *(?:\n+|$)/)
|
|
983
|
-
.replace(/bull/g, bullet) // lists can interrupt
|
|
984
|
-
.replace(/blockCode/g, /(?: {4}| {0,3}\t)/) // indented code blocks can interrupt
|
|
985
|
-
.replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/) // fenced code blocks can interrupt
|
|
986
|
-
.replace(/blockquote/g, / {0,3}>/) // blockquote can interrupt
|
|
987
|
-
.replace(/heading/g, / {0,3}#{1,6}/) // ATX heading can interrupt
|
|
988
|
-
.replace(/html/g, / {0,3}<[^\n>]+>\n/) // block html can interrupt
|
|
989
|
-
.getRegex();
|
|
990
|
-
const _paragraph = /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/;
|
|
991
|
-
const blockText = /^[^\n]+/;
|
|
992
|
-
const _blockLabel = /(?!\s*\])(?:\\.|[^\[\]\\])+/;
|
|
993
|
-
const def = edit(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/)
|
|
994
|
-
.replace('label', _blockLabel)
|
|
995
|
-
.replace('title', /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/)
|
|
996
|
-
.getRegex();
|
|
997
|
-
const list = edit(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/)
|
|
998
|
-
.replace(/bull/g, bullet)
|
|
999
|
-
.getRegex();
|
|
1000
|
-
const _tag = 'address|article|aside|base|basefont|blockquote|body|caption'
|
|
1001
|
-
+ '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
|
|
1002
|
-
+ '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
|
|
1003
|
-
+ '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
|
|
1004
|
-
+ '|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title'
|
|
1005
|
-
+ '|tr|track|ul';
|
|
1006
|
-
const _comment = /<!--(?:-?>|[\s\S]*?(?:-->|$))/;
|
|
1007
|
-
const html = edit('^ {0,3}(?:' // optional indentation
|
|
1008
|
-
+ '<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
|
|
1009
|
-
+ '|comment[^\\n]*(\\n+|$)' // (2)
|
|
1010
|
-
+ '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3)
|
|
1011
|
-
+ '|<![A-Z][\\s\\S]*?(?:>\\n*|$)' // (4)
|
|
1012
|
-
+ '|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)' // (5)
|
|
1013
|
-
+ '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)' // (6)
|
|
1014
|
-
+ '|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)' // (7) open tag
|
|
1015
|
-
+ '|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)' // (7) closing tag
|
|
1016
|
-
+ ')', 'i')
|
|
1017
|
-
.replace('comment', _comment)
|
|
1018
|
-
.replace('tag', _tag)
|
|
1019
|
-
.replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
|
|
1020
|
-
.getRegex();
|
|
1021
|
-
const paragraph = edit(_paragraph)
|
|
1022
|
-
.replace('hr', hr)
|
|
1023
|
-
.replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
|
|
1024
|
-
.replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs
|
|
1025
|
-
.replace('|table', '')
|
|
1026
|
-
.replace('blockquote', ' {0,3}>')
|
|
1027
|
-
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
|
|
1028
|
-
.replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
|
|
1029
|
-
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
|
|
1030
|
-
.replace('tag', _tag) // pars can be interrupted by type (6) html blocks
|
|
1031
|
-
.getRegex();
|
|
1032
|
-
const blockquote = edit(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/)
|
|
1033
|
-
.replace('paragraph', paragraph)
|
|
1034
|
-
.getRegex();
|
|
1035
|
-
/**
|
|
1036
|
-
* Normal Block Grammar
|
|
1037
|
-
*/
|
|
1038
|
-
const blockNormal = {
|
|
1039
|
-
blockquote,
|
|
1040
|
-
code: blockCode,
|
|
1041
|
-
def,
|
|
1042
|
-
fences,
|
|
1043
|
-
heading,
|
|
1044
|
-
hr,
|
|
1045
|
-
html,
|
|
1046
|
-
lheading,
|
|
1047
|
-
list,
|
|
1048
|
-
newline,
|
|
1049
|
-
paragraph,
|
|
1050
|
-
table: noopTest,
|
|
1051
|
-
text: blockText,
|
|
1052
|
-
};
|
|
1053
|
-
/**
|
|
1054
|
-
* GFM Block Grammar
|
|
1055
|
-
*/
|
|
1056
|
-
const gfmTable = edit('^ *([^\\n ].*)\\n' // Header
|
|
1057
|
-
+ ' {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)' // Align
|
|
1058
|
-
+ '(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)') // Cells
|
|
1059
|
-
.replace('hr', hr)
|
|
1060
|
-
.replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
|
|
1061
|
-
.replace('blockquote', ' {0,3}>')
|
|
1062
|
-
.replace('code', '(?: {4}| {0,3}\t)[^\\n]')
|
|
1063
|
-
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
|
|
1064
|
-
.replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
|
|
1065
|
-
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
|
|
1066
|
-
.replace('tag', _tag) // tables can be interrupted by type (6) html blocks
|
|
1067
|
-
.getRegex();
|
|
1068
|
-
const blockGfm = {
|
|
1069
|
-
...blockNormal,
|
|
1070
|
-
table: gfmTable,
|
|
1071
|
-
paragraph: edit(_paragraph)
|
|
1072
|
-
.replace('hr', hr)
|
|
1073
|
-
.replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
|
|
1074
|
-
.replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs
|
|
1075
|
-
.replace('table', gfmTable) // interrupt paragraphs with table
|
|
1076
|
-
.replace('blockquote', ' {0,3}>')
|
|
1077
|
-
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
|
|
1078
|
-
.replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
|
|
1079
|
-
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
|
|
1080
|
-
.replace('tag', _tag) // pars can be interrupted by type (6) html blocks
|
|
1081
|
-
.getRegex(),
|
|
1082
|
-
};
|
|
1083
|
-
/**
|
|
1084
|
-
* Pedantic grammar (original John Gruber's loose markdown specification)
|
|
1085
|
-
*/
|
|
1086
|
-
const blockPedantic = {
|
|
1087
|
-
...blockNormal,
|
|
1088
|
-
html: edit('^ *(?:comment *(?:\\n|\\s*$)'
|
|
1089
|
-
+ '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
|
|
1090
|
-
+ '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
|
|
1091
|
-
.replace('comment', _comment)
|
|
1092
|
-
.replace(/tag/g, '(?!(?:'
|
|
1093
|
-
+ 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
|
|
1094
|
-
+ '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
|
|
1095
|
-
+ '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
|
|
1096
|
-
.getRegex(),
|
|
1097
|
-
def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
|
|
1098
|
-
heading: /^(#{1,6})(.*)(?:\n+|$)/,
|
|
1099
|
-
fences: noopTest, // fences not supported
|
|
1100
|
-
lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,
|
|
1101
|
-
paragraph: edit(_paragraph)
|
|
1102
|
-
.replace('hr', hr)
|
|
1103
|
-
.replace('heading', ' *#{1,6} *[^\n]')
|
|
1104
|
-
.replace('lheading', lheading)
|
|
1105
|
-
.replace('|table', '')
|
|
1106
|
-
.replace('blockquote', ' {0,3}>')
|
|
1107
|
-
.replace('|fences', '')
|
|
1108
|
-
.replace('|list', '')
|
|
1109
|
-
.replace('|html', '')
|
|
1110
|
-
.replace('|tag', '')
|
|
1111
|
-
.getRegex(),
|
|
1112
|
-
};
|
|
1113
|
-
/**
|
|
1114
|
-
* Inline-Level Grammar
|
|
1115
|
-
*/
|
|
1116
|
-
const escape = /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/;
|
|
1117
|
-
const inlineCode = /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/;
|
|
1118
|
-
const br = /^( {2,}|\\)\n(?!\s*$)/;
|
|
1119
|
-
const inlineText = /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/;
|
|
1120
|
-
// list of unicode punctuation marks, plus any missing characters from CommonMark spec
|
|
1121
|
-
const _punctuation = '\\p{P}\\p{S}';
|
|
1122
|
-
const punctuation = edit(/^((?![*_])[\spunctuation])/, 'u')
|
|
1123
|
-
.replace(/punctuation/g, _punctuation).getRegex();
|
|
1124
|
-
// sequences em should skip over [title](link), `code`, <html>
|
|
1125
|
-
const blockSkip = /\[[^[\]]*?\]\((?:\\.|[^\\\(\)]|\((?:\\.|[^\\\(\)])*\))*\)|`[^`]*?`|<[^<>]*?>/g;
|
|
1126
|
-
const emStrongLDelim = edit(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/, 'u')
|
|
1127
|
-
.replace(/punct/g, _punctuation)
|
|
1128
|
-
.getRegex();
|
|
1129
|
-
const emStrongRDelimAst = edit('^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)' // Skip orphan inside strong
|
|
1130
|
-
+ '|[^*]+(?=[^*])' // Consume to delim
|
|
1131
|
-
+ '|(?!\\*)[punct](\\*+)(?=[\\s]|$)' // (1) #*** can only be a Right Delimiter
|
|
1132
|
-
+ '|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)' // (2) a***#, a*** can only be a Right Delimiter
|
|
1133
|
-
+ '|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])' // (3) #***a, ***a can only be Left Delimiter
|
|
1134
|
-
+ '|[\\s](\\*+)(?!\\*)(?=[punct])' // (4) ***# can only be Left Delimiter
|
|
1135
|
-
+ '|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])' // (5) #***# can be either Left or Right Delimiter
|
|
1136
|
-
+ '|[^punct\\s](\\*+)(?=[^punct\\s])', 'gu') // (6) a***a can be either Left or Right Delimiter
|
|
1137
|
-
.replace(/punct/g, _punctuation)
|
|
1138
|
-
.getRegex();
|
|
1139
|
-
// (6) Not allowed for _
|
|
1140
|
-
const emStrongRDelimUnd = edit('^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)' // Skip orphan inside strong
|
|
1141
|
-
+ '|[^_]+(?=[^_])' // Consume to delim
|
|
1142
|
-
+ '|(?!_)[punct](_+)(?=[\\s]|$)' // (1) #___ can only be a Right Delimiter
|
|
1143
|
-
+ '|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)' // (2) a___#, a___ can only be a Right Delimiter
|
|
1144
|
-
+ '|(?!_)[punct\\s](_+)(?=[^punct\\s])' // (3) #___a, ___a can only be Left Delimiter
|
|
1145
|
-
+ '|[\\s](_+)(?!_)(?=[punct])' // (4) ___# can only be Left Delimiter
|
|
1146
|
-
+ '|(?!_)[punct](_+)(?!_)(?=[punct])', 'gu') // (5) #___# can be either Left or Right Delimiter
|
|
1147
|
-
.replace(/punct/g, _punctuation)
|
|
1148
|
-
.getRegex();
|
|
1149
|
-
const anyPunctuation = edit(/\\([punct])/, 'gu')
|
|
1150
|
-
.replace(/punct/g, _punctuation)
|
|
1151
|
-
.getRegex();
|
|
1152
|
-
const autolink = edit(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/)
|
|
1153
|
-
.replace('scheme', /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/)
|
|
1154
|
-
.replace('email', /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/)
|
|
1155
|
-
.getRegex();
|
|
1156
|
-
const _inlineComment = edit(_comment).replace('(?:-->|$)', '-->').getRegex();
|
|
1157
|
-
const tag = edit('^comment'
|
|
1158
|
-
+ '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
|
|
1159
|
-
+ '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
|
|
1160
|
-
+ '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
|
|
1161
|
-
+ '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
|
|
1162
|
-
+ '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>') // CDATA section
|
|
1163
|
-
.replace('comment', _inlineComment)
|
|
1164
|
-
.replace('attribute', /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/)
|
|
1165
|
-
.getRegex();
|
|
1166
|
-
const _inlineLabel = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
|
|
1167
|
-
const link = edit(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/)
|
|
1168
|
-
.replace('label', _inlineLabel)
|
|
1169
|
-
.replace('href', /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/)
|
|
1170
|
-
.replace('title', /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/)
|
|
1171
|
-
.getRegex();
|
|
1172
|
-
const reflink = edit(/^!?\[(label)\]\[(ref)\]/)
|
|
1173
|
-
.replace('label', _inlineLabel)
|
|
1174
|
-
.replace('ref', _blockLabel)
|
|
1175
|
-
.getRegex();
|
|
1176
|
-
const nolink = edit(/^!?\[(ref)\](?:\[\])?/)
|
|
1177
|
-
.replace('ref', _blockLabel)
|
|
1178
|
-
.getRegex();
|
|
1179
|
-
const reflinkSearch = edit('reflink|nolink(?!\\()', 'g')
|
|
1180
|
-
.replace('reflink', reflink)
|
|
1181
|
-
.replace('nolink', nolink)
|
|
1182
|
-
.getRegex();
|
|
1183
|
-
/**
|
|
1184
|
-
* Normal Inline Grammar
|
|
1185
|
-
*/
|
|
1186
|
-
const inlineNormal = {
|
|
1187
|
-
_backpedal: noopTest, // only used for GFM url
|
|
1188
|
-
anyPunctuation,
|
|
1189
|
-
autolink,
|
|
1190
|
-
blockSkip,
|
|
1191
|
-
br,
|
|
1192
|
-
code: inlineCode,
|
|
1193
|
-
del: noopTest,
|
|
1194
|
-
emStrongLDelim,
|
|
1195
|
-
emStrongRDelimAst,
|
|
1196
|
-
emStrongRDelimUnd,
|
|
1197
|
-
escape,
|
|
1198
|
-
link,
|
|
1199
|
-
nolink,
|
|
1200
|
-
punctuation,
|
|
1201
|
-
reflink,
|
|
1202
|
-
reflinkSearch,
|
|
1203
|
-
tag,
|
|
1204
|
-
text: inlineText,
|
|
1205
|
-
url: noopTest,
|
|
1206
|
-
};
|
|
1207
|
-
/**
|
|
1208
|
-
* Pedantic Inline Grammar
|
|
1209
|
-
*/
|
|
1210
|
-
const inlinePedantic = {
|
|
1211
|
-
...inlineNormal,
|
|
1212
|
-
link: edit(/^!?\[(label)\]\((.*?)\)/)
|
|
1213
|
-
.replace('label', _inlineLabel)
|
|
1214
|
-
.getRegex(),
|
|
1215
|
-
reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
|
|
1216
|
-
.replace('label', _inlineLabel)
|
|
1217
|
-
.getRegex(),
|
|
1218
|
-
};
|
|
1219
|
-
/**
|
|
1220
|
-
* GFM Inline Grammar
|
|
1221
|
-
*/
|
|
1222
|
-
const inlineGfm = {
|
|
1223
|
-
...inlineNormal,
|
|
1224
|
-
escape: edit(escape).replace('])', '~|])').getRegex(),
|
|
1225
|
-
url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, 'i')
|
|
1226
|
-
.replace('email', /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/)
|
|
1227
|
-
.getRegex(),
|
|
1228
|
-
_backpedal: /(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,
|
|
1229
|
-
del: /^(~~?)(?=[^\s~])((?:\\.|[^\\])*?(?:\\.|[^\s~\\]))\1(?=[^~]|$)/,
|
|
1230
|
-
text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/,
|
|
1231
|
-
};
|
|
1232
|
-
/**
|
|
1233
|
-
* GFM + Line Breaks Inline Grammar
|
|
1234
|
-
*/
|
|
1235
|
-
const inlineBreaks = {
|
|
1236
|
-
...inlineGfm,
|
|
1237
|
-
br: edit(br).replace('{2,}', '*').getRegex(),
|
|
1238
|
-
text: edit(inlineGfm.text)
|
|
1239
|
-
.replace('\\b_', '\\b_| {2,}\\n')
|
|
1240
|
-
.replace(/\{2,\}/g, '*')
|
|
1241
|
-
.getRegex(),
|
|
1242
|
-
};
|
|
1243
|
-
/**
|
|
1244
|
-
* exports
|
|
1245
|
-
*/
|
|
1246
|
-
const block = {
|
|
1247
|
-
normal: blockNormal,
|
|
1248
|
-
gfm: blockGfm,
|
|
1249
|
-
pedantic: blockPedantic,
|
|
1250
|
-
};
|
|
1251
|
-
const inline = {
|
|
1252
|
-
normal: inlineNormal,
|
|
1253
|
-
gfm: inlineGfm,
|
|
1254
|
-
breaks: inlineBreaks,
|
|
1255
|
-
pedantic: inlinePedantic,
|
|
1256
|
-
};
|
|
1047
|
+
else if (this.lexer.state.inRawBlock && this.rules.other.endPreScriptTag.test(cap[0])) {
|
|
1048
|
+
this.lexer.state.inRawBlock = false;
|
|
1049
|
+
}
|
|
1050
|
+
return {
|
|
1051
|
+
type: 'html',
|
|
1052
|
+
raw: cap[0],
|
|
1053
|
+
inLink: this.lexer.state.inLink,
|
|
1054
|
+
inRawBlock: this.lexer.state.inRawBlock,
|
|
1055
|
+
block: false,
|
|
1056
|
+
text: cap[0],
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
link(src) {
|
|
1061
|
+
const cap = this.rules.inline.link.exec(src);
|
|
1062
|
+
if (cap) {
|
|
1063
|
+
const trimmedUrl = cap[2].trim();
|
|
1064
|
+
if (!this.options.pedantic && this.rules.other.startAngleBracket.test(trimmedUrl)) {
|
|
1065
|
+
// commonmark requires matching angle brackets
|
|
1066
|
+
if (!(this.rules.other.endAngleBracket.test(trimmedUrl))) {
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
// ending angle bracket cannot be escaped
|
|
1070
|
+
const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\');
|
|
1071
|
+
if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
else {
|
|
1076
|
+
// find closing parenthesis
|
|
1077
|
+
const lastParenIndex = findClosingBracket(cap[2], '()');
|
|
1078
|
+
if (lastParenIndex > -1) {
|
|
1079
|
+
const start = cap[0].indexOf('!') === 0 ? 5 : 4;
|
|
1080
|
+
const linkLen = start + cap[1].length + lastParenIndex;
|
|
1081
|
+
cap[2] = cap[2].substring(0, lastParenIndex);
|
|
1082
|
+
cap[0] = cap[0].substring(0, linkLen).trim();
|
|
1083
|
+
cap[3] = '';
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
let href = cap[2];
|
|
1087
|
+
let title = '';
|
|
1088
|
+
if (this.options.pedantic) {
|
|
1089
|
+
// split pedantic href and title
|
|
1090
|
+
const link = this.rules.other.pedanticHrefTitle.exec(href);
|
|
1091
|
+
if (link) {
|
|
1092
|
+
href = link[1];
|
|
1093
|
+
title = link[3];
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
else {
|
|
1097
|
+
title = cap[3] ? cap[3].slice(1, -1) : '';
|
|
1098
|
+
}
|
|
1099
|
+
href = href.trim();
|
|
1100
|
+
if (this.rules.other.startAngleBracket.test(href)) {
|
|
1101
|
+
if (this.options.pedantic && !(this.rules.other.endAngleBracket.test(trimmedUrl))) {
|
|
1102
|
+
// pedantic allows starting angle bracket without ending angle bracket
|
|
1103
|
+
href = href.slice(1);
|
|
1104
|
+
}
|
|
1105
|
+
else {
|
|
1106
|
+
href = href.slice(1, -1);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
return outputLink(cap, {
|
|
1110
|
+
href: href ? href.replace(this.rules.inline.anyPunctuation, '$1') : href,
|
|
1111
|
+
title: title ? title.replace(this.rules.inline.anyPunctuation, '$1') : title,
|
|
1112
|
+
}, cap[0], this.lexer, this.rules);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
reflink(src, links) {
|
|
1116
|
+
let cap;
|
|
1117
|
+
if ((cap = this.rules.inline.reflink.exec(src))
|
|
1118
|
+
|| (cap = this.rules.inline.nolink.exec(src))) {
|
|
1119
|
+
const linkString = (cap[2] || cap[1]).replace(this.rules.other.multipleSpaceGlobal, ' ');
|
|
1120
|
+
const link = links[linkString.toLowerCase()];
|
|
1121
|
+
if (!link) {
|
|
1122
|
+
const text = cap[0].charAt(0);
|
|
1123
|
+
return {
|
|
1124
|
+
type: 'text',
|
|
1125
|
+
raw: text,
|
|
1126
|
+
text,
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
return outputLink(cap, link, cap[0], this.lexer, this.rules);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
emStrong(src, maskedSrc, prevChar = '') {
|
|
1133
|
+
let match = this.rules.inline.emStrongLDelim.exec(src);
|
|
1134
|
+
if (!match)
|
|
1135
|
+
return;
|
|
1136
|
+
// _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
|
|
1137
|
+
if (match[3] && prevChar.match(this.rules.other.unicodeAlphaNumeric))
|
|
1138
|
+
return;
|
|
1139
|
+
const nextChar = match[1] || match[2] || '';
|
|
1140
|
+
if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {
|
|
1141
|
+
// unicode Regex counts emoji as 1 char; spread into array for proper count (used multiple times below)
|
|
1142
|
+
const lLength = [...match[0]].length - 1;
|
|
1143
|
+
let rDelim, rLength, delimTotal = lLength, midDelimTotal = 0;
|
|
1144
|
+
const endReg = match[0][0] === '*' ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd;
|
|
1145
|
+
endReg.lastIndex = 0;
|
|
1146
|
+
// Clip maskedSrc to same section of string as src (move to lexer?)
|
|
1147
|
+
maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
|
|
1148
|
+
while ((match = endReg.exec(maskedSrc)) != null) {
|
|
1149
|
+
rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
|
|
1150
|
+
if (!rDelim)
|
|
1151
|
+
continue; // skip single * in __abc*abc__
|
|
1152
|
+
rLength = [...rDelim].length;
|
|
1153
|
+
if (match[3] || match[4]) { // found another Left Delim
|
|
1154
|
+
delimTotal += rLength;
|
|
1155
|
+
continue;
|
|
1156
|
+
}
|
|
1157
|
+
else if (match[5] || match[6]) { // either Left or Right Delim
|
|
1158
|
+
if (lLength % 3 && !((lLength + rLength) % 3)) {
|
|
1159
|
+
midDelimTotal += rLength;
|
|
1160
|
+
continue; // CommonMark Emphasis Rules 9-10
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
delimTotal -= rLength;
|
|
1164
|
+
if (delimTotal > 0)
|
|
1165
|
+
continue; // Haven't found enough closing delimiters
|
|
1166
|
+
// Remove extra characters. *a*** -> *a*
|
|
1167
|
+
rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
|
|
1168
|
+
// char length can be >1 for unicode characters;
|
|
1169
|
+
const lastCharLength = [...match[0]][0].length;
|
|
1170
|
+
const raw = src.slice(0, lLength + match.index + lastCharLength + rLength);
|
|
1171
|
+
// Create `em` if smallest delimiter has odd char count. *a***
|
|
1172
|
+
if (Math.min(lLength, rLength) % 2) {
|
|
1173
|
+
const text = raw.slice(1, -1);
|
|
1174
|
+
return {
|
|
1175
|
+
type: 'em',
|
|
1176
|
+
raw,
|
|
1177
|
+
text,
|
|
1178
|
+
tokens: this.lexer.inlineTokens(text),
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
// Create 'strong' if smallest delimiter has even char count. **a***
|
|
1182
|
+
const text = raw.slice(2, -2);
|
|
1183
|
+
return {
|
|
1184
|
+
type: 'strong',
|
|
1185
|
+
raw,
|
|
1186
|
+
text,
|
|
1187
|
+
tokens: this.lexer.inlineTokens(text),
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
codespan(src) {
|
|
1193
|
+
const cap = this.rules.inline.code.exec(src);
|
|
1194
|
+
if (cap) {
|
|
1195
|
+
let text = cap[2].replace(this.rules.other.newLineCharGlobal, ' ');
|
|
1196
|
+
const hasNonSpaceChars = this.rules.other.nonSpaceChar.test(text);
|
|
1197
|
+
const hasSpaceCharsOnBothEnds = this.rules.other.startingSpaceChar.test(text) && this.rules.other.endingSpaceChar.test(text);
|
|
1198
|
+
if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
|
|
1199
|
+
text = text.substring(1, text.length - 1);
|
|
1200
|
+
}
|
|
1201
|
+
return {
|
|
1202
|
+
type: 'codespan',
|
|
1203
|
+
raw: cap[0],
|
|
1204
|
+
text,
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
br(src) {
|
|
1209
|
+
const cap = this.rules.inline.br.exec(src);
|
|
1210
|
+
if (cap) {
|
|
1211
|
+
return {
|
|
1212
|
+
type: 'br',
|
|
1213
|
+
raw: cap[0],
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
del(src) {
|
|
1218
|
+
const cap = this.rules.inline.del.exec(src);
|
|
1219
|
+
if (cap) {
|
|
1220
|
+
return {
|
|
1221
|
+
type: 'del',
|
|
1222
|
+
raw: cap[0],
|
|
1223
|
+
text: cap[2],
|
|
1224
|
+
tokens: this.lexer.inlineTokens(cap[2]),
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
autolink(src) {
|
|
1229
|
+
const cap = this.rules.inline.autolink.exec(src);
|
|
1230
|
+
if (cap) {
|
|
1231
|
+
let text, href;
|
|
1232
|
+
if (cap[2] === '@') {
|
|
1233
|
+
text = cap[1];
|
|
1234
|
+
href = 'mailto:' + text;
|
|
1235
|
+
}
|
|
1236
|
+
else {
|
|
1237
|
+
text = cap[1];
|
|
1238
|
+
href = text;
|
|
1239
|
+
}
|
|
1240
|
+
return {
|
|
1241
|
+
type: 'link',
|
|
1242
|
+
raw: cap[0],
|
|
1243
|
+
text,
|
|
1244
|
+
href,
|
|
1245
|
+
tokens: [
|
|
1246
|
+
{
|
|
1247
|
+
type: 'text',
|
|
1248
|
+
raw: text,
|
|
1249
|
+
text,
|
|
1250
|
+
},
|
|
1251
|
+
],
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
url(src) {
|
|
1256
|
+
let cap;
|
|
1257
|
+
if (cap = this.rules.inline.url.exec(src)) {
|
|
1258
|
+
let text, href;
|
|
1259
|
+
if (cap[2] === '@') {
|
|
1260
|
+
text = cap[0];
|
|
1261
|
+
href = 'mailto:' + text;
|
|
1262
|
+
}
|
|
1263
|
+
else {
|
|
1264
|
+
// do extended autolink path validation
|
|
1265
|
+
let prevCapZero;
|
|
1266
|
+
do {
|
|
1267
|
+
prevCapZero = cap[0];
|
|
1268
|
+
cap[0] = this.rules.inline._backpedal.exec(cap[0])?.[0] ?? '';
|
|
1269
|
+
} while (prevCapZero !== cap[0]);
|
|
1270
|
+
text = cap[0];
|
|
1271
|
+
if (cap[1] === 'www.') {
|
|
1272
|
+
href = 'http://' + cap[0];
|
|
1273
|
+
}
|
|
1274
|
+
else {
|
|
1275
|
+
href = cap[0];
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
return {
|
|
1279
|
+
type: 'link',
|
|
1280
|
+
raw: cap[0],
|
|
1281
|
+
text,
|
|
1282
|
+
href,
|
|
1283
|
+
tokens: [
|
|
1284
|
+
{
|
|
1285
|
+
type: 'text',
|
|
1286
|
+
raw: text,
|
|
1287
|
+
text,
|
|
1288
|
+
},
|
|
1289
|
+
],
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
inlineText(src) {
|
|
1294
|
+
const cap = this.rules.inline.text.exec(src);
|
|
1295
|
+
if (cap) {
|
|
1296
|
+
const escaped = this.lexer.state.inRawBlock;
|
|
1297
|
+
return {
|
|
1298
|
+
type: 'text',
|
|
1299
|
+
raw: cap[0],
|
|
1300
|
+
text: cap[0],
|
|
1301
|
+
escaped,
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1257
1306
|
|
|
1258
1307
|
/**
|
|
1259
1308
|
* Block Lexer
|
|
@@ -1280,6 +1329,7 @@ class _Lexer {
|
|
|
1280
1329
|
top: true,
|
|
1281
1330
|
};
|
|
1282
1331
|
const rules = {
|
|
1332
|
+
other,
|
|
1283
1333
|
block: block.normal,
|
|
1284
1334
|
inline: inline.normal,
|
|
1285
1335
|
};
|
|
@@ -1325,8 +1375,7 @@ class _Lexer {
|
|
|
1325
1375
|
* Preprocessing
|
|
1326
1376
|
*/
|
|
1327
1377
|
lex(src) {
|
|
1328
|
-
src = src
|
|
1329
|
-
.replace(/\r\n|\r/g, '\n');
|
|
1378
|
+
src = src.replace(other.carriageReturn, '\n');
|
|
1330
1379
|
this.blockTokens(src, this.tokens);
|
|
1331
1380
|
for (let i = 0; i < this.inlineQueue.length; i++) {
|
|
1332
1381
|
const next = this.inlineQueue[i];
|
|
@@ -1337,31 +1386,28 @@ class _Lexer {
|
|
|
1337
1386
|
}
|
|
1338
1387
|
blockTokens(src, tokens = [], lastParagraphClipped = false) {
|
|
1339
1388
|
if (this.options.pedantic) {
|
|
1340
|
-
src = src.replace(
|
|
1389
|
+
src = src.replace(other.tabCharGlobal, ' ').replace(other.spaceLine, '');
|
|
1341
1390
|
}
|
|
1342
|
-
let token;
|
|
1343
|
-
let lastToken;
|
|
1344
|
-
let cutSrc;
|
|
1345
1391
|
while (src) {
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
})) {
|
|
1392
|
+
let token;
|
|
1393
|
+
if (this.options.extensions?.block?.some((extTokenizer) => {
|
|
1394
|
+
if (token = extTokenizer.call({ lexer: this }, src, tokens)) {
|
|
1395
|
+
src = src.substring(token.raw.length);
|
|
1396
|
+
tokens.push(token);
|
|
1397
|
+
return true;
|
|
1398
|
+
}
|
|
1399
|
+
return false;
|
|
1400
|
+
})) {
|
|
1356
1401
|
continue;
|
|
1357
1402
|
}
|
|
1358
1403
|
// newline
|
|
1359
1404
|
if (token = this.tokenizer.space(src)) {
|
|
1360
1405
|
src = src.substring(token.raw.length);
|
|
1361
|
-
|
|
1406
|
+
const lastToken = tokens.at(-1);
|
|
1407
|
+
if (token.raw.length === 1 && lastToken !== undefined) {
|
|
1362
1408
|
// if there's a single \n as a spacer, it's terminating the last line,
|
|
1363
1409
|
// so move it there so that we don't get unnecessary paragraph tags
|
|
1364
|
-
|
|
1410
|
+
lastToken.raw += '\n';
|
|
1365
1411
|
}
|
|
1366
1412
|
else {
|
|
1367
1413
|
tokens.push(token);
|
|
@@ -1371,12 +1417,12 @@ class _Lexer {
|
|
|
1371
1417
|
// code
|
|
1372
1418
|
if (token = this.tokenizer.code(src)) {
|
|
1373
1419
|
src = src.substring(token.raw.length);
|
|
1374
|
-
lastToken = tokens
|
|
1420
|
+
const lastToken = tokens.at(-1);
|
|
1375
1421
|
// An indented code block cannot interrupt a paragraph.
|
|
1376
|
-
if (lastToken
|
|
1422
|
+
if (lastToken?.type === 'paragraph' || lastToken?.type === 'text') {
|
|
1377
1423
|
lastToken.raw += '\n' + token.raw;
|
|
1378
1424
|
lastToken.text += '\n' + token.text;
|
|
1379
|
-
this.inlineQueue
|
|
1425
|
+
this.inlineQueue.at(-1).src = lastToken.text;
|
|
1380
1426
|
}
|
|
1381
1427
|
else {
|
|
1382
1428
|
tokens.push(token);
|
|
@@ -1422,11 +1468,11 @@ class _Lexer {
|
|
|
1422
1468
|
// def
|
|
1423
1469
|
if (token = this.tokenizer.def(src)) {
|
|
1424
1470
|
src = src.substring(token.raw.length);
|
|
1425
|
-
lastToken = tokens
|
|
1426
|
-
if (lastToken
|
|
1471
|
+
const lastToken = tokens.at(-1);
|
|
1472
|
+
if (lastToken?.type === 'paragraph' || lastToken?.type === 'text') {
|
|
1427
1473
|
lastToken.raw += '\n' + token.raw;
|
|
1428
1474
|
lastToken.text += '\n' + token.raw;
|
|
1429
|
-
this.inlineQueue
|
|
1475
|
+
this.inlineQueue.at(-1).src = lastToken.text;
|
|
1430
1476
|
}
|
|
1431
1477
|
else if (!this.tokens.links[token.tag]) {
|
|
1432
1478
|
this.tokens.links[token.tag] = {
|
|
@@ -1450,8 +1496,8 @@ class _Lexer {
|
|
|
1450
1496
|
}
|
|
1451
1497
|
// top-level paragraph
|
|
1452
1498
|
// prevent paragraph consuming extensions by clipping 'src' to extension start
|
|
1453
|
-
cutSrc = src;
|
|
1454
|
-
if (this.options.extensions
|
|
1499
|
+
let cutSrc = src;
|
|
1500
|
+
if (this.options.extensions?.startBlock) {
|
|
1455
1501
|
let startIndex = Infinity;
|
|
1456
1502
|
const tempSrc = src.slice(1);
|
|
1457
1503
|
let tempStart;
|
|
@@ -1466,29 +1512,29 @@ class _Lexer {
|
|
|
1466
1512
|
}
|
|
1467
1513
|
}
|
|
1468
1514
|
if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) {
|
|
1469
|
-
lastToken = tokens
|
|
1515
|
+
const lastToken = tokens.at(-1);
|
|
1470
1516
|
if (lastParagraphClipped && lastToken?.type === 'paragraph') {
|
|
1471
1517
|
lastToken.raw += '\n' + token.raw;
|
|
1472
1518
|
lastToken.text += '\n' + token.text;
|
|
1473
1519
|
this.inlineQueue.pop();
|
|
1474
|
-
this.inlineQueue
|
|
1520
|
+
this.inlineQueue.at(-1).src = lastToken.text;
|
|
1475
1521
|
}
|
|
1476
1522
|
else {
|
|
1477
1523
|
tokens.push(token);
|
|
1478
1524
|
}
|
|
1479
|
-
lastParagraphClipped =
|
|
1525
|
+
lastParagraphClipped = cutSrc.length !== src.length;
|
|
1480
1526
|
src = src.substring(token.raw.length);
|
|
1481
1527
|
continue;
|
|
1482
1528
|
}
|
|
1483
1529
|
// text
|
|
1484
1530
|
if (token = this.tokenizer.text(src)) {
|
|
1485
1531
|
src = src.substring(token.raw.length);
|
|
1486
|
-
lastToken = tokens
|
|
1487
|
-
if (lastToken
|
|
1532
|
+
const lastToken = tokens.at(-1);
|
|
1533
|
+
if (lastToken?.type === 'text') {
|
|
1488
1534
|
lastToken.raw += '\n' + token.raw;
|
|
1489
1535
|
lastToken.text += '\n' + token.text;
|
|
1490
1536
|
this.inlineQueue.pop();
|
|
1491
|
-
this.inlineQueue
|
|
1537
|
+
this.inlineQueue.at(-1).src = lastToken.text;
|
|
1492
1538
|
}
|
|
1493
1539
|
else {
|
|
1494
1540
|
tokens.push(token);
|
|
@@ -1517,18 +1563,18 @@ class _Lexer {
|
|
|
1517
1563
|
* Lexing/Compiling
|
|
1518
1564
|
*/
|
|
1519
1565
|
inlineTokens(src, tokens = []) {
|
|
1520
|
-
let token, lastToken, cutSrc;
|
|
1521
1566
|
// String with links masked to avoid interference with em and strong
|
|
1522
1567
|
let maskedSrc = src;
|
|
1523
|
-
let match;
|
|
1524
|
-
let keepPrevChar, prevChar;
|
|
1568
|
+
let match = null;
|
|
1525
1569
|
// Mask out reflinks
|
|
1526
1570
|
if (this.tokens.links) {
|
|
1527
1571
|
const links = Object.keys(this.tokens.links);
|
|
1528
1572
|
if (links.length > 0) {
|
|
1529
1573
|
while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
|
|
1530
1574
|
if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {
|
|
1531
|
-
maskedSrc = maskedSrc.slice(0, match.index)
|
|
1575
|
+
maskedSrc = maskedSrc.slice(0, match.index)
|
|
1576
|
+
+ '[' + 'a'.repeat(match[0].length - 2) + ']'
|
|
1577
|
+
+ maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
|
|
1532
1578
|
}
|
|
1533
1579
|
}
|
|
1534
1580
|
}
|
|
@@ -1541,22 +1587,23 @@ class _Lexer {
|
|
|
1541
1587
|
while ((match = this.tokenizer.rules.inline.anyPunctuation.exec(maskedSrc)) != null) {
|
|
1542
1588
|
maskedSrc = maskedSrc.slice(0, match.index) + '++' + maskedSrc.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);
|
|
1543
1589
|
}
|
|
1590
|
+
let keepPrevChar = false;
|
|
1591
|
+
let prevChar = '';
|
|
1544
1592
|
while (src) {
|
|
1545
1593
|
if (!keepPrevChar) {
|
|
1546
1594
|
prevChar = '';
|
|
1547
1595
|
}
|
|
1548
1596
|
keepPrevChar = false;
|
|
1597
|
+
let token;
|
|
1549
1598
|
// extensions
|
|
1550
|
-
if (this.options.extensions
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
return false;
|
|
1559
|
-
})) {
|
|
1599
|
+
if (this.options.extensions?.inline?.some((extTokenizer) => {
|
|
1600
|
+
if (token = extTokenizer.call({ lexer: this }, src, tokens)) {
|
|
1601
|
+
src = src.substring(token.raw.length);
|
|
1602
|
+
tokens.push(token);
|
|
1603
|
+
return true;
|
|
1604
|
+
}
|
|
1605
|
+
return false;
|
|
1606
|
+
})) {
|
|
1560
1607
|
continue;
|
|
1561
1608
|
}
|
|
1562
1609
|
// escape
|
|
@@ -1568,14 +1615,7 @@ class _Lexer {
|
|
|
1568
1615
|
// tag
|
|
1569
1616
|
if (token = this.tokenizer.tag(src)) {
|
|
1570
1617
|
src = src.substring(token.raw.length);
|
|
1571
|
-
|
|
1572
|
-
if (lastToken && token.type === 'text' && lastToken.type === 'text') {
|
|
1573
|
-
lastToken.raw += token.raw;
|
|
1574
|
-
lastToken.text += token.text;
|
|
1575
|
-
}
|
|
1576
|
-
else {
|
|
1577
|
-
tokens.push(token);
|
|
1578
|
-
}
|
|
1618
|
+
tokens.push(token);
|
|
1579
1619
|
continue;
|
|
1580
1620
|
}
|
|
1581
1621
|
// link
|
|
@@ -1587,8 +1627,8 @@ class _Lexer {
|
|
|
1587
1627
|
// reflink, nolink
|
|
1588
1628
|
if (token = this.tokenizer.reflink(src, this.tokens.links)) {
|
|
1589
1629
|
src = src.substring(token.raw.length);
|
|
1590
|
-
lastToken = tokens
|
|
1591
|
-
if (
|
|
1630
|
+
const lastToken = tokens.at(-1);
|
|
1631
|
+
if (token.type === 'text' && lastToken?.type === 'text') {
|
|
1592
1632
|
lastToken.raw += token.raw;
|
|
1593
1633
|
lastToken.text += token.text;
|
|
1594
1634
|
}
|
|
@@ -1635,8 +1675,8 @@ class _Lexer {
|
|
|
1635
1675
|
}
|
|
1636
1676
|
// text
|
|
1637
1677
|
// prevent inlineText consuming extensions by clipping 'src' to extension start
|
|
1638
|
-
cutSrc = src;
|
|
1639
|
-
if (this.options.extensions
|
|
1678
|
+
let cutSrc = src;
|
|
1679
|
+
if (this.options.extensions?.startInline) {
|
|
1640
1680
|
let startIndex = Infinity;
|
|
1641
1681
|
const tempSrc = src.slice(1);
|
|
1642
1682
|
let tempStart;
|
|
@@ -1656,8 +1696,8 @@ class _Lexer {
|
|
|
1656
1696
|
prevChar = token.raw.slice(-1);
|
|
1657
1697
|
}
|
|
1658
1698
|
keepPrevChar = true;
|
|
1659
|
-
lastToken = tokens
|
|
1660
|
-
if (lastToken
|
|
1699
|
+
const lastToken = tokens.at(-1);
|
|
1700
|
+
if (lastToken?.type === 'text') {
|
|
1661
1701
|
lastToken.raw += token.raw;
|
|
1662
1702
|
lastToken.text += token.text;
|
|
1663
1703
|
}
|
|
@@ -1694,17 +1734,17 @@ class _Renderer {
|
|
|
1694
1734
|
return '';
|
|
1695
1735
|
}
|
|
1696
1736
|
code({ text, lang, escaped }) {
|
|
1697
|
-
const langString = (lang || '').match(
|
|
1698
|
-
const code = text.replace(
|
|
1737
|
+
const langString = (lang || '').match(other.notSpaceStart)?.[0];
|
|
1738
|
+
const code = text.replace(other.endingNewline, '') + '\n';
|
|
1699
1739
|
if (!langString) {
|
|
1700
1740
|
return '<pre><code>'
|
|
1701
|
-
+ (escaped ? code : escape
|
|
1741
|
+
+ (escaped ? code : escape(code, true))
|
|
1702
1742
|
+ '</code></pre>\n';
|
|
1703
1743
|
}
|
|
1704
1744
|
return '<pre><code class="language-'
|
|
1705
|
-
+ escape
|
|
1745
|
+
+ escape(langString)
|
|
1706
1746
|
+ '">'
|
|
1707
|
-
+ (escaped ? code : escape
|
|
1747
|
+
+ (escaped ? code : escape(code, true))
|
|
1708
1748
|
+ '</code></pre>\n';
|
|
1709
1749
|
}
|
|
1710
1750
|
blockquote({ tokens }) {
|
|
@@ -1737,10 +1777,11 @@ class _Renderer {
|
|
|
1737
1777
|
if (item.task) {
|
|
1738
1778
|
const checkbox = this.checkbox({ checked: !!item.checked });
|
|
1739
1779
|
if (item.loose) {
|
|
1740
|
-
if (item.tokens
|
|
1780
|
+
if (item.tokens[0]?.type === 'paragraph') {
|
|
1741
1781
|
item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
|
|
1742
1782
|
if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
|
|
1743
|
-
item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
|
|
1783
|
+
item.tokens[0].tokens[0].text = checkbox + ' ' + escape(item.tokens[0].tokens[0].text);
|
|
1784
|
+
item.tokens[0].tokens[0].escaped = true;
|
|
1744
1785
|
}
|
|
1745
1786
|
}
|
|
1746
1787
|
else {
|
|
@@ -1748,6 +1789,7 @@ class _Renderer {
|
|
|
1748
1789
|
type: 'text',
|
|
1749
1790
|
raw: checkbox + ' ',
|
|
1750
1791
|
text: checkbox + ' ',
|
|
1792
|
+
escaped: true,
|
|
1751
1793
|
});
|
|
1752
1794
|
}
|
|
1753
1795
|
}
|
|
@@ -1813,7 +1855,7 @@ class _Renderer {
|
|
|
1813
1855
|
return `<em>${this.parser.parseInline(tokens)}</em>`;
|
|
1814
1856
|
}
|
|
1815
1857
|
codespan({ text }) {
|
|
1816
|
-
return `<code>${text}</code>`;
|
|
1858
|
+
return `<code>${escape(text, true)}</code>`;
|
|
1817
1859
|
}
|
|
1818
1860
|
br(token) {
|
|
1819
1861
|
return '<br>';
|
|
@@ -1830,7 +1872,7 @@ class _Renderer {
|
|
|
1830
1872
|
href = cleanHref;
|
|
1831
1873
|
let out = '<a href="' + href + '"';
|
|
1832
1874
|
if (title) {
|
|
1833
|
-
out += ' title="' + title + '"';
|
|
1875
|
+
out += ' title="' + (escape(title)) + '"';
|
|
1834
1876
|
}
|
|
1835
1877
|
out += '>' + text + '</a>';
|
|
1836
1878
|
return out;
|
|
@@ -1838,18 +1880,20 @@ class _Renderer {
|
|
|
1838
1880
|
image({ href, title, text }) {
|
|
1839
1881
|
const cleanHref = cleanUrl(href);
|
|
1840
1882
|
if (cleanHref === null) {
|
|
1841
|
-
return text;
|
|
1883
|
+
return escape(text);
|
|
1842
1884
|
}
|
|
1843
1885
|
href = cleanHref;
|
|
1844
1886
|
let out = `<img src="${href}" alt="${text}"`;
|
|
1845
1887
|
if (title) {
|
|
1846
|
-
out += ` title="${title}"`;
|
|
1888
|
+
out += ` title="${escape(title)}"`;
|
|
1847
1889
|
}
|
|
1848
1890
|
out += '>';
|
|
1849
1891
|
return out;
|
|
1850
1892
|
}
|
|
1851
1893
|
text(token) {
|
|
1852
|
-
return 'tokens' in token && token.tokens
|
|
1894
|
+
return 'tokens' in token && token.tokens
|
|
1895
|
+
? this.parser.parseInline(token.tokens)
|
|
1896
|
+
: ('escaped' in token && token.escaped ? token.text : escape(token.text));
|
|
1853
1897
|
}
|
|
1854
1898
|
}
|
|
1855
1899
|
|
|
@@ -1925,7 +1969,7 @@ class _Parser {
|
|
|
1925
1969
|
for (let i = 0; i < tokens.length; i++) {
|
|
1926
1970
|
const anyToken = tokens[i];
|
|
1927
1971
|
// Run any renderer extensions
|
|
1928
|
-
if (this.options.extensions
|
|
1972
|
+
if (this.options.extensions?.renderers?.[anyToken.type]) {
|
|
1929
1973
|
const genericToken = anyToken;
|
|
1930
1974
|
const ret = this.options.extensions.renderers[genericToken.type].call({ parser: this }, genericToken);
|
|
1931
1975
|
if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'html', 'paragraph', 'text'].includes(genericToken.type)) {
|
|
@@ -1983,7 +2027,7 @@ class _Parser {
|
|
|
1983
2027
|
type: 'paragraph',
|
|
1984
2028
|
raw: body,
|
|
1985
2029
|
text: body,
|
|
1986
|
-
tokens: [{ type: 'text', raw: body, text: body }],
|
|
2030
|
+
tokens: [{ type: 'text', raw: body, text: body, escaped: true }],
|
|
1987
2031
|
});
|
|
1988
2032
|
}
|
|
1989
2033
|
else {
|
|
@@ -2008,13 +2052,12 @@ class _Parser {
|
|
|
2008
2052
|
/**
|
|
2009
2053
|
* Parse Inline Tokens
|
|
2010
2054
|
*/
|
|
2011
|
-
parseInline(tokens, renderer) {
|
|
2012
|
-
renderer = renderer || this.renderer;
|
|
2055
|
+
parseInline(tokens, renderer = this.renderer) {
|
|
2013
2056
|
let out = '';
|
|
2014
2057
|
for (let i = 0; i < tokens.length; i++) {
|
|
2015
2058
|
const anyToken = tokens[i];
|
|
2016
2059
|
// Run any renderer extensions
|
|
2017
|
-
if (this.options.extensions
|
|
2060
|
+
if (this.options.extensions?.renderers?.[anyToken.type]) {
|
|
2018
2061
|
const ret = this.options.extensions.renderers[anyToken.type].call({ parser: this }, anyToken);
|
|
2019
2062
|
if (ret !== false || !['escape', 'html', 'link', 'image', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(anyToken.type)) {
|
|
2020
2063
|
out += ret || '';
|
|
@@ -2419,7 +2462,7 @@ class Marked {
|
|
|
2419
2462
|
e.message += '\nPlease report this to https://github.com/markedjs/marked.';
|
|
2420
2463
|
if (silent) {
|
|
2421
2464
|
const msg = '<p>An error occurred:</p><pre>'
|
|
2422
|
-
+ escape
|
|
2465
|
+
+ escape(e.message + '', true)
|
|
2423
2466
|
+ '</pre>';
|
|
2424
2467
|
if (async) {
|
|
2425
2468
|
return Promise.resolve(msg);
|