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