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