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