marked 14.1.4 → 15.0.0
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 +902 -857
- package/lib/marked.cjs.map +1 -1
- package/lib/marked.d.cts +65 -5
- package/lib/marked.d.ts +65 -5
- package/lib/marked.esm.js +902 -857
- package/lib/marked.esm.js.map +1 -1
- package/lib/marked.umd.js +902 -857
- package/lib/marked.umd.js.map +1 -1
- package/man/marked.1 +1 -1
- package/marked.min.js +2 -2
- package/package.json +1 -1
package/lib/marked.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* marked
|
|
2
|
+
* marked v15.0.0 - 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,229 +49,595 @@ 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
|
-
|
|
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[cells.length - 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 {
|
|
304
641
|
type: 'hr',
|
|
305
642
|
raw: rtrim(cap[0], '\n'),
|
|
306
643
|
};
|
|
@@ -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
|
|
@@ -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({
|
|
@@ -549,7 +886,7 @@ class _Tokenizer {
|
|
|
549
886
|
if (!list.loose) {
|
|
550
887
|
// Check if list should be loose
|
|
551
888
|
const spacers = list.items[i].tokens.filter(t => t.type === 'space');
|
|
552
|
-
const hasMultipleLineBreaks = spacers.length > 0 && spacers.some(t =>
|
|
889
|
+
const hasMultipleLineBreaks = spacers.length > 0 && spacers.some(t => this.rules.other.anyLine.test(t.raw));
|
|
553
890
|
list.loose = hasMultipleLineBreaks;
|
|
554
891
|
}
|
|
555
892
|
}
|
|
@@ -578,8 +915,8 @@ class _Tokenizer {
|
|
|
578
915
|
def(src) {
|
|
579
916
|
const cap = this.rules.block.def.exec(src);
|
|
580
917
|
if (cap) {
|
|
581
|
-
const tag = cap[1].toLowerCase().replace(
|
|
582
|
-
const href = cap[2] ? cap[2].replace(
|
|
918
|
+
const tag = cap[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal, ' ');
|
|
919
|
+
const href = cap[2] ? cap[2].replace(this.rules.other.hrefBrackets, '$1').replace(this.rules.inline.anyPunctuation, '$1') : '';
|
|
583
920
|
const title = cap[3] ? cap[3].substring(1, cap[3].length - 1).replace(this.rules.inline.anyPunctuation, '$1') : cap[3];
|
|
584
921
|
return {
|
|
585
922
|
type: 'def',
|
|
@@ -595,13 +932,13 @@ class _Tokenizer {
|
|
|
595
932
|
if (!cap) {
|
|
596
933
|
return;
|
|
597
934
|
}
|
|
598
|
-
if (
|
|
935
|
+
if (!this.rules.other.tableDelimiter.test(cap[2])) {
|
|
599
936
|
// delimiter row must have a pipe (|) or colon (:) otherwise it is a setext heading
|
|
600
937
|
return;
|
|
601
938
|
}
|
|
602
939
|
const headers = splitCells(cap[1]);
|
|
603
|
-
const aligns = cap[2].replace(
|
|
604
|
-
const rows = cap[3] && cap[3].trim() ? cap[3].replace(
|
|
940
|
+
const aligns = cap[2].replace(this.rules.other.tableAlignChars, '').split('|');
|
|
941
|
+
const rows = cap[3] && cap[3].trim() ? cap[3].replace(this.rules.other.tableRowBlankLine, '').split('\n') : [];
|
|
605
942
|
const item = {
|
|
606
943
|
type: 'table',
|
|
607
944
|
raw: cap[0],
|
|
@@ -614,13 +951,13 @@ class _Tokenizer {
|
|
|
614
951
|
return;
|
|
615
952
|
}
|
|
616
953
|
for (const align of aligns) {
|
|
617
|
-
if (
|
|
954
|
+
if (this.rules.other.tableAlignRight.test(align)) {
|
|
618
955
|
item.align.push('right');
|
|
619
956
|
}
|
|
620
|
-
else if (
|
|
957
|
+
else if (this.rules.other.tableAlignCenter.test(align)) {
|
|
621
958
|
item.align.push('center');
|
|
622
959
|
}
|
|
623
|
-
else if (
|
|
960
|
+
else if (this.rules.other.tableAlignLeft.test(align)) {
|
|
624
961
|
item.align.push('left');
|
|
625
962
|
}
|
|
626
963
|
else {
|
|
@@ -690,572 +1027,281 @@ class _Tokenizer {
|
|
|
690
1027
|
return {
|
|
691
1028
|
type: 'escape',
|
|
692
1029
|
raw: cap[0],
|
|
693
|
-
text:
|
|
1030
|
+
text: cap[1],
|
|
694
1031
|
};
|
|
695
1032
|
}
|
|
696
1033
|
}
|
|
697
1034
|
tag(src) {
|
|
698
1035
|
const cap = this.rules.inline.tag.exec(src);
|
|
699
1036
|
if (cap) {
|
|
700
|
-
if (!this.lexer.state.inLink &&
|
|
1037
|
+
if (!this.lexer.state.inLink && this.rules.other.startATag.test(cap[0])) {
|
|
701
1038
|
this.lexer.state.inLink = true;
|
|
702
|
-
}
|
|
703
|
-
else if (this.lexer.state.inLink &&
|
|
704
|
-
this.lexer.state.inLink = false;
|
|
705
|
-
}
|
|
706
|
-
if (!this.lexer.state.inRawBlock &&
|
|
707
|
-
this.lexer.state.inRawBlock = true;
|
|
708
|
-
}
|
|
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
|
-
};
|
|
1039
|
+
}
|
|
1040
|
+
else if (this.lexer.state.inLink && this.rules.other.endATag.test(cap[0])) {
|
|
1041
|
+
this.lexer.state.inLink = false;
|
|
1042
|
+
}
|
|
1043
|
+
if (!this.lexer.state.inRawBlock && this.rules.other.startPreScriptTag.test(cap[0])) {
|
|
1044
|
+
this.lexer.state.inRawBlock = true;
|
|
1045
|
+
}
|
|
1046
|
+
else if (this.lexer.state.inRawBlock && this.rules.other.endPreScriptTag.test(cap[0])) {
|
|
1047
|
+
this.lexer.state.inRawBlock = false;
|
|
1048
|
+
}
|
|
1049
|
+
return {
|
|
1050
|
+
type: 'html',
|
|
1051
|
+
raw: cap[0],
|
|
1052
|
+
inLink: this.lexer.state.inLink,
|
|
1053
|
+
inRawBlock: this.lexer.state.inRawBlock,
|
|
1054
|
+
block: false,
|
|
1055
|
+
text: cap[0],
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
link(src) {
|
|
1060
|
+
const cap = this.rules.inline.link.exec(src);
|
|
1061
|
+
if (cap) {
|
|
1062
|
+
const trimmedUrl = cap[2].trim();
|
|
1063
|
+
if (!this.options.pedantic && this.rules.other.startAngleBracket.test(trimmedUrl)) {
|
|
1064
|
+
// commonmark requires matching angle brackets
|
|
1065
|
+
if (!(this.rules.other.endAngleBracket.test(trimmedUrl))) {
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
// ending angle bracket cannot be escaped
|
|
1069
|
+
const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\');
|
|
1070
|
+
if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
else {
|
|
1075
|
+
// find closing parenthesis
|
|
1076
|
+
const lastParenIndex = findClosingBracket(cap[2], '()');
|
|
1077
|
+
if (lastParenIndex > -1) {
|
|
1078
|
+
const start = cap[0].indexOf('!') === 0 ? 5 : 4;
|
|
1079
|
+
const linkLen = start + cap[1].length + lastParenIndex;
|
|
1080
|
+
cap[2] = cap[2].substring(0, lastParenIndex);
|
|
1081
|
+
cap[0] = cap[0].substring(0, linkLen).trim();
|
|
1082
|
+
cap[3] = '';
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
let href = cap[2];
|
|
1086
|
+
let title = '';
|
|
1087
|
+
if (this.options.pedantic) {
|
|
1088
|
+
// split pedantic href and title
|
|
1089
|
+
const link = this.rules.other.pedanticHrefTitle.exec(href);
|
|
1090
|
+
if (link) {
|
|
1091
|
+
href = link[1];
|
|
1092
|
+
title = link[3];
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
title = cap[3] ? cap[3].slice(1, -1) : '';
|
|
1097
|
+
}
|
|
1098
|
+
href = href.trim();
|
|
1099
|
+
if (this.rules.other.startAngleBracket.test(href)) {
|
|
1100
|
+
if (this.options.pedantic && !(this.rules.other.endAngleBracket.test(trimmedUrl))) {
|
|
1101
|
+
// pedantic allows starting angle bracket without ending angle bracket
|
|
1102
|
+
href = href.slice(1);
|
|
1103
|
+
}
|
|
1104
|
+
else {
|
|
1105
|
+
href = href.slice(1, -1);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
return outputLink(cap, {
|
|
1109
|
+
href: href ? href.replace(this.rules.inline.anyPunctuation, '$1') : href,
|
|
1110
|
+
title: title ? title.replace(this.rules.inline.anyPunctuation, '$1') : title,
|
|
1111
|
+
}, cap[0], this.lexer, this.rules);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
reflink(src, links) {
|
|
1115
|
+
let cap;
|
|
1116
|
+
if ((cap = this.rules.inline.reflink.exec(src))
|
|
1117
|
+
|| (cap = this.rules.inline.nolink.exec(src))) {
|
|
1118
|
+
const linkString = (cap[2] || cap[1]).replace(this.rules.other.multipleSpaceGlobal, ' ');
|
|
1119
|
+
const link = links[linkString.toLowerCase()];
|
|
1120
|
+
if (!link) {
|
|
1121
|
+
const text = cap[0].charAt(0);
|
|
1122
|
+
return {
|
|
1123
|
+
type: 'text',
|
|
1124
|
+
raw: text,
|
|
1125
|
+
text,
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
return outputLink(cap, link, cap[0], this.lexer, this.rules);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
emStrong(src, maskedSrc, prevChar = '') {
|
|
1132
|
+
let match = this.rules.inline.emStrongLDelim.exec(src);
|
|
1133
|
+
if (!match)
|
|
1134
|
+
return;
|
|
1135
|
+
// _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
|
|
1136
|
+
if (match[3] && prevChar.match(this.rules.other.unicodeAlphaNumeric))
|
|
1137
|
+
return;
|
|
1138
|
+
const nextChar = match[1] || match[2] || '';
|
|
1139
|
+
if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {
|
|
1140
|
+
// unicode Regex counts emoji as 1 char; spread into array for proper count (used multiple times below)
|
|
1141
|
+
const lLength = [...match[0]].length - 1;
|
|
1142
|
+
let rDelim, rLength, delimTotal = lLength, midDelimTotal = 0;
|
|
1143
|
+
const endReg = match[0][0] === '*' ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd;
|
|
1144
|
+
endReg.lastIndex = 0;
|
|
1145
|
+
// Clip maskedSrc to same section of string as src (move to lexer?)
|
|
1146
|
+
maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
|
|
1147
|
+
while ((match = endReg.exec(maskedSrc)) != null) {
|
|
1148
|
+
rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
|
|
1149
|
+
if (!rDelim)
|
|
1150
|
+
continue; // skip single * in __abc*abc__
|
|
1151
|
+
rLength = [...rDelim].length;
|
|
1152
|
+
if (match[3] || match[4]) { // found another Left Delim
|
|
1153
|
+
delimTotal += rLength;
|
|
1154
|
+
continue;
|
|
1155
|
+
}
|
|
1156
|
+
else if (match[5] || match[6]) { // either Left or Right Delim
|
|
1157
|
+
if (lLength % 3 && !((lLength + rLength) % 3)) {
|
|
1158
|
+
midDelimTotal += rLength;
|
|
1159
|
+
continue; // CommonMark Emphasis Rules 9-10
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
delimTotal -= rLength;
|
|
1163
|
+
if (delimTotal > 0)
|
|
1164
|
+
continue; // Haven't found enough closing delimiters
|
|
1165
|
+
// Remove extra characters. *a*** -> *a*
|
|
1166
|
+
rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
|
|
1167
|
+
// char length can be >1 for unicode characters;
|
|
1168
|
+
const lastCharLength = [...match[0]][0].length;
|
|
1169
|
+
const raw = src.slice(0, lLength + match.index + lastCharLength + rLength);
|
|
1170
|
+
// Create `em` if smallest delimiter has odd char count. *a***
|
|
1171
|
+
if (Math.min(lLength, rLength) % 2) {
|
|
1172
|
+
const text = raw.slice(1, -1);
|
|
1173
|
+
return {
|
|
1174
|
+
type: 'em',
|
|
1175
|
+
raw,
|
|
1176
|
+
text,
|
|
1177
|
+
tokens: this.lexer.inlineTokens(text),
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
// Create 'strong' if smallest delimiter has even char count. **a***
|
|
1181
|
+
const text = raw.slice(2, -2);
|
|
1182
|
+
return {
|
|
1183
|
+
type: 'strong',
|
|
1184
|
+
raw,
|
|
1185
|
+
text,
|
|
1186
|
+
tokens: this.lexer.inlineTokens(text),
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
codespan(src) {
|
|
1192
|
+
const cap = this.rules.inline.code.exec(src);
|
|
1193
|
+
if (cap) {
|
|
1194
|
+
let text = cap[2].replace(this.rules.other.newLineCharGlobal, ' ');
|
|
1195
|
+
const hasNonSpaceChars = this.rules.other.nonSpaceChar.test(text);
|
|
1196
|
+
const hasSpaceCharsOnBothEnds = this.rules.other.startingSpaceChar.test(text) && this.rules.other.endingSpaceChar.test(text);
|
|
1197
|
+
if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
|
|
1198
|
+
text = text.substring(1, text.length - 1);
|
|
1199
|
+
}
|
|
1200
|
+
return {
|
|
1201
|
+
type: 'codespan',
|
|
1202
|
+
raw: cap[0],
|
|
1203
|
+
text,
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
br(src) {
|
|
1208
|
+
const cap = this.rules.inline.br.exec(src);
|
|
1209
|
+
if (cap) {
|
|
1210
|
+
return {
|
|
1211
|
+
type: 'br',
|
|
1212
|
+
raw: cap[0],
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
del(src) {
|
|
1217
|
+
const cap = this.rules.inline.del.exec(src);
|
|
1218
|
+
if (cap) {
|
|
1219
|
+
return {
|
|
1220
|
+
type: 'del',
|
|
1221
|
+
raw: cap[0],
|
|
1222
|
+
text: cap[2],
|
|
1223
|
+
tokens: this.lexer.inlineTokens(cap[2]),
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
autolink(src) {
|
|
1228
|
+
const cap = this.rules.inline.autolink.exec(src);
|
|
1229
|
+
if (cap) {
|
|
1230
|
+
let text, href;
|
|
1231
|
+
if (cap[2] === '@') {
|
|
1232
|
+
text = cap[1];
|
|
1233
|
+
href = 'mailto:' + text;
|
|
1234
|
+
}
|
|
1235
|
+
else {
|
|
1236
|
+
text = cap[1];
|
|
1237
|
+
href = text;
|
|
1238
|
+
}
|
|
1239
|
+
return {
|
|
1240
|
+
type: 'link',
|
|
1241
|
+
raw: cap[0],
|
|
1242
|
+
text,
|
|
1243
|
+
href,
|
|
1244
|
+
tokens: [
|
|
1245
|
+
{
|
|
1246
|
+
type: 'text',
|
|
1247
|
+
raw: text,
|
|
1248
|
+
text,
|
|
1249
|
+
},
|
|
1250
|
+
],
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
url(src) {
|
|
1255
|
+
let cap;
|
|
1256
|
+
if (cap = this.rules.inline.url.exec(src)) {
|
|
1257
|
+
let text, href;
|
|
1258
|
+
if (cap[2] === '@') {
|
|
1259
|
+
text = cap[0];
|
|
1260
|
+
href = 'mailto:' + text;
|
|
1261
|
+
}
|
|
1262
|
+
else {
|
|
1263
|
+
// do extended autolink path validation
|
|
1264
|
+
let prevCapZero;
|
|
1265
|
+
do {
|
|
1266
|
+
prevCapZero = cap[0];
|
|
1267
|
+
cap[0] = this.rules.inline._backpedal.exec(cap[0])?.[0] ?? '';
|
|
1268
|
+
} while (prevCapZero !== cap[0]);
|
|
1269
|
+
text = cap[0];
|
|
1270
|
+
if (cap[1] === 'www.') {
|
|
1271
|
+
href = 'http://' + cap[0];
|
|
1272
|
+
}
|
|
1273
|
+
else {
|
|
1274
|
+
href = cap[0];
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
return {
|
|
1278
|
+
type: 'link',
|
|
1279
|
+
raw: cap[0],
|
|
1280
|
+
text,
|
|
1281
|
+
href,
|
|
1282
|
+
tokens: [
|
|
1283
|
+
{
|
|
1284
|
+
type: 'text',
|
|
1285
|
+
raw: text,
|
|
1286
|
+
text,
|
|
1287
|
+
},
|
|
1288
|
+
],
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
inlineText(src) {
|
|
1293
|
+
const cap = this.rules.inline.text.exec(src);
|
|
1294
|
+
if (cap) {
|
|
1295
|
+
const escaped = this.lexer.state.inRawBlock;
|
|
1296
|
+
return {
|
|
1297
|
+
type: 'text',
|
|
1298
|
+
raw: cap[0],
|
|
1299
|
+
text: cap[0],
|
|
1300
|
+
escaped,
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1259
1305
|
|
|
1260
1306
|
/**
|
|
1261
1307
|
* Block Lexer
|
|
@@ -1282,6 +1328,7 @@ class _Lexer {
|
|
|
1282
1328
|
top: true,
|
|
1283
1329
|
};
|
|
1284
1330
|
const rules = {
|
|
1331
|
+
other,
|
|
1285
1332
|
block: block.normal,
|
|
1286
1333
|
inline: inline.normal,
|
|
1287
1334
|
};
|
|
@@ -1328,7 +1375,7 @@ class _Lexer {
|
|
|
1328
1375
|
*/
|
|
1329
1376
|
lex(src) {
|
|
1330
1377
|
src = src
|
|
1331
|
-
.replace(
|
|
1378
|
+
.replace(other.carriageReturn, '\n');
|
|
1332
1379
|
this.blockTokens(src, this.tokens);
|
|
1333
1380
|
for (let i = 0; i < this.inlineQueue.length; i++) {
|
|
1334
1381
|
const next = this.inlineQueue[i];
|
|
@@ -1339,7 +1386,7 @@ class _Lexer {
|
|
|
1339
1386
|
}
|
|
1340
1387
|
blockTokens(src, tokens = [], lastParagraphClipped = false) {
|
|
1341
1388
|
if (this.options.pedantic) {
|
|
1342
|
-
src = src.replace(
|
|
1389
|
+
src = src.replace(other.tabCharGlobal, ' ').replace(other.spaceLine, '');
|
|
1343
1390
|
}
|
|
1344
1391
|
let token;
|
|
1345
1392
|
let lastToken;
|
|
@@ -1571,13 +1618,7 @@ class _Lexer {
|
|
|
1571
1618
|
if (token = this.tokenizer.tag(src)) {
|
|
1572
1619
|
src = src.substring(token.raw.length);
|
|
1573
1620
|
lastToken = tokens[tokens.length - 1];
|
|
1574
|
-
|
|
1575
|
-
lastToken.raw += token.raw;
|
|
1576
|
-
lastToken.text += token.text;
|
|
1577
|
-
}
|
|
1578
|
-
else {
|
|
1579
|
-
tokens.push(token);
|
|
1580
|
-
}
|
|
1621
|
+
tokens.push(token);
|
|
1581
1622
|
continue;
|
|
1582
1623
|
}
|
|
1583
1624
|
// link
|
|
@@ -1696,17 +1737,17 @@ class _Renderer {
|
|
|
1696
1737
|
return '';
|
|
1697
1738
|
}
|
|
1698
1739
|
code({ text, lang, escaped }) {
|
|
1699
|
-
const langString = (lang || '').match(
|
|
1700
|
-
const code = text.replace(
|
|
1740
|
+
const langString = (lang || '').match(other.notSpaceStart)?.[0];
|
|
1741
|
+
const code = text.replace(other.endingNewline, '') + '\n';
|
|
1701
1742
|
if (!langString) {
|
|
1702
1743
|
return '<pre><code>'
|
|
1703
|
-
+ (escaped ? code : escape
|
|
1744
|
+
+ (escaped ? code : escape(code, true))
|
|
1704
1745
|
+ '</code></pre>\n';
|
|
1705
1746
|
}
|
|
1706
1747
|
return '<pre><code class="language-'
|
|
1707
|
-
+ escape
|
|
1748
|
+
+ escape(langString)
|
|
1708
1749
|
+ '">'
|
|
1709
|
-
+ (escaped ? code : escape
|
|
1750
|
+
+ (escaped ? code : escape(code, true))
|
|
1710
1751
|
+ '</code></pre>\n';
|
|
1711
1752
|
}
|
|
1712
1753
|
blockquote({ tokens }) {
|
|
@@ -1742,7 +1783,8 @@ class _Renderer {
|
|
|
1742
1783
|
if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') {
|
|
1743
1784
|
item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
|
|
1744
1785
|
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;
|
|
1786
|
+
item.tokens[0].tokens[0].text = checkbox + ' ' + escape(item.tokens[0].tokens[0].text);
|
|
1787
|
+
item.tokens[0].tokens[0].escaped = true;
|
|
1746
1788
|
}
|
|
1747
1789
|
}
|
|
1748
1790
|
else {
|
|
@@ -1750,6 +1792,7 @@ class _Renderer {
|
|
|
1750
1792
|
type: 'text',
|
|
1751
1793
|
raw: checkbox + ' ',
|
|
1752
1794
|
text: checkbox + ' ',
|
|
1795
|
+
escaped: true,
|
|
1753
1796
|
});
|
|
1754
1797
|
}
|
|
1755
1798
|
}
|
|
@@ -1815,7 +1858,7 @@ class _Renderer {
|
|
|
1815
1858
|
return `<em>${this.parser.parseInline(tokens)}</em>`;
|
|
1816
1859
|
}
|
|
1817
1860
|
codespan({ text }) {
|
|
1818
|
-
return `<code>${text}</code>`;
|
|
1861
|
+
return `<code>${escape(text, true)}</code>`;
|
|
1819
1862
|
}
|
|
1820
1863
|
br(token) {
|
|
1821
1864
|
return '<br>';
|
|
@@ -1832,7 +1875,7 @@ class _Renderer {
|
|
|
1832
1875
|
href = cleanHref;
|
|
1833
1876
|
let out = '<a href="' + href + '"';
|
|
1834
1877
|
if (title) {
|
|
1835
|
-
out += ' title="' + title + '"';
|
|
1878
|
+
out += ' title="' + (escape(title)) + '"';
|
|
1836
1879
|
}
|
|
1837
1880
|
out += '>' + text + '</a>';
|
|
1838
1881
|
return out;
|
|
@@ -1840,18 +1883,20 @@ class _Renderer {
|
|
|
1840
1883
|
image({ href, title, text }) {
|
|
1841
1884
|
const cleanHref = cleanUrl(href);
|
|
1842
1885
|
if (cleanHref === null) {
|
|
1843
|
-
return text;
|
|
1886
|
+
return escape(text);
|
|
1844
1887
|
}
|
|
1845
1888
|
href = cleanHref;
|
|
1846
1889
|
let out = `<img src="${href}" alt="${text}"`;
|
|
1847
1890
|
if (title) {
|
|
1848
|
-
out += ` title="${title}"`;
|
|
1891
|
+
out += ` title="${escape(title)}"`;
|
|
1849
1892
|
}
|
|
1850
1893
|
out += '>';
|
|
1851
1894
|
return out;
|
|
1852
1895
|
}
|
|
1853
1896
|
text(token) {
|
|
1854
|
-
return 'tokens' in token && token.tokens
|
|
1897
|
+
return 'tokens' in token && token.tokens
|
|
1898
|
+
? this.parser.parseInline(token.tokens)
|
|
1899
|
+
: ('escaped' in token && token.escaped ? token.text : escape(token.text));
|
|
1855
1900
|
}
|
|
1856
1901
|
}
|
|
1857
1902
|
|
|
@@ -1985,7 +2030,7 @@ class _Parser {
|
|
|
1985
2030
|
type: 'paragraph',
|
|
1986
2031
|
raw: body,
|
|
1987
2032
|
text: body,
|
|
1988
|
-
tokens: [{ type: 'text', raw: body, text: body }],
|
|
2033
|
+
tokens: [{ type: 'text', raw: body, text: body, escaped: true }],
|
|
1989
2034
|
});
|
|
1990
2035
|
}
|
|
1991
2036
|
else {
|
|
@@ -2421,7 +2466,7 @@ class Marked {
|
|
|
2421
2466
|
e.message += '\nPlease report this to https://github.com/markedjs/marked.';
|
|
2422
2467
|
if (silent) {
|
|
2423
2468
|
const msg = '<p>An error occurred:</p><pre>'
|
|
2424
|
-
+ escape
|
|
2469
|
+
+ escape(e.message + '', true)
|
|
2425
2470
|
+ '</pre>';
|
|
2426
2471
|
if (async) {
|
|
2427
2472
|
return Promise.resolve(msg);
|