marked 14.1.4 → 15.0.0

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