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