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