From 4482e0c801bb7986144af194a34d852bfa94be9c Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Fri, 18 Jun 2021 00:58:35 -0400 Subject: [PATCH 01/20] Rewrite List Tokenizer, Fix incorrect "new" spec tests --- src/Lexer.js | 6 +- src/Parser.js | 2 +- src/Tokenizer.js | 184 ++++++++++----------- src/rules.js | 10 +- test/specs/commonmark/commonmark.0.29.json | 12 +- test/specs/gfm/commonmark.0.29.json | 12 +- test/specs/new/def_blocks.html | 15 +- test/specs/new/list_paren_delimiter.html | 6 +- test/specs/new/list_paren_delimiter.md | 1 + 9 files changed, 121 insertions(+), 127 deletions(-) diff --git a/src/Lexer.js b/src/Lexer.js index 6c02ed65e5..26add3cf0a 100644 --- a/src/Lexer.js +++ b/src/Lexer.js @@ -140,7 +140,7 @@ module.exports = class Lexer { src = src.substring(token.raw.length); lastToken = tokens[tokens.length - 1]; // An indented code block cannot interrupt a paragraph. - if (lastToken && lastToken.type === 'paragraph') { + if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) { lastToken.raw += '\n' + token.raw; lastToken.text += '\n' + token.text; } else { @@ -190,7 +190,7 @@ module.exports = class Lexer { src = src.substring(token.raw.length); l = token.items.length; for (i = 0; i < l; i++) { - token.items[i].tokens = this.blockTokens(token.items[i].text, [], false); + token.items[i].tokens = this.blockTokens(token.items[i].text, [], token.loose); } tokens.push(token); continue; @@ -203,7 +203,7 @@ module.exports = class Lexer { continue; } - // def + // def //NOTE: 'top' here doesn't make a difference on spec tests, and CM spec says definitions *can* go inside other blocks if (top && (token = this.tokenizer.def(src))) { src = src.substring(token.raw.length); if (!this.tokens.links[token.tag]) { diff --git a/src/Parser.js b/src/Parser.js index 81fcb7da1e..13230f7d49 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -138,7 +138,7 @@ module.exports = class Parser { if (item.task) { checkbox = this.renderer.checkbox(checked); if (loose) { - if (item.tokens.length > 0 && item.tokens[0].type === 'text') { + if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') { item.tokens[0].text = checkbox + ' ' + item.tokens[0].text; if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') { item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text; diff --git a/src/Tokenizer.js b/src/Tokenizer.js index 0177551af7..31f79298f7 100644 --- a/src/Tokenizer.js +++ b/src/Tokenizer.js @@ -3,7 +3,8 @@ const { rtrim, splitCells, escape, - findClosingBracket + findClosingBracket, + edit } = require('./helpers.js'); function outputLink(cap, link, raw) { @@ -194,132 +195,129 @@ module.exports = class Tokenizer { } list(src) { - const cap = this.rules.block.list.exec(src); + let cap = this.rules.block.list.exec(src); if (cap) { let raw = cap[0]; - const bull = cap[2]; + let bull = cap[1].trim(); + let indent = cap[1].length + cap[2].search(/[^ ]/); //Find first non-space char const isordered = bull.length > 1; const list = { type: 'list', - raw, + raw: '', ordered: isordered, start: isordered ? +bull.slice(0, -1) : '', loose: false, items: [] }; - // Get each top-level item. - const itemMatch = cap[0].match(this.rules.block.item); - - let next = false, - item, - space, - bcurr, - bnext, - addBack, - loose, - istask, - ischecked, - endMatch; - - let l = itemMatch.length; - bcurr = this.rules.block.listItemStart.exec(itemMatch[0]); - for (let i = 0; i < l; i++) { - item = itemMatch[i]; - raw = item; - - if (!this.options.pedantic) { - // Determine if current item contains the end of the list - endMatch = item.match(new RegExp('\\n\\s*\\n {0,' + (bcurr[0].length - 1) + '}\\S')); - if (endMatch) { - addBack = item.length - endMatch.index + itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - - item = item.substring(0, endMatch.index); - raw = item; - l = i + 1; - } + let istask, + ischecked; + + bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`; + + if(this.options.pedantic) { + bull = isordered ? bull : '[*+-]'; + } + + //Get next list item, stopping at next top-level bullet or blank line followed by insufficient indent. TODO: REPLACE \\s*\\n with \\h*\\n so \n isn't included in \s, then just trim string. + //let itemRegex = `^( {0,3}${bull})( [^\\n]*(?:\\n(?!hr)(?! {0,1}bull)(?!\\s*\\n {0,${indent - 1}}[^\\s])[^\\n]*)*(?:\\s*\\n)*|\\s*)`; + let itemRegex = new RegExp(`^( {0,3}${bull})((?: [^\\n]*|\\h*)(?:\\n[^\\n]*)*(?:\\n|$))`); + + let blankLine = false; + let endsWithBlankLine = false; + + // Get each top-level item + while(src) { + if(this.rules.block.hr.exec(src)) // End list if we encounter an HR (possibly move into itemRegex?) + break; + + cap = itemRegex.exec(src); + if (!cap) + break; + + // If the previous item ended with a blank line, the list is loose + if(endsWithBlankLine) { + list.loose = true; } - // Determine whether the next list item belongs here. - // Backpedal if it does not belong in this list. - if (i !== l - 1) { - bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]); - if ( - !this.options.pedantic - ? bnext[1].length >= bcurr[0].length || bnext[1].length > 3 - : bnext[1].length > bcurr[1].length - ) { - // nested list or continuation - itemMatch.splice(i, 2, itemMatch[i] + (!this.options.pedantic && bnext[1].length < bcurr[0].length && !itemMatch[i].match(/\n$/) ? '' : '\n') + itemMatch[i + 1]); - i--; - l--; - continue; - } else if ( - // different bullet style - !this.options.pedantic || this.options.smartLists - ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] - : isordered === (bnext[2].length === 1) - ) { - addBack = itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - i = l - 1; + let lines = cap[2].split('\n'); + indent = cap[1].length + Math.min(4, cap[2].search(/[^ ]/)); //Find first non-space char + + let line; + blankLine = false; + let itemContents = lines[0].slice(indent - cap[1].length); + + raw = cap[0]; + + let i; + for (i = 1; i < lines.length; i++) { + line = lines[i]; + + // End list item if found start of new bullet + if(line.match(new RegExp(`^ {0,${Math.min(3, indent - 1)}}[*+-]|\\d{1,9}[.)]`))) { + raw = cap[1] + cap[2].split('\n').slice(0, i).join('\n') + '\n'; + break; } - bcurr = bnext; - } - // Remove the list item's bullet - // so it is seen as the next token. - space = item.length; - item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); - - // Outdent whatever the - // list item contains. Hacky. - if (~item.indexOf('\n ')) { - space -= item.length; - item = !this.options.pedantic - ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') - : item.replace(/^ {1,4}/gm, ''); - } + // Until we encounter a blank line, item contents do not need indentation + if(!blankLine) { + if(!line.trim()) { // Check if current line is empty + blankLine = true; + } + + // Dedent if possible + if(line.search(/[^ ]/) >= indent) { + itemContents += '\n' + line.slice(indent); + } + else { + itemContents += '\n' + line; + } + continue; + } - // trim item newlines at end - item = rtrim(item, '\n'); - if (i !== l - 1) { - raw = raw + '\n'; + // Dedent this line + if(line.search(/[^ ]/) >= indent || !line.trim()) { + itemContents += '\n' + line.slice(indent); + continue; + } + else { // Line was not properly indented; end of this item + raw = cap[1] + cap[2].split('\n').slice(0, i).join('\n') + '\n'; + break; + } } - // Determine whether item is loose or not. - // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ - // for discount behavior. - loose = next || /\n\n(?!\s*$)/.test(raw); - if (i !== l - 1) { - next = raw.slice(-2) === '\n\n'; - if (!loose) loose = next; + // List is loose if this item had any blank lines + if(itemContents.match(/\n\h*\n[^\n]/)) { + list.loose = true; } - if (loose) { - list.loose = true; + // List is loose if item ended in blank lines, unless it's the final item + if(raw.match(/\n\h*\n\h*$/)) { + endsWithBlankLine = true; } // Check for task list items if (this.options.gfm) { - istask = /^\[[ xX]\] /.test(item); + istask = /^\[[ xX]\] /.exec(itemContents); ischecked = undefined; if (istask) { - ischecked = item[1] !== ' '; - item = item.replace(/^\[[ xX]\] +/, ''); + ischecked = istask[0] !== '[ ] '; + itemContents = itemContents.replace(/^\[[ xX]\] +/, ''); } } list.items.push({ type: 'list_item', - raw, - task: istask, + raw: raw, + task: istask ? true : false, checked: ischecked, - loose: loose, - text: item + loose: blankLine, + text: itemContents }); + + list.raw += raw; + src = src.slice(raw.length); } return list; diff --git a/src/rules.js b/src/rules.js index 3cd33fe0d7..361d6ef6c5 100644 --- a/src/rules.js +++ b/src/rules.js @@ -14,7 +14,7 @@ const block = { hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/, + list: /^( {0,3}bull)( [^\n]+?)(?:\n|$)/, html: '^ {0,3}(?:' // optional indentation + '<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + '|comment[^\\n]*(\\n+|$)' // (2) @@ -43,10 +43,10 @@ block.def = edit(block.def) .getRegex(); block.bullet = /(?:[*+-]|\d{1,9}[.)])/; -block.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/; -block.item = edit(block.item, 'gm') - .replace(/bull/g, block.bullet) - .getRegex(); +// block.item = /^( {0,3})(bull)( [^\n]*(?:\n(?! {0,3}bull )[^\n]*)*\n?)/; +// block.item = edit(block.item, 'gm') +// .replace(/bull/g, block.bullet) +// .getRegex(); block.listItemStart = edit(/^( *)(bull) */) .replace('bull', block.bullet) diff --git a/test/specs/commonmark/commonmark.0.29.json b/test/specs/commonmark/commonmark.0.29.json index 2e47a39b95..653ec691b5 100644 --- a/test/specs/commonmark/commonmark.0.29.json +++ b/test/specs/commonmark/commonmark.0.29.json @@ -1861,8 +1861,7 @@ "example": 232, "start_line": 3954, "end_line": 3966, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "1. foo\n\n ```\n bar\n ```\n\n baz\n\n > bam\n", @@ -1878,8 +1877,7 @@ "example": 234, "start_line": 3999, "end_line": 4017, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "123456789. ok\n", @@ -2220,8 +2218,7 @@ "example": 276, "start_line": 5029, "end_line": 5048, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- foo\n - bar\n - baz\n\n\n bim\n", @@ -2310,8 +2307,7 @@ "example": 287, "start_line": 5286, "end_line": 5304, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- a\n- ```\n b\n\n\n ```\n- c\n", diff --git a/test/specs/gfm/commonmark.0.29.json b/test/specs/gfm/commonmark.0.29.json index a412d89779..03edb79a68 100644 --- a/test/specs/gfm/commonmark.0.29.json +++ b/test/specs/gfm/commonmark.0.29.json @@ -1861,8 +1861,7 @@ "example": 232, "start_line": 3954, "end_line": 3966, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "1. foo\n\n ```\n bar\n ```\n\n baz\n\n > bam\n", @@ -1878,8 +1877,7 @@ "example": 234, "start_line": 3999, "end_line": 4017, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "123456789. ok\n", @@ -2220,8 +2218,7 @@ "example": 276, "start_line": 5029, "end_line": 5048, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- foo\n - bar\n - baz\n\n\n bim\n", @@ -2310,8 +2307,7 @@ "example": 287, "start_line": 5286, "end_line": 5304, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- a\n- ```\n b\n\n\n ```\n- c\n", diff --git a/test/specs/new/def_blocks.html b/test/specs/new/def_blocks.html index 5d8de49c04..23207da91e 100644 --- a/test/specs/new/def_blocks.html +++ b/test/specs/new/def_blocks.html @@ -12,13 +12,14 @@ - - - diff --git a/test/specs/new/list_paren_delimiter.html b/test/specs/new/list_paren_delimiter.html index 4999ee3efa..5f06396fab 100644 --- a/test/specs/new/list_paren_delimiter.html +++ b/test/specs/new/list_paren_delimiter.html @@ -3,9 +3,11 @@
  • two
  • three
  • - + +
    +
    1. two
    2. three
    3. four
    4. -
    \ No newline at end of file + diff --git a/test/specs/new/list_paren_delimiter.md b/test/specs/new/list_paren_delimiter.md index d7ff7f3aeb..2443a64a85 100644 --- a/test/specs/new/list_paren_delimiter.md +++ b/test/specs/new/list_paren_delimiter.md @@ -2,6 +2,7 @@ 2) two 3) three +*** 2) two 3) three From ec216ae8895b62c08576c3c000013d9bb64b0bcf Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Fri, 18 Jun 2021 02:07:45 -0400 Subject: [PATCH 02/20] cleanup --- lib/marked.esm.js | 640 ++++++++++++++++++++++------------------------ lib/marked.js | 594 +++++++++++++++++++++--------------------- src/Tokenizer.js | 54 ++-- 3 files changed, 626 insertions(+), 662 deletions(-) diff --git a/lib/marked.esm.js b/lib/marked.esm.js index 8d916b5c92..53a297398a 100644 --- a/lib/marked.esm.js +++ b/lib/marked.esm.js @@ -9,9 +9,13 @@ * The code in this file is generated from files in ./src/ */ -var defaults$5 = {exports: {}}; +function createCommonjsModule(fn) { + var module = { exports: {} }; + return fn(module, module.exports), module.exports; +} -function getDefaults$1() { +var defaults = createCommonjsModule(function (module) { +function getDefaults() { return { baseUrl: null, breaks: false, @@ -34,20 +38,20 @@ function getDefaults$1() { }; } -function changeDefaults$1(newDefaults) { - defaults$5.exports.defaults = newDefaults; +function changeDefaults(newDefaults) { + module.exports.defaults = newDefaults; } -defaults$5.exports = { - defaults: getDefaults$1(), - getDefaults: getDefaults$1, - changeDefaults: changeDefaults$1 +module.exports = { + defaults: getDefaults(), + getDefaults, + changeDefaults }; +}); /** * Helpers */ - const escapeTest = /[&<>"']/; const escapeReplace = /[&<>"']/g; const escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/; @@ -60,7 +64,7 @@ const escapeReplacements = { "'": ''' }; const getEscapeReplacement = (ch) => escapeReplacements[ch]; -function escape$3(html, encode) { +function escape(html, encode) { if (encode) { if (escapeTest.test(html)) { return html.replace(escapeReplace, getEscapeReplacement); @@ -76,7 +80,7 @@ function escape$3(html, encode) { const unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig; -function unescape$1(html) { +function unescape(html) { // explicitly match decimal, hex, and named HTML entities return html.replace(unescapeTest, (_, n) => { n = n.toLowerCase(); @@ -91,7 +95,7 @@ function unescape$1(html) { } const caret = /(^|[^\[])\^/g; -function edit$1(regex, opt) { +function edit(regex, opt) { regex = regex.source || regex; opt = opt || ''; const obj = { @@ -110,11 +114,11 @@ function edit$1(regex, opt) { const nonWordAndColonTest = /[^\w:]/g; const originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; -function cleanUrl$1(sanitize, base, href) { +function cleanUrl(sanitize, base, href) { if (sanitize) { let prot; try { - prot = decodeURIComponent(unescape$1(href)) + prot = decodeURIComponent(unescape(href)) .replace(nonWordAndColonTest, '') .toLowerCase(); } catch (e) { @@ -148,7 +152,7 @@ function resolveUrl(base, href) { if (justDomain.test(base)) { baseUrls[' ' + base] = base + '/'; } else { - baseUrls[' ' + base] = rtrim$1(base, '/', true); + baseUrls[' ' + base] = rtrim(base, '/', true); } } base = baseUrls[' ' + base]; @@ -169,9 +173,9 @@ function resolveUrl(base, href) { } } -const noopTest$1 = { exec: function noopTest() {} }; +const noopTest = { exec: function noopTest() {} }; -function merge$2(obj) { +function merge(obj) { let i = 1, target, key; @@ -188,7 +192,7 @@ function merge$2(obj) { return obj; } -function splitCells$1(tableRow, count) { +function splitCells(tableRow, count) { // ensure that every cell-delimiting pipe has a space // before it to distinguish it from an escaped pipe const row = tableRow.replace(/\|/g, (match, offset, str) => { @@ -223,7 +227,7 @@ function splitCells$1(tableRow, count) { // Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). // /c*$/ is vulnerable to REDOS. // invert: Remove suffix of non-c chars instead. Default falsey. -function rtrim$1(str, c, invert) { +function rtrim(str, c, invert) { const l = str.length; if (l === 0) { return ''; @@ -247,7 +251,7 @@ function rtrim$1(str, c, invert) { return str.substr(0, l - suffLen); } -function findClosingBracket$1(str, b) { +function findClosingBracket(str, b) { if (str.indexOf(b[1]) === -1) { return -1; } @@ -269,14 +273,14 @@ function findClosingBracket$1(str, b) { return -1; } -function checkSanitizeDeprecation$1(opt) { +function checkSanitizeDeprecation(opt) { if (opt && opt.sanitize && !opt.silent) { console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); } } // copied from https://stackoverflow.com/a/5450113/806777 -function repeatString$1(pattern, count) { +function repeatString(pattern, count) { if (count < 1) { return ''; } @@ -292,31 +296,32 @@ function repeatString$1(pattern, count) { } var helpers = { - escape: escape$3, - unescape: unescape$1, - edit: edit$1, - cleanUrl: cleanUrl$1, + escape, + unescape, + edit, + cleanUrl, resolveUrl, - noopTest: noopTest$1, - merge: merge$2, - splitCells: splitCells$1, - rtrim: rtrim$1, - findClosingBracket: findClosingBracket$1, - checkSanitizeDeprecation: checkSanitizeDeprecation$1, - repeatString: repeatString$1 + noopTest, + merge, + splitCells, + rtrim, + findClosingBracket, + checkSanitizeDeprecation, + repeatString }; -const { defaults: defaults$4 } = defaults$5.exports; +const { defaults: defaults$1 } = defaults; const { - rtrim, - splitCells, - escape: escape$2, - findClosingBracket + rtrim: rtrim$1, + splitCells: splitCells$1, + escape: escape$1, + findClosingBracket: findClosingBracket$1, + edit: edit$1 } = helpers; function outputLink(cap, link, raw) { const href = link.href; - const title = link.title ? escape$2(link.title) : null; + const title = link.title ? escape$1(link.title) : null; const text = cap[1].replace(/\\([\[\]])/g, '$1'); if (cap[0].charAt(0) !== '!') { @@ -333,7 +338,7 @@ function outputLink(cap, link, raw) { raw, href, title, - text: escape$2(text) + text: escape$1(text) }; } } @@ -371,7 +376,7 @@ function indentCodeCompensation(raw, text) { */ var Tokenizer_1 = class Tokenizer { constructor(options) { - this.options = options || defaults$4; + this.options = options || defaults$1; } space(src) { @@ -396,7 +401,7 @@ var Tokenizer_1 = class Tokenizer { raw: cap[0], codeBlockStyle: 'indented', text: !this.options.pedantic - ? rtrim(text, '\n') + ? rtrim$1(text, '\n') : text }; } @@ -424,7 +429,7 @@ var Tokenizer_1 = class Tokenizer { // remove trailing #s if (/#$/.test(text)) { - const trimmed = rtrim(text, '#'); + const trimmed = rtrim$1(text, '#'); if (this.options.pedantic) { text = trimmed.trim(); } else if (!trimmed || / $/.test(trimmed)) { @@ -447,7 +452,7 @@ var Tokenizer_1 = class Tokenizer { if (cap) { const item = { type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [], raw: cap[0] @@ -470,7 +475,7 @@ var Tokenizer_1 = class Tokenizer { l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells(item.cells[i], item.header.length); + item.cells[i] = splitCells$1(item.cells[i], item.header.length); } return item; @@ -502,132 +507,123 @@ var Tokenizer_1 = class Tokenizer { } list(src) { - const cap = this.rules.block.list.exec(src); + let cap = this.rules.block.list.exec(src); if (cap) { - let raw = cap[0]; - const bull = cap[2]; + let raw, istask, ischecked, indent, i, blankLine, endsWithBlankLine, + line, lines, itemContents; + + let bull = cap[1].trim(); const isordered = bull.length > 1; const list = { type: 'list', - raw, + raw: '', ordered: isordered, start: isordered ? +bull.slice(0, -1) : '', loose: false, items: [] }; - // Get each top-level item. - const itemMatch = cap[0].match(this.rules.block.item); - - let next = false, - item, - space, - bcurr, - bnext, - addBack, - loose, - istask, - ischecked, - endMatch; - - let l = itemMatch.length; - bcurr = this.rules.block.listItemStart.exec(itemMatch[0]); - for (let i = 0; i < l; i++) { - item = itemMatch[i]; - raw = item; - - if (!this.options.pedantic) { - // Determine if current item contains the end of the list - endMatch = item.match(new RegExp('\\n\\s*\\n {0,' + (bcurr[0].length - 1) + '}\\S')); - if (endMatch) { - addBack = item.length - endMatch.index + itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - - item = item.substring(0, endMatch.index); - raw = item; - l = i + 1; + bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`; + + if(this.options.pedantic) { + bull = isordered ? bull : '[*+-]'; + } + + //Get next list item + //let itemRegex = `^( {0,3}${bull})( [^\\n]*(?:\\n(?!hr)(?! {0,1}bull)(?!\\s*\\n {0,${indent - 1}}[^\\s])[^\\n]*)*(?:\\s*\\n)*|\\s*)`; + const itemRegex = new RegExp(`^( {0,3}${bull})((?: [^\\n]*|\\h*)(?:\\n[^\\n]*)*(?:\\n|$))`); + + // Get each top-level item + while(src) { + if(this.rules.block.hr.exec(src)) // End list if we encounter an HR (possibly move into itemRegex?) + break; + + if(!(cap = itemRegex.exec(src))) + break; + + lines = cap[2].split('\n'); + indent = cap[1].length + Math.min(4, cap[2].search(/[^ ]/)); //Find first non-space char + itemContents = lines[0].slice(indent - cap[1].length); + + blankLine = false; + + raw = cap[0]; + + for (i = 1; i < lines.length; i++) { + line = lines[i]; + + // End list item if found start of new bullet + if(line.match(new RegExp(`^ {0,${Math.min(3, indent - 1)}}[*+-]|\\d{1,9}[.)]`))) { + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; + break; } - } - // Determine whether the next list item belongs here. - // Backpedal if it does not belong in this list. - if (i !== l - 1) { - bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]); - if ( - !this.options.pedantic - ? bnext[1].length >= bcurr[0].length || bnext[1].length > 3 - : bnext[1].length > bcurr[1].length - ) { - // nested list or continuation - itemMatch.splice(i, 2, itemMatch[i] + (!this.options.pedantic && bnext[1].length < bcurr[0].length && !itemMatch[i].match(/\n$/) ? '' : '\n') + itemMatch[i + 1]); - i--; - l--; + // Until we encounter a blank line, item contents do not need indentation + if(!blankLine) { + if(!line.trim()) { // Check if current line is empty + blankLine = true; + } + + // Dedent if possible + if(line.search(/[^ ]/) >= indent) { + itemContents += '\n' + line.slice(indent); + } + else { + itemContents += '\n' + line; + } continue; - } else if ( - // different bullet style - !this.options.pedantic || this.options.smartLists - ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] - : isordered === (bnext[2].length === 1) - ) { - addBack = itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - i = l - 1; } - bcurr = bnext; - } - // Remove the list item's bullet - // so it is seen as the next token. - space = item.length; - item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); - - // Outdent whatever the - // list item contains. Hacky. - if (~item.indexOf('\n ')) { - space -= item.length; - item = !this.options.pedantic - ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') - : item.replace(/^ {1,4}/gm, ''); + // Dedent this line + if(line.search(/[^ ]/) >= indent || !line.trim()) { + itemContents += '\n' + line.slice(indent); + continue; + } + else { // Line was not properly indented; end of this item + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; + break; + } } - // trim item newlines at end - item = rtrim(item, '\n'); - if (i !== l - 1) { - raw = raw + '\n'; - } + if(!list.loose) { + // List is loose if this item had any internal blank lines + if(itemContents.match(/\n\h*\n[^\n]/)) { + list.loose = true; + } - // Determine whether item is loose or not. - // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ - // for discount behavior. - loose = next || /\n\n(?!\s*$)/.test(raw); - if (i !== l - 1) { - next = raw.slice(-2) === '\n\n'; - if (!loose) loose = next; - } + // If the previous item ended with a blank line, the list is loose + else if(endsWithBlankLine) { + list.loose = true; + } - if (loose) { - list.loose = true; + // Check if current item ended in a blank line + else if(raw.match(/\n\h*\n\h*$/)) { + endsWithBlankLine = true; + } } // Check for task list items if (this.options.gfm) { - istask = /^\[[ xX]\] /.test(item); + istask = /^\[[ xX]\] /.exec(itemContents); ischecked = undefined; if (istask) { - ischecked = item[1] !== ' '; - item = item.replace(/^\[[ xX]\] +/, ''); + ischecked = istask[0] !== '[ ] '; + itemContents = itemContents.replace(/^\[[ xX]\] +/, ''); } } list.items.push({ type: 'list_item', - raw, - task: istask, + raw: raw, + task: istask ? true : false, checked: ischecked, - loose: loose, - text: item + loose: blankLine, + text: itemContents }); + + list.raw += raw; + src = src.slice(raw.length); } return list; @@ -644,7 +640,7 @@ var Tokenizer_1 = class Tokenizer { raw: cap[0], pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), - text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0] + text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$1(cap[0])) : cap[0] }; } } @@ -669,7 +665,7 @@ var Tokenizer_1 = class Tokenizer { if (cap) { const item = { type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] }; @@ -693,7 +689,7 @@ var Tokenizer_1 = class Tokenizer { l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells( + item.cells[i] = splitCells$1( item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length); } @@ -745,7 +741,7 @@ var Tokenizer_1 = class Tokenizer { return { type: 'escape', raw: cap[0], - text: escape$2(cap[1]) + text: escape$1(cap[1]) }; } } @@ -774,7 +770,7 @@ var Tokenizer_1 = class Tokenizer { text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) - : escape$2(cap[0])) + : escape$1(cap[0])) : cap[0] }; } @@ -791,13 +787,13 @@ var Tokenizer_1 = class Tokenizer { } // ending angle bracket cannot be escaped - const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\'); + const rtrimSlash = rtrim$1(trimmedUrl.slice(0, -1), '\\'); if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) { return; } } else { // find closing parenthesis - const lastParenIndex = findClosingBracket(cap[2], '()'); + const lastParenIndex = findClosingBracket$1(cap[2], '()'); if (lastParenIndex > -1) { const start = cap[0].indexOf('!') === 0 ? 5 : 4; const linkLen = start + cap[1].length + lastParenIndex; @@ -925,7 +921,7 @@ var Tokenizer_1 = class Tokenizer { if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) { text = text.substring(1, text.length - 1); } - text = escape$2(text, true); + text = escape$1(text, true); return { type: 'codespan', raw: cap[0], @@ -960,10 +956,10 @@ var Tokenizer_1 = class Tokenizer { if (cap) { let text, href; if (cap[2] === '@') { - text = escape$2(this.options.mangle ? mangle(cap[1]) : cap[1]); + text = escape$1(this.options.mangle ? mangle(cap[1]) : cap[1]); href = 'mailto:' + text; } else { - text = escape$2(cap[1]); + text = escape$1(cap[1]); href = text; } @@ -988,7 +984,7 @@ var Tokenizer_1 = class Tokenizer { if (cap = this.rules.inline.url.exec(src)) { let text, href; if (cap[2] === '@') { - text = escape$2(this.options.mangle ? mangle(cap[0]) : cap[0]); + text = escape$1(this.options.mangle ? mangle(cap[0]) : cap[0]); href = 'mailto:' + text; } else { // do extended autolink path validation @@ -997,7 +993,7 @@ var Tokenizer_1 = class Tokenizer { prevCapZero = cap[0]; cap[0] = this.rules.inline._backpedal.exec(cap[0])[0]; } while (prevCapZero !== cap[0]); - text = escape$2(cap[0]); + text = escape$1(cap[0]); if (cap[1] === 'www.') { href = 'http://' + text; } else { @@ -1025,9 +1021,9 @@ var Tokenizer_1 = class Tokenizer { if (cap) { let text; if (inRawBlock) { - text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0]; + text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$1(cap[0])) : cap[0]; } else { - text = escape$2(this.options.smartypants ? smartypants(cap[0]) : cap[0]); + text = escape$1(this.options.smartypants ? smartypants(cap[0]) : cap[0]); } return { type: 'text', @@ -1039,22 +1035,22 @@ var Tokenizer_1 = class Tokenizer { }; const { - noopTest, - edit, + noopTest: noopTest$1, + edit: edit$2, merge: merge$1 } = helpers; /** * Block-Level Grammar */ -const block$1 = { +const block = { newline: /^(?: *(?:\n|$))+/, code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/, fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/, + list: /^( {0,3}bull)( [^\n]+?)(?:\n|$)/, html: '^ {0,3}(?:' // optional indentation + '<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + '|comment[^\\n]*(\\n+|$)' // (2) @@ -1066,8 +1062,8 @@ const block$1 = { + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag + ')', def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, - nptable: noopTest, - table: noopTest, + nptable: noopTest$1, + table: noopTest$1, lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/, // regex template, placeholders will be replaced according to different paragraph // interruption rules of commonmark and the original markdown spec: @@ -1075,68 +1071,68 @@ const block$1 = { text: /^[^\n]+/ }; -block$1._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; -block$1._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; -block$1.def = edit(block$1.def) - .replace('label', block$1._label) - .replace('title', block$1._title) +block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; +block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; +block.def = edit$2(block.def) + .replace('label', block._label) + .replace('title', block._title) .getRegex(); -block$1.bullet = /(?:[*+-]|\d{1,9}[.)])/; -block$1.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/; -block$1.item = edit(block$1.item, 'gm') - .replace(/bull/g, block$1.bullet) - .getRegex(); +block.bullet = /(?:[*+-]|\d{1,9}[.)])/; +// block.item = /^( {0,3})(bull)( [^\n]*(?:\n(?! {0,3}bull )[^\n]*)*\n?)/; +// block.item = edit(block.item, 'gm') +// .replace(/bull/g, block.bullet) +// .getRegex(); -block$1.listItemStart = edit(/^( *)(bull) */) - .replace('bull', block$1.bullet) +block.listItemStart = edit$2(/^( *)(bull) */) + .replace('bull', block.bullet) .getRegex(); -block$1.list = edit(block$1.list) - .replace(/bull/g, block$1.bullet) +block.list = edit$2(block.list) + .replace(/bull/g, block.bullet) .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))') - .replace('def', '\\n+(?=' + block$1.def.source + ')') + .replace('def', '\\n+(?=' + block.def.source + ')') .getRegex(); -block$1._tag = 'address|article|aside|base|basefont|blockquote|body|caption' +block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul'; -block$1._comment = /|$)/; -block$1.html = edit(block$1.html, 'i') - .replace('comment', block$1._comment) - .replace('tag', block$1._tag) +block._comment = /|$)/; +block.html = edit$2(block.html, 'i') + .replace('comment', block._comment) + .replace('tag', block._tag) .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/) .getRegex(); -block$1.paragraph = edit(block$1._paragraph) - .replace('hr', block$1.hr) +block.paragraph = edit$2(block._paragraph) + .replace('hr', block.hr) .replace('heading', ' {0,3}#{1,6} ') .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs .replace('blockquote', ' {0,3}>') .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt .replace('html', ')|<(?:script|pre|style|!--)') - .replace('tag', block$1._tag) // pars can be interrupted by type (6) html blocks + .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks .getRegex(); -block$1.blockquote = edit(block$1.blockquote) - .replace('paragraph', block$1.paragraph) +block.blockquote = edit$2(block.blockquote) + .replace('paragraph', block.paragraph) .getRegex(); /** * Normal Block Grammar */ -block$1.normal = merge$1({}, block$1); +block.normal = merge$1({}, block); /** * GFM Block Grammar */ -block$1.gfm = merge$1({}, block$1.normal, { +block.gfm = merge$1({}, block.normal, { nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header + ' {0,3}([-:]+ *\\|[-| :]*)' // Align + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', // Cells @@ -1145,38 +1141,38 @@ block$1.gfm = merge$1({}, block$1.normal, { + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells }); -block$1.gfm.nptable = edit(block$1.gfm.nptable) - .replace('hr', block$1.hr) +block.gfm.nptable = edit$2(block.gfm.nptable) + .replace('hr', block.hr) .replace('heading', ' {0,3}#{1,6} ') .replace('blockquote', ' {0,3}>') .replace('code', ' {4}[^\\n]') .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt .replace('html', ')|<(?:script|pre|style|!--)') - .replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks + .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks .getRegex(); -block$1.gfm.table = edit(block$1.gfm.table) - .replace('hr', block$1.hr) +block.gfm.table = edit$2(block.gfm.table) + .replace('hr', block.hr) .replace('heading', ' {0,3}#{1,6} ') .replace('blockquote', ' {0,3}>') .replace('code', ' {4}[^\\n]') .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt .replace('html', ')|<(?:script|pre|style|!--)') - .replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks + .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks .getRegex(); /** * Pedantic grammar (original John Gruber's loose markdown specification) */ -block$1.pedantic = merge$1({}, block$1.normal, { - html: edit( +block.pedantic = merge$1({}, block.normal, { + html: edit$2( '^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))') - .replace('comment', block$1._comment) + .replace('comment', block._comment) .replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' @@ -1184,11 +1180,11 @@ block$1.pedantic = merge$1({}, block$1.normal, { .getRegex(), def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, heading: /^(#{1,6})(.*)(?:\n+|$)/, - fences: noopTest, // fences not supported - paragraph: edit(block$1.normal._paragraph) - .replace('hr', block$1.hr) + fences: noopTest$1, // fences not supported + paragraph: edit$2(block.normal._paragraph) + .replace('hr', block.hr) .replace('heading', ' *#{1,6} *[^\n]') - .replace('lheading', block$1.lheading) + .replace('lheading', block.lheading) .replace('blockquote', ' {0,3}>') .replace('|fences', '') .replace('|list', '') @@ -1199,10 +1195,10 @@ block$1.pedantic = merge$1({}, block$1.normal, { /** * Inline-Level Grammar */ -const inline$1 = { +const inline = { escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, - url: noopTest, + url: noopTest$1, tag: '^comment' + '|^' // self-closing tag + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag @@ -1222,80 +1218,80 @@ const inline$1 = { }, code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, br: /^( {2,}|\\)\n(?!\s*$)/, - del: noopTest, + del: noopTest$1, text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~'; -inline$1.punctuation = edit(inline$1.punctuation).replace(/punctuation/g, inline$1._punctuation).getRegex(); +inline._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~'; +inline.punctuation = edit$2(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, -inline$1.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g; -inline$1.escapedEmSt = /\\\*|\\_/g; +inline.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g; +inline.escapedEmSt = /\\\*|\\_/g; -inline$1._comment = edit(block$1._comment).replace('(?:-->|$)', '-->').getRegex(); +inline._comment = edit$2(block._comment).replace('(?:-->|$)', '-->').getRegex(); -inline$1.emStrong.lDelim = edit(inline$1.emStrong.lDelim) - .replace(/punct/g, inline$1._punctuation) +inline.emStrong.lDelim = edit$2(inline.emStrong.lDelim) + .replace(/punct/g, inline._punctuation) .getRegex(); -inline$1.emStrong.rDelimAst = edit(inline$1.emStrong.rDelimAst, 'g') - .replace(/punct/g, inline$1._punctuation) +inline.emStrong.rDelimAst = edit$2(inline.emStrong.rDelimAst, 'g') + .replace(/punct/g, inline._punctuation) .getRegex(); -inline$1.emStrong.rDelimUnd = edit(inline$1.emStrong.rDelimUnd, 'g') - .replace(/punct/g, inline$1._punctuation) +inline.emStrong.rDelimUnd = edit$2(inline.emStrong.rDelimUnd, 'g') + .replace(/punct/g, inline._punctuation) .getRegex(); -inline$1._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; +inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; -inline$1._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; -inline$1._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])?)+(?![-_])/; -inline$1.autolink = edit(inline$1.autolink) - .replace('scheme', inline$1._scheme) - .replace('email', inline$1._email) +inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; +inline._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])?)+(?![-_])/; +inline.autolink = edit$2(inline.autolink) + .replace('scheme', inline._scheme) + .replace('email', inline._email) .getRegex(); -inline$1._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; +inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; -inline$1.tag = edit(inline$1.tag) - .replace('comment', inline$1._comment) - .replace('attribute', inline$1._attribute) +inline.tag = edit$2(inline.tag) + .replace('comment', inline._comment) + .replace('attribute', inline._attribute) .getRegex(); -inline$1._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; -inline$1._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/; -inline$1._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; +inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; +inline._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/; +inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; -inline$1.link = edit(inline$1.link) - .replace('label', inline$1._label) - .replace('href', inline$1._href) - .replace('title', inline$1._title) +inline.link = edit$2(inline.link) + .replace('label', inline._label) + .replace('href', inline._href) + .replace('title', inline._title) .getRegex(); -inline$1.reflink = edit(inline$1.reflink) - .replace('label', inline$1._label) +inline.reflink = edit$2(inline.reflink) + .replace('label', inline._label) .getRegex(); -inline$1.reflinkSearch = edit(inline$1.reflinkSearch, 'g') - .replace('reflink', inline$1.reflink) - .replace('nolink', inline$1.nolink) +inline.reflinkSearch = edit$2(inline.reflinkSearch, 'g') + .replace('reflink', inline.reflink) + .replace('nolink', inline.nolink) .getRegex(); /** * Normal Inline Grammar */ -inline$1.normal = merge$1({}, inline$1); +inline.normal = merge$1({}, inline); /** * Pedantic Inline Grammar */ -inline$1.pedantic = merge$1({}, inline$1.normal, { +inline.pedantic = merge$1({}, inline.normal, { strong: { start: /^__|\*\*/, middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, @@ -1308,11 +1304,11 @@ inline$1.pedantic = merge$1({}, inline$1.normal, { endAst: /\*(?!\*)/g, endUnd: /_(?!_)/g }, - link: edit(/^!?\[(label)\]\((.*?)\)/) - .replace('label', inline$1._label) + link: edit$2(/^!?\[(label)\]\((.*?)\)/) + .replace('label', inline._label) .getRegex(), - reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/) - .replace('label', inline$1._label) + reflink: edit$2(/^!?\[(label)\]\s*\[([^\]]*)\]/) + .replace('label', inline._label) .getRegex() }); @@ -1320,8 +1316,8 @@ inline$1.pedantic = merge$1({}, inline$1.normal, { * GFM Inline Grammar */ -inline$1.gfm = merge$1({}, inline$1.normal, { - escape: edit(inline$1.escape).replace('])', '~|])').getRegex(), +inline.gfm = merge$1({}, inline.normal, { + escape: edit$2(inline.escape).replace('])', '~|])').getRegex(), _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/, url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/, @@ -1329,30 +1325,29 @@ inline$1.gfm = merge$1({}, inline$1.normal, { text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\ 0) { while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) { if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); } } } } // Mask out other blocks while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); } // Mask out escaped em & strong delimiters @@ -1838,10 +1833,10 @@ var Lexer_1 = class Lexer { } }; -const { defaults: defaults$2 } = defaults$5.exports; +const { defaults: defaults$3 } = defaults; const { - cleanUrl, - escape: escape$1 + cleanUrl: cleanUrl$1, + escape: escape$2 } = helpers; /** @@ -1849,7 +1844,7 @@ const { */ var Renderer_1 = class Renderer { constructor(options) { - this.options = options || defaults$2; + this.options = options || defaults$3; } code(code, infostring, escaped) { @@ -1866,15 +1861,15 @@ var Renderer_1 = class Renderer { if (!lang) { return '
    '
    -        + (escaped ? code : escape$1(code, true))
    +        + (escaped ? code : escape$2(code, true))
             + '
    \n'; } return '
    '
    -      + (escaped ? code : escape$1(code, true))
    +      + (escaped ? code : escape$2(code, true))
           + '
    \n'; } @@ -1974,11 +1969,11 @@ var Renderer_1 = class Renderer { } link(href, title, text) { - href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); if (href === null) { return text; } - let out = ' 0 && item.tokens[0].type === 'text') { + if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') { item.tokens[0].text = checkbox + ' ' + item.tokens[0].text; if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') { item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text; @@ -2364,22 +2354,16 @@ var Parser_1 = class Parser { } }; -const Lexer = Lexer_1; -const Parser = Parser_1; -const Tokenizer = Tokenizer_1; -const Renderer = Renderer_1; -const TextRenderer = TextRenderer_1; -const Slugger = Slugger_1; const { - merge, - checkSanitizeDeprecation, - escape + merge: merge$2, + checkSanitizeDeprecation: checkSanitizeDeprecation$1, + escape: escape$3 } = helpers; const { getDefaults, changeDefaults, - defaults -} = defaults$5.exports; + defaults: defaults$5 +} = defaults; /** * Marked @@ -2399,15 +2383,15 @@ function marked(src, opt, callback) { opt = null; } - opt = merge({}, marked.defaults, opt || {}); - checkSanitizeDeprecation(opt); + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); if (callback) { const highlight = opt.highlight; let tokens; try { - tokens = Lexer.lex(src, opt); + tokens = Lexer_1.lex(src, opt); } catch (e) { return callback(e); } @@ -2420,7 +2404,7 @@ function marked(src, opt, callback) { if (opt.walkTokens) { marked.walkTokens(tokens, opt.walkTokens); } - out = Parser.parse(tokens, opt); + out = Parser_1.parse(tokens, opt); } catch (e) { err = e; } @@ -2472,16 +2456,16 @@ function marked(src, opt, callback) { } try { - const tokens = Lexer.lex(src, opt); + const tokens = Lexer_1.lex(src, opt); if (opt.walkTokens) { marked.walkTokens(tokens, opt.walkTokens); } - return Parser.parse(tokens, opt); + return Parser_1.parse(tokens, opt); } catch (e) { e.message += '\nPlease report this to https://github.com/markedjs/marked.'; if (opt.silent) { return '

    An error occurred:

    '
    -        + escape(e.message + '', true)
    +        + escape$3(e.message + '', true)
             + '
    '; } throw e; @@ -2494,23 +2478,23 @@ function marked(src, opt, callback) { marked.options = marked.setOptions = function(opt) { - merge(marked.defaults, opt); + merge$2(marked.defaults, opt); changeDefaults(marked.defaults); return marked; }; marked.getDefaults = getDefaults; -marked.defaults = defaults; +marked.defaults = defaults$5; /** * Use Extension */ marked.use = function(extension) { - const opts = merge({}, extension); + const opts = merge$2({}, extension); if (extension.renderer) { - const renderer = marked.defaults.renderer || new Renderer(); + const renderer = marked.defaults.renderer || new Renderer_1(); for (const prop in extension.renderer) { const prevRenderer = renderer[prop]; renderer[prop] = (...args) => { @@ -2524,7 +2508,7 @@ marked.use = function(extension) { opts.renderer = renderer; } if (extension.tokenizer) { - const tokenizer = marked.defaults.tokenizer || new Tokenizer(); + const tokenizer = marked.defaults.tokenizer || new Tokenizer_1(); for (const prop in extension.tokenizer) { const prevTokenizer = tokenizer[prop]; tokenizer[prop] = (...args) => { @@ -2594,20 +2578,20 @@ marked.parseInline = function(src, opt) { + Object.prototype.toString.call(src) + ', string expected'); } - opt = merge({}, marked.defaults, opt || {}); - checkSanitizeDeprecation(opt); + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); try { - const tokens = Lexer.lexInline(src, opt); + const tokens = Lexer_1.lexInline(src, opt); if (opt.walkTokens) { marked.walkTokens(tokens, opt.walkTokens); } - return Parser.parseInline(tokens, opt); + return Parser_1.parseInline(tokens, opt); } catch (e) { e.message += '\nPlease report this to https://github.com/markedjs/marked.'; if (opt.silent) { return '

    An error occurred:

    '
    -        + escape(e.message + '', true)
    +        + escape$3(e.message + '', true)
             + '
    '; } throw e; @@ -2618,18 +2602,18 @@ marked.parseInline = function(src, opt) { * Expose */ -marked.Parser = Parser; -marked.parser = Parser.parse; +marked.Parser = Parser_1; +marked.parser = Parser_1.parse; -marked.Renderer = Renderer; -marked.TextRenderer = TextRenderer; +marked.Renderer = Renderer_1; +marked.TextRenderer = TextRenderer_1; -marked.Lexer = Lexer; -marked.lexer = Lexer.lex; +marked.Lexer = Lexer_1; +marked.lexer = Lexer_1.lex; -marked.Tokenizer = Tokenizer; +marked.Tokenizer = Tokenizer_1; -marked.Slugger = Slugger; +marked.Slugger = Slugger_1; marked.parse = marked; diff --git a/lib/marked.js b/lib/marked.js index 70bbc77f88..d2ba3bb5b0 100644 --- a/lib/marked.js +++ b/lib/marked.js @@ -49,60 +49,69 @@ } function _createForOfIteratorHelperLoose(o, allowArrayLike) { - var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; - if (it) return (it = it.call(o)).next.bind(it); - - if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { - if (it) o = it; - var i = 0; - return function () { - if (i >= o.length) return { - done: true - }; - return { - done: false, - value: o[i++] + var it; + + if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + return function () { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; }; - }; + } + + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } - throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + it = o[Symbol.iterator](); + return it.next.bind(it); } - var defaults$5 = {exports: {}}; - - function getDefaults$1() { - return { - baseUrl: null, - breaks: false, - gfm: true, - headerIds: true, - headerPrefix: '', - highlight: null, - langPrefix: 'language-', - mangle: true, - pedantic: false, - renderer: null, - sanitize: false, - sanitizer: null, - silent: false, - smartLists: false, - smartypants: false, - tokenizer: null, - walkTokens: null, - xhtml: false - }; + function createCommonjsModule(fn) { + var module = { exports: {} }; + return fn(module, module.exports), module.exports; } - function changeDefaults$1(newDefaults) { - defaults$5.exports.defaults = newDefaults; - } + var defaults = createCommonjsModule(function (module) { + function getDefaults() { + return { + baseUrl: null, + breaks: false, + gfm: true, + headerIds: true, + headerPrefix: '', + highlight: null, + langPrefix: 'language-', + mangle: true, + pedantic: false, + renderer: null, + sanitize: false, + sanitizer: null, + silent: false, + smartLists: false, + smartypants: false, + tokenizer: null, + walkTokens: null, + xhtml: false + }; + } - defaults$5.exports = { - defaults: getDefaults$1(), - getDefaults: getDefaults$1, - changeDefaults: changeDefaults$1 - }; + function changeDefaults(newDefaults) { + module.exports.defaults = newDefaults; + } + + module.exports = { + defaults: getDefaults(), + getDefaults: getDefaults, + changeDefaults: changeDefaults + }; + }); /** * Helpers @@ -123,7 +132,7 @@ return escapeReplacements[ch]; }; - function escape$2(html, encode) { + function escape(html, encode) { if (encode) { if (escapeTest.test(html)) { return html.replace(escapeReplace, getEscapeReplacement); @@ -139,7 +148,7 @@ var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig; - function unescape$1(html) { + function unescape(html) { // explicitly match decimal, hex, and named HTML entities return html.replace(unescapeTest, function (_, n) { n = n.toLowerCase(); @@ -155,7 +164,7 @@ var caret = /(^|[^\[])\^/g; - function edit$1(regex, opt) { + function edit(regex, opt) { regex = regex.source || regex; opt = opt || ''; var obj = { @@ -175,12 +184,12 @@ var nonWordAndColonTest = /[^\w:]/g; var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; - function cleanUrl$1(sanitize, base, href) { + function cleanUrl(sanitize, base, href) { if (sanitize) { var prot; try { - prot = decodeURIComponent(unescape$1(href)).replace(nonWordAndColonTest, '').toLowerCase(); + prot = decodeURIComponent(unescape(href)).replace(nonWordAndColonTest, '').toLowerCase(); } catch (e) { return null; } @@ -216,7 +225,7 @@ if (justDomain.test(base)) { baseUrls[' ' + base] = base + '/'; } else { - baseUrls[' ' + base] = rtrim$1(base, '/', true); + baseUrls[' ' + base] = rtrim(base, '/', true); } } @@ -240,11 +249,11 @@ } } - var noopTest$1 = { + var noopTest = { exec: function noopTest() {} }; - function merge$2(obj) { + function merge(obj) { var i = 1, target, key; @@ -262,7 +271,7 @@ return obj; } - function splitCells$1(tableRow, count) { + function splitCells(tableRow, count) { // ensure that every cell-delimiting pipe has a space // before it to distinguish it from an escaped pipe var row = tableRow.replace(/\|/g, function (match, offset, str) { @@ -304,7 +313,7 @@ // invert: Remove suffix of non-c chars instead. Default falsey. - function rtrim$1(str, c, invert) { + function rtrim(str, c, invert) { var l = str.length; if (l === 0) { @@ -329,7 +338,7 @@ return str.substr(0, l - suffLen); } - function findClosingBracket$1(str, b) { + function findClosingBracket(str, b) { if (str.indexOf(b[1]) === -1) { return -1; } @@ -355,14 +364,14 @@ return -1; } - function checkSanitizeDeprecation$1(opt) { + function checkSanitizeDeprecation(opt) { if (opt && opt.sanitize && !opt.silent) { console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); } } // copied from https://stackoverflow.com/a/5450113/806777 - function repeatString$1(pattern, count) { + function repeatString(pattern, count) { if (count < 1) { return ''; } @@ -382,25 +391,25 @@ } var helpers = { - escape: escape$2, - unescape: unescape$1, - edit: edit$1, - cleanUrl: cleanUrl$1, + escape: escape, + unescape: unescape, + edit: edit, + cleanUrl: cleanUrl, resolveUrl: resolveUrl, - noopTest: noopTest$1, - merge: merge$2, - splitCells: splitCells$1, - rtrim: rtrim$1, - findClosingBracket: findClosingBracket$1, - checkSanitizeDeprecation: checkSanitizeDeprecation$1, - repeatString: repeatString$1 + noopTest: noopTest, + merge: merge, + splitCells: splitCells, + rtrim: rtrim, + findClosingBracket: findClosingBracket, + checkSanitizeDeprecation: checkSanitizeDeprecation, + repeatString: repeatString }; - var defaults$4 = defaults$5.exports.defaults; - var rtrim = helpers.rtrim, - splitCells = helpers.splitCells, + var defaults$1 = defaults.defaults; + var rtrim$1 = helpers.rtrim, + splitCells$1 = helpers.splitCells, _escape = helpers.escape, - findClosingBracket = helpers.findClosingBracket; + findClosingBracket$1 = helpers.findClosingBracket; function outputLink(cap, link, raw) { var href = link.href; @@ -457,7 +466,7 @@ var Tokenizer_1 = /*#__PURE__*/function () { function Tokenizer(options) { - this.options = options || defaults$4; + this.options = options || defaults$1; } var _proto = Tokenizer.prototype; @@ -488,7 +497,7 @@ type: 'code', raw: cap[0], codeBlockStyle: 'indented', - text: !this.options.pedantic ? rtrim(text, '\n') : text + text: !this.options.pedantic ? rtrim$1(text, '\n') : text }; } }; @@ -515,7 +524,7 @@ var text = cap[2].trim(); // remove trailing #s if (/#$/.test(text)) { - var trimmed = rtrim(text, '#'); + var trimmed = rtrim$1(text, '#'); if (this.options.pedantic) { text = trimmed.trim(); @@ -540,7 +549,7 @@ if (cap) { var item = { type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [], raw: cap[0] @@ -565,7 +574,7 @@ l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells(item.cells[i], item.header.length); + item.cells[i] = splitCells$1(item.cells[i], item.header.length); } return item; @@ -601,121 +610,108 @@ var cap = this.rules.block.list.exec(src); if (cap) { - var raw = cap[0]; - var bull = cap[2]; + var raw, istask, ischecked, indent, i, blankLine, endsWithBlankLine, line, lines, itemContents; + var bull = cap[1].trim(); var isordered = bull.length > 1; var list = { type: 'list', - raw: raw, + raw: '', ordered: isordered, start: isordered ? +bull.slice(0, -1) : '', loose: false, items: [] - }; // Get each top-level item. - - var itemMatch = cap[0].match(this.rules.block.item); - var next = false, - item, - space, - bcurr, - bnext, - addBack, - loose, - istask, - ischecked, - endMatch; - var l = itemMatch.length; - bcurr = this.rules.block.listItemStart.exec(itemMatch[0]); - - for (var i = 0; i < l; i++) { - item = itemMatch[i]; - raw = item; - - if (!this.options.pedantic) { - // Determine if current item contains the end of the list - endMatch = item.match(new RegExp('\\n\\s*\\n {0,' + (bcurr[0].length - 1) + '}\\S')); - - if (endMatch) { - addBack = item.length - endMatch.index + itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - item = item.substring(0, endMatch.index); - raw = item; - l = i + 1; - } - } // Determine whether the next list item belongs here. - // Backpedal if it does not belong in this list. + }; + bull = isordered ? "\\d{1,9}\\" + bull.slice(-1) : "\\" + bull; + if (this.options.pedantic) { + bull = isordered ? bull : '[*+-]'; + } //Get next list item + //let itemRegex = `^( {0,3}${bull})( [^\\n]*(?:\\n(?!hr)(?! {0,1}bull)(?!\\s*\\n {0,${indent - 1}}[^\\s])[^\\n]*)*(?:\\s*\\n)*|\\s*)`; - if (i !== l - 1) { - bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]); - if (!this.options.pedantic ? bnext[1].length >= bcurr[0].length || bnext[1].length > 3 : bnext[1].length > bcurr[1].length) { - // nested list or continuation - itemMatch.splice(i, 2, itemMatch[i] + (!this.options.pedantic && bnext[1].length < bcurr[0].length && !itemMatch[i].match(/\n$/) ? '' : '\n') + itemMatch[i + 1]); - i--; - l--; - continue; - } else if ( // different bullet style - !this.options.pedantic || this.options.smartLists ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] : isordered === (bnext[2].length === 1)) { - addBack = itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - i = l - 1; - } + var itemRegex = new RegExp("^( {0,3}" + bull + ")((?: [^\\n]*|\\h*)(?:\\n[^\\n]*)*(?:\\n|$))"); // Get each top-level item + + while (src) { + if (this.rules.block.hr.exec(src)) // End list if we encounter an HR (possibly move into itemRegex?) + break; + if (!(cap = itemRegex.exec(src))) break; + lines = cap[2].split('\n'); + indent = cap[1].length + Math.min(4, cap[2].search(/[^ ]/)); //Find first non-space char - bcurr = bnext; - } // Remove the list item's bullet - // so it is seen as the next token. + itemContents = lines[0].slice(indent - cap[1].length); + blankLine = false; + raw = cap[0]; + for (i = 1; i < lines.length; i++) { + line = lines[i]; // End list item if found start of new bullet - space = item.length; - item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the - // list item contains. Hacky. + if (line.match(new RegExp("^ {0," + Math.min(3, indent - 1) + "}[*+-]|\\d{1,9}[.)]"))) { + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; + break; + } // Until we encounter a blank line, item contents do not need indentation - if (~item.indexOf('\n ')) { - space -= item.length; - item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, ''); - } // trim item newlines at end + if (!blankLine) { + if (!line.trim()) { + // Check if current line is empty + blankLine = true; + } // Dedent if possible - item = rtrim(item, '\n'); - if (i !== l - 1) { - raw = raw + '\n'; - } // Determine whether item is loose or not. - // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ - // for discount behavior. + if (line.search(/[^ ]/) >= indent) { + itemContents += '\n' + line.slice(indent); + } else { + itemContents += '\n' + line; + } + continue; + } // Dedent this line - loose = next || /\n\n(?!\s*$)/.test(raw); - if (i !== l - 1) { - next = raw.slice(-2) === '\n\n'; - if (!loose) loose = next; + if (line.search(/[^ ]/) >= indent || !line.trim()) { + itemContents += '\n' + line.slice(indent); + continue; + } else { + // Line was not properly indented; end of this item + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; + break; + } } - if (loose) { - list.loose = true; + if (!list.loose) { + // List is loose if this item had any internal blank lines + if (itemContents.match(/\n\h*\n[^\n]/)) { + list.loose = true; + } // If the previous item ended with a blank line, the list is loose + else if (endsWithBlankLine) { + list.loose = true; + } // Check if current item ended in a blank line + else if (raw.match(/\n\h*\n\h*$/)) { + endsWithBlankLine = true; + } } // Check for task list items if (this.options.gfm) { - istask = /^\[[ xX]\] /.test(item); + istask = /^\[[ xX]\] /.exec(itemContents); ischecked = undefined; if (istask) { - ischecked = item[1] !== ' '; - item = item.replace(/^\[[ xX]\] +/, ''); + ischecked = istask[0] !== '[ ] '; + itemContents = itemContents.replace(/^\[[ xX]\] +/, ''); } } list.items.push({ type: 'list_item', raw: raw, - task: istask, + task: istask ? true : false, checked: ischecked, - loose: loose, - text: item + loose: blankLine, + text: itemContents }); + list.raw += raw; + src = src.slice(raw.length); } return list; @@ -757,7 +753,7 @@ if (cap) { var item = { type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] }; @@ -782,7 +778,7 @@ l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length); + item.cells[i] = splitCells$1(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length); } return item; @@ -878,14 +874,14 @@ } // ending angle bracket cannot be escaped - var rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\'); + var rtrimSlash = rtrim$1(trimmedUrl.slice(0, -1), '\\'); if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) { return; } } else { // find closing parenthesis - var lastParenIndex = findClosingBracket(cap[2], '()'); + var lastParenIndex = findClosingBracket$1(cap[2], '()'); if (lastParenIndex > -1) { var start = cap[0].indexOf('!') === 0 ? 5 : 4; @@ -1149,21 +1145,21 @@ return Tokenizer; }(); - var noopTest = helpers.noopTest, - edit = helpers.edit, + var noopTest$1 = helpers.noopTest, + edit$1 = helpers.edit, merge$1 = helpers.merge; /** * Block-Level Grammar */ - var block$1 = { + var block = { newline: /^(?: *(?:\n|$))+/, code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/, fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/, + list: /^( {0,3}bull)( [^\n]+?)(?:\n|$)/, html: '^ {0,3}(?:' // optional indentation + '<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + '|comment[^\\n]*(\\n+|$)' // (2) @@ -1175,40 +1171,42 @@ + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag + ')', def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, - nptable: noopTest, - table: noopTest, + nptable: noopTest$1, + table: noopTest$1, lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/, // regex template, placeholders will be replaced according to different paragraph // interruption rules of commonmark and the original markdown spec: _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html| +\n)[^\n]+)*)/, text: /^[^\n]+/ }; - block$1._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; - block$1._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; - block$1.def = edit(block$1.def).replace('label', block$1._label).replace('title', block$1._title).getRegex(); - block$1.bullet = /(?:[*+-]|\d{1,9}[.)])/; - block$1.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/; - block$1.item = edit(block$1.item, 'gm').replace(/bull/g, block$1.bullet).getRegex(); - block$1.listItemStart = edit(/^( *)(bull) */).replace('bull', block$1.bullet).getRegex(); - block$1.list = edit(block$1.list).replace(/bull/g, block$1.bullet).replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))').replace('def', '\\n+(?=' + block$1.def.source + ')').getRegex(); - block$1._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul'; - block$1._comment = /|$)/; - block$1.html = edit(block$1.html, 'i').replace('comment', block$1._comment).replace('tag', block$1._tag).replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(); - block$1.paragraph = edit(block$1._paragraph).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs + block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; + block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; + block.def = edit$1(block.def).replace('label', block._label).replace('title', block._title).getRegex(); + block.bullet = /(?:[*+-]|\d{1,9}[.)])/; // block.item = /^( {0,3})(bull)( [^\n]*(?:\n(?! {0,3}bull )[^\n]*)*\n?)/; + // block.item = edit(block.item, 'gm') + // .replace(/bull/g, block.bullet) + // .getRegex(); + + block.listItemStart = edit$1(/^( *)(bull) */).replace('bull', block.bullet).getRegex(); + block.list = edit$1(block.list).replace(/bull/g, block.bullet).replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))').replace('def', '\\n+(?=' + block.def.source + ')').getRegex(); + block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul'; + block._comment = /|$)/; + block.html = edit$1(block.html, 'i').replace('comment', block._comment).replace('tag', block._tag).replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(); + block.paragraph = edit$1(block._paragraph).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs .replace('blockquote', ' {0,3}>').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt - .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // pars can be interrupted by type (6) html blocks + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block._tag) // pars can be interrupted by type (6) html blocks .getRegex(); - block$1.blockquote = edit(block$1.blockquote).replace('paragraph', block$1.paragraph).getRegex(); + block.blockquote = edit$1(block.blockquote).replace('paragraph', block.paragraph).getRegex(); /** * Normal Block Grammar */ - block$1.normal = merge$1({}, block$1); + block.normal = merge$1({}, block); /** * GFM Block Grammar */ - block$1.gfm = merge$1({}, block$1.normal, { + block.gfm = merge$1({}, block.normal, { nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header + ' {0,3}([-:]+ *\\|[-| :]*)' // Align + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', @@ -1218,33 +1216,33 @@ + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells }); - block$1.gfm.nptable = edit(block$1.gfm.nptable).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt - .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks + block.gfm.nptable = edit$1(block.gfm.nptable).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks .getRegex(); - block$1.gfm.table = edit(block$1.gfm.table).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt - .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks + block.gfm.table = edit$1(block.gfm.table).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks .getRegex(); /** * Pedantic grammar (original John Gruber's loose markdown specification) */ - block$1.pedantic = merge$1({}, block$1.normal, { - html: edit('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag - + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))').replace('comment', block$1._comment).replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b').getRegex(), + block.pedantic = merge$1({}, block.normal, { + html: edit$1('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag + + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))').replace('comment', block._comment).replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b').getRegex(), def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, heading: /^(#{1,6})(.*)(?:\n+|$)/, - fences: noopTest, + fences: noopTest$1, // fences not supported - paragraph: edit(block$1.normal._paragraph).replace('hr', block$1.hr).replace('heading', ' *#{1,6} *[^\n]').replace('lheading', block$1.lheading).replace('blockquote', ' {0,3}>').replace('|fences', '').replace('|list', '').replace('|html', '').getRegex() + paragraph: edit$1(block.normal._paragraph).replace('hr', block.hr).replace('heading', ' *#{1,6} *[^\n]').replace('lheading', block.lheading).replace('blockquote', ' {0,3}>').replace('|fences', '').replace('|list', '').replace('|html', '').getRegex() }); /** * Inline-Level Grammar */ - var inline$1 = { + var inline = { escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, - url: noopTest, + url: noopTest$1, tag: '^comment' + '|^' // self-closing tag + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. @@ -1265,43 +1263,43 @@ }, code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, br: /^( {2,}|\\)\n(?!\s*$)/, - del: noopTest, + del: noopTest$1, text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~'; - inline$1.punctuation = edit(inline$1.punctuation).replace(/punctuation/g, inline$1._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, - - inline$1.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g; - inline$1.escapedEmSt = /\\\*|\\_/g; - inline$1._comment = edit(block$1._comment).replace('(?:-->|$)', '-->').getRegex(); - inline$1.emStrong.lDelim = edit(inline$1.emStrong.lDelim).replace(/punct/g, inline$1._punctuation).getRegex(); - inline$1.emStrong.rDelimAst = edit(inline$1.emStrong.rDelimAst, 'g').replace(/punct/g, inline$1._punctuation).getRegex(); - inline$1.emStrong.rDelimUnd = edit(inline$1.emStrong.rDelimUnd, 'g').replace(/punct/g, inline$1._punctuation).getRegex(); - inline$1._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; - inline$1._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; - inline$1._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])?)+(?![-_])/; - inline$1.autolink = edit(inline$1.autolink).replace('scheme', inline$1._scheme).replace('email', inline$1._email).getRegex(); - inline$1._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; - inline$1.tag = edit(inline$1.tag).replace('comment', inline$1._comment).replace('attribute', inline$1._attribute).getRegex(); - inline$1._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; - inline$1._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/; - inline$1._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; - inline$1.link = edit(inline$1.link).replace('label', inline$1._label).replace('href', inline$1._href).replace('title', inline$1._title).getRegex(); - inline$1.reflink = edit(inline$1.reflink).replace('label', inline$1._label).getRegex(); - inline$1.reflinkSearch = edit(inline$1.reflinkSearch, 'g').replace('reflink', inline$1.reflink).replace('nolink', inline$1.nolink).getRegex(); + inline._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~'; + inline.punctuation = edit$1(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, + + inline.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g; + inline.escapedEmSt = /\\\*|\\_/g; + inline._comment = edit$1(block._comment).replace('(?:-->|$)', '-->').getRegex(); + inline.emStrong.lDelim = edit$1(inline.emStrong.lDelim).replace(/punct/g, inline._punctuation).getRegex(); + inline.emStrong.rDelimAst = edit$1(inline.emStrong.rDelimAst, 'g').replace(/punct/g, inline._punctuation).getRegex(); + inline.emStrong.rDelimUnd = edit$1(inline.emStrong.rDelimUnd, 'g').replace(/punct/g, inline._punctuation).getRegex(); + inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; + inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; + inline._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])?)+(?![-_])/; + inline.autolink = edit$1(inline.autolink).replace('scheme', inline._scheme).replace('email', inline._email).getRegex(); + inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; + inline.tag = edit$1(inline.tag).replace('comment', inline._comment).replace('attribute', inline._attribute).getRegex(); + inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; + inline._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/; + inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; + inline.link = edit$1(inline.link).replace('label', inline._label).replace('href', inline._href).replace('title', inline._title).getRegex(); + inline.reflink = edit$1(inline.reflink).replace('label', inline._label).getRegex(); + inline.reflinkSearch = edit$1(inline.reflinkSearch, 'g').replace('reflink', inline.reflink).replace('nolink', inline.nolink).getRegex(); /** * Normal Inline Grammar */ - inline$1.normal = merge$1({}, inline$1); + inline.normal = merge$1({}, inline); /** * Pedantic Inline Grammar */ - inline$1.pedantic = merge$1({}, inline$1.normal, { + inline.pedantic = merge$1({}, inline.normal, { strong: { start: /^__|\*\*/, middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, @@ -1314,40 +1312,39 @@ endAst: /\*(?!\*)/g, endUnd: /_(?!_)/g }, - link: edit(/^!?\[(label)\]\((.*?)\)/).replace('label', inline$1._label).getRegex(), - reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline$1._label).getRegex() + link: edit$1(/^!?\[(label)\]\((.*?)\)/).replace('label', inline._label).getRegex(), + reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline._label).getRegex() }); /** * GFM Inline Grammar */ - inline$1.gfm = merge$1({}, inline$1.normal, { - escape: edit(inline$1.escape).replace('])', '~|])').getRegex(), + inline.gfm = merge$1({}, inline.normal, { + escape: edit$1(inline.escape).replace('])', '~|])').getRegex(), _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/, url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/, del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/, text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\ 0) { while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) { if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); } } } @@ -1726,7 +1723,7 @@ while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); } // Mask out escaped em & strong delimiters @@ -1879,8 +1876,8 @@ key: "rules", get: function get() { return { - block: block, - inline: inline + block: block$1, + inline: inline$1 }; } }]); @@ -1888,8 +1885,8 @@ return Lexer; }(); - var defaults$2 = defaults$5.exports.defaults; - var cleanUrl = helpers.cleanUrl, + var defaults$3 = defaults.defaults; + var cleanUrl$1 = helpers.cleanUrl, escape$1 = helpers.escape; /** * Renderer @@ -1897,7 +1894,7 @@ var Renderer_1 = /*#__PURE__*/function () { function Renderer(options) { - this.options = options || defaults$2; + this.options = options || defaults$3; } var _proto = Renderer.prototype; @@ -1999,7 +1996,7 @@ }; _proto.link = function link(href, title, text) { - href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); if (href === null) { return text; @@ -2016,7 +2013,7 @@ }; _proto.image = function image(href, title, text) { - href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); if (href === null) { return text; @@ -2043,7 +2040,6 @@ * TextRenderer * returns only the textual part of the token */ - var TextRenderer_1 = /*#__PURE__*/function () { function TextRenderer() {} @@ -2092,7 +2088,6 @@ /** * Slugger generates header id */ - var Slugger_1 = /*#__PURE__*/function () { function Slugger() { this.seen = {}; @@ -2149,23 +2144,20 @@ return Slugger; }(); - var Renderer$1 = Renderer_1; - var TextRenderer$1 = TextRenderer_1; - var Slugger$1 = Slugger_1; - var defaults$1 = defaults$5.exports.defaults; - var unescape = helpers.unescape; + var defaults$4 = defaults.defaults; + var unescape$1 = helpers.unescape; /** * Parsing & Compiling */ var Parser_1 = /*#__PURE__*/function () { function Parser(options) { - this.options = options || defaults$1; - this.options.renderer = this.options.renderer || new Renderer$1(); + this.options = options || defaults$4; + this.options.renderer = this.options.renderer || new Renderer_1(); this.renderer = this.options.renderer; this.renderer.options = this.options; - this.textRenderer = new TextRenderer$1(); - this.slugger = new Slugger$1(); + this.textRenderer = new TextRenderer_1(); + this.slugger = new Slugger_1(); } /** * Static Parse Method @@ -2235,7 +2227,7 @@ case 'heading': { - out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape(this.parseInline(token.tokens, this.textRenderer)), this.slugger); + out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape$1(this.parseInline(token.tokens, this.textRenderer)), this.slugger); continue; } @@ -2307,7 +2299,7 @@ checkbox = this.renderer.checkbox(checked); if (loose) { - if (item.tokens.length > 0 && item.tokens[0].type === 'text') { + if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') { item.tokens[0].text = checkbox + ' ' + item.tokens[0].text; if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') { @@ -2470,18 +2462,12 @@ return Parser; }(); - var Lexer = Lexer_1; - var Parser = Parser_1; - var Tokenizer = Tokenizer_1; - var Renderer = Renderer_1; - var TextRenderer = TextRenderer_1; - var Slugger = Slugger_1; - var merge = helpers.merge, - checkSanitizeDeprecation = helpers.checkSanitizeDeprecation, - escape = helpers.escape; - var getDefaults = defaults$5.exports.getDefaults, - changeDefaults = defaults$5.exports.changeDefaults, - defaults = defaults$5.exports.defaults; + var merge$2 = helpers.merge, + checkSanitizeDeprecation$1 = helpers.checkSanitizeDeprecation, + escape$2 = helpers.escape; + var getDefaults = defaults.getDefaults, + changeDefaults = defaults.changeDefaults, + defaults$5 = defaults.defaults; /** * Marked */ @@ -2501,15 +2487,15 @@ opt = null; } - opt = merge({}, marked.defaults, opt || {}); - checkSanitizeDeprecation(opt); + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); if (callback) { var highlight = opt.highlight; var tokens; try { - tokens = Lexer.lex(src, opt); + tokens = Lexer_1.lex(src, opt); } catch (e) { return callback(e); } @@ -2523,7 +2509,7 @@ marked.walkTokens(tokens, opt.walkTokens); } - out = Parser.parse(tokens, opt); + out = Parser_1.parse(tokens, opt); } catch (e) { err = e; } @@ -2572,18 +2558,18 @@ } try { - var _tokens = Lexer.lex(src, opt); + var _tokens = Lexer_1.lex(src, opt); if (opt.walkTokens) { marked.walkTokens(_tokens, opt.walkTokens); } - return Parser.parse(_tokens, opt); + return Parser_1.parse(_tokens, opt); } catch (e) { e.message += '\nPlease report this to https://github.com/markedjs/marked.'; if (opt.silent) { - return '

    An error occurred:

    ' + escape(e.message + '', true) + '
    '; + return '

    An error occurred:

    ' + escape$2(e.message + '', true) + '
    '; } throw e; @@ -2595,23 +2581,23 @@ marked.options = marked.setOptions = function (opt) { - merge(marked.defaults, opt); + merge$2(marked.defaults, opt); changeDefaults(marked.defaults); return marked; }; marked.getDefaults = getDefaults; - marked.defaults = defaults; + marked.defaults = defaults$5; /** * Use Extension */ marked.use = function (extension) { - var opts = merge({}, extension); + var opts = merge$2({}, extension); if (extension.renderer) { (function () { - var renderer = marked.defaults.renderer || new Renderer(); + var renderer = marked.defaults.renderer || new Renderer_1(); var _loop = function _loop(prop) { var prevRenderer = renderer[prop]; @@ -2641,7 +2627,7 @@ if (extension.tokenizer) { (function () { - var tokenizer = marked.defaults.tokenizer || new Tokenizer(); + var tokenizer = marked.defaults.tokenizer || new Tokenizer_1(); var _loop2 = function _loop2(prop) { var prevTokenizer = tokenizer[prop]; @@ -2743,22 +2729,22 @@ throw new Error('marked.parseInline(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected'); } - opt = merge({}, marked.defaults, opt || {}); - checkSanitizeDeprecation(opt); + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); try { - var tokens = Lexer.lexInline(src, opt); + var tokens = Lexer_1.lexInline(src, opt); if (opt.walkTokens) { marked.walkTokens(tokens, opt.walkTokens); } - return Parser.parseInline(tokens, opt); + return Parser_1.parseInline(tokens, opt); } catch (e) { e.message += '\nPlease report this to https://github.com/markedjs/marked.'; if (opt.silent) { - return '

    An error occurred:

    ' + escape(e.message + '', true) + '
    '; + return '

    An error occurred:

    ' + escape$2(e.message + '', true) + '
    '; } throw e; @@ -2769,14 +2755,14 @@ */ - marked.Parser = Parser; - marked.parser = Parser.parse; - marked.Renderer = Renderer; - marked.TextRenderer = TextRenderer; - marked.Lexer = Lexer; - marked.lexer = Lexer.lex; - marked.Tokenizer = Tokenizer; - marked.Slugger = Slugger; + marked.Parser = Parser_1; + marked.parser = Parser_1.parse; + marked.Renderer = Renderer_1; + marked.TextRenderer = TextRenderer_1; + marked.Lexer = Lexer_1; + marked.lexer = Lexer_1.lex; + marked.Tokenizer = Tokenizer_1; + marked.Slugger = Slugger_1; marked.parse = marked; var marked_1 = marked; diff --git a/src/Tokenizer.js b/src/Tokenizer.js index 31f79298f7..81dca16148 100644 --- a/src/Tokenizer.js +++ b/src/Tokenizer.js @@ -197,9 +197,10 @@ module.exports = class Tokenizer { list(src) { let cap = this.rules.block.list.exec(src); if (cap) { - let raw = cap[0]; + let raw, istask, ischecked, indent, i, blankLine, endsWithBlankLine, + line, lines, itemContents; + let bull = cap[1].trim(); - let indent = cap[1].length + cap[2].search(/[^ ]/); //Find first non-space char const isordered = bull.length > 1; const list = { @@ -211,52 +212,38 @@ module.exports = class Tokenizer { items: [] }; - let istask, - ischecked; - bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`; if(this.options.pedantic) { bull = isordered ? bull : '[*+-]'; } - //Get next list item, stopping at next top-level bullet or blank line followed by insufficient indent. TODO: REPLACE \\s*\\n with \\h*\\n so \n isn't included in \s, then just trim string. + //Get next list item //let itemRegex = `^( {0,3}${bull})( [^\\n]*(?:\\n(?!hr)(?! {0,1}bull)(?!\\s*\\n {0,${indent - 1}}[^\\s])[^\\n]*)*(?:\\s*\\n)*|\\s*)`; - let itemRegex = new RegExp(`^( {0,3}${bull})((?: [^\\n]*|\\h*)(?:\\n[^\\n]*)*(?:\\n|$))`); - - let blankLine = false; - let endsWithBlankLine = false; + const itemRegex = new RegExp(`^( {0,3}${bull})((?: [^\\n]*|\\h*)(?:\\n[^\\n]*)*(?:\\n|$))`); // Get each top-level item while(src) { if(this.rules.block.hr.exec(src)) // End list if we encounter an HR (possibly move into itemRegex?) break; - cap = itemRegex.exec(src); - if (!cap) + if(!(cap = itemRegex.exec(src))) break; - // If the previous item ended with a blank line, the list is loose - if(endsWithBlankLine) { - list.loose = true; - } - - let lines = cap[2].split('\n'); + lines = cap[2].split('\n'); indent = cap[1].length + Math.min(4, cap[2].search(/[^ ]/)); //Find first non-space char + itemContents = lines[0].slice(indent - cap[1].length); - let line; blankLine = false; - let itemContents = lines[0].slice(indent - cap[1].length); raw = cap[0]; - let i; for (i = 1; i < lines.length; i++) { line = lines[i]; // End list item if found start of new bullet if(line.match(new RegExp(`^ {0,${Math.min(3, indent - 1)}}[*+-]|\\d{1,9}[.)]`))) { - raw = cap[1] + cap[2].split('\n').slice(0, i).join('\n') + '\n'; + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; break; } @@ -282,19 +269,26 @@ module.exports = class Tokenizer { continue; } else { // Line was not properly indented; end of this item - raw = cap[1] + cap[2].split('\n').slice(0, i).join('\n') + '\n'; + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; break; } } - // List is loose if this item had any blank lines - if(itemContents.match(/\n\h*\n[^\n]/)) { - list.loose = true; - } + if(!list.loose) { + // List is loose if this item had any internal blank lines + if(itemContents.match(/\n\h*\n[^\n]/)) { + list.loose = true; + } - // List is loose if item ended in blank lines, unless it's the final item - if(raw.match(/\n\h*\n\h*$/)) { - endsWithBlankLine = true; + // If the previous item ended with a blank line, the list is loose + else if(endsWithBlankLine) { + list.loose = true; + } + + // Check if current item ended in a blank line + else if(raw.match(/\n\h*\n\h*$/)) { + endsWithBlankLine = true; + } } // Check for task list items From 5cfa5deda7aaa662142b2921105a93c7bdaf9111 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Fri, 18 Jun 2021 02:57:28 -0400 Subject: [PATCH 03/20] more cleanup --- lib/marked.esm.js | 58 +++++++++++++++++++++++------------------------ lib/marked.js | 1 - src/Tokenizer.js | 4 +--- 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/lib/marked.esm.js b/lib/marked.esm.js index 53a297398a..d61a5a7b2c 100644 --- a/lib/marked.esm.js +++ b/lib/marked.esm.js @@ -315,8 +315,7 @@ const { rtrim: rtrim$1, splitCells: splitCells$1, escape: escape$1, - findClosingBracket: findClosingBracket$1, - edit: edit$1 + findClosingBracket: findClosingBracket$1 } = helpers; function outputLink(cap, link, raw) { @@ -606,7 +605,6 @@ var Tokenizer_1 = class Tokenizer { // Check for task list items if (this.options.gfm) { istask = /^\[[ xX]\] /.exec(itemContents); - ischecked = undefined; if (istask) { ischecked = istask[0] !== '[ ] '; itemContents = itemContents.replace(/^\[[ xX]\] +/, ''); @@ -1036,7 +1034,7 @@ var Tokenizer_1 = class Tokenizer { const { noopTest: noopTest$1, - edit: edit$2, + edit: edit$1, merge: merge$1 } = helpers; @@ -1073,7 +1071,7 @@ const block = { block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; -block.def = edit$2(block.def) +block.def = edit$1(block.def) .replace('label', block._label) .replace('title', block._title) .getRegex(); @@ -1084,11 +1082,11 @@ block.bullet = /(?:[*+-]|\d{1,9}[.)])/; // .replace(/bull/g, block.bullet) // .getRegex(); -block.listItemStart = edit$2(/^( *)(bull) */) +block.listItemStart = edit$1(/^( *)(bull) */) .replace('bull', block.bullet) .getRegex(); -block.list = edit$2(block.list) +block.list = edit$1(block.list) .replace(/bull/g, block.bullet) .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))') .replace('def', '\\n+(?=' + block.def.source + ')') @@ -1101,13 +1099,13 @@ block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul'; block._comment = /|$)/; -block.html = edit$2(block.html, 'i') +block.html = edit$1(block.html, 'i') .replace('comment', block._comment) .replace('tag', block._tag) .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/) .getRegex(); -block.paragraph = edit$2(block._paragraph) +block.paragraph = edit$1(block._paragraph) .replace('hr', block.hr) .replace('heading', ' {0,3}#{1,6} ') .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs @@ -1118,7 +1116,7 @@ block.paragraph = edit$2(block._paragraph) .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks .getRegex(); -block.blockquote = edit$2(block.blockquote) +block.blockquote = edit$1(block.blockquote) .replace('paragraph', block.paragraph) .getRegex(); @@ -1141,7 +1139,7 @@ block.gfm = merge$1({}, block.normal, { + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells }); -block.gfm.nptable = edit$2(block.gfm.nptable) +block.gfm.nptable = edit$1(block.gfm.nptable) .replace('hr', block.hr) .replace('heading', ' {0,3}#{1,6} ') .replace('blockquote', ' {0,3}>') @@ -1152,7 +1150,7 @@ block.gfm.nptable = edit$2(block.gfm.nptable) .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks .getRegex(); -block.gfm.table = edit$2(block.gfm.table) +block.gfm.table = edit$1(block.gfm.table) .replace('hr', block.hr) .replace('heading', ' {0,3}#{1,6} ') .replace('blockquote', ' {0,3}>') @@ -1168,7 +1166,7 @@ block.gfm.table = edit$2(block.gfm.table) */ block.pedantic = merge$1({}, block.normal, { - html: edit$2( + html: edit$1( '^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))') @@ -1181,7 +1179,7 @@ block.pedantic = merge$1({}, block.normal, { def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, heading: /^(#{1,6})(.*)(?:\n+|$)/, fences: noopTest$1, // fences not supported - paragraph: edit$2(block.normal._paragraph) + paragraph: edit$1(block.normal._paragraph) .replace('hr', block.hr) .replace('heading', ' *#{1,6} *[^\n]') .replace('lheading', block.lheading) @@ -1226,23 +1224,23 @@ const inline = { // list of punctuation marks from CommonMark spec // without * and _ to handle the different emphasis markers * and _ inline._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~'; -inline.punctuation = edit$2(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); +inline.punctuation = edit$1(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, inline.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g; inline.escapedEmSt = /\\\*|\\_/g; -inline._comment = edit$2(block._comment).replace('(?:-->|$)', '-->').getRegex(); +inline._comment = edit$1(block._comment).replace('(?:-->|$)', '-->').getRegex(); -inline.emStrong.lDelim = edit$2(inline.emStrong.lDelim) +inline.emStrong.lDelim = edit$1(inline.emStrong.lDelim) .replace(/punct/g, inline._punctuation) .getRegex(); -inline.emStrong.rDelimAst = edit$2(inline.emStrong.rDelimAst, 'g') +inline.emStrong.rDelimAst = edit$1(inline.emStrong.rDelimAst, 'g') .replace(/punct/g, inline._punctuation) .getRegex(); -inline.emStrong.rDelimUnd = edit$2(inline.emStrong.rDelimUnd, 'g') +inline.emStrong.rDelimUnd = edit$1(inline.emStrong.rDelimUnd, 'g') .replace(/punct/g, inline._punctuation) .getRegex(); @@ -1250,14 +1248,14 @@ inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; inline._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])?)+(?![-_])/; -inline.autolink = edit$2(inline.autolink) +inline.autolink = edit$1(inline.autolink) .replace('scheme', inline._scheme) .replace('email', inline._email) .getRegex(); inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; -inline.tag = edit$2(inline.tag) +inline.tag = edit$1(inline.tag) .replace('comment', inline._comment) .replace('attribute', inline._attribute) .getRegex(); @@ -1266,17 +1264,17 @@ inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; inline._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/; inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; -inline.link = edit$2(inline.link) +inline.link = edit$1(inline.link) .replace('label', inline._label) .replace('href', inline._href) .replace('title', inline._title) .getRegex(); -inline.reflink = edit$2(inline.reflink) +inline.reflink = edit$1(inline.reflink) .replace('label', inline._label) .getRegex(); -inline.reflinkSearch = edit$2(inline.reflinkSearch, 'g') +inline.reflinkSearch = edit$1(inline.reflinkSearch, 'g') .replace('reflink', inline.reflink) .replace('nolink', inline.nolink) .getRegex(); @@ -1304,10 +1302,10 @@ inline.pedantic = merge$1({}, inline.normal, { endAst: /\*(?!\*)/g, endUnd: /_(?!_)/g }, - link: edit$2(/^!?\[(label)\]\((.*?)\)/) + link: edit$1(/^!?\[(label)\]\((.*?)\)/) .replace('label', inline._label) .getRegex(), - reflink: edit$2(/^!?\[(label)\]\s*\[([^\]]*)\]/) + reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/) .replace('label', inline._label) .getRegex() }); @@ -1317,7 +1315,7 @@ inline.pedantic = merge$1({}, inline.normal, { */ inline.gfm = merge$1({}, inline.normal, { - escape: edit$2(inline.escape).replace('])', '~|])').getRegex(), + escape: edit$1(inline.escape).replace('])', '~|])').getRegex(), _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/, url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/, @@ -1325,7 +1323,7 @@ inline.gfm = merge$1({}, inline.normal, { text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\ Date: Mon, 21 Jun 2021 17:07:29 -0400 Subject: [PATCH 04/20] Passing all Spec tests --- src/Lexer.js | 25 ++++++--- src/Tokenizer.js | 64 ++++++++++++---------- src/rules.js | 2 +- test/specs/commonmark/commonmark.0.29.json | 9 +-- test/specs/gfm/commonmark.0.29.json | 9 +-- test/specs/new/main.html | 4 -- test/specs/new/main.md | 55 ------------------- 7 files changed, 60 insertions(+), 108 deletions(-) delete mode 100644 test/specs/new/main.html delete mode 100644 test/specs/new/main.md diff --git a/src/Lexer.js b/src/Lexer.js index 652b4cf3fd..7af056e632 100644 --- a/src/Lexer.js +++ b/src/Lexer.js @@ -203,7 +203,10 @@ module.exports = class Lexer { src = src.substring(token.raw.length); l = token.items.length; for (i = 0; i < l; i++) { - token.items[i].tokens = this.blockTokens(token.items[i].text, [], token.loose); + token.items[i].tokens = this.blockTokens(token.items[i].text, [], false); + if (token.items[i].tokens.some(t => t.type === 'space')) { + token.loose = true; + } } tokens.push(token); continue; @@ -216,14 +219,20 @@ module.exports = class Lexer { continue; } - // def //NOTE: 'top' here doesn't make a difference on spec tests, and CM spec says definitions *can* go inside other blocks - if (top && (token = this.tokenizer.def(src))) { + // def + if (token = this.tokenizer.def(src)) { src = src.substring(token.raw.length); - if (!this.tokens.links[token.tag]) { - this.tokens.links[token.tag] = { - href: token.href, - title: token.title - }; + lastToken = tokens[tokens.length - 1]; + if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) { + lastToken.raw += '\n' + token.raw; + lastToken.text += '\n' + token.raw; + } else { + if (!this.tokens.links[token.tag]) { + this.tokens.links[token.tag] = { + href: token.href, + title: token.title + }; + } } continue; } diff --git a/src/Tokenizer.js b/src/Tokenizer.js index d5024a802e..f8452db03d 100644 --- a/src/Tokenizer.js +++ b/src/Tokenizer.js @@ -197,7 +197,7 @@ module.exports = class Tokenizer { let cap = this.rules.block.list.exec(src); if (cap) { let raw, istask, ischecked, indent, i, blankLine, endsWithBlankLine, - line, lines, itemContents; + line, lines, itemContents; let bull = cap[1].trim(); const isordered = bull.length > 1; @@ -213,25 +213,31 @@ module.exports = class Tokenizer { bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`; - if(this.options.pedantic) { + if (this.options.pedantic) { bull = isordered ? bull : '[*+-]'; } - //Get next list item - //let itemRegex = `^( {0,3}${bull})( [^\\n]*(?:\\n(?!hr)(?! {0,1}bull)(?!\\s*\\n {0,${indent - 1}}[^\\s])[^\\n]*)*(?:\\s*\\n)*|\\s*)`; + // Get next list item + // let itemRegex = `^( {0,3}${bull})( [^\\n]*(?:\\n(?!hr)(?! {0,1}bull)(?!\\s*\\n {0,${indent - 1}}[^\\s])[^\\n]*)*(?:\\s*\\n)*|\\s*)`; const itemRegex = new RegExp(`^( {0,3}${bull})((?: [^\\n]*|\\h*)(?:\\n[^\\n]*)*(?:\\n|$))`); // Get each top-level item - while(src) { - if(this.rules.block.hr.exec(src)) // End list if we encounter an HR (possibly move into itemRegex?) + while (src) { + if (this.rules.block.hr.exec(src)) // End list if we encounter an HR (possibly move into itemRegex?) break; - if(!(cap = itemRegex.exec(src))) + if (!(cap = itemRegex.exec(src))) break; lines = cap[2].split('\n'); - indent = cap[1].length + Math.min(4, cap[2].search(/[^ ]/)); //Find first non-space char - itemContents = lines[0].slice(indent - cap[1].length); + + if (this.options.pedantic) { + indent = 2; + itemContents = lines[0].trimLeft(); + } else { + indent = cap[1].length + Math.min(4, cap[2].search(/[^ ]/)); // Find first non-space char + itemContents = lines[0].slice(indent - cap[1].length); + } blankLine = false; @@ -240,53 +246,50 @@ module.exports = class Tokenizer { for (i = 1; i < lines.length; i++) { line = lines[i]; + if (this.options.pedantic) { // Re-align to follow commonmark nesting rules + line = line.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' '); + } + // End list item if found start of new bullet - if(line.match(new RegExp(`^ {0,${Math.min(3, indent - 1)}}[*+-]|\\d{1,9}[.)]`))) { + if (line.match(new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])`))) { raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; break; } // Until we encounter a blank line, item contents do not need indentation - if(!blankLine) { - if(!line.trim()) { // Check if current line is empty + if (!blankLine) { + if (!line.trim()) { // Check if current line is empty blankLine = true; } // Dedent if possible - if(line.search(/[^ ]/) >= indent) { + if (line.search(/[^ ]/) >= indent) { itemContents += '\n' + line.slice(indent); - } - else { + } else { itemContents += '\n' + line; } continue; } // Dedent this line - if(line.search(/[^ ]/) >= indent || !line.trim()) { + if (line.search(/[^ ]/) >= indent || !line.trim()) { itemContents += '\n' + line.slice(indent); continue; - } - else { // Line was not properly indented; end of this item + } else { // Line was not properly indented; end of this item raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; break; } } - if(!list.loose) { - // List is loose if this item had any internal blank lines - if(itemContents.match(/\n\h*\n[^\n]/)) { - list.loose = true; - } - + if (!list.loose) { // If the previous item ended with a blank line, the list is loose - else if(endsWithBlankLine) { + if (endsWithBlankLine) { list.loose = true; } // Check if current item ended in a blank line - else if(raw.match(/\n\h*\n\h*$/)) { - endsWithBlankLine = true; + else if (raw.match(/\n\h*\n\h*$/)) { + endsWithBlankLine = true; } } @@ -302,7 +305,7 @@ module.exports = class Tokenizer { list.items.push({ type: 'list_item', raw: raw, - task: istask ? true : false, + task: !!istask, checked: ischecked, loose: blankLine, text: itemContents @@ -312,6 +315,11 @@ module.exports = class Tokenizer { src = src.slice(raw.length); } + // Do not consume ending newlines. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic + list.items[list.items.length - 1].raw = raw.trimRight(); + list.items[list.items.length - 1].text = itemContents.trimRight(); + list.raw = list.raw.trimRight(); + return list; } } diff --git a/src/rules.js b/src/rules.js index 361d6ef6c5..fb04000107 100644 --- a/src/rules.js +++ b/src/rules.js @@ -10,7 +10,7 @@ const { const block = { newline: /^(?: *(?:\n|$))+/, code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/, - fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, + fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?=\n|$)|$)/, hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, diff --git a/test/specs/commonmark/commonmark.0.29.json b/test/specs/commonmark/commonmark.0.29.json index 653ec691b5..fc27506cb6 100644 --- a/test/specs/commonmark/commonmark.0.29.json +++ b/test/specs/commonmark/commonmark.0.29.json @@ -2226,8 +2226,7 @@ "example": 277, "start_line": 5050, "end_line": 5072, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- foo\n- bar\n\n\n\n- baz\n- bim\n", @@ -2315,8 +2314,7 @@ "example": 288, "start_line": 5309, "end_line": 5328, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- a\n - b\n\n c\n- d\n", @@ -2324,8 +2322,7 @@ "example": 289, "start_line": 5335, "end_line": 5353, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "* a\n > b\n >\n* c\n", diff --git a/test/specs/gfm/commonmark.0.29.json b/test/specs/gfm/commonmark.0.29.json index 03edb79a68..e6479a84ed 100644 --- a/test/specs/gfm/commonmark.0.29.json +++ b/test/specs/gfm/commonmark.0.29.json @@ -2226,8 +2226,7 @@ "example": 277, "start_line": 5050, "end_line": 5072, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- foo\n- bar\n\n\n\n- baz\n- bim\n", @@ -2315,8 +2314,7 @@ "example": 288, "start_line": 5309, "end_line": 5328, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- a\n - b\n\n c\n- d\n", @@ -2324,8 +2322,7 @@ "example": 289, "start_line": 5335, "end_line": 5353, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "* a\n > b\n >\n* c\n", diff --git a/test/specs/new/main.html b/test/specs/new/main.html deleted file mode 100644 index 8f78c4d30e..0000000000 --- a/test/specs/new/main.html +++ /dev/null @@ -1,4 +0,0 @@ -

    A heading

    Just a note, I've found that I can't test my markdown parser vs others. For example, both markdown.js and showdown code blocks in lists wrong. They're also completely inconsistent with regards to paragraphs in list items.

    A link. Not anymore.

    • List Item 1

    • List Item 2

      • New List Item 1 Hi, this is a list item.
      • New List Item 2 Another item
        Code goes here.
        -Lots of it...
      • New List Item 3 The last item
    • List Item 3 The final item.

    • List Item 4 The real final item.

    Paragraph.

    • bq Item 1
    • bq Item 2
      • New bq Item 1
      • New bq Item 2 Text here

    Another blockquote! I really need to get more creative with mockup text.. markdown.js breaks here again

    Another Heading

    Hello world. Here is a link. And an image alt.

    Code goes here.
    -Lots of it...
    diff --git a/test/specs/new/main.md b/test/specs/new/main.md deleted file mode 100644 index 58e17a6a76..0000000000 --- a/test/specs/new/main.md +++ /dev/null @@ -1,55 +0,0 @@ -[test]: http://google.com/ "Google" - -# A heading - -Just a note, I've found that I can't test my markdown parser vs others. -For example, both markdown.js and showdown code blocks in lists wrong. They're -also completely [inconsistent][test] with regards to paragraphs in list items. - -A link. Not anymore. - - - -* List Item 1 - -* List Item 2 - * New List Item 1 - Hi, this is a list item. - * New List Item 2 - Another item - Code goes here. - Lots of it... - * New List Item 3 - The last item - -* List Item 3 -The final item. - -* List Item 4 -The real final item. - -Paragraph. - -> * bq Item 1 -> * bq Item 2 -> * New bq Item 1 -> * New bq Item 2 -> Text here - -* * * - -> Another blockquote! -> I really need to get -> more creative with -> mockup text.. -> markdown.js breaks here again - -Another Heading -------------- - -Hello *world*. Here is a [link](//hello). -And an image ![alt](src). - - Code goes here. - Lots of it... From 65907eec09167d970646f6cfc2d43e99d44d7296 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Mon, 21 Jun 2021 17:14:18 -0400 Subject: [PATCH 05/20] Fix some unit tests (lists no longer consume blank lines at end of list) --- src/Lexer.js | 1 + src/Tokenizer.js | 2 +- test/unit/Lexer-spec.js | 12 ++++++------ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Lexer.js b/src/Lexer.js index 7af056e632..acfcb3f2d7 100644 --- a/src/Lexer.js +++ b/src/Lexer.js @@ -206,6 +206,7 @@ module.exports = class Lexer { token.items[i].tokens = this.blockTokens(token.items[i].text, [], false); if (token.items[i].tokens.some(t => t.type === 'space')) { token.loose = true; + token.items[i].loose = true; } } tokens.push(token); diff --git a/src/Tokenizer.js b/src/Tokenizer.js index f8452db03d..fad83e442d 100644 --- a/src/Tokenizer.js +++ b/src/Tokenizer.js @@ -307,7 +307,7 @@ module.exports = class Tokenizer { raw: raw, task: !!istask, checked: ischecked, - loose: blankLine, + loose: false, text: itemContents }); diff --git a/test/unit/Lexer-spec.js b/test/unit/Lexer-spec.js index f8361ace77..eb2ced0969 100644 --- a/test/unit/Lexer-spec.js +++ b/test/unit/Lexer-spec.js @@ -301,7 +301,7 @@ a | b tokens: [ { type: 'list', - raw: '- item 1\n- item 2\n', + raw: '- item 1\n- item 2', ordered: false, start: '', loose: false, @@ -322,7 +322,7 @@ a | b }, { type: 'list_item', - raw: '- item 2\n', + raw: '- item 2', task: false, checked: undefined, loose: false, @@ -374,7 +374,7 @@ a | b tokens: jasmine.arrayContaining([ jasmine.objectContaining({ type: 'list', - raw: '1) item 1\n2) item 2\n', + raw: '1) item 1\n2) item 2', ordered: true, start: 1, items: [ @@ -382,7 +382,7 @@ a | b raw: '1) item 1\n' }), jasmine.objectContaining({ - raw: '2) item 2\n' + raw: '2) item 2' }) ] }) @@ -485,14 +485,14 @@ paragraph tokens: jasmine.arrayContaining([ jasmine.objectContaining({ type: 'list', - raw: '- item 1\n\n- item 2\n', + raw: '- item 1\n\n- item 2', loose: true, items: [ jasmine.objectContaining({ raw: '- item 1\n\n' }), jasmine.objectContaining({ - raw: '- item 2\n' + raw: '- item 2' }) ] }) From 3dd2affb589855b116ae32e7e6f30a46ffb964b7 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Mon, 21 Jun 2021 17:17:42 -0400 Subject: [PATCH 06/20] Fix more "lists consuming blank lines" unit tests. --- test/unit/Lexer-spec.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/unit/Lexer-spec.js b/test/unit/Lexer-spec.js index eb2ced0969..638f0abd3d 100644 --- a/test/unit/Lexer-spec.js +++ b/test/unit/Lexer-spec.js @@ -349,7 +349,7 @@ a | b tokens: jasmine.arrayContaining([ jasmine.objectContaining({ type: 'list', - raw: '1. item 1\n2. item 2\n', + raw: '1. item 1\n2. item 2', ordered: true, start: 1, items: [ @@ -357,7 +357,7 @@ a | b raw: '1. item 1\n' }), jasmine.objectContaining({ - raw: '2. item 2\n' + raw: '2. item 2' }) ] }) @@ -401,7 +401,7 @@ paragraph tokens: [ { type: 'list', - raw: '- item 1\n- item 2\n\n', + raw: '- item 1\n- item 2', ordered: false, start: '', loose: false, @@ -422,7 +422,7 @@ paragraph }, { type: 'list_item', - raw: '- item 2\n\n', + raw: '- item 2', task: false, checked: undefined, loose: false, @@ -436,6 +436,7 @@ paragraph } ] }, + { type: 'space', raw: '\n\n' }, { type: 'paragraph', raw: 'paragraph', @@ -459,7 +460,7 @@ paragraph tokens: jasmine.arrayContaining([ jasmine.objectContaining({ type: 'list', - raw: '2. item 1\n3. item 2\n', + raw: '2. item 1\n3. item 2', ordered: true, start: 2, items: [ @@ -467,7 +468,7 @@ paragraph raw: '2. item 1\n' }), jasmine.objectContaining({ - raw: '3. item 2\n' + raw: '3. item 2' }) ] }) @@ -509,7 +510,7 @@ paragraph tokens: jasmine.arrayContaining([ jasmine.objectContaining({ type: 'list', - raw: '- [ ] item 1\n- [x] item 2\n', + raw: '- [ ] item 1\n- [x] item 2', items: [ jasmine.objectContaining({ raw: '- [ ] item 1\n', @@ -517,7 +518,7 @@ paragraph checked: false }), jasmine.objectContaining({ - raw: '- [x] item 2\n', + raw: '- [x] item 2', task: true, checked: true }) From 4fe6e7c07d9b1a2535d1de0817a19b02181f0513 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Mon, 21 Jun 2021 17:21:44 -0400 Subject: [PATCH 07/20] All unit tests passing! --- test/unit/marked-spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/unit/marked-spec.js b/test/unit/marked-spec.js index 4e4b1b46f9..f636a6c7ca 100644 --- a/test/unit/marked-spec.js +++ b/test/unit/marked-spec.js @@ -984,6 +984,7 @@ br ['heading', '# heading'], ['text', 'heading'], ['code', '```code```'], + ['space', ''], ['table', '| a | b ||---|---|| 1 | 2 || 3 | 4 |'], ['text', 'a'], ['text', 'b'], @@ -998,6 +999,7 @@ br ['list_item', '- list'], ['text', 'list'], ['text', 'list'], + ['space', ''], ['html', '
    html
    '], ['paragraph', '[link](https://example.com)'], ['link', '[link](https://example.com)'], From 41f9bde2e25c1906a0a10152e6d17b541cab0312 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Mon, 21 Jun 2021 17:27:45 -0400 Subject: [PATCH 08/20] Lint --- src/Tokenizer.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Tokenizer.js b/src/Tokenizer.js index fad83e442d..6978f8c696 100644 --- a/src/Tokenizer.js +++ b/src/Tokenizer.js @@ -223,11 +223,13 @@ module.exports = class Tokenizer { // Get each top-level item while (src) { - if (this.rules.block.hr.exec(src)) // End list if we encounter an HR (possibly move into itemRegex?) + if (this.rules.block.hr.exec(src)) { // End list if we encounter an HR (possibly move into itemRegex?) break; + } - if (!(cap = itemRegex.exec(src))) + if (!(cap = itemRegex.exec(src))) { break; + } lines = cap[2].split('\n'); @@ -285,10 +287,7 @@ module.exports = class Tokenizer { // If the previous item ended with a blank line, the list is loose if (endsWithBlankLine) { list.loose = true; - } - - // Check if current item ended in a blank line - else if (raw.match(/\n\h*\n\h*$/)) { + } else if (raw.match(/\n\h*\n\h*$/)) { endsWithBlankLine = true; } } From bb8fe0075cd34fb893c1f8c0a092ad6ca458e8d1 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Mon, 21 Jun 2021 17:44:12 -0400 Subject: [PATCH 09/20] Two more commonmark examples fixed --- src/rules.js | 2 +- test/specs/commonmark/commonmark.0.29.json | 6 ++---- test/specs/gfm/commonmark.0.29.json | 6 ++---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/rules.js b/src/rules.js index fb04000107..f49ddb0928 100644 --- a/src/rules.js +++ b/src/rules.js @@ -14,7 +14,7 @@ const block = { hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( {0,3}bull)( [^\n]+?)(?:\n|$)/, + list: /^( {0,3}bull)( [^\n]+?)?(?:\n|$)/, html: '^ {0,3}(?:' // optional indentation + '<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + '|comment[^\\n]*(\\n+|$)' // (2) diff --git a/test/specs/commonmark/commonmark.0.29.json b/test/specs/commonmark/commonmark.0.29.json index fc27506cb6..c6719d376d 100644 --- a/test/specs/commonmark/commonmark.0.29.json +++ b/test/specs/commonmark/commonmark.0.29.json @@ -1991,8 +1991,7 @@ "example": 248, "start_line": 4237, "end_line": 4258, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "- \n foo\n", @@ -2041,8 +2040,7 @@ "example": 254, "start_line": 4336, "end_line": 4342, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "foo\n*\n\nfoo\n1.\n", diff --git a/test/specs/gfm/commonmark.0.29.json b/test/specs/gfm/commonmark.0.29.json index e6479a84ed..f10fbcfbbe 100644 --- a/test/specs/gfm/commonmark.0.29.json +++ b/test/specs/gfm/commonmark.0.29.json @@ -1991,8 +1991,7 @@ "example": 248, "start_line": 4237, "end_line": 4258, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "- \n foo\n", @@ -2041,8 +2040,7 @@ "example": 254, "start_line": 4336, "end_line": 4342, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "foo\n*\n\nfoo\n1.\n", From d5ba0f3158ed8e5363e97e69d28a419adc332abd Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Tue, 22 Jun 2021 00:18:55 -0400 Subject: [PATCH 10/20] List and List Items fully Commonmark Compliant!!! --- src/Tokenizer.js | 11 ++++++++--- test/specs/commonmark/commonmark.0.29.json | 9 +++------ test/specs/gfm/commonmark.0.29.json | 9 +++------ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/Tokenizer.js b/src/Tokenizer.js index 6978f8c696..6b8d66578a 100644 --- a/src/Tokenizer.js +++ b/src/Tokenizer.js @@ -218,7 +218,6 @@ module.exports = class Tokenizer { } // Get next list item - // let itemRegex = `^( {0,3}${bull})( [^\\n]*(?:\\n(?!hr)(?! {0,1}bull)(?!\\s*\\n {0,${indent - 1}}[^\\s])[^\\n]*)*(?:\\s*\\n)*|\\s*)`; const itemRegex = new RegExp(`^( {0,3}${bull})((?: [^\\n]*|\\h*)(?:\\n[^\\n]*)*(?:\\n|$))`); // Get each top-level item @@ -237,14 +236,20 @@ module.exports = class Tokenizer { indent = 2; itemContents = lines[0].trimLeft(); } else { - indent = cap[1].length + Math.min(4, cap[2].search(/[^ ]/)); // Find first non-space char + indent = cap[2].search(/[^ ]/); // Find first non-space char + indent = cap[1].length + (indent > 4 ? 1 : indent); // intented code blocks after 4 spaces; indent is always 1 itemContents = lines[0].slice(indent - cap[1].length); } blankLine = false; - raw = cap[0]; + if (!lines[0] && lines[1].match(/^\h*$/)) { // items begin with at most one blank line + raw = cap[1] + lines.slice(0, 2).join('\n') + '\n'; + list.loose = true; + lines = []; + } + for (i = 1; i < lines.length; i++) { line = lines[i]; diff --git a/test/specs/commonmark/commonmark.0.29.json b/test/specs/commonmark/commonmark.0.29.json index c6719d376d..79646177b0 100644 --- a/test/specs/commonmark/commonmark.0.29.json +++ b/test/specs/commonmark/commonmark.0.29.json @@ -1949,8 +1949,7 @@ "example": 243, "start_line": 4133, "end_line": 4149, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "1. indented code\n\n paragraph\n\n more code\n", @@ -1958,8 +1957,7 @@ "example": 244, "start_line": 4155, "end_line": 4171, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": " foo\n\nbar\n", @@ -2007,8 +2005,7 @@ "example": 250, "start_line": 4277, "end_line": 4286, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "- foo\n-\n- bar\n", diff --git a/test/specs/gfm/commonmark.0.29.json b/test/specs/gfm/commonmark.0.29.json index f10fbcfbbe..98d27ae27d 100644 --- a/test/specs/gfm/commonmark.0.29.json +++ b/test/specs/gfm/commonmark.0.29.json @@ -1949,8 +1949,7 @@ "example": 243, "start_line": 4133, "end_line": 4149, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "1. indented code\n\n paragraph\n\n more code\n", @@ -1958,8 +1957,7 @@ "example": 244, "start_line": 4155, "end_line": 4171, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": " foo\n\nbar\n", @@ -2007,8 +2005,7 @@ "example": 250, "start_line": 4277, "end_line": 4286, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "- foo\n-\n- bar\n", From 3a409a166102678e218243295f106ea4ab0962aa Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Tue, 22 Jun 2021 00:22:27 -0400 Subject: [PATCH 11/20] bundles --- lib/marked.esm.js | 668 +++++++++++++++++++++++----------------------- lib/marked.js | 649 ++++++++++++++++++++++---------------------- 2 files changed, 673 insertions(+), 644 deletions(-) diff --git a/lib/marked.esm.js b/lib/marked.esm.js index d6bd317891..b56d4c4aac 100644 --- a/lib/marked.esm.js +++ b/lib/marked.esm.js @@ -9,9 +9,13 @@ * The code in this file is generated from files in ./src/ */ -var defaults$5 = {exports: {}}; +function createCommonjsModule(fn) { + var module = { exports: {} }; + return fn(module, module.exports), module.exports; +} -function getDefaults$1() { +var defaults = createCommonjsModule(function (module) { +function getDefaults() { return { baseUrl: null, breaks: false, @@ -35,20 +39,20 @@ function getDefaults$1() { }; } -function changeDefaults$1(newDefaults) { - defaults$5.exports.defaults = newDefaults; +function changeDefaults(newDefaults) { + module.exports.defaults = newDefaults; } -defaults$5.exports = { - defaults: getDefaults$1(), - getDefaults: getDefaults$1, - changeDefaults: changeDefaults$1 +module.exports = { + defaults: getDefaults(), + getDefaults, + changeDefaults }; +}); /** * Helpers */ - const escapeTest = /[&<>"']/; const escapeReplace = /[&<>"']/g; const escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/; @@ -61,7 +65,7 @@ const escapeReplacements = { "'": ''' }; const getEscapeReplacement = (ch) => escapeReplacements[ch]; -function escape$3(html, encode) { +function escape(html, encode) { if (encode) { if (escapeTest.test(html)) { return html.replace(escapeReplace, getEscapeReplacement); @@ -77,7 +81,7 @@ function escape$3(html, encode) { const unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig; -function unescape$1(html) { +function unescape(html) { // explicitly match decimal, hex, and named HTML entities return html.replace(unescapeTest, (_, n) => { n = n.toLowerCase(); @@ -92,7 +96,7 @@ function unescape$1(html) { } const caret = /(^|[^\[])\^/g; -function edit$1(regex, opt) { +function edit(regex, opt) { regex = regex.source || regex; opt = opt || ''; const obj = { @@ -111,11 +115,11 @@ function edit$1(regex, opt) { const nonWordAndColonTest = /[^\w:]/g; const originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; -function cleanUrl$1(sanitize, base, href) { +function cleanUrl(sanitize, base, href) { if (sanitize) { let prot; try { - prot = decodeURIComponent(unescape$1(href)) + prot = decodeURIComponent(unescape(href)) .replace(nonWordAndColonTest, '') .toLowerCase(); } catch (e) { @@ -149,7 +153,7 @@ function resolveUrl(base, href) { if (justDomain.test(base)) { baseUrls[' ' + base] = base + '/'; } else { - baseUrls[' ' + base] = rtrim$1(base, '/', true); + baseUrls[' ' + base] = rtrim(base, '/', true); } } base = baseUrls[' ' + base]; @@ -170,9 +174,9 @@ function resolveUrl(base, href) { } } -const noopTest$1 = { exec: function noopTest() {} }; +const noopTest = { exec: function noopTest() {} }; -function merge$2(obj) { +function merge(obj) { let i = 1, target, key; @@ -189,7 +193,7 @@ function merge$2(obj) { return obj; } -function splitCells$1(tableRow, count) { +function splitCells(tableRow, count) { // ensure that every cell-delimiting pipe has a space // before it to distinguish it from an escaped pipe const row = tableRow.replace(/\|/g, (match, offset, str) => { @@ -224,7 +228,7 @@ function splitCells$1(tableRow, count) { // Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). // /c*$/ is vulnerable to REDOS. // invert: Remove suffix of non-c chars instead. Default falsey. -function rtrim$1(str, c, invert) { +function rtrim(str, c, invert) { const l = str.length; if (l === 0) { return ''; @@ -248,7 +252,7 @@ function rtrim$1(str, c, invert) { return str.substr(0, l - suffLen); } -function findClosingBracket$1(str, b) { +function findClosingBracket(str, b) { if (str.indexOf(b[1]) === -1) { return -1; } @@ -270,14 +274,14 @@ function findClosingBracket$1(str, b) { return -1; } -function checkSanitizeDeprecation$1(opt) { +function checkSanitizeDeprecation(opt) { if (opt && opt.sanitize && !opt.silent) { console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); } } // copied from https://stackoverflow.com/a/5450113/806777 -function repeatString$1(pattern, count) { +function repeatString(pattern, count) { if (count < 1) { return ''; } @@ -293,31 +297,31 @@ function repeatString$1(pattern, count) { } var helpers = { - escape: escape$3, - unescape: unescape$1, - edit: edit$1, - cleanUrl: cleanUrl$1, + escape, + unescape, + edit, + cleanUrl, resolveUrl, - noopTest: noopTest$1, - merge: merge$2, - splitCells: splitCells$1, - rtrim: rtrim$1, - findClosingBracket: findClosingBracket$1, - checkSanitizeDeprecation: checkSanitizeDeprecation$1, - repeatString: repeatString$1 + noopTest, + merge, + splitCells, + rtrim, + findClosingBracket, + checkSanitizeDeprecation, + repeatString }; -const { defaults: defaults$4 } = defaults$5.exports; +const { defaults: defaults$1 } = defaults; const { - rtrim, - splitCells, - escape: escape$2, - findClosingBracket + rtrim: rtrim$1, + splitCells: splitCells$1, + escape: escape$1, + findClosingBracket: findClosingBracket$1 } = helpers; function outputLink(cap, link, raw) { const href = link.href; - const title = link.title ? escape$2(link.title) : null; + const title = link.title ? escape$1(link.title) : null; const text = cap[1].replace(/\\([\[\]])/g, '$1'); if (cap[0].charAt(0) !== '!') { @@ -334,7 +338,7 @@ function outputLink(cap, link, raw) { raw, href, title, - text: escape$2(text) + text: escape$1(text) }; } } @@ -372,7 +376,7 @@ function indentCodeCompensation(raw, text) { */ var Tokenizer_1 = class Tokenizer { constructor(options) { - this.options = options || defaults$4; + this.options = options || defaults$1; } space(src) { @@ -397,7 +401,7 @@ var Tokenizer_1 = class Tokenizer { raw: cap[0], codeBlockStyle: 'indented', text: !this.options.pedantic - ? rtrim(text, '\n') + ? rtrim$1(text, '\n') : text }; } @@ -425,7 +429,7 @@ var Tokenizer_1 = class Tokenizer { // remove trailing #s if (/#$/.test(text)) { - const trimmed = rtrim(text, '#'); + const trimmed = rtrim$1(text, '#'); if (this.options.pedantic) { text = trimmed.trim(); } else if (!trimmed || / $/.test(trimmed)) { @@ -448,7 +452,7 @@ var Tokenizer_1 = class Tokenizer { if (cap) { const item = { type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [], raw: cap[0] @@ -471,7 +475,7 @@ var Tokenizer_1 = class Tokenizer { l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells(item.cells[i], item.header.length); + item.cells[i] = splitCells$1(item.cells[i], item.header.length); } return item; @@ -503,134 +507,136 @@ var Tokenizer_1 = class Tokenizer { } list(src) { - const cap = this.rules.block.list.exec(src); + let cap = this.rules.block.list.exec(src); if (cap) { - let raw = cap[0]; - const bull = cap[2]; + let raw, istask, ischecked, indent, i, blankLine, endsWithBlankLine, + line, lines, itemContents; + + let bull = cap[1].trim(); const isordered = bull.length > 1; const list = { type: 'list', - raw, + raw: '', ordered: isordered, start: isordered ? +bull.slice(0, -1) : '', loose: false, items: [] }; - // Get each top-level item. - const itemMatch = cap[0].match(this.rules.block.item); - - let next = false, - item, - space, - bcurr, - bnext, - addBack, - loose, - istask, - ischecked, - endMatch; - - let l = itemMatch.length; - bcurr = this.rules.block.listItemStart.exec(itemMatch[0]); - for (let i = 0; i < l; i++) { - item = itemMatch[i]; - raw = item; - - if (!this.options.pedantic) { - // Determine if current item contains the end of the list - endMatch = item.match(new RegExp('\\n\\s*\\n {0,' + (bcurr[0].length - 1) + '}\\S')); - if (endMatch) { - addBack = item.length - endMatch.index + itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - - item = item.substring(0, endMatch.index); - raw = item; - l = i + 1; - } + bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`; + + if (this.options.pedantic) { + bull = isordered ? bull : '[*+-]'; + } + + // Get next list item + const itemRegex = new RegExp(`^( {0,3}${bull})((?: [^\\n]*|\\h*)(?:\\n[^\\n]*)*(?:\\n|$))`); + + // Get each top-level item + while (src) { + if (this.rules.block.hr.exec(src)) { // End list if we encounter an HR (possibly move into itemRegex?) + break; } - // Determine whether the next list item belongs here. - // Backpedal if it does not belong in this list. - if (i !== l - 1) { - bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]); - if ( - !this.options.pedantic - ? bnext[1].length >= bcurr[0].length || bnext[1].length > 3 - : bnext[1].length > bcurr[1].length - ) { - // nested list or continuation - itemMatch.splice(i, 2, itemMatch[i] + (!this.options.pedantic && bnext[1].length < bcurr[0].length && !itemMatch[i].match(/\n$/) ? '' : '\n') + itemMatch[i + 1]); - i--; - l--; - continue; - } else if ( - // different bullet style - !this.options.pedantic || this.options.smartLists - ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] - : isordered === (bnext[2].length === 1) - ) { - addBack = itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - i = l - 1; - } - bcurr = bnext; + if (!(cap = itemRegex.exec(src))) { + break; } - // Remove the list item's bullet - // so it is seen as the next token. - space = item.length; - item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); + lines = cap[2].split('\n'); - // Outdent whatever the - // list item contains. Hacky. - if (~item.indexOf('\n ')) { - space -= item.length; - item = !this.options.pedantic - ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') - : item.replace(/^ {1,4}/gm, ''); + if (this.options.pedantic) { + indent = 2; + itemContents = lines[0].trimLeft(); + } else { + indent = cap[2].search(/[^ ]/); // Find first non-space char + indent = cap[1].length + (indent > 4 ? 1 : indent); // intented code blocks after 4 spaces; indent is always 1 + itemContents = lines[0].slice(indent - cap[1].length); } - // trim item newlines at end - item = rtrim(item, '\n'); - if (i !== l - 1) { - raw = raw + '\n'; + blankLine = false; + raw = cap[0]; + + if (!lines[0] && lines[1].match(/^\h*$/)) { // items begin with at most one blank line + raw = cap[1] + lines.slice(0, 2).join('\n') + '\n'; + list.loose = true; + lines = []; } - // Determine whether item is loose or not. - // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ - // for discount behavior. - loose = next || /\n\n(?!\s*$)/.test(raw); - if (i !== l - 1) { - next = raw.slice(-2) === '\n\n'; - if (!loose) loose = next; + for (i = 1; i < lines.length; i++) { + line = lines[i]; + + if (this.options.pedantic) { // Re-align to follow commonmark nesting rules + line = line.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' '); + } + + // End list item if found start of new bullet + if (line.match(new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])`))) { + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; + break; + } + + // Until we encounter a blank line, item contents do not need indentation + if (!blankLine) { + if (!line.trim()) { // Check if current line is empty + blankLine = true; + } + + // Dedent if possible + if (line.search(/[^ ]/) >= indent) { + itemContents += '\n' + line.slice(indent); + } else { + itemContents += '\n' + line; + } + continue; + } + + // Dedent this line + if (line.search(/[^ ]/) >= indent || !line.trim()) { + itemContents += '\n' + line.slice(indent); + continue; + } else { // Line was not properly indented; end of this item + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; + break; + } } - if (loose) { - list.loose = true; + if (!list.loose) { + // If the previous item ended with a blank line, the list is loose + if (endsWithBlankLine) { + list.loose = true; + } else if (raw.match(/\n\h*\n\h*$/)) { + endsWithBlankLine = true; + } } // Check for task list items if (this.options.gfm) { - istask = /^\[[ xX]\] /.test(item); - ischecked = undefined; + istask = /^\[[ xX]\] /.exec(itemContents); if (istask) { - ischecked = item[1] !== ' '; - item = item.replace(/^\[[ xX]\] +/, ''); + ischecked = istask[0] !== '[ ] '; + itemContents = itemContents.replace(/^\[[ xX]\] +/, ''); } } list.items.push({ type: 'list_item', - raw, - task: istask, + raw: raw, + task: !!istask, checked: ischecked, - loose: loose, - text: item + loose: false, + text: itemContents }); + + list.raw += raw; + src = src.slice(raw.length); } + // Do not consume ending newlines. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic + list.items[list.items.length - 1].raw = raw.trimRight(); + list.items[list.items.length - 1].text = itemContents.trimRight(); + list.raw = list.raw.trimRight(); + return list; } } @@ -645,7 +651,7 @@ var Tokenizer_1 = class Tokenizer { raw: cap[0], pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), - text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0] + text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$1(cap[0])) : cap[0] }; } } @@ -670,7 +676,7 @@ var Tokenizer_1 = class Tokenizer { if (cap) { const item = { type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] }; @@ -694,7 +700,7 @@ var Tokenizer_1 = class Tokenizer { l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells( + item.cells[i] = splitCells$1( item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length); } @@ -746,7 +752,7 @@ var Tokenizer_1 = class Tokenizer { return { type: 'escape', raw: cap[0], - text: escape$2(cap[1]) + text: escape$1(cap[1]) }; } } @@ -775,7 +781,7 @@ var Tokenizer_1 = class Tokenizer { text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) - : escape$2(cap[0])) + : escape$1(cap[0])) : cap[0] }; } @@ -792,13 +798,13 @@ var Tokenizer_1 = class Tokenizer { } // ending angle bracket cannot be escaped - const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\'); + const rtrimSlash = rtrim$1(trimmedUrl.slice(0, -1), '\\'); if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) { return; } } else { // find closing parenthesis - const lastParenIndex = findClosingBracket(cap[2], '()'); + const lastParenIndex = findClosingBracket$1(cap[2], '()'); if (lastParenIndex > -1) { const start = cap[0].indexOf('!') === 0 ? 5 : 4; const linkLen = start + cap[1].length + lastParenIndex; @@ -926,7 +932,7 @@ var Tokenizer_1 = class Tokenizer { if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) { text = text.substring(1, text.length - 1); } - text = escape$2(text, true); + text = escape$1(text, true); return { type: 'codespan', raw: cap[0], @@ -961,10 +967,10 @@ var Tokenizer_1 = class Tokenizer { if (cap) { let text, href; if (cap[2] === '@') { - text = escape$2(this.options.mangle ? mangle(cap[1]) : cap[1]); + text = escape$1(this.options.mangle ? mangle(cap[1]) : cap[1]); href = 'mailto:' + text; } else { - text = escape$2(cap[1]); + text = escape$1(cap[1]); href = text; } @@ -989,7 +995,7 @@ var Tokenizer_1 = class Tokenizer { if (cap = this.rules.inline.url.exec(src)) { let text, href; if (cap[2] === '@') { - text = escape$2(this.options.mangle ? mangle(cap[0]) : cap[0]); + text = escape$1(this.options.mangle ? mangle(cap[0]) : cap[0]); href = 'mailto:' + text; } else { // do extended autolink path validation @@ -998,7 +1004,7 @@ var Tokenizer_1 = class Tokenizer { prevCapZero = cap[0]; cap[0] = this.rules.inline._backpedal.exec(cap[0])[0]; } while (prevCapZero !== cap[0]); - text = escape$2(cap[0]); + text = escape$1(cap[0]); if (cap[1] === 'www.') { href = 'http://' + text; } else { @@ -1026,9 +1032,9 @@ var Tokenizer_1 = class Tokenizer { if (cap) { let text; if (inRawBlock) { - text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0]; + text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$1(cap[0])) : cap[0]; } else { - text = escape$2(this.options.smartypants ? smartypants(cap[0]) : cap[0]); + text = escape$1(this.options.smartypants ? smartypants(cap[0]) : cap[0]); } return { type: 'text', @@ -1040,22 +1046,22 @@ var Tokenizer_1 = class Tokenizer { }; const { - noopTest, - edit, + noopTest: noopTest$1, + edit: edit$1, merge: merge$1 } = helpers; /** * Block-Level Grammar */ -const block$1 = { +const block = { newline: /^(?: *(?:\n|$))+/, code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/, - fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, + fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?=\n|$)|$)/, hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/, + list: /^( {0,3}bull)( [^\n]+?)?(?:\n|$)/, html: '^ {0,3}(?:' // optional indentation + '<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + '|comment[^\\n]*(\\n+|$)' // (2) @@ -1067,8 +1073,8 @@ const block$1 = { + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag + ')', def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, - nptable: noopTest, - table: noopTest, + nptable: noopTest$1, + table: noopTest$1, lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/, // regex template, placeholders will be replaced according to different paragraph // interruption rules of commonmark and the original markdown spec: @@ -1076,68 +1082,68 @@ const block$1 = { text: /^[^\n]+/ }; -block$1._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; -block$1._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; -block$1.def = edit(block$1.def) - .replace('label', block$1._label) - .replace('title', block$1._title) +block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; +block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; +block.def = edit$1(block.def) + .replace('label', block._label) + .replace('title', block._title) .getRegex(); -block$1.bullet = /(?:[*+-]|\d{1,9}[.)])/; -block$1.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/; -block$1.item = edit(block$1.item, 'gm') - .replace(/bull/g, block$1.bullet) - .getRegex(); +block.bullet = /(?:[*+-]|\d{1,9}[.)])/; +// block.item = /^( {0,3})(bull)( [^\n]*(?:\n(?! {0,3}bull )[^\n]*)*\n?)/; +// block.item = edit(block.item, 'gm') +// .replace(/bull/g, block.bullet) +// .getRegex(); -block$1.listItemStart = edit(/^( *)(bull) */) - .replace('bull', block$1.bullet) +block.listItemStart = edit$1(/^( *)(bull) */) + .replace('bull', block.bullet) .getRegex(); -block$1.list = edit(block$1.list) - .replace(/bull/g, block$1.bullet) +block.list = edit$1(block.list) + .replace(/bull/g, block.bullet) .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))') - .replace('def', '\\n+(?=' + block$1.def.source + ')') + .replace('def', '\\n+(?=' + block.def.source + ')') .getRegex(); -block$1._tag = 'address|article|aside|base|basefont|blockquote|body|caption' +block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul'; -block$1._comment = /|$)/; -block$1.html = edit(block$1.html, 'i') - .replace('comment', block$1._comment) - .replace('tag', block$1._tag) +block._comment = /|$)/; +block.html = edit$1(block.html, 'i') + .replace('comment', block._comment) + .replace('tag', block._tag) .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/) .getRegex(); -block$1.paragraph = edit(block$1._paragraph) - .replace('hr', block$1.hr) +block.paragraph = edit$1(block._paragraph) + .replace('hr', block.hr) .replace('heading', ' {0,3}#{1,6} ') .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs .replace('blockquote', ' {0,3}>') .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt .replace('html', ')|<(?:script|pre|style|!--)') - .replace('tag', block$1._tag) // pars can be interrupted by type (6) html blocks + .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks .getRegex(); -block$1.blockquote = edit(block$1.blockquote) - .replace('paragraph', block$1.paragraph) +block.blockquote = edit$1(block.blockquote) + .replace('paragraph', block.paragraph) .getRegex(); /** * Normal Block Grammar */ -block$1.normal = merge$1({}, block$1); +block.normal = merge$1({}, block); /** * GFM Block Grammar */ -block$1.gfm = merge$1({}, block$1.normal, { +block.gfm = merge$1({}, block.normal, { nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header + ' {0,3}([-:]+ *\\|[-| :]*)' // Align + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', // Cells @@ -1146,38 +1152,38 @@ block$1.gfm = merge$1({}, block$1.normal, { + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells }); -block$1.gfm.nptable = edit(block$1.gfm.nptable) - .replace('hr', block$1.hr) +block.gfm.nptable = edit$1(block.gfm.nptable) + .replace('hr', block.hr) .replace('heading', ' {0,3}#{1,6} ') .replace('blockquote', ' {0,3}>') .replace('code', ' {4}[^\\n]') .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt .replace('html', ')|<(?:script|pre|style|!--)') - .replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks + .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks .getRegex(); -block$1.gfm.table = edit(block$1.gfm.table) - .replace('hr', block$1.hr) +block.gfm.table = edit$1(block.gfm.table) + .replace('hr', block.hr) .replace('heading', ' {0,3}#{1,6} ') .replace('blockquote', ' {0,3}>') .replace('code', ' {4}[^\\n]') .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt .replace('html', ')|<(?:script|pre|style|!--)') - .replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks + .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks .getRegex(); /** * Pedantic grammar (original John Gruber's loose markdown specification) */ -block$1.pedantic = merge$1({}, block$1.normal, { - html: edit( +block.pedantic = merge$1({}, block.normal, { + html: edit$1( '^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))') - .replace('comment', block$1._comment) + .replace('comment', block._comment) .replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' @@ -1185,11 +1191,11 @@ block$1.pedantic = merge$1({}, block$1.normal, { .getRegex(), def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, heading: /^(#{1,6})(.*)(?:\n+|$)/, - fences: noopTest, // fences not supported - paragraph: edit(block$1.normal._paragraph) - .replace('hr', block$1.hr) + fences: noopTest$1, // fences not supported + paragraph: edit$1(block.normal._paragraph) + .replace('hr', block.hr) .replace('heading', ' *#{1,6} *[^\n]') - .replace('lheading', block$1.lheading) + .replace('lheading', block.lheading) .replace('blockquote', ' {0,3}>') .replace('|fences', '') .replace('|list', '') @@ -1200,10 +1206,10 @@ block$1.pedantic = merge$1({}, block$1.normal, { /** * Inline-Level Grammar */ -const inline$1 = { +const inline = { escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, - url: noopTest, + url: noopTest$1, tag: '^comment' + '|^' // self-closing tag + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag @@ -1223,80 +1229,80 @@ const inline$1 = { }, code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, br: /^( {2,}|\\)\n(?!\s*$)/, - del: noopTest, + del: noopTest$1, text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~'; -inline$1.punctuation = edit(inline$1.punctuation).replace(/punctuation/g, inline$1._punctuation).getRegex(); +inline._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~'; +inline.punctuation = edit$1(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, -inline$1.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g; -inline$1.escapedEmSt = /\\\*|\\_/g; +inline.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g; +inline.escapedEmSt = /\\\*|\\_/g; -inline$1._comment = edit(block$1._comment).replace('(?:-->|$)', '-->').getRegex(); +inline._comment = edit$1(block._comment).replace('(?:-->|$)', '-->').getRegex(); -inline$1.emStrong.lDelim = edit(inline$1.emStrong.lDelim) - .replace(/punct/g, inline$1._punctuation) +inline.emStrong.lDelim = edit$1(inline.emStrong.lDelim) + .replace(/punct/g, inline._punctuation) .getRegex(); -inline$1.emStrong.rDelimAst = edit(inline$1.emStrong.rDelimAst, 'g') - .replace(/punct/g, inline$1._punctuation) +inline.emStrong.rDelimAst = edit$1(inline.emStrong.rDelimAst, 'g') + .replace(/punct/g, inline._punctuation) .getRegex(); -inline$1.emStrong.rDelimUnd = edit(inline$1.emStrong.rDelimUnd, 'g') - .replace(/punct/g, inline$1._punctuation) +inline.emStrong.rDelimUnd = edit$1(inline.emStrong.rDelimUnd, 'g') + .replace(/punct/g, inline._punctuation) .getRegex(); -inline$1._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; +inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; -inline$1._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; -inline$1._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])?)+(?![-_])/; -inline$1.autolink = edit(inline$1.autolink) - .replace('scheme', inline$1._scheme) - .replace('email', inline$1._email) +inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; +inline._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])?)+(?![-_])/; +inline.autolink = edit$1(inline.autolink) + .replace('scheme', inline._scheme) + .replace('email', inline._email) .getRegex(); -inline$1._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; +inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; -inline$1.tag = edit(inline$1.tag) - .replace('comment', inline$1._comment) - .replace('attribute', inline$1._attribute) +inline.tag = edit$1(inline.tag) + .replace('comment', inline._comment) + .replace('attribute', inline._attribute) .getRegex(); -inline$1._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; -inline$1._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/; -inline$1._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; +inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; +inline._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/; +inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; -inline$1.link = edit(inline$1.link) - .replace('label', inline$1._label) - .replace('href', inline$1._href) - .replace('title', inline$1._title) +inline.link = edit$1(inline.link) + .replace('label', inline._label) + .replace('href', inline._href) + .replace('title', inline._title) .getRegex(); -inline$1.reflink = edit(inline$1.reflink) - .replace('label', inline$1._label) +inline.reflink = edit$1(inline.reflink) + .replace('label', inline._label) .getRegex(); -inline$1.reflinkSearch = edit(inline$1.reflinkSearch, 'g') - .replace('reflink', inline$1.reflink) - .replace('nolink', inline$1.nolink) +inline.reflinkSearch = edit$1(inline.reflinkSearch, 'g') + .replace('reflink', inline.reflink) + .replace('nolink', inline.nolink) .getRegex(); /** * Normal Inline Grammar */ -inline$1.normal = merge$1({}, inline$1); +inline.normal = merge$1({}, inline); /** * Pedantic Inline Grammar */ -inline$1.pedantic = merge$1({}, inline$1.normal, { +inline.pedantic = merge$1({}, inline.normal, { strong: { start: /^__|\*\*/, middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, @@ -1309,11 +1315,11 @@ inline$1.pedantic = merge$1({}, inline$1.normal, { endAst: /\*(?!\*)/g, endUnd: /_(?!_)/g }, - link: edit(/^!?\[(label)\]\((.*?)\)/) - .replace('label', inline$1._label) + link: edit$1(/^!?\[(label)\]\((.*?)\)/) + .replace('label', inline._label) .getRegex(), - reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/) - .replace('label', inline$1._label) + reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/) + .replace('label', inline._label) .getRegex() }); @@ -1321,8 +1327,8 @@ inline$1.pedantic = merge$1({}, inline$1.normal, { * GFM Inline Grammar */ -inline$1.gfm = merge$1({}, inline$1.normal, { - escape: edit(inline$1.escape).replace('])', '~|])').getRegex(), +inline.gfm = merge$1({}, inline.normal, { + escape: edit$1(inline.escape).replace('])', '~|])').getRegex(), _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/, url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/, @@ -1330,30 +1336,29 @@ inline$1.gfm = merge$1({}, inline$1.normal, { text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\ t.type === 'space')) { + token.loose = true; + token.items[i].loose = true; + } } tokens.push(token); continue; @@ -1569,13 +1578,19 @@ var Lexer_1 = class Lexer { } // def - if (top && (token = this.tokenizer.def(src))) { + if (token = this.tokenizer.def(src)) { src = src.substring(token.raw.length); - if (!this.tokens.links[token.tag]) { - this.tokens.links[token.tag] = { - href: token.href, - title: token.title - }; + lastToken = tokens[tokens.length - 1]; + if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) { + lastToken.raw += '\n' + token.raw; + lastToken.text += '\n' + token.raw; + } else { + if (!this.tokens.links[token.tag]) { + this.tokens.links[token.tag] = { + href: token.href, + title: token.title + }; + } } continue; } @@ -1728,14 +1743,14 @@ var Lexer_1 = class Lexer { if (links.length > 0) { while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) { if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); } } } } // Mask out other blocks while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); } // Mask out escaped em & strong delimiters @@ -1901,10 +1916,10 @@ var Lexer_1 = class Lexer { } }; -const { defaults: defaults$2 } = defaults$5.exports; +const { defaults: defaults$3 } = defaults; const { - cleanUrl, - escape: escape$1 + cleanUrl: cleanUrl$1, + escape: escape$2 } = helpers; /** @@ -1912,7 +1927,7 @@ const { */ var Renderer_1 = class Renderer { constructor(options) { - this.options = options || defaults$2; + this.options = options || defaults$3; } code(code, infostring, escaped) { @@ -1929,15 +1944,15 @@ var Renderer_1 = class Renderer { if (!lang) { return '
    '
    -        + (escaped ? code : escape$1(code, true))
    +        + (escaped ? code : escape$2(code, true))
             + '
    \n'; } return '
    '
    -      + (escaped ? code : escape$1(code, true))
    +      + (escaped ? code : escape$2(code, true))
           + '
    \n'; } @@ -2037,11 +2052,11 @@ var Renderer_1 = class Renderer { } link(href, title, text) { - href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); if (href === null) { return text; } - let out = ' 0 && item.tokens[0].type === 'text') { + if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') { item.tokens[0].text = checkbox + ' ' + item.tokens[0].text; if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') { item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text; @@ -2450,22 +2460,16 @@ var Parser_1 = class Parser { } }; -const Lexer = Lexer_1; -const Parser = Parser_1; -const Tokenizer = Tokenizer_1; -const Renderer = Renderer_1; -const TextRenderer = TextRenderer_1; -const Slugger = Slugger_1; const { - merge, - checkSanitizeDeprecation, - escape + merge: merge$2, + checkSanitizeDeprecation: checkSanitizeDeprecation$1, + escape: escape$3 } = helpers; const { getDefaults, changeDefaults, - defaults -} = defaults$5.exports; + defaults: defaults$5 +} = defaults; /** * Marked @@ -2485,15 +2489,15 @@ function marked(src, opt, callback) { opt = null; } - opt = merge({}, marked.defaults, opt || {}); - checkSanitizeDeprecation(opt); + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); if (callback) { const highlight = opt.highlight; let tokens; try { - tokens = Lexer.lex(src, opt); + tokens = Lexer_1.lex(src, opt); } catch (e) { return callback(e); } @@ -2506,7 +2510,7 @@ function marked(src, opt, callback) { if (opt.walkTokens) { marked.walkTokens(tokens, opt.walkTokens); } - out = Parser.parse(tokens, opt); + out = Parser_1.parse(tokens, opt); } catch (e) { err = e; } @@ -2558,16 +2562,16 @@ function marked(src, opt, callback) { } try { - const tokens = Lexer.lex(src, opt); + const tokens = Lexer_1.lex(src, opt); if (opt.walkTokens) { marked.walkTokens(tokens, opt.walkTokens); } - return Parser.parse(tokens, opt); + return Parser_1.parse(tokens, opt); } catch (e) { e.message += '\nPlease report this to https://github.com/markedjs/marked.'; if (opt.silent) { return '

    An error occurred:

    '
    -        + escape(e.message + '', true)
    +        + escape$3(e.message + '', true)
             + '
    '; } throw e; @@ -2580,21 +2584,21 @@ function marked(src, opt, callback) { marked.options = marked.setOptions = function(opt) { - merge(marked.defaults, opt); + merge$2(marked.defaults, opt); changeDefaults(marked.defaults); return marked; }; marked.getDefaults = getDefaults; -marked.defaults = defaults; +marked.defaults = defaults$5; /** * Use Extension */ marked.use = function(...args) { - const opts = merge({}, ...args); + const opts = merge$2({}, ...args); const extensions = marked.defaults.extensions || { renderers: {}, childTokens: {} }; let hasExtensions; @@ -2654,7 +2658,7 @@ marked.use = function(...args) { // ==-- Parse "overwrite" extensions --== // if (pack.renderer) { - const renderer = marked.defaults.renderer || new Renderer(); + const renderer = marked.defaults.renderer || new Renderer_1(); for (const prop in pack.renderer) { const prevRenderer = renderer[prop]; // Replace renderer with func to run extension, but fall back if false @@ -2669,7 +2673,7 @@ marked.use = function(...args) { opts.renderer = renderer; } if (pack.tokenizer) { - const tokenizer = marked.defaults.tokenizer || new Tokenizer(); + const tokenizer = marked.defaults.tokenizer || new Tokenizer_1(); for (const prop in pack.tokenizer) { const prevTokenizer = tokenizer[prop]; // Replace tokenizer with func to run extension, but fall back if false @@ -2752,20 +2756,20 @@ marked.parseInline = function(src, opt) { + Object.prototype.toString.call(src) + ', string expected'); } - opt = merge({}, marked.defaults, opt || {}); - checkSanitizeDeprecation(opt); + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); try { - const tokens = Lexer.lexInline(src, opt); + const tokens = Lexer_1.lexInline(src, opt); if (opt.walkTokens) { marked.walkTokens(tokens, opt.walkTokens); } - return Parser.parseInline(tokens, opt); + return Parser_1.parseInline(tokens, opt); } catch (e) { e.message += '\nPlease report this to https://github.com/markedjs/marked.'; if (opt.silent) { return '

    An error occurred:

    '
    -        + escape(e.message + '', true)
    +        + escape$3(e.message + '', true)
             + '
    '; } throw e; @@ -2776,18 +2780,18 @@ marked.parseInline = function(src, opt) { * Expose */ -marked.Parser = Parser; -marked.parser = Parser.parse; +marked.Parser = Parser_1; +marked.parser = Parser_1.parse; -marked.Renderer = Renderer; -marked.TextRenderer = TextRenderer; +marked.Renderer = Renderer_1; +marked.TextRenderer = TextRenderer_1; -marked.Lexer = Lexer; -marked.lexer = Lexer.lex; +marked.Lexer = Lexer_1; +marked.lexer = Lexer_1.lex; -marked.Tokenizer = Tokenizer; +marked.Tokenizer = Tokenizer_1; -marked.Slugger = Slugger; +marked.Slugger = Slugger_1; marked.parse = marked; diff --git a/lib/marked.js b/lib/marked.js index 4f068e801c..51767a54ba 100644 --- a/lib/marked.js +++ b/lib/marked.js @@ -49,61 +49,70 @@ } function _createForOfIteratorHelperLoose(o, allowArrayLike) { - var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; - if (it) return (it = it.call(o)).next.bind(it); - - if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { - if (it) o = it; - var i = 0; - return function () { - if (i >= o.length) return { - done: true - }; - return { - done: false, - value: o[i++] + var it; + + if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + return function () { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; }; - }; + } + + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } - throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + it = o[Symbol.iterator](); + return it.next.bind(it); } - var defaults$5 = {exports: {}}; - - function getDefaults$1() { - return { - baseUrl: null, - breaks: false, - extensions: null, - gfm: true, - headerIds: true, - headerPrefix: '', - highlight: null, - langPrefix: 'language-', - mangle: true, - pedantic: false, - renderer: null, - sanitize: false, - sanitizer: null, - silent: false, - smartLists: false, - smartypants: false, - tokenizer: null, - walkTokens: null, - xhtml: false - }; + function createCommonjsModule(fn) { + var module = { exports: {} }; + return fn(module, module.exports), module.exports; } - function changeDefaults$1(newDefaults) { - defaults$5.exports.defaults = newDefaults; - } + var defaults = createCommonjsModule(function (module) { + function getDefaults() { + return { + baseUrl: null, + breaks: false, + extensions: null, + gfm: true, + headerIds: true, + headerPrefix: '', + highlight: null, + langPrefix: 'language-', + mangle: true, + pedantic: false, + renderer: null, + sanitize: false, + sanitizer: null, + silent: false, + smartLists: false, + smartypants: false, + tokenizer: null, + walkTokens: null, + xhtml: false + }; + } - defaults$5.exports = { - defaults: getDefaults$1(), - getDefaults: getDefaults$1, - changeDefaults: changeDefaults$1 - }; + function changeDefaults(newDefaults) { + module.exports.defaults = newDefaults; + } + + module.exports = { + defaults: getDefaults(), + getDefaults: getDefaults, + changeDefaults: changeDefaults + }; + }); /** * Helpers @@ -124,7 +133,7 @@ return escapeReplacements[ch]; }; - function escape$2(html, encode) { + function escape(html, encode) { if (encode) { if (escapeTest.test(html)) { return html.replace(escapeReplace, getEscapeReplacement); @@ -140,7 +149,7 @@ var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig; - function unescape$1(html) { + function unescape(html) { // explicitly match decimal, hex, and named HTML entities return html.replace(unescapeTest, function (_, n) { n = n.toLowerCase(); @@ -156,7 +165,7 @@ var caret = /(^|[^\[])\^/g; - function edit$1(regex, opt) { + function edit(regex, opt) { regex = regex.source || regex; opt = opt || ''; var obj = { @@ -176,12 +185,12 @@ var nonWordAndColonTest = /[^\w:]/g; var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; - function cleanUrl$1(sanitize, base, href) { + function cleanUrl(sanitize, base, href) { if (sanitize) { var prot; try { - prot = decodeURIComponent(unescape$1(href)).replace(nonWordAndColonTest, '').toLowerCase(); + prot = decodeURIComponent(unescape(href)).replace(nonWordAndColonTest, '').toLowerCase(); } catch (e) { return null; } @@ -217,7 +226,7 @@ if (justDomain.test(base)) { baseUrls[' ' + base] = base + '/'; } else { - baseUrls[' ' + base] = rtrim$1(base, '/', true); + baseUrls[' ' + base] = rtrim(base, '/', true); } } @@ -241,11 +250,11 @@ } } - var noopTest$1 = { + var noopTest = { exec: function noopTest() {} }; - function merge$2(obj) { + function merge(obj) { var i = 1, target, key; @@ -263,7 +272,7 @@ return obj; } - function splitCells$1(tableRow, count) { + function splitCells(tableRow, count) { // ensure that every cell-delimiting pipe has a space // before it to distinguish it from an escaped pipe var row = tableRow.replace(/\|/g, function (match, offset, str) { @@ -305,7 +314,7 @@ // invert: Remove suffix of non-c chars instead. Default falsey. - function rtrim$1(str, c, invert) { + function rtrim(str, c, invert) { var l = str.length; if (l === 0) { @@ -330,7 +339,7 @@ return str.substr(0, l - suffLen); } - function findClosingBracket$1(str, b) { + function findClosingBracket(str, b) { if (str.indexOf(b[1]) === -1) { return -1; } @@ -356,14 +365,14 @@ return -1; } - function checkSanitizeDeprecation$1(opt) { + function checkSanitizeDeprecation(opt) { if (opt && opt.sanitize && !opt.silent) { console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); } } // copied from https://stackoverflow.com/a/5450113/806777 - function repeatString$1(pattern, count) { + function repeatString(pattern, count) { if (count < 1) { return ''; } @@ -383,25 +392,25 @@ } var helpers = { - escape: escape$2, - unescape: unescape$1, - edit: edit$1, - cleanUrl: cleanUrl$1, + escape: escape, + unescape: unescape, + edit: edit, + cleanUrl: cleanUrl, resolveUrl: resolveUrl, - noopTest: noopTest$1, - merge: merge$2, - splitCells: splitCells$1, - rtrim: rtrim$1, - findClosingBracket: findClosingBracket$1, - checkSanitizeDeprecation: checkSanitizeDeprecation$1, - repeatString: repeatString$1 + noopTest: noopTest, + merge: merge, + splitCells: splitCells, + rtrim: rtrim, + findClosingBracket: findClosingBracket, + checkSanitizeDeprecation: checkSanitizeDeprecation, + repeatString: repeatString }; - var defaults$4 = defaults$5.exports.defaults; - var rtrim = helpers.rtrim, - splitCells = helpers.splitCells, + var defaults$1 = defaults.defaults; + var rtrim$1 = helpers.rtrim, + splitCells$1 = helpers.splitCells, _escape = helpers.escape, - findClosingBracket = helpers.findClosingBracket; + findClosingBracket$1 = helpers.findClosingBracket; function outputLink(cap, link, raw) { var href = link.href; @@ -458,7 +467,7 @@ var Tokenizer_1 = /*#__PURE__*/function () { function Tokenizer(options) { - this.options = options || defaults$4; + this.options = options || defaults$1; } var _proto = Tokenizer.prototype; @@ -489,7 +498,7 @@ type: 'code', raw: cap[0], codeBlockStyle: 'indented', - text: !this.options.pedantic ? rtrim(text, '\n') : text + text: !this.options.pedantic ? rtrim$1(text, '\n') : text }; } }; @@ -516,7 +525,7 @@ var text = cap[2].trim(); // remove trailing #s if (/#$/.test(text)) { - var trimmed = rtrim(text, '#'); + var trimmed = rtrim$1(text, '#'); if (this.options.pedantic) { text = trimmed.trim(); @@ -541,7 +550,7 @@ if (cap) { var item = { type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [], raw: cap[0] @@ -566,7 +575,7 @@ l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells(item.cells[i], item.header.length); + item.cells[i] = splitCells$1(item.cells[i], item.header.length); } return item; @@ -602,123 +611,136 @@ var cap = this.rules.block.list.exec(src); if (cap) { - var raw = cap[0]; - var bull = cap[2]; + var raw, istask, ischecked, indent, i, blankLine, endsWithBlankLine, line, lines, itemContents; + var bull = cap[1].trim(); var isordered = bull.length > 1; var list = { type: 'list', - raw: raw, + raw: '', ordered: isordered, start: isordered ? +bull.slice(0, -1) : '', loose: false, items: [] - }; // Get each top-level item. - - var itemMatch = cap[0].match(this.rules.block.item); - var next = false, - item, - space, - bcurr, - bnext, - addBack, - loose, - istask, - ischecked, - endMatch; - var l = itemMatch.length; - bcurr = this.rules.block.listItemStart.exec(itemMatch[0]); - - for (var i = 0; i < l; i++) { - item = itemMatch[i]; - raw = item; - - if (!this.options.pedantic) { - // Determine if current item contains the end of the list - endMatch = item.match(new RegExp('\\n\\s*\\n {0,' + (bcurr[0].length - 1) + '}\\S')); - - if (endMatch) { - addBack = item.length - endMatch.index + itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - item = item.substring(0, endMatch.index); - raw = item; - l = i + 1; - } - } // Determine whether the next list item belongs here. - // Backpedal if it does not belong in this list. + }; + bull = isordered ? "\\d{1,9}\\" + bull.slice(-1) : "\\" + bull; + if (this.options.pedantic) { + bull = isordered ? bull : '[*+-]'; + } // Get next list item - if (i !== l - 1) { - bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]); - if (!this.options.pedantic ? bnext[1].length >= bcurr[0].length || bnext[1].length > 3 : bnext[1].length > bcurr[1].length) { - // nested list or continuation - itemMatch.splice(i, 2, itemMatch[i] + (!this.options.pedantic && bnext[1].length < bcurr[0].length && !itemMatch[i].match(/\n$/) ? '' : '\n') + itemMatch[i + 1]); - i--; - l--; - continue; - } else if ( // different bullet style - !this.options.pedantic || this.options.smartLists ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] : isordered === (bnext[2].length === 1)) { - addBack = itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - i = l - 1; - } + var itemRegex = new RegExp("^( {0,3}" + bull + ")((?: [^\\n]*|\\h*)(?:\\n[^\\n]*)*(?:\\n|$))"); // Get each top-level item + + while (src) { + if (this.rules.block.hr.exec(src)) { + // End list if we encounter an HR (possibly move into itemRegex?) + break; + } - bcurr = bnext; - } // Remove the list item's bullet - // so it is seen as the next token. + if (!(cap = itemRegex.exec(src))) { + break; + } + lines = cap[2].split('\n'); - space = item.length; - item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the - // list item contains. Hacky. + if (this.options.pedantic) { + indent = 2; + itemContents = lines[0].trimLeft(); + } else { + indent = cap[2].search(/[^ ]/); // Find first non-space char - if (~item.indexOf('\n ')) { - space -= item.length; - item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, ''); - } // trim item newlines at end + indent = cap[1].length + (indent > 4 ? 1 : indent); // intented code blocks after 4 spaces; indent is always 1 + itemContents = lines[0].slice(indent - cap[1].length); + } - item = rtrim(item, '\n'); + blankLine = false; + raw = cap[0]; - if (i !== l - 1) { - raw = raw + '\n'; - } // Determine whether item is loose or not. - // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ - // for discount behavior. + if (!lines[0] && lines[1].match(/^\h*$/)) { + // items begin with at most one blank line + raw = cap[1] + lines.slice(0, 2).join('\n') + '\n'; + list.loose = true; + lines = []; + } + for (i = 1; i < lines.length; i++) { + line = lines[i]; - loose = next || /\n\n(?!\s*$)/.test(raw); + if (this.options.pedantic) { + // Re-align to follow commonmark nesting rules + line = line.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' '); + } // End list item if found start of new bullet - if (i !== l - 1) { - next = raw.slice(-2) === '\n\n'; - if (!loose) loose = next; + + if (line.match(new RegExp("^ {0," + Math.min(3, indent - 1) + "}(?:[*+-]|\\d{1,9}[.)])"))) { + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; + break; + } // Until we encounter a blank line, item contents do not need indentation + + + if (!blankLine) { + if (!line.trim()) { + // Check if current line is empty + blankLine = true; + } // Dedent if possible + + + if (line.search(/[^ ]/) >= indent) { + itemContents += '\n' + line.slice(indent); + } else { + itemContents += '\n' + line; + } + + continue; + } // Dedent this line + + + if (line.search(/[^ ]/) >= indent || !line.trim()) { + itemContents += '\n' + line.slice(indent); + continue; + } else { + // Line was not properly indented; end of this item + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; + break; + } } - if (loose) { - list.loose = true; + if (!list.loose) { + // If the previous item ended with a blank line, the list is loose + if (endsWithBlankLine) { + list.loose = true; + } else if (raw.match(/\n\h*\n\h*$/)) { + endsWithBlankLine = true; + } } // Check for task list items if (this.options.gfm) { - istask = /^\[[ xX]\] /.test(item); - ischecked = undefined; + istask = /^\[[ xX]\] /.exec(itemContents); if (istask) { - ischecked = item[1] !== ' '; - item = item.replace(/^\[[ xX]\] +/, ''); + ischecked = istask[0] !== '[ ] '; + itemContents = itemContents.replace(/^\[[ xX]\] +/, ''); } } list.items.push({ type: 'list_item', raw: raw, - task: istask, + task: !!istask, checked: ischecked, - loose: loose, - text: item + loose: false, + text: itemContents }); - } + list.raw += raw; + src = src.slice(raw.length); + } // Do not consume ending newlines. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic + + list.items[list.items.length - 1].raw = raw.trimRight(); + list.items[list.items.length - 1].text = itemContents.trimRight(); + list.raw = list.raw.trimRight(); return list; } }; @@ -758,7 +780,7 @@ if (cap) { var item = { type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] }; @@ -783,7 +805,7 @@ l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length); + item.cells[i] = splitCells$1(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length); } return item; @@ -879,14 +901,14 @@ } // ending angle bracket cannot be escaped - var rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\'); + var rtrimSlash = rtrim$1(trimmedUrl.slice(0, -1), '\\'); if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) { return; } } else { // find closing parenthesis - var lastParenIndex = findClosingBracket(cap[2], '()'); + var lastParenIndex = findClosingBracket$1(cap[2], '()'); if (lastParenIndex > -1) { var start = cap[0].indexOf('!') === 0 ? 5 : 4; @@ -1150,21 +1172,21 @@ return Tokenizer; }(); - var noopTest = helpers.noopTest, - edit = helpers.edit, + var noopTest$1 = helpers.noopTest, + edit$1 = helpers.edit, merge$1 = helpers.merge; /** * Block-Level Grammar */ - var block$1 = { + var block = { newline: /^(?: *(?:\n|$))+/, code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/, - fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, + fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?=\n|$)|$)/, hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/, + list: /^( {0,3}bull)( [^\n]+?)?(?:\n|$)/, html: '^ {0,3}(?:' // optional indentation + '<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + '|comment[^\\n]*(\\n+|$)' // (2) @@ -1176,40 +1198,42 @@ + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag + ')', def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, - nptable: noopTest, - table: noopTest, + nptable: noopTest$1, + table: noopTest$1, lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/, // regex template, placeholders will be replaced according to different paragraph // interruption rules of commonmark and the original markdown spec: _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html| +\n)[^\n]+)*)/, text: /^[^\n]+/ }; - block$1._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; - block$1._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; - block$1.def = edit(block$1.def).replace('label', block$1._label).replace('title', block$1._title).getRegex(); - block$1.bullet = /(?:[*+-]|\d{1,9}[.)])/; - block$1.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/; - block$1.item = edit(block$1.item, 'gm').replace(/bull/g, block$1.bullet).getRegex(); - block$1.listItemStart = edit(/^( *)(bull) */).replace('bull', block$1.bullet).getRegex(); - block$1.list = edit(block$1.list).replace(/bull/g, block$1.bullet).replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))').replace('def', '\\n+(?=' + block$1.def.source + ')').getRegex(); - block$1._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul'; - block$1._comment = /|$)/; - block$1.html = edit(block$1.html, 'i').replace('comment', block$1._comment).replace('tag', block$1._tag).replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(); - block$1.paragraph = edit(block$1._paragraph).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs + block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; + block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; + block.def = edit$1(block.def).replace('label', block._label).replace('title', block._title).getRegex(); + block.bullet = /(?:[*+-]|\d{1,9}[.)])/; // block.item = /^( {0,3})(bull)( [^\n]*(?:\n(?! {0,3}bull )[^\n]*)*\n?)/; + // block.item = edit(block.item, 'gm') + // .replace(/bull/g, block.bullet) + // .getRegex(); + + block.listItemStart = edit$1(/^( *)(bull) */).replace('bull', block.bullet).getRegex(); + block.list = edit$1(block.list).replace(/bull/g, block.bullet).replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))').replace('def', '\\n+(?=' + block.def.source + ')').getRegex(); + block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul'; + block._comment = /|$)/; + block.html = edit$1(block.html, 'i').replace('comment', block._comment).replace('tag', block._tag).replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(); + block.paragraph = edit$1(block._paragraph).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs .replace('blockquote', ' {0,3}>').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt - .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // pars can be interrupted by type (6) html blocks + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block._tag) // pars can be interrupted by type (6) html blocks .getRegex(); - block$1.blockquote = edit(block$1.blockquote).replace('paragraph', block$1.paragraph).getRegex(); + block.blockquote = edit$1(block.blockquote).replace('paragraph', block.paragraph).getRegex(); /** * Normal Block Grammar */ - block$1.normal = merge$1({}, block$1); + block.normal = merge$1({}, block); /** * GFM Block Grammar */ - block$1.gfm = merge$1({}, block$1.normal, { + block.gfm = merge$1({}, block.normal, { nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header + ' {0,3}([-:]+ *\\|[-| :]*)' // Align + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', @@ -1219,33 +1243,33 @@ + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells }); - block$1.gfm.nptable = edit(block$1.gfm.nptable).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt - .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks + block.gfm.nptable = edit$1(block.gfm.nptable).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks .getRegex(); - block$1.gfm.table = edit(block$1.gfm.table).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt - .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks + block.gfm.table = edit$1(block.gfm.table).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks .getRegex(); /** * Pedantic grammar (original John Gruber's loose markdown specification) */ - block$1.pedantic = merge$1({}, block$1.normal, { - html: edit('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag - + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))').replace('comment', block$1._comment).replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b').getRegex(), + block.pedantic = merge$1({}, block.normal, { + html: edit$1('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag + + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))').replace('comment', block._comment).replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b').getRegex(), def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, heading: /^(#{1,6})(.*)(?:\n+|$)/, - fences: noopTest, + fences: noopTest$1, // fences not supported - paragraph: edit(block$1.normal._paragraph).replace('hr', block$1.hr).replace('heading', ' *#{1,6} *[^\n]').replace('lheading', block$1.lheading).replace('blockquote', ' {0,3}>').replace('|fences', '').replace('|list', '').replace('|html', '').getRegex() + paragraph: edit$1(block.normal._paragraph).replace('hr', block.hr).replace('heading', ' *#{1,6} *[^\n]').replace('lheading', block.lheading).replace('blockquote', ' {0,3}>').replace('|fences', '').replace('|list', '').replace('|html', '').getRegex() }); /** * Inline-Level Grammar */ - var inline$1 = { + var inline = { escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, - url: noopTest, + url: noopTest$1, tag: '^comment' + '|^' // self-closing tag + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. @@ -1266,43 +1290,43 @@ }, code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, br: /^( {2,}|\\)\n(?!\s*$)/, - del: noopTest, + del: noopTest$1, text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~'; - inline$1.punctuation = edit(inline$1.punctuation).replace(/punctuation/g, inline$1._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, - - inline$1.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g; - inline$1.escapedEmSt = /\\\*|\\_/g; - inline$1._comment = edit(block$1._comment).replace('(?:-->|$)', '-->').getRegex(); - inline$1.emStrong.lDelim = edit(inline$1.emStrong.lDelim).replace(/punct/g, inline$1._punctuation).getRegex(); - inline$1.emStrong.rDelimAst = edit(inline$1.emStrong.rDelimAst, 'g').replace(/punct/g, inline$1._punctuation).getRegex(); - inline$1.emStrong.rDelimUnd = edit(inline$1.emStrong.rDelimUnd, 'g').replace(/punct/g, inline$1._punctuation).getRegex(); - inline$1._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; - inline$1._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; - inline$1._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])?)+(?![-_])/; - inline$1.autolink = edit(inline$1.autolink).replace('scheme', inline$1._scheme).replace('email', inline$1._email).getRegex(); - inline$1._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; - inline$1.tag = edit(inline$1.tag).replace('comment', inline$1._comment).replace('attribute', inline$1._attribute).getRegex(); - inline$1._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; - inline$1._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/; - inline$1._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; - inline$1.link = edit(inline$1.link).replace('label', inline$1._label).replace('href', inline$1._href).replace('title', inline$1._title).getRegex(); - inline$1.reflink = edit(inline$1.reflink).replace('label', inline$1._label).getRegex(); - inline$1.reflinkSearch = edit(inline$1.reflinkSearch, 'g').replace('reflink', inline$1.reflink).replace('nolink', inline$1.nolink).getRegex(); + inline._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~'; + inline.punctuation = edit$1(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, + + inline.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g; + inline.escapedEmSt = /\\\*|\\_/g; + inline._comment = edit$1(block._comment).replace('(?:-->|$)', '-->').getRegex(); + inline.emStrong.lDelim = edit$1(inline.emStrong.lDelim).replace(/punct/g, inline._punctuation).getRegex(); + inline.emStrong.rDelimAst = edit$1(inline.emStrong.rDelimAst, 'g').replace(/punct/g, inline._punctuation).getRegex(); + inline.emStrong.rDelimUnd = edit$1(inline.emStrong.rDelimUnd, 'g').replace(/punct/g, inline._punctuation).getRegex(); + inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; + inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; + inline._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])?)+(?![-_])/; + inline.autolink = edit$1(inline.autolink).replace('scheme', inline._scheme).replace('email', inline._email).getRegex(); + inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; + inline.tag = edit$1(inline.tag).replace('comment', inline._comment).replace('attribute', inline._attribute).getRegex(); + inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; + inline._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/; + inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; + inline.link = edit$1(inline.link).replace('label', inline._label).replace('href', inline._href).replace('title', inline._title).getRegex(); + inline.reflink = edit$1(inline.reflink).replace('label', inline._label).getRegex(); + inline.reflinkSearch = edit$1(inline.reflinkSearch, 'g').replace('reflink', inline.reflink).replace('nolink', inline.nolink).getRegex(); /** * Normal Inline Grammar */ - inline$1.normal = merge$1({}, inline$1); + inline.normal = merge$1({}, inline); /** * Pedantic Inline Grammar */ - inline$1.pedantic = merge$1({}, inline$1.normal, { + inline.pedantic = merge$1({}, inline.normal, { strong: { start: /^__|\*\*/, middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, @@ -1315,40 +1339,39 @@ endAst: /\*(?!\*)/g, endUnd: /_(?!_)/g }, - link: edit(/^!?\[(label)\]\((.*?)\)/).replace('label', inline$1._label).getRegex(), - reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline$1._label).getRegex() + link: edit$1(/^!?\[(label)\]\((.*?)\)/).replace('label', inline._label).getRegex(), + reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline._label).getRegex() }); /** * GFM Inline Grammar */ - inline$1.gfm = merge$1({}, inline$1.normal, { - escape: edit(inline$1.escape).replace('])', '~|])').getRegex(), + inline.gfm = merge$1({}, inline.normal, { + escape: edit$1(inline.escape).replace('])', '~|])').getRegex(), _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/, url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/, del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/, text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\ 0) { while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) { if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); } } } @@ -1775,7 +1811,7 @@ while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); } // Mask out escaped em & strong delimiters @@ -1964,8 +2000,8 @@ key: "rules", get: function get() { return { - block: block, - inline: inline + block: block$1, + inline: inline$1 }; } }]); @@ -1973,8 +2009,8 @@ return Lexer; }(); - var defaults$2 = defaults$5.exports.defaults; - var cleanUrl = helpers.cleanUrl, + var defaults$3 = defaults.defaults; + var cleanUrl$1 = helpers.cleanUrl, escape$1 = helpers.escape; /** * Renderer @@ -1982,7 +2018,7 @@ var Renderer_1 = /*#__PURE__*/function () { function Renderer(options) { - this.options = options || defaults$2; + this.options = options || defaults$3; } var _proto = Renderer.prototype; @@ -2084,7 +2120,7 @@ }; _proto.link = function link(href, title, text) { - href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); if (href === null) { return text; @@ -2101,7 +2137,7 @@ }; _proto.image = function image(href, title, text) { - href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); if (href === null) { return text; @@ -2128,7 +2164,6 @@ * TextRenderer * returns only the textual part of the token */ - var TextRenderer_1 = /*#__PURE__*/function () { function TextRenderer() {} @@ -2177,7 +2212,6 @@ /** * Slugger generates header id */ - var Slugger_1 = /*#__PURE__*/function () { function Slugger() { this.seen = {}; @@ -2234,23 +2268,20 @@ return Slugger; }(); - var Renderer$1 = Renderer_1; - var TextRenderer$1 = TextRenderer_1; - var Slugger$1 = Slugger_1; - var defaults$1 = defaults$5.exports.defaults; - var unescape = helpers.unescape; + var defaults$4 = defaults.defaults; + var unescape$1 = helpers.unescape; /** * Parsing & Compiling */ var Parser_1 = /*#__PURE__*/function () { function Parser(options) { - this.options = options || defaults$1; - this.options.renderer = this.options.renderer || new Renderer$1(); + this.options = options || defaults$4; + this.options.renderer = this.options.renderer || new Renderer_1(); this.renderer = this.options.renderer; this.renderer.options = this.options; - this.textRenderer = new TextRenderer$1(); - this.slugger = new Slugger$1(); + this.textRenderer = new TextRenderer_1(); + this.slugger = new Slugger_1(); } /** * Static Parse Method @@ -2330,7 +2361,7 @@ case 'heading': { - out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape(this.parseInline(token.tokens, this.textRenderer)), this.slugger); + out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape$1(this.parseInline(token.tokens, this.textRenderer)), this.slugger); continue; } @@ -2402,7 +2433,7 @@ checkbox = this.renderer.checkbox(checked); if (loose) { - if (item.tokens.length > 0 && item.tokens[0].type === 'text') { + if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') { item.tokens[0].text = checkbox + ' ' + item.tokens[0].text; if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') { @@ -2575,18 +2606,12 @@ return Parser; }(); - var Lexer = Lexer_1; - var Parser = Parser_1; - var Tokenizer = Tokenizer_1; - var Renderer = Renderer_1; - var TextRenderer = TextRenderer_1; - var Slugger = Slugger_1; - var merge = helpers.merge, - checkSanitizeDeprecation = helpers.checkSanitizeDeprecation, - escape = helpers.escape; - var getDefaults = defaults$5.exports.getDefaults, - changeDefaults = defaults$5.exports.changeDefaults, - defaults = defaults$5.exports.defaults; + var merge$2 = helpers.merge, + checkSanitizeDeprecation$1 = helpers.checkSanitizeDeprecation, + escape$2 = helpers.escape; + var getDefaults = defaults.getDefaults, + changeDefaults = defaults.changeDefaults, + defaults$5 = defaults.defaults; /** * Marked */ @@ -2606,15 +2631,15 @@ opt = null; } - opt = merge({}, marked.defaults, opt || {}); - checkSanitizeDeprecation(opt); + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); if (callback) { var highlight = opt.highlight; var tokens; try { - tokens = Lexer.lex(src, opt); + tokens = Lexer_1.lex(src, opt); } catch (e) { return callback(e); } @@ -2628,7 +2653,7 @@ marked.walkTokens(tokens, opt.walkTokens); } - out = Parser.parse(tokens, opt); + out = Parser_1.parse(tokens, opt); } catch (e) { err = e; } @@ -2677,18 +2702,18 @@ } try { - var _tokens = Lexer.lex(src, opt); + var _tokens = Lexer_1.lex(src, opt); if (opt.walkTokens) { marked.walkTokens(_tokens, opt.walkTokens); } - return Parser.parse(_tokens, opt); + return Parser_1.parse(_tokens, opt); } catch (e) { e.message += '\nPlease report this to https://github.com/markedjs/marked.'; if (opt.silent) { - return '

    An error occurred:

    ' + escape(e.message + '', true) + '
    '; + return '

    An error occurred:

    ' + escape$2(e.message + '', true) + '
    '; } throw e; @@ -2700,13 +2725,13 @@ marked.options = marked.setOptions = function (opt) { - merge(marked.defaults, opt); + merge$2(marked.defaults, opt); changeDefaults(marked.defaults); return marked; }; marked.getDefaults = getDefaults; - marked.defaults = defaults; + marked.defaults = defaults$5; /** * Use Extension */ @@ -2718,7 +2743,7 @@ args[_key] = arguments[_key]; } - var opts = merge.apply(void 0, [{}].concat(args)); + var opts = merge$2.apply(void 0, [{}].concat(args)); var extensions = marked.defaults.extensions || { renderers: {}, childTokens: {} @@ -2797,7 +2822,7 @@ if (pack.renderer) { (function () { - var renderer = marked.defaults.renderer || new Renderer(); + var renderer = marked.defaults.renderer || new Renderer_1(); var _loop = function _loop(prop) { var prevRenderer = renderer[prop]; // Replace renderer with func to run extension, but fall back if false @@ -2827,7 +2852,7 @@ if (pack.tokenizer) { (function () { - var tokenizer = marked.defaults.tokenizer || new Tokenizer(); + var tokenizer = marked.defaults.tokenizer || new Tokenizer_1(); var _loop2 = function _loop2(prop) { var prevTokenizer = tokenizer[prop]; // Replace tokenizer with func to run extension, but fall back if false @@ -2944,22 +2969,22 @@ throw new Error('marked.parseInline(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected'); } - opt = merge({}, marked.defaults, opt || {}); - checkSanitizeDeprecation(opt); + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); try { - var tokens = Lexer.lexInline(src, opt); + var tokens = Lexer_1.lexInline(src, opt); if (opt.walkTokens) { marked.walkTokens(tokens, opt.walkTokens); } - return Parser.parseInline(tokens, opt); + return Parser_1.parseInline(tokens, opt); } catch (e) { e.message += '\nPlease report this to https://github.com/markedjs/marked.'; if (opt.silent) { - return '

    An error occurred:

    ' + escape(e.message + '', true) + '
    '; + return '

    An error occurred:

    ' + escape$2(e.message + '', true) + '
    '; } throw e; @@ -2970,14 +2995,14 @@ */ - marked.Parser = Parser; - marked.parser = Parser.parse; - marked.Renderer = Renderer; - marked.TextRenderer = TextRenderer; - marked.Lexer = Lexer; - marked.lexer = Lexer.lex; - marked.Tokenizer = Tokenizer; - marked.Slugger = Slugger; + marked.Parser = Parser_1; + marked.parser = Parser_1.parse; + marked.Renderer = Renderer_1; + marked.TextRenderer = TextRenderer_1; + marked.Lexer = Lexer_1; + marked.lexer = Lexer_1.lex; + marked.Tokenizer = Tokenizer_1; + marked.Slugger = Slugger_1; marked.parse = marked; var marked_1 = marked; From 724ea9ec7c4a3a20854e100ce8435cd2cad6e437 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 23 Jun 2021 11:42:24 -0400 Subject: [PATCH 12/20] Replace \h with ' ' --- src/Tokenizer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Tokenizer.js b/src/Tokenizer.js index 6b8d66578a..e46254fa28 100644 --- a/src/Tokenizer.js +++ b/src/Tokenizer.js @@ -218,7 +218,7 @@ module.exports = class Tokenizer { } // Get next list item - const itemRegex = new RegExp(`^( {0,3}${bull})((?: [^\\n]*|\\h*)(?:\\n[^\\n]*)*(?:\\n|$))`); + const itemRegex = new RegExp(`^( {0,3}${bull})((?: [^\\n]*| *)(?:\\n[^\\n]*)*(?:\\n|$))`); // Get each top-level item while (src) { @@ -244,7 +244,7 @@ module.exports = class Tokenizer { blankLine = false; raw = cap[0]; - if (!lines[0] && lines[1].match(/^\h*$/)) { // items begin with at most one blank line + if (!lines[0] && lines[1].match(/^ *$/)) { // items begin with at most one blank line raw = cap[1] + lines.slice(0, 2).join('\n') + '\n'; list.loose = true; lines = []; @@ -292,7 +292,7 @@ module.exports = class Tokenizer { // If the previous item ended with a blank line, the list is loose if (endsWithBlankLine) { list.loose = true; - } else if (raw.match(/\n\h*\n\h*$/)) { + } else if (raw.match(/\n *\n *$/)) { endsWithBlankLine = true; } } From c934790c3fb2e45d4cbabf96c0536ce2e84a1efd Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 23 Jun 2021 11:43:40 -0400 Subject: [PATCH 13/20] Clean comments out of Rules.js --- src/rules.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/rules.js b/src/rules.js index f49ddb0928..29a6241f87 100644 --- a/src/rules.js +++ b/src/rules.js @@ -43,11 +43,6 @@ block.def = edit(block.def) .getRegex(); block.bullet = /(?:[*+-]|\d{1,9}[.)])/; -// block.item = /^( {0,3})(bull)( [^\n]*(?:\n(?! {0,3}bull )[^\n]*)*\n?)/; -// block.item = edit(block.item, 'gm') -// .replace(/bull/g, block.bullet) -// .getRegex(); - block.listItemStart = edit(/^( *)(bull) */) .replace('bull', block.bullet) .getRegex(); From 8007f8b776e8616878b2cb9b51441eb8a2b1636f Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 23 Jun 2021 16:17:28 -0400 Subject: [PATCH 14/20] Don't rebuild "next bullet regex" for each line / replace 'match' with 'test' --- src/Tokenizer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Tokenizer.js b/src/Tokenizer.js index e46254fa28..3d7624fe7c 100644 --- a/src/Tokenizer.js +++ b/src/Tokenizer.js @@ -250,6 +250,8 @@ module.exports = class Tokenizer { lines = []; } + const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])`); + for (i = 1; i < lines.length; i++) { line = lines[i]; @@ -258,7 +260,7 @@ module.exports = class Tokenizer { } // End list item if found start of new bullet - if (line.match(new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])`))) { + if (nextBulletRegex.test(line)) { raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; break; } From a79772b0829c9c4a20caf321c26fff00926b142f Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 23 Jun 2021 16:26:57 -0400 Subject: [PATCH 15/20] Replace more .match and .exec with .test for speed --- src/Tokenizer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Tokenizer.js b/src/Tokenizer.js index 3d7624fe7c..2a7cf83fa1 100644 --- a/src/Tokenizer.js +++ b/src/Tokenizer.js @@ -222,7 +222,7 @@ module.exports = class Tokenizer { // Get each top-level item while (src) { - if (this.rules.block.hr.exec(src)) { // End list if we encounter an HR (possibly move into itemRegex?) + if (this.rules.block.hr.test(src)) { // End list if we encounter an HR (possibly move into itemRegex?) break; } @@ -244,7 +244,7 @@ module.exports = class Tokenizer { blankLine = false; raw = cap[0]; - if (!lines[0] && lines[1].match(/^ *$/)) { // items begin with at most one blank line + if (!lines[0] && /^ *$/.test(lines[1])) { // items begin with at most one blank line raw = cap[1] + lines.slice(0, 2).join('\n') + '\n'; list.loose = true; lines = []; @@ -294,7 +294,7 @@ module.exports = class Tokenizer { // If the previous item ended with a blank line, the list is loose if (endsWithBlankLine) { list.loose = true; - } else if (raw.match(/\n *\n *$/)) { + } else if (/\n *\n *$/.test(raw)) { endsWithBlankLine = true; } } From f78d06ec5ee3995a9271efbd6d8be7e93bcb2437 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Fri, 25 Jun 2021 16:36:49 -0400 Subject: [PATCH 16/20] update to commonmark 0.30. Still passing all tests... --- lib/marked.esm.js | 667 +++++++++++---------- lib/marked.js | 647 ++++++++++---------- test/specs/commonmark/commonmark.0.30.json | 36 +- test/specs/gfm/commonmark.0.30.json | 36 +- 4 files changed, 693 insertions(+), 693 deletions(-) diff --git a/lib/marked.esm.js b/lib/marked.esm.js index 40d25560da..4c00523bd1 100644 --- a/lib/marked.esm.js +++ b/lib/marked.esm.js @@ -9,9 +9,13 @@ * The code in this file is generated from files in ./src/ */ -var defaults$5 = {exports: {}}; +function createCommonjsModule(fn) { + var module = { exports: {} }; + return fn(module, module.exports), module.exports; +} -function getDefaults$1() { +var defaults = createCommonjsModule(function (module) { +function getDefaults() { return { baseUrl: null, breaks: false, @@ -35,20 +39,20 @@ function getDefaults$1() { }; } -function changeDefaults$1(newDefaults) { - defaults$5.exports.defaults = newDefaults; +function changeDefaults(newDefaults) { + module.exports.defaults = newDefaults; } -defaults$5.exports = { - defaults: getDefaults$1(), - getDefaults: getDefaults$1, - changeDefaults: changeDefaults$1 +module.exports = { + defaults: getDefaults(), + getDefaults, + changeDefaults }; +}); /** * Helpers */ - const escapeTest = /[&<>"']/; const escapeReplace = /[&<>"']/g; const escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/; @@ -61,7 +65,7 @@ const escapeReplacements = { "'": ''' }; const getEscapeReplacement = (ch) => escapeReplacements[ch]; -function escape$3(html, encode) { +function escape(html, encode) { if (encode) { if (escapeTest.test(html)) { return html.replace(escapeReplace, getEscapeReplacement); @@ -77,7 +81,7 @@ function escape$3(html, encode) { const unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig; -function unescape$1(html) { +function unescape(html) { // explicitly match decimal, hex, and named HTML entities return html.replace(unescapeTest, (_, n) => { n = n.toLowerCase(); @@ -92,7 +96,7 @@ function unescape$1(html) { } const caret = /(^|[^\[])\^/g; -function edit$1(regex, opt) { +function edit(regex, opt) { regex = regex.source || regex; opt = opt || ''; const obj = { @@ -111,11 +115,11 @@ function edit$1(regex, opt) { const nonWordAndColonTest = /[^\w:]/g; const originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; -function cleanUrl$1(sanitize, base, href) { +function cleanUrl(sanitize, base, href) { if (sanitize) { let prot; try { - prot = decodeURIComponent(unescape$1(href)) + prot = decodeURIComponent(unescape(href)) .replace(nonWordAndColonTest, '') .toLowerCase(); } catch (e) { @@ -149,7 +153,7 @@ function resolveUrl(base, href) { if (justDomain.test(base)) { baseUrls[' ' + base] = base + '/'; } else { - baseUrls[' ' + base] = rtrim$1(base, '/', true); + baseUrls[' ' + base] = rtrim(base, '/', true); } } base = baseUrls[' ' + base]; @@ -170,9 +174,9 @@ function resolveUrl(base, href) { } } -const noopTest$1 = { exec: function noopTest() {} }; +const noopTest = { exec: function noopTest() {} }; -function merge$2(obj) { +function merge(obj) { let i = 1, target, key; @@ -189,7 +193,7 @@ function merge$2(obj) { return obj; } -function splitCells$1(tableRow, count) { +function splitCells(tableRow, count) { // ensure that every cell-delimiting pipe has a space // before it to distinguish it from an escaped pipe const row = tableRow.replace(/\|/g, (match, offset, str) => { @@ -224,7 +228,7 @@ function splitCells$1(tableRow, count) { // Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). // /c*$/ is vulnerable to REDOS. // invert: Remove suffix of non-c chars instead. Default falsey. -function rtrim$1(str, c, invert) { +function rtrim(str, c, invert) { const l = str.length; if (l === 0) { return ''; @@ -248,7 +252,7 @@ function rtrim$1(str, c, invert) { return str.substr(0, l - suffLen); } -function findClosingBracket$1(str, b) { +function findClosingBracket(str, b) { if (str.indexOf(b[1]) === -1) { return -1; } @@ -270,14 +274,14 @@ function findClosingBracket$1(str, b) { return -1; } -function checkSanitizeDeprecation$1(opt) { +function checkSanitizeDeprecation(opt) { if (opt && opt.sanitize && !opt.silent) { console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); } } // copied from https://stackoverflow.com/a/5450113/806777 -function repeatString$1(pattern, count) { +function repeatString(pattern, count) { if (count < 1) { return ''; } @@ -293,31 +297,31 @@ function repeatString$1(pattern, count) { } var helpers = { - escape: escape$3, - unescape: unescape$1, - edit: edit$1, - cleanUrl: cleanUrl$1, + escape, + unescape, + edit, + cleanUrl, resolveUrl, - noopTest: noopTest$1, - merge: merge$2, - splitCells: splitCells$1, - rtrim: rtrim$1, - findClosingBracket: findClosingBracket$1, - checkSanitizeDeprecation: checkSanitizeDeprecation$1, - repeatString: repeatString$1 + noopTest, + merge, + splitCells, + rtrim, + findClosingBracket, + checkSanitizeDeprecation, + repeatString }; -const { defaults: defaults$4 } = defaults$5.exports; +const { defaults: defaults$1 } = defaults; const { - rtrim, - splitCells, - escape: escape$2, - findClosingBracket + rtrim: rtrim$1, + splitCells: splitCells$1, + escape: escape$1, + findClosingBracket: findClosingBracket$1 } = helpers; function outputLink(cap, link, raw) { const href = link.href; - const title = link.title ? escape$2(link.title) : null; + const title = link.title ? escape$1(link.title) : null; const text = cap[1].replace(/\\([\[\]])/g, '$1'); if (cap[0].charAt(0) !== '!') { @@ -334,7 +338,7 @@ function outputLink(cap, link, raw) { raw, href, title, - text: escape$2(text) + text: escape$1(text) }; } } @@ -372,7 +376,7 @@ function indentCodeCompensation(raw, text) { */ var Tokenizer_1 = class Tokenizer { constructor(options) { - this.options = options || defaults$4; + this.options = options || defaults$1; } space(src) { @@ -397,7 +401,7 @@ var Tokenizer_1 = class Tokenizer { raw: cap[0], codeBlockStyle: 'indented', text: !this.options.pedantic - ? rtrim(text, '\n') + ? rtrim$1(text, '\n') : text }; } @@ -425,7 +429,7 @@ var Tokenizer_1 = class Tokenizer { // remove trailing #s if (/#$/.test(text)) { - const trimmed = rtrim(text, '#'); + const trimmed = rtrim$1(text, '#'); if (this.options.pedantic) { text = trimmed.trim(); } else if (!trimmed || / $/.test(trimmed)) { @@ -448,7 +452,7 @@ var Tokenizer_1 = class Tokenizer { if (cap) { const item = { type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [], raw: cap[0] @@ -471,7 +475,7 @@ var Tokenizer_1 = class Tokenizer { l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells(item.cells[i], item.header.length); + item.cells[i] = splitCells$1(item.cells[i], item.header.length); } return item; @@ -503,134 +507,138 @@ var Tokenizer_1 = class Tokenizer { } list(src) { - const cap = this.rules.block.list.exec(src); + let cap = this.rules.block.list.exec(src); if (cap) { - let raw = cap[0]; - const bull = cap[2]; + let raw, istask, ischecked, indent, i, blankLine, endsWithBlankLine, + line, lines, itemContents; + + let bull = cap[1].trim(); const isordered = bull.length > 1; const list = { type: 'list', - raw, + raw: '', ordered: isordered, start: isordered ? +bull.slice(0, -1) : '', loose: false, items: [] }; - // Get each top-level item. - const itemMatch = cap[0].match(this.rules.block.item); - - let next = false, - item, - space, - bcurr, - bnext, - addBack, - loose, - istask, - ischecked, - endMatch; - - let l = itemMatch.length; - bcurr = this.rules.block.listItemStart.exec(itemMatch[0]); - for (let i = 0; i < l; i++) { - item = itemMatch[i]; - raw = item; - - if (!this.options.pedantic) { - // Determine if current item contains the end of the list - endMatch = item.match(new RegExp('\\n\\s*\\n {0,' + (bcurr[0].length - 1) + '}\\S')); - if (endMatch) { - addBack = item.length - endMatch.index + itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - - item = item.substring(0, endMatch.index); - raw = item; - l = i + 1; - } + bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`; + + if (this.options.pedantic) { + bull = isordered ? bull : '[*+-]'; + } + + // Get next list item + const itemRegex = new RegExp(`^( {0,3}${bull})((?: [^\\n]*| *)(?:\\n[^\\n]*)*(?:\\n|$))`); + + // Get each top-level item + while (src) { + if (this.rules.block.hr.test(src)) { // End list if we encounter an HR (possibly move into itemRegex?) + break; } - // Determine whether the next list item belongs here. - // Backpedal if it does not belong in this list. - if (i !== l - 1) { - bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]); - if ( - !this.options.pedantic - ? bnext[1].length >= bcurr[0].length || bnext[1].length > 3 - : bnext[1].length > bcurr[1].length - ) { - // nested list or continuation - itemMatch.splice(i, 2, itemMatch[i] + (!this.options.pedantic && bnext[1].length < bcurr[0].length && !itemMatch[i].match(/\n$/) ? '' : '\n') + itemMatch[i + 1]); - i--; - l--; - continue; - } else if ( - // different bullet style - !this.options.pedantic || this.options.smartLists - ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] - : isordered === (bnext[2].length === 1) - ) { - addBack = itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - i = l - 1; - } - bcurr = bnext; + if (!(cap = itemRegex.exec(src))) { + break; } - // Remove the list item's bullet - // so it is seen as the next token. - space = item.length; - item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); + lines = cap[2].split('\n'); - // Outdent whatever the - // list item contains. Hacky. - if (~item.indexOf('\n ')) { - space -= item.length; - item = !this.options.pedantic - ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') - : item.replace(/^ {1,4}/gm, ''); + if (this.options.pedantic) { + indent = 2; + itemContents = lines[0].trimLeft(); + } else { + indent = cap[2].search(/[^ ]/); // Find first non-space char + indent = cap[1].length + (indent > 4 ? 1 : indent); // intented code blocks after 4 spaces; indent is always 1 + itemContents = lines[0].slice(indent - cap[1].length); } - // trim item newlines at end - item = rtrim(item, '\n'); - if (i !== l - 1) { - raw = raw + '\n'; + blankLine = false; + raw = cap[0]; + + if (!lines[0] && /^ *$/.test(lines[1])) { // items begin with at most one blank line + raw = cap[1] + lines.slice(0, 2).join('\n') + '\n'; + list.loose = true; + lines = []; } - // Determine whether item is loose or not. - // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ - // for discount behavior. - loose = next || /\n\n(?!\s*$)/.test(raw); - if (i !== l - 1) { - next = raw.slice(-2) === '\n\n'; - if (!loose) loose = next; + const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])`); + + for (i = 1; i < lines.length; i++) { + line = lines[i]; + + if (this.options.pedantic) { // Re-align to follow commonmark nesting rules + line = line.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' '); + } + + // End list item if found start of new bullet + if (nextBulletRegex.test(line)) { + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; + break; + } + + // Until we encounter a blank line, item contents do not need indentation + if (!blankLine) { + if (!line.trim()) { // Check if current line is empty + blankLine = true; + } + + // Dedent if possible + if (line.search(/[^ ]/) >= indent) { + itemContents += '\n' + line.slice(indent); + } else { + itemContents += '\n' + line; + } + continue; + } + + // Dedent this line + if (line.search(/[^ ]/) >= indent || !line.trim()) { + itemContents += '\n' + line.slice(indent); + continue; + } else { // Line was not properly indented; end of this item + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; + break; + } } - if (loose) { - list.loose = true; + if (!list.loose) { + // If the previous item ended with a blank line, the list is loose + if (endsWithBlankLine) { + list.loose = true; + } else if (/\n *\n *$/.test(raw)) { + endsWithBlankLine = true; + } } // Check for task list items if (this.options.gfm) { - istask = /^\[[ xX]\] /.test(item); - ischecked = undefined; + istask = /^\[[ xX]\] /.exec(itemContents); if (istask) { - ischecked = item[1] !== ' '; - item = item.replace(/^\[[ xX]\] +/, ''); + ischecked = istask[0] !== '[ ] '; + itemContents = itemContents.replace(/^\[[ xX]\] +/, ''); } } list.items.push({ type: 'list_item', - raw, - task: istask, + raw: raw, + task: !!istask, checked: ischecked, - loose: loose, - text: item + loose: false, + text: itemContents }); + + list.raw += raw; + src = src.slice(raw.length); } + // Do not consume ending newlines. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic + list.items[list.items.length - 1].raw = raw.trimRight(); + list.items[list.items.length - 1].text = itemContents.trimRight(); + list.raw = list.raw.trimRight(); + return list; } } @@ -645,7 +653,7 @@ var Tokenizer_1 = class Tokenizer { raw: cap[0], pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), - text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0] + text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$1(cap[0])) : cap[0] }; } } @@ -670,7 +678,7 @@ var Tokenizer_1 = class Tokenizer { if (cap) { const item = { type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] }; @@ -694,7 +702,7 @@ var Tokenizer_1 = class Tokenizer { l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells( + item.cells[i] = splitCells$1( item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length); } @@ -746,7 +754,7 @@ var Tokenizer_1 = class Tokenizer { return { type: 'escape', raw: cap[0], - text: escape$2(cap[1]) + text: escape$1(cap[1]) }; } } @@ -775,7 +783,7 @@ var Tokenizer_1 = class Tokenizer { text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) - : escape$2(cap[0])) + : escape$1(cap[0])) : cap[0] }; } @@ -792,13 +800,13 @@ var Tokenizer_1 = class Tokenizer { } // ending angle bracket cannot be escaped - const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\'); + const rtrimSlash = rtrim$1(trimmedUrl.slice(0, -1), '\\'); if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) { return; } } else { // find closing parenthesis - const lastParenIndex = findClosingBracket(cap[2], '()'); + const lastParenIndex = findClosingBracket$1(cap[2], '()'); if (lastParenIndex > -1) { const start = cap[0].indexOf('!') === 0 ? 5 : 4; const linkLen = start + cap[1].length + lastParenIndex; @@ -926,7 +934,7 @@ var Tokenizer_1 = class Tokenizer { if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) { text = text.substring(1, text.length - 1); } - text = escape$2(text, true); + text = escape$1(text, true); return { type: 'codespan', raw: cap[0], @@ -961,10 +969,10 @@ var Tokenizer_1 = class Tokenizer { if (cap) { let text, href; if (cap[2] === '@') { - text = escape$2(this.options.mangle ? mangle(cap[1]) : cap[1]); + text = escape$1(this.options.mangle ? mangle(cap[1]) : cap[1]); href = 'mailto:' + text; } else { - text = escape$2(cap[1]); + text = escape$1(cap[1]); href = text; } @@ -989,7 +997,7 @@ var Tokenizer_1 = class Tokenizer { if (cap = this.rules.inline.url.exec(src)) { let text, href; if (cap[2] === '@') { - text = escape$2(this.options.mangle ? mangle(cap[0]) : cap[0]); + text = escape$1(this.options.mangle ? mangle(cap[0]) : cap[0]); href = 'mailto:' + text; } else { // do extended autolink path validation @@ -998,7 +1006,7 @@ var Tokenizer_1 = class Tokenizer { prevCapZero = cap[0]; cap[0] = this.rules.inline._backpedal.exec(cap[0])[0]; } while (prevCapZero !== cap[0]); - text = escape$2(cap[0]); + text = escape$1(cap[0]); if (cap[1] === 'www.') { href = 'http://' + text; } else { @@ -1026,9 +1034,9 @@ var Tokenizer_1 = class Tokenizer { if (cap) { let text; if (inRawBlock) { - text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0]; + text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$1(cap[0])) : cap[0]; } else { - text = escape$2(this.options.smartypants ? smartypants(cap[0]) : cap[0]); + text = escape$1(this.options.smartypants ? smartypants(cap[0]) : cap[0]); } return { type: 'text', @@ -1040,22 +1048,22 @@ var Tokenizer_1 = class Tokenizer { }; const { - noopTest, - edit, + noopTest: noopTest$1, + edit: edit$1, merge: merge$1 } = helpers; /** * Block-Level Grammar */ -const block$1 = { +const block = { newline: /^(?: *(?:\n|$))+/, code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/, - fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, + fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?=\n|$)|$)/, hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/, + list: /^( {0,3}bull)( [^\n]+?)?(?:\n|$)/, html: '^ {0,3}(?:' // optional indentation + '<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + '|comment[^\\n]*(\\n+|$)' // (2) @@ -1067,8 +1075,8 @@ const block$1 = { + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag + ')', def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, - nptable: noopTest, - table: noopTest, + nptable: noopTest$1, + table: noopTest$1, lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/, // regex template, placeholders will be replaced according to different paragraph // interruption rules of commonmark and the original markdown spec: @@ -1076,68 +1084,63 @@ const block$1 = { text: /^[^\n]+/ }; -block$1._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; -block$1._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; -block$1.def = edit(block$1.def) - .replace('label', block$1._label) - .replace('title', block$1._title) - .getRegex(); - -block$1.bullet = /(?:[*+-]|\d{1,9}[.)])/; -block$1.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/; -block$1.item = edit(block$1.item, 'gm') - .replace(/bull/g, block$1.bullet) +block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; +block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; +block.def = edit$1(block.def) + .replace('label', block._label) + .replace('title', block._title) .getRegex(); -block$1.listItemStart = edit(/^( *)(bull) */) - .replace('bull', block$1.bullet) +block.bullet = /(?:[*+-]|\d{1,9}[.)])/; +block.listItemStart = edit$1(/^( *)(bull) */) + .replace('bull', block.bullet) .getRegex(); -block$1.list = edit(block$1.list) - .replace(/bull/g, block$1.bullet) +block.list = edit$1(block.list) + .replace(/bull/g, block.bullet) .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))') - .replace('def', '\\n+(?=' + block$1.def.source + ')') + .replace('def', '\\n+(?=' + block.def.source + ')') .getRegex(); -block$1._tag = 'address|article|aside|base|basefont|blockquote|body|caption' +block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul'; -block$1._comment = /|$)/; -block$1.html = edit(block$1.html, 'i') - .replace('comment', block$1._comment) - .replace('tag', block$1._tag) +block._comment = /|$)/; +block.html = edit$1(block.html, 'i') + .replace('comment', block._comment) + .replace('tag', block._tag) .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/) .getRegex(); -block$1.paragraph = edit(block$1._paragraph) - .replace('hr', block$1.hr) +block.paragraph = edit$1(block._paragraph) + .replace('hr', block.hr) .replace('heading', ' {0,3}#{1,6} ') .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs .replace('blockquote', ' {0,3}>') .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt .replace('html', ')|<(?:script|pre|style|textarea|!--)') - .replace('tag', block$1._tag) // pars can be interrupted by type (6) html blocks + .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks .getRegex(); -block$1.blockquote = edit(block$1.blockquote) - .replace('paragraph', block$1.paragraph) +block.blockquote = edit$1(block.blockquote) + .replace('paragraph', block.paragraph) .getRegex(); /** * Normal Block Grammar */ -block$1.normal = merge$1({}, block$1); +block.normal = merge$1({}, block); /** * GFM Block Grammar */ -block$1.gfm = merge$1({}, block$1.normal, { +block.gfm = merge$1({}, block.normal, { nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header + ' {0,3}([-:]+ *\\|[-| :]*)' // Align + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', // Cells @@ -1146,38 +1149,38 @@ block$1.gfm = merge$1({}, block$1.normal, { + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells }); -block$1.gfm.nptable = edit(block$1.gfm.nptable) - .replace('hr', block$1.hr) +block.gfm.nptable = edit$1(block.gfm.nptable) + .replace('hr', block.hr) .replace('heading', ' {0,3}#{1,6} ') .replace('blockquote', ' {0,3}>') .replace('code', ' {4}[^\\n]') .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt .replace('html', ')|<(?:script|pre|style|textarea|!--)') - .replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks + .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks .getRegex(); -block$1.gfm.table = edit(block$1.gfm.table) - .replace('hr', block$1.hr) +block.gfm.table = edit$1(block.gfm.table) + .replace('hr', block.hr) .replace('heading', ' {0,3}#{1,6} ') .replace('blockquote', ' {0,3}>') .replace('code', ' {4}[^\\n]') .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt .replace('html', ')|<(?:script|pre|style|textarea|!--)') - .replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks + .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks .getRegex(); /** * Pedantic grammar (original John Gruber's loose markdown specification) */ -block$1.pedantic = merge$1({}, block$1.normal, { - html: edit( +block.pedantic = merge$1({}, block.normal, { + html: edit$1( '^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))') - .replace('comment', block$1._comment) + .replace('comment', block._comment) .replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' @@ -1185,11 +1188,11 @@ block$1.pedantic = merge$1({}, block$1.normal, { .getRegex(), def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, heading: /^(#{1,6})(.*)(?:\n+|$)/, - fences: noopTest, // fences not supported - paragraph: edit(block$1.normal._paragraph) - .replace('hr', block$1.hr) + fences: noopTest$1, // fences not supported + paragraph: edit$1(block.normal._paragraph) + .replace('hr', block.hr) .replace('heading', ' *#{1,6} *[^\n]') - .replace('lheading', block$1.lheading) + .replace('lheading', block.lheading) .replace('blockquote', ' {0,3}>') .replace('|fences', '') .replace('|list', '') @@ -1200,10 +1203,10 @@ block$1.pedantic = merge$1({}, block$1.normal, { /** * Inline-Level Grammar */ -const inline$1 = { +const inline = { escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, - url: noopTest, + url: noopTest$1, tag: '^comment' + '|^' // self-closing tag + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag @@ -1223,80 +1226,80 @@ const inline$1 = { }, code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, br: /^( {2,}|\\)\n(?!\s*$)/, - del: noopTest, + del: noopTest$1, text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~'; -inline$1.punctuation = edit(inline$1.punctuation).replace(/punctuation/g, inline$1._punctuation).getRegex(); +inline._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~'; +inline.punctuation = edit$1(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, -inline$1.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g; -inline$1.escapedEmSt = /\\\*|\\_/g; +inline.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g; +inline.escapedEmSt = /\\\*|\\_/g; -inline$1._comment = edit(block$1._comment).replace('(?:-->|$)', '-->').getRegex(); +inline._comment = edit$1(block._comment).replace('(?:-->|$)', '-->').getRegex(); -inline$1.emStrong.lDelim = edit(inline$1.emStrong.lDelim) - .replace(/punct/g, inline$1._punctuation) +inline.emStrong.lDelim = edit$1(inline.emStrong.lDelim) + .replace(/punct/g, inline._punctuation) .getRegex(); -inline$1.emStrong.rDelimAst = edit(inline$1.emStrong.rDelimAst, 'g') - .replace(/punct/g, inline$1._punctuation) +inline.emStrong.rDelimAst = edit$1(inline.emStrong.rDelimAst, 'g') + .replace(/punct/g, inline._punctuation) .getRegex(); -inline$1.emStrong.rDelimUnd = edit(inline$1.emStrong.rDelimUnd, 'g') - .replace(/punct/g, inline$1._punctuation) +inline.emStrong.rDelimUnd = edit$1(inline.emStrong.rDelimUnd, 'g') + .replace(/punct/g, inline._punctuation) .getRegex(); -inline$1._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; +inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; -inline$1._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; -inline$1._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])?)+(?![-_])/; -inline$1.autolink = edit(inline$1.autolink) - .replace('scheme', inline$1._scheme) - .replace('email', inline$1._email) +inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; +inline._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])?)+(?![-_])/; +inline.autolink = edit$1(inline.autolink) + .replace('scheme', inline._scheme) + .replace('email', inline._email) .getRegex(); -inline$1._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; +inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; -inline$1.tag = edit(inline$1.tag) - .replace('comment', inline$1._comment) - .replace('attribute', inline$1._attribute) +inline.tag = edit$1(inline.tag) + .replace('comment', inline._comment) + .replace('attribute', inline._attribute) .getRegex(); -inline$1._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; -inline$1._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/; -inline$1._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; +inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; +inline._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/; +inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; -inline$1.link = edit(inline$1.link) - .replace('label', inline$1._label) - .replace('href', inline$1._href) - .replace('title', inline$1._title) +inline.link = edit$1(inline.link) + .replace('label', inline._label) + .replace('href', inline._href) + .replace('title', inline._title) .getRegex(); -inline$1.reflink = edit(inline$1.reflink) - .replace('label', inline$1._label) +inline.reflink = edit$1(inline.reflink) + .replace('label', inline._label) .getRegex(); -inline$1.reflinkSearch = edit(inline$1.reflinkSearch, 'g') - .replace('reflink', inline$1.reflink) - .replace('nolink', inline$1.nolink) +inline.reflinkSearch = edit$1(inline.reflinkSearch, 'g') + .replace('reflink', inline.reflink) + .replace('nolink', inline.nolink) .getRegex(); /** * Normal Inline Grammar */ -inline$1.normal = merge$1({}, inline$1); +inline.normal = merge$1({}, inline); /** * Pedantic Inline Grammar */ -inline$1.pedantic = merge$1({}, inline$1.normal, { +inline.pedantic = merge$1({}, inline.normal, { strong: { start: /^__|\*\*/, middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, @@ -1309,11 +1312,11 @@ inline$1.pedantic = merge$1({}, inline$1.normal, { endAst: /\*(?!\*)/g, endUnd: /_(?!_)/g }, - link: edit(/^!?\[(label)\]\((.*?)\)/) - .replace('label', inline$1._label) + link: edit$1(/^!?\[(label)\]\((.*?)\)/) + .replace('label', inline._label) .getRegex(), - reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/) - .replace('label', inline$1._label) + reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/) + .replace('label', inline._label) .getRegex() }); @@ -1321,8 +1324,8 @@ inline$1.pedantic = merge$1({}, inline$1.normal, { * GFM Inline Grammar */ -inline$1.gfm = merge$1({}, inline$1.normal, { - escape: edit(inline$1.escape).replace('])', '~|])').getRegex(), +inline.gfm = merge$1({}, inline.normal, { + escape: edit$1(inline.escape).replace('])', '~|])').getRegex(), _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/, url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/, @@ -1330,30 +1333,29 @@ inline$1.gfm = merge$1({}, inline$1.normal, { text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\ t.type === 'space')) { + token.loose = true; + token.items[i].loose = true; + } } tokens.push(token); continue; @@ -1569,13 +1575,19 @@ var Lexer_1 = class Lexer { } // def - if (top && (token = this.tokenizer.def(src))) { + if (token = this.tokenizer.def(src)) { src = src.substring(token.raw.length); - if (!this.tokens.links[token.tag]) { - this.tokens.links[token.tag] = { - href: token.href, - title: token.title - }; + lastToken = tokens[tokens.length - 1]; + if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) { + lastToken.raw += '\n' + token.raw; + lastToken.text += '\n' + token.raw; + } else { + if (!this.tokens.links[token.tag]) { + this.tokens.links[token.tag] = { + href: token.href, + title: token.title + }; + } } continue; } @@ -1728,14 +1740,14 @@ var Lexer_1 = class Lexer { if (links.length > 0) { while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) { if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); } } } } // Mask out other blocks while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); } // Mask out escaped em & strong delimiters @@ -1901,10 +1913,10 @@ var Lexer_1 = class Lexer { } }; -const { defaults: defaults$2 } = defaults$5.exports; +const { defaults: defaults$3 } = defaults; const { - cleanUrl, - escape: escape$1 + cleanUrl: cleanUrl$1, + escape: escape$2 } = helpers; /** @@ -1912,7 +1924,7 @@ const { */ var Renderer_1 = class Renderer { constructor(options) { - this.options = options || defaults$2; + this.options = options || defaults$3; } code(code, infostring, escaped) { @@ -1929,15 +1941,15 @@ var Renderer_1 = class Renderer { if (!lang) { return '
    '
    -        + (escaped ? code : escape$1(code, true))
    +        + (escaped ? code : escape$2(code, true))
             + '
    \n'; } return '
    '
    -      + (escaped ? code : escape$1(code, true))
    +      + (escaped ? code : escape$2(code, true))
           + '
    \n'; } @@ -2037,11 +2049,11 @@ var Renderer_1 = class Renderer { } link(href, title, text) { - href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); if (href === null) { return text; } - let out = '
    0 && item.tokens[0].type === 'text') { + if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') { item.tokens[0].text = checkbox + ' ' + item.tokens[0].text; if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') { item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text; @@ -2450,22 +2457,16 @@ var Parser_1 = class Parser { } }; -const Lexer = Lexer_1; -const Parser = Parser_1; -const Tokenizer = Tokenizer_1; -const Renderer = Renderer_1; -const TextRenderer = TextRenderer_1; -const Slugger = Slugger_1; const { - merge, - checkSanitizeDeprecation, - escape + merge: merge$2, + checkSanitizeDeprecation: checkSanitizeDeprecation$1, + escape: escape$3 } = helpers; const { getDefaults, changeDefaults, - defaults -} = defaults$5.exports; + defaults: defaults$5 +} = defaults; /** * Marked @@ -2485,15 +2486,15 @@ function marked(src, opt, callback) { opt = null; } - opt = merge({}, marked.defaults, opt || {}); - checkSanitizeDeprecation(opt); + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); if (callback) { const highlight = opt.highlight; let tokens; try { - tokens = Lexer.lex(src, opt); + tokens = Lexer_1.lex(src, opt); } catch (e) { return callback(e); } @@ -2506,7 +2507,7 @@ function marked(src, opt, callback) { if (opt.walkTokens) { marked.walkTokens(tokens, opt.walkTokens); } - out = Parser.parse(tokens, opt); + out = Parser_1.parse(tokens, opt); } catch (e) { err = e; } @@ -2558,16 +2559,16 @@ function marked(src, opt, callback) { } try { - const tokens = Lexer.lex(src, opt); + const tokens = Lexer_1.lex(src, opt); if (opt.walkTokens) { marked.walkTokens(tokens, opt.walkTokens); } - return Parser.parse(tokens, opt); + return Parser_1.parse(tokens, opt); } catch (e) { e.message += '\nPlease report this to https://github.com/markedjs/marked.'; if (opt.silent) { return '

    An error occurred:

    '
    -        + escape(e.message + '', true)
    +        + escape$3(e.message + '', true)
             + '
    '; } throw e; @@ -2580,21 +2581,21 @@ function marked(src, opt, callback) { marked.options = marked.setOptions = function(opt) { - merge(marked.defaults, opt); + merge$2(marked.defaults, opt); changeDefaults(marked.defaults); return marked; }; marked.getDefaults = getDefaults; -marked.defaults = defaults; +marked.defaults = defaults$5; /** * Use Extension */ marked.use = function(...args) { - const opts = merge({}, ...args); + const opts = merge$2({}, ...args); const extensions = marked.defaults.extensions || { renderers: {}, childTokens: {} }; let hasExtensions; @@ -2654,7 +2655,7 @@ marked.use = function(...args) { // ==-- Parse "overwrite" extensions --== // if (pack.renderer) { - const renderer = marked.defaults.renderer || new Renderer(); + const renderer = marked.defaults.renderer || new Renderer_1(); for (const prop in pack.renderer) { const prevRenderer = renderer[prop]; // Replace renderer with func to run extension, but fall back if false @@ -2669,7 +2670,7 @@ marked.use = function(...args) { opts.renderer = renderer; } if (pack.tokenizer) { - const tokenizer = marked.defaults.tokenizer || new Tokenizer(); + const tokenizer = marked.defaults.tokenizer || new Tokenizer_1(); for (const prop in pack.tokenizer) { const prevTokenizer = tokenizer[prop]; // Replace tokenizer with func to run extension, but fall back if false @@ -2752,20 +2753,20 @@ marked.parseInline = function(src, opt) { + Object.prototype.toString.call(src) + ', string expected'); } - opt = merge({}, marked.defaults, opt || {}); - checkSanitizeDeprecation(opt); + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); try { - const tokens = Lexer.lexInline(src, opt); + const tokens = Lexer_1.lexInline(src, opt); if (opt.walkTokens) { marked.walkTokens(tokens, opt.walkTokens); } - return Parser.parseInline(tokens, opt); + return Parser_1.parseInline(tokens, opt); } catch (e) { e.message += '\nPlease report this to https://github.com/markedjs/marked.'; if (opt.silent) { return '

    An error occurred:

    '
    -        + escape(e.message + '', true)
    +        + escape$3(e.message + '', true)
             + '
    '; } throw e; @@ -2776,18 +2777,18 @@ marked.parseInline = function(src, opt) { * Expose */ -marked.Parser = Parser; -marked.parser = Parser.parse; +marked.Parser = Parser_1; +marked.parser = Parser_1.parse; -marked.Renderer = Renderer; -marked.TextRenderer = TextRenderer; +marked.Renderer = Renderer_1; +marked.TextRenderer = TextRenderer_1; -marked.Lexer = Lexer; -marked.lexer = Lexer.lex; +marked.Lexer = Lexer_1; +marked.lexer = Lexer_1.lex; -marked.Tokenizer = Tokenizer; +marked.Tokenizer = Tokenizer_1; -marked.Slugger = Slugger; +marked.Slugger = Slugger_1; marked.parse = marked; diff --git a/lib/marked.js b/lib/marked.js index 10fe5d2815..882ee312d6 100644 --- a/lib/marked.js +++ b/lib/marked.js @@ -49,61 +49,70 @@ } function _createForOfIteratorHelperLoose(o, allowArrayLike) { - var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; - if (it) return (it = it.call(o)).next.bind(it); - - if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { - if (it) o = it; - var i = 0; - return function () { - if (i >= o.length) return { - done: true - }; - return { - done: false, - value: o[i++] + var it; + + if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + return function () { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; }; - }; + } + + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } - throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + it = o[Symbol.iterator](); + return it.next.bind(it); } - var defaults$5 = {exports: {}}; - - function getDefaults$1() { - return { - baseUrl: null, - breaks: false, - extensions: null, - gfm: true, - headerIds: true, - headerPrefix: '', - highlight: null, - langPrefix: 'language-', - mangle: true, - pedantic: false, - renderer: null, - sanitize: false, - sanitizer: null, - silent: false, - smartLists: false, - smartypants: false, - tokenizer: null, - walkTokens: null, - xhtml: false - }; + function createCommonjsModule(fn) { + var module = { exports: {} }; + return fn(module, module.exports), module.exports; } - function changeDefaults$1(newDefaults) { - defaults$5.exports.defaults = newDefaults; - } + var defaults = createCommonjsModule(function (module) { + function getDefaults() { + return { + baseUrl: null, + breaks: false, + extensions: null, + gfm: true, + headerIds: true, + headerPrefix: '', + highlight: null, + langPrefix: 'language-', + mangle: true, + pedantic: false, + renderer: null, + sanitize: false, + sanitizer: null, + silent: false, + smartLists: false, + smartypants: false, + tokenizer: null, + walkTokens: null, + xhtml: false + }; + } - defaults$5.exports = { - defaults: getDefaults$1(), - getDefaults: getDefaults$1, - changeDefaults: changeDefaults$1 - }; + function changeDefaults(newDefaults) { + module.exports.defaults = newDefaults; + } + + module.exports = { + defaults: getDefaults(), + getDefaults: getDefaults, + changeDefaults: changeDefaults + }; + }); /** * Helpers @@ -124,7 +133,7 @@ return escapeReplacements[ch]; }; - function escape$2(html, encode) { + function escape(html, encode) { if (encode) { if (escapeTest.test(html)) { return html.replace(escapeReplace, getEscapeReplacement); @@ -140,7 +149,7 @@ var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig; - function unescape$1(html) { + function unescape(html) { // explicitly match decimal, hex, and named HTML entities return html.replace(unescapeTest, function (_, n) { n = n.toLowerCase(); @@ -156,7 +165,7 @@ var caret = /(^|[^\[])\^/g; - function edit$1(regex, opt) { + function edit(regex, opt) { regex = regex.source || regex; opt = opt || ''; var obj = { @@ -176,12 +185,12 @@ var nonWordAndColonTest = /[^\w:]/g; var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; - function cleanUrl$1(sanitize, base, href) { + function cleanUrl(sanitize, base, href) { if (sanitize) { var prot; try { - prot = decodeURIComponent(unescape$1(href)).replace(nonWordAndColonTest, '').toLowerCase(); + prot = decodeURIComponent(unescape(href)).replace(nonWordAndColonTest, '').toLowerCase(); } catch (e) { return null; } @@ -217,7 +226,7 @@ if (justDomain.test(base)) { baseUrls[' ' + base] = base + '/'; } else { - baseUrls[' ' + base] = rtrim$1(base, '/', true); + baseUrls[' ' + base] = rtrim(base, '/', true); } } @@ -241,11 +250,11 @@ } } - var noopTest$1 = { + var noopTest = { exec: function noopTest() {} }; - function merge$2(obj) { + function merge(obj) { var i = 1, target, key; @@ -263,7 +272,7 @@ return obj; } - function splitCells$1(tableRow, count) { + function splitCells(tableRow, count) { // ensure that every cell-delimiting pipe has a space // before it to distinguish it from an escaped pipe var row = tableRow.replace(/\|/g, function (match, offset, str) { @@ -305,7 +314,7 @@ // invert: Remove suffix of non-c chars instead. Default falsey. - function rtrim$1(str, c, invert) { + function rtrim(str, c, invert) { var l = str.length; if (l === 0) { @@ -330,7 +339,7 @@ return str.substr(0, l - suffLen); } - function findClosingBracket$1(str, b) { + function findClosingBracket(str, b) { if (str.indexOf(b[1]) === -1) { return -1; } @@ -356,14 +365,14 @@ return -1; } - function checkSanitizeDeprecation$1(opt) { + function checkSanitizeDeprecation(opt) { if (opt && opt.sanitize && !opt.silent) { console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); } } // copied from https://stackoverflow.com/a/5450113/806777 - function repeatString$1(pattern, count) { + function repeatString(pattern, count) { if (count < 1) { return ''; } @@ -383,25 +392,25 @@ } var helpers = { - escape: escape$2, - unescape: unescape$1, - edit: edit$1, - cleanUrl: cleanUrl$1, + escape: escape, + unescape: unescape, + edit: edit, + cleanUrl: cleanUrl, resolveUrl: resolveUrl, - noopTest: noopTest$1, - merge: merge$2, - splitCells: splitCells$1, - rtrim: rtrim$1, - findClosingBracket: findClosingBracket$1, - checkSanitizeDeprecation: checkSanitizeDeprecation$1, - repeatString: repeatString$1 + noopTest: noopTest, + merge: merge, + splitCells: splitCells, + rtrim: rtrim, + findClosingBracket: findClosingBracket, + checkSanitizeDeprecation: checkSanitizeDeprecation, + repeatString: repeatString }; - var defaults$4 = defaults$5.exports.defaults; - var rtrim = helpers.rtrim, - splitCells = helpers.splitCells, + var defaults$1 = defaults.defaults; + var rtrim$1 = helpers.rtrim, + splitCells$1 = helpers.splitCells, _escape = helpers.escape, - findClosingBracket = helpers.findClosingBracket; + findClosingBracket$1 = helpers.findClosingBracket; function outputLink(cap, link, raw) { var href = link.href; @@ -458,7 +467,7 @@ var Tokenizer_1 = /*#__PURE__*/function () { function Tokenizer(options) { - this.options = options || defaults$4; + this.options = options || defaults$1; } var _proto = Tokenizer.prototype; @@ -489,7 +498,7 @@ type: 'code', raw: cap[0], codeBlockStyle: 'indented', - text: !this.options.pedantic ? rtrim(text, '\n') : text + text: !this.options.pedantic ? rtrim$1(text, '\n') : text }; } }; @@ -516,7 +525,7 @@ var text = cap[2].trim(); // remove trailing #s if (/#$/.test(text)) { - var trimmed = rtrim(text, '#'); + var trimmed = rtrim$1(text, '#'); if (this.options.pedantic) { text = trimmed.trim(); @@ -541,7 +550,7 @@ if (cap) { var item = { type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [], raw: cap[0] @@ -566,7 +575,7 @@ l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells(item.cells[i], item.header.length); + item.cells[i] = splitCells$1(item.cells[i], item.header.length); } return item; @@ -602,123 +611,138 @@ var cap = this.rules.block.list.exec(src); if (cap) { - var raw = cap[0]; - var bull = cap[2]; + var raw, istask, ischecked, indent, i, blankLine, endsWithBlankLine, line, lines, itemContents; + var bull = cap[1].trim(); var isordered = bull.length > 1; var list = { type: 'list', - raw: raw, + raw: '', ordered: isordered, start: isordered ? +bull.slice(0, -1) : '', loose: false, items: [] - }; // Get each top-level item. - - var itemMatch = cap[0].match(this.rules.block.item); - var next = false, - item, - space, - bcurr, - bnext, - addBack, - loose, - istask, - ischecked, - endMatch; - var l = itemMatch.length; - bcurr = this.rules.block.listItemStart.exec(itemMatch[0]); - - for (var i = 0; i < l; i++) { - item = itemMatch[i]; - raw = item; - - if (!this.options.pedantic) { - // Determine if current item contains the end of the list - endMatch = item.match(new RegExp('\\n\\s*\\n {0,' + (bcurr[0].length - 1) + '}\\S')); - - if (endMatch) { - addBack = item.length - endMatch.index + itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - item = item.substring(0, endMatch.index); - raw = item; - l = i + 1; - } - } // Determine whether the next list item belongs here. - // Backpedal if it does not belong in this list. + }; + bull = isordered ? "\\d{1,9}\\" + bull.slice(-1) : "\\" + bull; + if (this.options.pedantic) { + bull = isordered ? bull : '[*+-]'; + } // Get next list item - if (i !== l - 1) { - bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]); - if (!this.options.pedantic ? bnext[1].length >= bcurr[0].length || bnext[1].length > 3 : bnext[1].length > bcurr[1].length) { - // nested list or continuation - itemMatch.splice(i, 2, itemMatch[i] + (!this.options.pedantic && bnext[1].length < bcurr[0].length && !itemMatch[i].match(/\n$/) ? '' : '\n') + itemMatch[i + 1]); - i--; - l--; - continue; - } else if ( // different bullet style - !this.options.pedantic || this.options.smartLists ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] : isordered === (bnext[2].length === 1)) { - addBack = itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - i = l - 1; - } + var itemRegex = new RegExp("^( {0,3}" + bull + ")((?: [^\\n]*| *)(?:\\n[^\\n]*)*(?:\\n|$))"); // Get each top-level item + + while (src) { + if (this.rules.block.hr.test(src)) { + // End list if we encounter an HR (possibly move into itemRegex?) + break; + } + + if (!(cap = itemRegex.exec(src))) { + break; + } + + lines = cap[2].split('\n'); + + if (this.options.pedantic) { + indent = 2; + itemContents = lines[0].trimLeft(); + } else { + indent = cap[2].search(/[^ ]/); // Find first non-space char - bcurr = bnext; - } // Remove the list item's bullet - // so it is seen as the next token. + indent = cap[1].length + (indent > 4 ? 1 : indent); // intented code blocks after 4 spaces; indent is always 1 + itemContents = lines[0].slice(indent - cap[1].length); + } + + blankLine = false; + raw = cap[0]; + + if (!lines[0] && /^ *$/.test(lines[1])) { + // items begin with at most one blank line + raw = cap[1] + lines.slice(0, 2).join('\n') + '\n'; + list.loose = true; + lines = []; + } - space = item.length; - item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the - // list item contains. Hacky. + var nextBulletRegex = new RegExp("^ {0," + Math.min(3, indent - 1) + "}(?:[*+-]|\\d{1,9}[.)])"); - if (~item.indexOf('\n ')) { - space -= item.length; - item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, ''); - } // trim item newlines at end + for (i = 1; i < lines.length; i++) { + line = lines[i]; + if (this.options.pedantic) { + // Re-align to follow commonmark nesting rules + line = line.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' '); + } // End list item if found start of new bullet - item = rtrim(item, '\n'); - if (i !== l - 1) { - raw = raw + '\n'; - } // Determine whether item is loose or not. - // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ - // for discount behavior. + if (nextBulletRegex.test(line)) { + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; + break; + } // Until we encounter a blank line, item contents do not need indentation + + + if (!blankLine) { + if (!line.trim()) { + // Check if current line is empty + blankLine = true; + } // Dedent if possible - loose = next || /\n\n(?!\s*$)/.test(raw); + if (line.search(/[^ ]/) >= indent) { + itemContents += '\n' + line.slice(indent); + } else { + itemContents += '\n' + line; + } - if (i !== l - 1) { - next = raw.slice(-2) === '\n\n'; - if (!loose) loose = next; + continue; + } // Dedent this line + + + if (line.search(/[^ ]/) >= indent || !line.trim()) { + itemContents += '\n' + line.slice(indent); + continue; + } else { + // Line was not properly indented; end of this item + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; + break; + } } - if (loose) { - list.loose = true; + if (!list.loose) { + // If the previous item ended with a blank line, the list is loose + if (endsWithBlankLine) { + list.loose = true; + } else if (/\n *\n *$/.test(raw)) { + endsWithBlankLine = true; + } } // Check for task list items if (this.options.gfm) { - istask = /^\[[ xX]\] /.test(item); - ischecked = undefined; + istask = /^\[[ xX]\] /.exec(itemContents); if (istask) { - ischecked = item[1] !== ' '; - item = item.replace(/^\[[ xX]\] +/, ''); + ischecked = istask[0] !== '[ ] '; + itemContents = itemContents.replace(/^\[[ xX]\] +/, ''); } } list.items.push({ type: 'list_item', raw: raw, - task: istask, + task: !!istask, checked: ischecked, - loose: loose, - text: item + loose: false, + text: itemContents }); - } + list.raw += raw; + src = src.slice(raw.length); + } // Do not consume ending newlines. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic + + list.items[list.items.length - 1].raw = raw.trimRight(); + list.items[list.items.length - 1].text = itemContents.trimRight(); + list.raw = list.raw.trimRight(); return list; } }; @@ -758,7 +782,7 @@ if (cap) { var item = { type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] }; @@ -783,7 +807,7 @@ l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length); + item.cells[i] = splitCells$1(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length); } return item; @@ -879,14 +903,14 @@ } // ending angle bracket cannot be escaped - var rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\'); + var rtrimSlash = rtrim$1(trimmedUrl.slice(0, -1), '\\'); if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) { return; } } else { // find closing parenthesis - var lastParenIndex = findClosingBracket(cap[2], '()'); + var lastParenIndex = findClosingBracket$1(cap[2], '()'); if (lastParenIndex > -1) { var start = cap[0].indexOf('!') === 0 ? 5 : 4; @@ -1150,21 +1174,21 @@ return Tokenizer; }(); - var noopTest = helpers.noopTest, - edit = helpers.edit, + var noopTest$1 = helpers.noopTest, + edit$1 = helpers.edit, merge$1 = helpers.merge; /** * Block-Level Grammar */ - var block$1 = { + var block = { newline: /^(?: *(?:\n|$))+/, code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/, - fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, + fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?=\n|$)|$)/, hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/, + list: /^( {0,3}bull)( [^\n]+?)?(?:\n|$)/, html: '^ {0,3}(?:' // optional indentation + '<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + '|comment[^\\n]*(\\n+|$)' // (2) @@ -1176,40 +1200,38 @@ + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag + ')', def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, - nptable: noopTest, - table: noopTest, + nptable: noopTest$1, + table: noopTest$1, lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/, // regex template, placeholders will be replaced according to different paragraph // interruption rules of commonmark and the original markdown spec: _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html| +\n)[^\n]+)*)/, text: /^[^\n]+/ }; - block$1._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; - block$1._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; - block$1.def = edit(block$1.def).replace('label', block$1._label).replace('title', block$1._title).getRegex(); - block$1.bullet = /(?:[*+-]|\d{1,9}[.)])/; - block$1.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/; - block$1.item = edit(block$1.item, 'gm').replace(/bull/g, block$1.bullet).getRegex(); - block$1.listItemStart = edit(/^( *)(bull) */).replace('bull', block$1.bullet).getRegex(); - block$1.list = edit(block$1.list).replace(/bull/g, block$1.bullet).replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))').replace('def', '\\n+(?=' + block$1.def.source + ')').getRegex(); - block$1._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul'; - block$1._comment = /|$)/; - block$1.html = edit(block$1.html, 'i').replace('comment', block$1._comment).replace('tag', block$1._tag).replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(); - block$1.paragraph = edit(block$1._paragraph).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs + block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; + block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; + block.def = edit$1(block.def).replace('label', block._label).replace('title', block._title).getRegex(); + block.bullet = /(?:[*+-]|\d{1,9}[.)])/; + block.listItemStart = edit$1(/^( *)(bull) */).replace('bull', block.bullet).getRegex(); + block.list = edit$1(block.list).replace(/bull/g, block.bullet).replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))').replace('def', '\\n+(?=' + block.def.source + ')').getRegex(); + block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul'; + block._comment = /|$)/; + block.html = edit$1(block.html, 'i').replace('comment', block._comment).replace('tag', block._tag).replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(); + block.paragraph = edit$1(block._paragraph).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs .replace('blockquote', ' {0,3}>').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt - .replace('html', ')|<(?:script|pre|style|textarea|!--)').replace('tag', block$1._tag) // pars can be interrupted by type (6) html blocks + .replace('html', ')|<(?:script|pre|style|textarea|!--)').replace('tag', block._tag) // pars can be interrupted by type (6) html blocks .getRegex(); - block$1.blockquote = edit(block$1.blockquote).replace('paragraph', block$1.paragraph).getRegex(); + block.blockquote = edit$1(block.blockquote).replace('paragraph', block.paragraph).getRegex(); /** * Normal Block Grammar */ - block$1.normal = merge$1({}, block$1); + block.normal = merge$1({}, block); /** * GFM Block Grammar */ - block$1.gfm = merge$1({}, block$1.normal, { + block.gfm = merge$1({}, block.normal, { nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header + ' {0,3}([-:]+ *\\|[-| :]*)' // Align + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', @@ -1219,33 +1241,33 @@ + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells }); - block$1.gfm.nptable = edit(block$1.gfm.nptable).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt - .replace('html', ')|<(?:script|pre|style|textarea|!--)').replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks + block.gfm.nptable = edit$1(block.gfm.nptable).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('html', ')|<(?:script|pre|style|textarea|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks .getRegex(); - block$1.gfm.table = edit(block$1.gfm.table).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt - .replace('html', ')|<(?:script|pre|style|textarea|!--)').replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks + block.gfm.table = edit$1(block.gfm.table).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('html', ')|<(?:script|pre|style|textarea|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks .getRegex(); /** * Pedantic grammar (original John Gruber's loose markdown specification) */ - block$1.pedantic = merge$1({}, block$1.normal, { - html: edit('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag - + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))').replace('comment', block$1._comment).replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b').getRegex(), + block.pedantic = merge$1({}, block.normal, { + html: edit$1('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag + + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))').replace('comment', block._comment).replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b').getRegex(), def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, heading: /^(#{1,6})(.*)(?:\n+|$)/, - fences: noopTest, + fences: noopTest$1, // fences not supported - paragraph: edit(block$1.normal._paragraph).replace('hr', block$1.hr).replace('heading', ' *#{1,6} *[^\n]').replace('lheading', block$1.lheading).replace('blockquote', ' {0,3}>').replace('|fences', '').replace('|list', '').replace('|html', '').getRegex() + paragraph: edit$1(block.normal._paragraph).replace('hr', block.hr).replace('heading', ' *#{1,6} *[^\n]').replace('lheading', block.lheading).replace('blockquote', ' {0,3}>').replace('|fences', '').replace('|list', '').replace('|html', '').getRegex() }); /** * Inline-Level Grammar */ - var inline$1 = { + var inline = { escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, - url: noopTest, + url: noopTest$1, tag: '^comment' + '|^' // self-closing tag + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. @@ -1266,43 +1288,43 @@ }, code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, br: /^( {2,}|\\)\n(?!\s*$)/, - del: noopTest, + del: noopTest$1, text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~'; - inline$1.punctuation = edit(inline$1.punctuation).replace(/punctuation/g, inline$1._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, - - inline$1.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g; - inline$1.escapedEmSt = /\\\*|\\_/g; - inline$1._comment = edit(block$1._comment).replace('(?:-->|$)', '-->').getRegex(); - inline$1.emStrong.lDelim = edit(inline$1.emStrong.lDelim).replace(/punct/g, inline$1._punctuation).getRegex(); - inline$1.emStrong.rDelimAst = edit(inline$1.emStrong.rDelimAst, 'g').replace(/punct/g, inline$1._punctuation).getRegex(); - inline$1.emStrong.rDelimUnd = edit(inline$1.emStrong.rDelimUnd, 'g').replace(/punct/g, inline$1._punctuation).getRegex(); - inline$1._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; - inline$1._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; - inline$1._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])?)+(?![-_])/; - inline$1.autolink = edit(inline$1.autolink).replace('scheme', inline$1._scheme).replace('email', inline$1._email).getRegex(); - inline$1._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; - inline$1.tag = edit(inline$1.tag).replace('comment', inline$1._comment).replace('attribute', inline$1._attribute).getRegex(); - inline$1._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; - inline$1._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/; - inline$1._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; - inline$1.link = edit(inline$1.link).replace('label', inline$1._label).replace('href', inline$1._href).replace('title', inline$1._title).getRegex(); - inline$1.reflink = edit(inline$1.reflink).replace('label', inline$1._label).getRegex(); - inline$1.reflinkSearch = edit(inline$1.reflinkSearch, 'g').replace('reflink', inline$1.reflink).replace('nolink', inline$1.nolink).getRegex(); + inline._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~'; + inline.punctuation = edit$1(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, + + inline.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g; + inline.escapedEmSt = /\\\*|\\_/g; + inline._comment = edit$1(block._comment).replace('(?:-->|$)', '-->').getRegex(); + inline.emStrong.lDelim = edit$1(inline.emStrong.lDelim).replace(/punct/g, inline._punctuation).getRegex(); + inline.emStrong.rDelimAst = edit$1(inline.emStrong.rDelimAst, 'g').replace(/punct/g, inline._punctuation).getRegex(); + inline.emStrong.rDelimUnd = edit$1(inline.emStrong.rDelimUnd, 'g').replace(/punct/g, inline._punctuation).getRegex(); + inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; + inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; + inline._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])?)+(?![-_])/; + inline.autolink = edit$1(inline.autolink).replace('scheme', inline._scheme).replace('email', inline._email).getRegex(); + inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; + inline.tag = edit$1(inline.tag).replace('comment', inline._comment).replace('attribute', inline._attribute).getRegex(); + inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; + inline._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/; + inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; + inline.link = edit$1(inline.link).replace('label', inline._label).replace('href', inline._href).replace('title', inline._title).getRegex(); + inline.reflink = edit$1(inline.reflink).replace('label', inline._label).getRegex(); + inline.reflinkSearch = edit$1(inline.reflinkSearch, 'g').replace('reflink', inline.reflink).replace('nolink', inline.nolink).getRegex(); /** * Normal Inline Grammar */ - inline$1.normal = merge$1({}, inline$1); + inline.normal = merge$1({}, inline); /** * Pedantic Inline Grammar */ - inline$1.pedantic = merge$1({}, inline$1.normal, { + inline.pedantic = merge$1({}, inline.normal, { strong: { start: /^__|\*\*/, middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, @@ -1315,40 +1337,39 @@ endAst: /\*(?!\*)/g, endUnd: /_(?!_)/g }, - link: edit(/^!?\[(label)\]\((.*?)\)/).replace('label', inline$1._label).getRegex(), - reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline$1._label).getRegex() + link: edit$1(/^!?\[(label)\]\((.*?)\)/).replace('label', inline._label).getRegex(), + reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline._label).getRegex() }); /** * GFM Inline Grammar */ - inline$1.gfm = merge$1({}, inline$1.normal, { - escape: edit(inline$1.escape).replace('])', '~|])').getRegex(), + inline.gfm = merge$1({}, inline.normal, { + escape: edit$1(inline.escape).replace('])', '~|])').getRegex(), _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/, url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/, del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/, text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\ 0) { while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) { if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); } } } @@ -1775,7 +1809,7 @@ while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); } // Mask out escaped em & strong delimiters @@ -1964,8 +1998,8 @@ key: "rules", get: function get() { return { - block: block, - inline: inline + block: block$1, + inline: inline$1 }; } }]); @@ -1973,8 +2007,8 @@ return Lexer; }(); - var defaults$2 = defaults$5.exports.defaults; - var cleanUrl = helpers.cleanUrl, + var defaults$3 = defaults.defaults; + var cleanUrl$1 = helpers.cleanUrl, escape$1 = helpers.escape; /** * Renderer @@ -1982,7 +2016,7 @@ var Renderer_1 = /*#__PURE__*/function () { function Renderer(options) { - this.options = options || defaults$2; + this.options = options || defaults$3; } var _proto = Renderer.prototype; @@ -2084,7 +2118,7 @@ }; _proto.link = function link(href, title, text) { - href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); if (href === null) { return text; @@ -2101,7 +2135,7 @@ }; _proto.image = function image(href, title, text) { - href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); if (href === null) { return text; @@ -2128,7 +2162,6 @@ * TextRenderer * returns only the textual part of the token */ - var TextRenderer_1 = /*#__PURE__*/function () { function TextRenderer() {} @@ -2177,7 +2210,6 @@ /** * Slugger generates header id */ - var Slugger_1 = /*#__PURE__*/function () { function Slugger() { this.seen = {}; @@ -2234,23 +2266,20 @@ return Slugger; }(); - var Renderer$1 = Renderer_1; - var TextRenderer$1 = TextRenderer_1; - var Slugger$1 = Slugger_1; - var defaults$1 = defaults$5.exports.defaults; - var unescape = helpers.unescape; + var defaults$4 = defaults.defaults; + var unescape$1 = helpers.unescape; /** * Parsing & Compiling */ var Parser_1 = /*#__PURE__*/function () { function Parser(options) { - this.options = options || defaults$1; - this.options.renderer = this.options.renderer || new Renderer$1(); + this.options = options || defaults$4; + this.options.renderer = this.options.renderer || new Renderer_1(); this.renderer = this.options.renderer; this.renderer.options = this.options; - this.textRenderer = new TextRenderer$1(); - this.slugger = new Slugger$1(); + this.textRenderer = new TextRenderer_1(); + this.slugger = new Slugger_1(); } /** * Static Parse Method @@ -2330,7 +2359,7 @@ case 'heading': { - out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape(this.parseInline(token.tokens, this.textRenderer)), this.slugger); + out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape$1(this.parseInline(token.tokens, this.textRenderer)), this.slugger); continue; } @@ -2402,7 +2431,7 @@ checkbox = this.renderer.checkbox(checked); if (loose) { - if (item.tokens.length > 0 && item.tokens[0].type === 'text') { + if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') { item.tokens[0].text = checkbox + ' ' + item.tokens[0].text; if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') { @@ -2575,18 +2604,12 @@ return Parser; }(); - var Lexer = Lexer_1; - var Parser = Parser_1; - var Tokenizer = Tokenizer_1; - var Renderer = Renderer_1; - var TextRenderer = TextRenderer_1; - var Slugger = Slugger_1; - var merge = helpers.merge, - checkSanitizeDeprecation = helpers.checkSanitizeDeprecation, - escape = helpers.escape; - var getDefaults = defaults$5.exports.getDefaults, - changeDefaults = defaults$5.exports.changeDefaults, - defaults = defaults$5.exports.defaults; + var merge$2 = helpers.merge, + checkSanitizeDeprecation$1 = helpers.checkSanitizeDeprecation, + escape$2 = helpers.escape; + var getDefaults = defaults.getDefaults, + changeDefaults = defaults.changeDefaults, + defaults$5 = defaults.defaults; /** * Marked */ @@ -2606,15 +2629,15 @@ opt = null; } - opt = merge({}, marked.defaults, opt || {}); - checkSanitizeDeprecation(opt); + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); if (callback) { var highlight = opt.highlight; var tokens; try { - tokens = Lexer.lex(src, opt); + tokens = Lexer_1.lex(src, opt); } catch (e) { return callback(e); } @@ -2628,7 +2651,7 @@ marked.walkTokens(tokens, opt.walkTokens); } - out = Parser.parse(tokens, opt); + out = Parser_1.parse(tokens, opt); } catch (e) { err = e; } @@ -2677,18 +2700,18 @@ } try { - var _tokens = Lexer.lex(src, opt); + var _tokens = Lexer_1.lex(src, opt); if (opt.walkTokens) { marked.walkTokens(_tokens, opt.walkTokens); } - return Parser.parse(_tokens, opt); + return Parser_1.parse(_tokens, opt); } catch (e) { e.message += '\nPlease report this to https://github.com/markedjs/marked.'; if (opt.silent) { - return '

    An error occurred:

    ' + escape(e.message + '', true) + '
    '; + return '

    An error occurred:

    ' + escape$2(e.message + '', true) + '
    '; } throw e; @@ -2700,13 +2723,13 @@ marked.options = marked.setOptions = function (opt) { - merge(marked.defaults, opt); + merge$2(marked.defaults, opt); changeDefaults(marked.defaults); return marked; }; marked.getDefaults = getDefaults; - marked.defaults = defaults; + marked.defaults = defaults$5; /** * Use Extension */ @@ -2718,7 +2741,7 @@ args[_key] = arguments[_key]; } - var opts = merge.apply(void 0, [{}].concat(args)); + var opts = merge$2.apply(void 0, [{}].concat(args)); var extensions = marked.defaults.extensions || { renderers: {}, childTokens: {} @@ -2797,7 +2820,7 @@ if (pack.renderer) { (function () { - var renderer = marked.defaults.renderer || new Renderer(); + var renderer = marked.defaults.renderer || new Renderer_1(); var _loop = function _loop(prop) { var prevRenderer = renderer[prop]; // Replace renderer with func to run extension, but fall back if false @@ -2827,7 +2850,7 @@ if (pack.tokenizer) { (function () { - var tokenizer = marked.defaults.tokenizer || new Tokenizer(); + var tokenizer = marked.defaults.tokenizer || new Tokenizer_1(); var _loop2 = function _loop2(prop) { var prevTokenizer = tokenizer[prop]; // Replace tokenizer with func to run extension, but fall back if false @@ -2944,22 +2967,22 @@ throw new Error('marked.parseInline(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected'); } - opt = merge({}, marked.defaults, opt || {}); - checkSanitizeDeprecation(opt); + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); try { - var tokens = Lexer.lexInline(src, opt); + var tokens = Lexer_1.lexInline(src, opt); if (opt.walkTokens) { marked.walkTokens(tokens, opt.walkTokens); } - return Parser.parseInline(tokens, opt); + return Parser_1.parseInline(tokens, opt); } catch (e) { e.message += '\nPlease report this to https://github.com/markedjs/marked.'; if (opt.silent) { - return '

    An error occurred:

    ' + escape(e.message + '', true) + '
    '; + return '

    An error occurred:

    ' + escape$2(e.message + '', true) + '
    '; } throw e; @@ -2970,14 +2993,14 @@ */ - marked.Parser = Parser; - marked.parser = Parser.parse; - marked.Renderer = Renderer; - marked.TextRenderer = TextRenderer; - marked.Lexer = Lexer; - marked.lexer = Lexer.lex; - marked.Tokenizer = Tokenizer; - marked.Slugger = Slugger; + marked.Parser = Parser_1; + marked.parser = Parser_1.parse; + marked.Renderer = Renderer_1; + marked.TextRenderer = TextRenderer_1; + marked.Lexer = Lexer_1; + marked.lexer = Lexer_1.lex; + marked.Tokenizer = Tokenizer_1; + marked.Slugger = Slugger_1; marked.parse = marked; var marked_1 = marked; diff --git a/test/specs/commonmark/commonmark.0.30.json b/test/specs/commonmark/commonmark.0.30.json index 18a49682bd..62a0dfe076 100644 --- a/test/specs/commonmark/commonmark.0.30.json +++ b/test/specs/commonmark/commonmark.0.30.json @@ -2107,8 +2107,7 @@ "example": 262, "start_line": 4314, "end_line": 4326, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "1. foo\n\n ```\n bar\n ```\n\n baz\n\n > bam\n", @@ -2124,8 +2123,7 @@ "example": 264, "start_line": 4359, "end_line": 4377, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "123456789. ok\n", @@ -2197,8 +2195,7 @@ "example": 273, "start_line": 4493, "end_line": 4509, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "1. indented code\n\n paragraph\n\n more code\n", @@ -2206,8 +2203,7 @@ "example": 274, "start_line": 4515, "end_line": 4531, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": " foo\n\nbar\n", @@ -2239,8 +2235,7 @@ "example": 278, "start_line": 4596, "end_line": 4617, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "- \n foo\n", @@ -2256,8 +2251,7 @@ "example": 280, "start_line": 4636, "end_line": 4645, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "- foo\n-\n- bar\n", @@ -2289,8 +2283,7 @@ "example": 284, "start_line": 4695, "end_line": 4701, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "foo\n*\n\nfoo\n1.\n", @@ -2466,8 +2459,7 @@ "example": 306, "start_line": 5388, "end_line": 5407, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- foo\n - bar\n - baz\n\n\n bim\n", @@ -2475,8 +2467,7 @@ "example": 307, "start_line": 5409, "end_line": 5431, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- foo\n- bar\n\n\n\n- baz\n- bim\n", @@ -2556,8 +2547,7 @@ "example": 317, "start_line": 5645, "end_line": 5663, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- a\n- ```\n b\n\n\n ```\n- c\n", @@ -2565,8 +2555,7 @@ "example": 318, "start_line": 5668, "end_line": 5687, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- a\n - b\n\n c\n- d\n", @@ -2574,8 +2563,7 @@ "example": 319, "start_line": 5694, "end_line": 5712, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "* a\n > b\n >\n* c\n", diff --git a/test/specs/gfm/commonmark.0.30.json b/test/specs/gfm/commonmark.0.30.json index 1b01311866..e32558a3a3 100644 --- a/test/specs/gfm/commonmark.0.30.json +++ b/test/specs/gfm/commonmark.0.30.json @@ -2107,8 +2107,7 @@ "example": 262, "start_line": 4314, "end_line": 4326, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "1. foo\n\n ```\n bar\n ```\n\n baz\n\n > bam\n", @@ -2124,8 +2123,7 @@ "example": 264, "start_line": 4359, "end_line": 4377, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "123456789. ok\n", @@ -2197,8 +2195,7 @@ "example": 273, "start_line": 4493, "end_line": 4509, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "1. indented code\n\n paragraph\n\n more code\n", @@ -2206,8 +2203,7 @@ "example": 274, "start_line": 4515, "end_line": 4531, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": " foo\n\nbar\n", @@ -2239,8 +2235,7 @@ "example": 278, "start_line": 4596, "end_line": 4617, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "- \n foo\n", @@ -2256,8 +2251,7 @@ "example": 280, "start_line": 4636, "end_line": 4645, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "- foo\n-\n- bar\n", @@ -2289,8 +2283,7 @@ "example": 284, "start_line": 4695, "end_line": 4701, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "foo\n*\n\nfoo\n1.\n", @@ -2466,8 +2459,7 @@ "example": 306, "start_line": 5388, "end_line": 5407, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- foo\n - bar\n - baz\n\n\n bim\n", @@ -2475,8 +2467,7 @@ "example": 307, "start_line": 5409, "end_line": 5431, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- foo\n- bar\n\n\n\n- baz\n- bim\n", @@ -2556,8 +2547,7 @@ "example": 317, "start_line": 5645, "end_line": 5663, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- a\n- ```\n b\n\n\n ```\n- c\n", @@ -2565,8 +2555,7 @@ "example": 318, "start_line": 5668, "end_line": 5687, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- a\n - b\n\n c\n- d\n", @@ -2574,8 +2563,7 @@ "example": 319, "start_line": 5694, "end_line": 5712, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "* a\n > b\n >\n* c\n", From 7140744b796ada0e63054b943a46bfb47c1f5261 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Fri, 6 Aug 2021 00:22:14 -0400 Subject: [PATCH 17/20] Rebase onto #2124 --- src/Lexer.js | 12 +- src/Parser.js | 2 +- src/Tokenizer.js | 214 +++++++++++---------- src/rules.js | 9 +- test/specs/commonmark/commonmark.0.30.json | 36 ++-- test/specs/gfm/commonmark.0.30.json | 36 ++-- test/specs/new/def_blocks.html | 15 +- test/specs/new/list_paren_delimiter.html | 18 +- test/specs/new/list_paren_delimiter.md | 1 + test/specs/new/main.html | 4 - test/specs/new/main.md | 55 ------ test/unit/Lexer-spec.js | 29 +-- test/unit/marked-spec.js | 2 + 13 files changed, 180 insertions(+), 253 deletions(-) delete mode 100644 test/specs/new/main.html delete mode 100644 test/specs/new/main.md diff --git a/src/Lexer.js b/src/Lexer.js index c1d8b03cb4..d34aa47bc0 100644 --- a/src/Lexer.js +++ b/src/Lexer.js @@ -163,10 +163,9 @@ module.exports = class Lexer { src = src.substring(token.raw.length); lastToken = tokens[tokens.length - 1]; // An indented code block cannot interrupt a paragraph. - if (lastToken && lastToken.type === 'paragraph') { + if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) { lastToken.raw += '\n' + token.raw; lastToken.text += '\n' + token.text; - this.inlineQueue.pop(); this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text; } else { tokens.push(token); @@ -217,9 +216,14 @@ module.exports = class Lexer { } // def - if (this.state.top && (token = this.tokenizer.def(src))) { + if (token = this.tokenizer.def(src)) { src = src.substring(token.raw.length); - if (!this.tokens.links[token.tag]) { + lastToken = tokens[tokens.length - 1]; + if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) { + lastToken.raw += '\n' + token.raw; + lastToken.text += '\n' + token.raw; + this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text; + } else if (!this.tokens.links[token.tag]) { this.tokens.links[token.tag] = { href: token.href, title: token.title diff --git a/src/Parser.js b/src/Parser.js index e804c143c4..ef5009aea8 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -149,7 +149,7 @@ module.exports = class Parser { if (item.task) { checkbox = this.renderer.checkbox(checked); if (loose) { - if (item.tokens.length > 0 && item.tokens[0].type === 'text') { + if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') { item.tokens[0].text = checkbox + ' ' + item.tokens[0].text; if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') { item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text; diff --git a/src/Tokenizer.js b/src/Tokenizer.js index 922452e7be..85bdb33675 100644 --- a/src/Tokenizer.js +++ b/src/Tokenizer.js @@ -164,145 +164,149 @@ module.exports = class Tokenizer { } list(src) { - const cap = this.rules.block.list.exec(src); + let cap = this.rules.block.list.exec(src); if (cap) { - let raw = cap[0]; - const bull = cap[2]; + let raw, istask, ischecked, indent, i, blankLine, endsWithBlankLine, + line, lines, itemContents; + + let bull = cap[1].trim(); const isordered = bull.length > 1; const list = { type: 'list', - raw, + raw: '', ordered: isordered, start: isordered ? +bull.slice(0, -1) : '', loose: false, items: [] }; - // Get each top-level item. - const itemMatch = cap[0].match(this.rules.block.item); - - let next = false, - item, - space, - bcurr, - bnext, - addBack, - loose, - istask, - ischecked, - endMatch; - - let l = itemMatch.length; - bcurr = this.rules.block.listItemStart.exec(itemMatch[0]); - for (let i = 0; i < l; i++) { - item = itemMatch[i]; - raw = item; - - if (!this.options.pedantic) { - // Determine if current item contains the end of the list - endMatch = item.match(new RegExp('\\n\\s*\\n {0,' + (bcurr[0].length - 1) + '}\\S')); - if (endMatch) { - addBack = item.length - endMatch.index + itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - - item = item.substring(0, endMatch.index); - raw = item; - l = i + 1; - } + bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`; + + if (this.options.pedantic) { + bull = isordered ? bull : '[*+-]'; + } + + // Get next list item + const itemRegex = new RegExp(`^( {0,3}${bull})((?: [^\\n]*| *)(?:\\n[^\\n]*)*(?:\\n|$))`); + + // Get each top-level item + while (src) { + if (this.rules.block.hr.test(src)) { // End list if we encounter an HR (possibly move into itemRegex?) + break; } - // Determine whether the next list item belongs here. - // Backpedal if it does not belong in this list. - if (i !== l - 1) { - bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]); - if ( - !this.options.pedantic - ? bnext[1].length >= bcurr[0].length || bnext[1].length > 3 - : bnext[1].length > bcurr[1].length - ) { - // nested list or continuation - itemMatch.splice(i, 2, itemMatch[i] + (!this.options.pedantic && bnext[1].length < bcurr[0].length && !itemMatch[i].match(/\n$/) ? '' : '\n') + itemMatch[i + 1]); - i--; - l--; - continue; - } else if ( - // different bullet style - !this.options.pedantic || this.options.smartLists - ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] - : isordered === (bnext[2].length === 1) - ) { - addBack = itemMatch.slice(i + 1).join('\n').length; - list.raw = list.raw.substring(0, list.raw.length - addBack); - i = l - 1; - } - bcurr = bnext; + if (!(cap = itemRegex.exec(src))) { + break; } - // Remove the list item's bullet - // so it is seen as the next token. - space = item.length; - item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); - - // Outdent whatever the - // list item contains. Hacky. - if (~item.indexOf('\n ')) { - space -= item.length; - item = !this.options.pedantic - ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') - : item.replace(/^ {1,4}/gm, ''); + lines = cap[2].split('\n'); + + if (this.options.pedantic) { + indent = 2; + itemContents = lines[0].trimLeft(); + } else { + indent = cap[2].search(/[^ ]/); // Find first non-space char + indent = cap[1].length + (indent > 4 ? 1 : indent); // intented code blocks after 4 spaces; indent is always 1 + itemContents = lines[0].slice(indent - cap[1].length); } - // trim item newlines at end - item = rtrim(item, '\n'); - if (i !== l - 1) { - raw = raw + '\n'; + blankLine = false; + raw = cap[0]; + + if (!lines[0] && /^ *$/.test(lines[1])) { // items begin with at most one blank line + raw = cap[1] + lines.slice(0, 2).join('\n') + '\n'; + list.loose = true; + lines = []; } - // Determine whether item is loose or not. - // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ - // for discount behavior. - loose = next || /\n\n(?!\s*$)/.test(raw); - if (i !== l - 1) { - next = raw.slice(-2) === '\n\n'; - if (!loose) loose = next; + const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])`); + + for (i = 1; i < lines.length; i++) { + line = lines[i]; + + if (this.options.pedantic) { // Re-align to follow commonmark nesting rules + line = line.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' '); + } + + // End list item if found start of new bullet + if (nextBulletRegex.test(line)) { + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; + break; + } + + // Until we encounter a blank line, item contents do not need indentation + if (!blankLine) { + if (!line.trim()) { // Check if current line is empty + blankLine = true; + } + + // Dedent if possible + if (line.search(/[^ ]/) >= indent) { + itemContents += '\n' + line.slice(indent); + } else { + itemContents += '\n' + line; + } + continue; + } + + // Dedent this line + if (line.search(/[^ ]/) >= indent || !line.trim()) { + itemContents += '\n' + line.slice(indent); + continue; + } else { // Line was not properly indented; end of this item + raw = cap[1] + lines.slice(0, i).join('\n') + '\n'; + break; + } } - if (loose) { - list.loose = true; + if (!list.loose) { + // If the previous item ended with a blank line, the list is loose + if (endsWithBlankLine) { + list.loose = true; + } else if (/\n *\n *$/.test(raw)) { + endsWithBlankLine = true; + } } // Check for task list items if (this.options.gfm) { - istask = /^\[[ xX]\] /.test(item); - ischecked = undefined; + istask = /^\[[ xX]\] /.exec(itemContents); if (istask) { - ischecked = item[1] !== ' '; - item = item.replace(/^\[[ xX]\] +/, ''); + ischecked = istask[0] !== '[ ] '; + itemContents = itemContents.replace(/^\[[ xX]\] +/, ''); } } - this.lexer.state.top = false; - - const token = { + list.items.push({ type: 'list_item', - raw, - task: istask, + raw: raw, + task: !!istask, checked: ischecked, - loose: loose, - text: item, - tokens: this.lexer.blockTokens(item, []) - }; + loose: false, + text: itemContents + }); - // this.lexer.inline(token.text, ) - list.items.push(token); + list.raw += raw; + src = src.slice(raw.length); } - // l2 = token.items.length; - // for (j = 0; j < l2; j++) { - // this.inline(token.items[j].tokens); - // } - // break; + // Do not consume newlines at end of final item. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic + list.items[list.items.length - 1].raw = raw.trimRight(); + list.items[list.items.length - 1].text = itemContents.trimRight(); + list.raw = list.raw.trimRight(); + + const l = list.items.length; + + // Item child tokens handled here at end because we needed to have the final item to trim it first + for (i = 0; i < l; i++) { + this.lexer.state.top = false; + list.items[i].tokens = this.lexer.blockTokens(list.items[i].text, []); + if (list.items[i].tokens.some(t => t.type === 'space')) { + list.loose = true; + list.items[i].loose = true; + } + } return list; } diff --git a/src/rules.js b/src/rules.js index 13961f2251..39fa8e56c4 100644 --- a/src/rules.js +++ b/src/rules.js @@ -10,11 +10,11 @@ const { const block = { newline: /^(?: *(?:\n|$))+/, code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/, - fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, + fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?=\n|$)|$)/, hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/, + list: /^( {0,3}bull)( [^\n]+?)?(?:\n|$)/, html: '^ {0,3}(?:' // optional indentation + '<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + '|comment[^\\n]*(\\n+|$)' // (2) @@ -42,11 +42,6 @@ block.def = edit(block.def) .getRegex(); block.bullet = /(?:[*+-]|\d{1,9}[.)])/; -block.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/; -block.item = edit(block.item, 'gm') - .replace(/bull/g, block.bullet) - .getRegex(); - block.listItemStart = edit(/^( *)(bull) */) .replace('bull', block.bullet) .getRegex(); diff --git a/test/specs/commonmark/commonmark.0.30.json b/test/specs/commonmark/commonmark.0.30.json index 18a49682bd..62a0dfe076 100644 --- a/test/specs/commonmark/commonmark.0.30.json +++ b/test/specs/commonmark/commonmark.0.30.json @@ -2107,8 +2107,7 @@ "example": 262, "start_line": 4314, "end_line": 4326, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "1. foo\n\n ```\n bar\n ```\n\n baz\n\n > bam\n", @@ -2124,8 +2123,7 @@ "example": 264, "start_line": 4359, "end_line": 4377, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "123456789. ok\n", @@ -2197,8 +2195,7 @@ "example": 273, "start_line": 4493, "end_line": 4509, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "1. indented code\n\n paragraph\n\n more code\n", @@ -2206,8 +2203,7 @@ "example": 274, "start_line": 4515, "end_line": 4531, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": " foo\n\nbar\n", @@ -2239,8 +2235,7 @@ "example": 278, "start_line": 4596, "end_line": 4617, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "- \n foo\n", @@ -2256,8 +2251,7 @@ "example": 280, "start_line": 4636, "end_line": 4645, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "- foo\n-\n- bar\n", @@ -2289,8 +2283,7 @@ "example": 284, "start_line": 4695, "end_line": 4701, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "foo\n*\n\nfoo\n1.\n", @@ -2466,8 +2459,7 @@ "example": 306, "start_line": 5388, "end_line": 5407, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- foo\n - bar\n - baz\n\n\n bim\n", @@ -2475,8 +2467,7 @@ "example": 307, "start_line": 5409, "end_line": 5431, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- foo\n- bar\n\n\n\n- baz\n- bim\n", @@ -2556,8 +2547,7 @@ "example": 317, "start_line": 5645, "end_line": 5663, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- a\n- ```\n b\n\n\n ```\n- c\n", @@ -2565,8 +2555,7 @@ "example": 318, "start_line": 5668, "end_line": 5687, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- a\n - b\n\n c\n- d\n", @@ -2574,8 +2563,7 @@ "example": 319, "start_line": 5694, "end_line": 5712, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "* a\n > b\n >\n* c\n", diff --git a/test/specs/gfm/commonmark.0.30.json b/test/specs/gfm/commonmark.0.30.json index 1b01311866..e32558a3a3 100644 --- a/test/specs/gfm/commonmark.0.30.json +++ b/test/specs/gfm/commonmark.0.30.json @@ -2107,8 +2107,7 @@ "example": 262, "start_line": 4314, "end_line": 4326, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "1. foo\n\n ```\n bar\n ```\n\n baz\n\n > bam\n", @@ -2124,8 +2123,7 @@ "example": 264, "start_line": 4359, "end_line": 4377, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "123456789. ok\n", @@ -2197,8 +2195,7 @@ "example": 273, "start_line": 4493, "end_line": 4509, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "1. indented code\n\n paragraph\n\n more code\n", @@ -2206,8 +2203,7 @@ "example": 274, "start_line": 4515, "end_line": 4531, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": " foo\n\nbar\n", @@ -2239,8 +2235,7 @@ "example": 278, "start_line": 4596, "end_line": 4617, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "- \n foo\n", @@ -2256,8 +2251,7 @@ "example": 280, "start_line": 4636, "end_line": 4645, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "- foo\n-\n- bar\n", @@ -2289,8 +2283,7 @@ "example": 284, "start_line": 4695, "end_line": 4701, - "section": "List items", - "shouldFail": true + "section": "List items" }, { "markdown": "foo\n*\n\nfoo\n1.\n", @@ -2466,8 +2459,7 @@ "example": 306, "start_line": 5388, "end_line": 5407, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- foo\n - bar\n - baz\n\n\n bim\n", @@ -2475,8 +2467,7 @@ "example": 307, "start_line": 5409, "end_line": 5431, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- foo\n- bar\n\n\n\n- baz\n- bim\n", @@ -2556,8 +2547,7 @@ "example": 317, "start_line": 5645, "end_line": 5663, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- a\n- ```\n b\n\n\n ```\n- c\n", @@ -2565,8 +2555,7 @@ "example": 318, "start_line": 5668, "end_line": 5687, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "- a\n - b\n\n c\n- d\n", @@ -2574,8 +2563,7 @@ "example": 319, "start_line": 5694, "end_line": 5712, - "section": "Lists", - "shouldFail": true + "section": "Lists" }, { "markdown": "* a\n > b\n >\n* c\n", diff --git a/test/specs/new/def_blocks.html b/test/specs/new/def_blocks.html index 5d8de49c04..23207da91e 100644 --- a/test/specs/new/def_blocks.html +++ b/test/specs/new/def_blocks.html @@ -12,13 +12,14 @@
      -
    • hello
    • -
    • [3]: hello
    • -
    - - -
      -
    • hello
    • +
    • +

      hello

      +
    • +
    • +
    • +

      hello +[4]: hello

      +
    diff --git a/test/specs/new/list_paren_delimiter.html b/test/specs/new/list_paren_delimiter.html index 4999ee3efa..ba8ecf36dd 100644 --- a/test/specs/new/list_paren_delimiter.html +++ b/test/specs/new/list_paren_delimiter.html @@ -1,11 +1,13 @@
      -
    1. one
    2. -
    3. two
    4. -
    5. three
    6. +
    7. one
    8. +
    9. two
    10. +
    11. three
    - + +
    +
      -
    1. two
    2. -
    3. three
    4. -
    5. four
    6. -
    \ No newline at end of file +
  • two
  • +
  • three
  • +
  • four
  • + diff --git a/test/specs/new/list_paren_delimiter.md b/test/specs/new/list_paren_delimiter.md index d7ff7f3aeb..2443a64a85 100644 --- a/test/specs/new/list_paren_delimiter.md +++ b/test/specs/new/list_paren_delimiter.md @@ -2,6 +2,7 @@ 2) two 3) three +*** 2) two 3) three diff --git a/test/specs/new/main.html b/test/specs/new/main.html deleted file mode 100644 index 8f78c4d30e..0000000000 --- a/test/specs/new/main.html +++ /dev/null @@ -1,4 +0,0 @@ -

    A heading

    Just a note, I've found that I can't test my markdown parser vs others. For example, both markdown.js and showdown code blocks in lists wrong. They're also completely inconsistent with regards to paragraphs in list items.

    A link. Not anymore.

    • List Item 1

    • List Item 2

      • New List Item 1 Hi, this is a list item.
      • New List Item 2 Another item
        Code goes here.
        -Lots of it...
      • New List Item 3 The last item
    • List Item 3 The final item.

    • List Item 4 The real final item.

    Paragraph.

    • bq Item 1
    • bq Item 2
      • New bq Item 1
      • New bq Item 2 Text here

    Another blockquote! I really need to get more creative with mockup text.. markdown.js breaks here again

    Another Heading

    Hello world. Here is a link. And an image alt.

    Code goes here.
    -Lots of it...
    diff --git a/test/specs/new/main.md b/test/specs/new/main.md deleted file mode 100644 index 58e17a6a76..0000000000 --- a/test/specs/new/main.md +++ /dev/null @@ -1,55 +0,0 @@ -[test]: http://google.com/ "Google" - -# A heading - -Just a note, I've found that I can't test my markdown parser vs others. -For example, both markdown.js and showdown code blocks in lists wrong. They're -also completely [inconsistent][test] with regards to paragraphs in list items. - -A link. Not anymore. - - - -* List Item 1 - -* List Item 2 - * New List Item 1 - Hi, this is a list item. - * New List Item 2 - Another item - Code goes here. - Lots of it... - * New List Item 3 - The last item - -* List Item 3 -The final item. - -* List Item 4 -The real final item. - -Paragraph. - -> * bq Item 1 -> * bq Item 2 -> * New bq Item 1 -> * New bq Item 2 -> Text here - -* * * - -> Another blockquote! -> I really need to get -> more creative with -> mockup text.. -> markdown.js breaks here again - -Another Heading -------------- - -Hello *world*. Here is a [link](//hello). -And an image ![alt](src). - - Code goes here. - Lots of it... diff --git a/test/unit/Lexer-spec.js b/test/unit/Lexer-spec.js index f94cd6e70d..af3f5781b1 100644 --- a/test/unit/Lexer-spec.js +++ b/test/unit/Lexer-spec.js @@ -295,7 +295,7 @@ a | b tokens: [ { type: 'list', - raw: '- item 1\n- item 2\n', + raw: '- item 1\n- item 2', ordered: false, start: '', loose: false, @@ -316,7 +316,7 @@ a | b }, { type: 'list_item', - raw: '- item 2\n', + raw: '- item 2', task: false, checked: undefined, loose: false, @@ -343,7 +343,7 @@ a | b tokens: jasmine.arrayContaining([ jasmine.objectContaining({ type: 'list', - raw: '1. item 1\n2. item 2\n', + raw: '1. item 1\n2. item 2', ordered: true, start: 1, items: [ @@ -351,7 +351,7 @@ a | b raw: '1. item 1\n' }), jasmine.objectContaining({ - raw: '2. item 2\n' + raw: '2. item 2' }) ] }) @@ -368,7 +368,7 @@ a | b tokens: jasmine.arrayContaining([ jasmine.objectContaining({ type: 'list', - raw: '1) item 1\n2) item 2\n', + raw: '1) item 1\n2) item 2', ordered: true, start: 1, items: [ @@ -376,7 +376,7 @@ a | b raw: '1) item 1\n' }), jasmine.objectContaining({ - raw: '2) item 2\n' + raw: '2) item 2' }) ] }) @@ -395,7 +395,7 @@ paragraph tokens: [ { type: 'list', - raw: '- item 1\n- item 2\n\n', + raw: '- item 1\n- item 2', ordered: false, start: '', loose: false, @@ -416,7 +416,7 @@ paragraph }, { type: 'list_item', - raw: '- item 2\n\n', + raw: '- item 2', task: false, checked: undefined, loose: false, @@ -430,6 +430,7 @@ paragraph } ] }, + { type: 'space', raw: '\n\n' }, { type: 'paragraph', raw: 'paragraph', @@ -453,7 +454,7 @@ paragraph tokens: jasmine.arrayContaining([ jasmine.objectContaining({ type: 'list', - raw: '2. item 1\n3. item 2\n', + raw: '2. item 1\n3. item 2', ordered: true, start: 2, items: [ @@ -461,7 +462,7 @@ paragraph raw: '2. item 1\n' }), jasmine.objectContaining({ - raw: '3. item 2\n' + raw: '3. item 2' }) ] }) @@ -479,14 +480,14 @@ paragraph tokens: jasmine.arrayContaining([ jasmine.objectContaining({ type: 'list', - raw: '- item 1\n\n- item 2\n', + raw: '- item 1\n\n- item 2', loose: true, items: [ jasmine.objectContaining({ raw: '- item 1\n\n' }), jasmine.objectContaining({ - raw: '- item 2\n' + raw: '- item 2' }) ] }) @@ -503,7 +504,7 @@ paragraph tokens: jasmine.arrayContaining([ jasmine.objectContaining({ type: 'list', - raw: '- [ ] item 1\n- [x] item 2\n', + raw: '- [ ] item 1\n- [x] item 2', items: [ jasmine.objectContaining({ raw: '- [ ] item 1\n', @@ -511,7 +512,7 @@ paragraph checked: false }), jasmine.objectContaining({ - raw: '- [x] item 2\n', + raw: '- [x] item 2', task: true, checked: true }) diff --git a/test/unit/marked-spec.js b/test/unit/marked-spec.js index 6b67a8fe61..79fa151520 100644 --- a/test/unit/marked-spec.js +++ b/test/unit/marked-spec.js @@ -1001,6 +1001,7 @@ br ['heading', '# heading'], ['text', 'heading'], ['code', '```code```'], + ['space', ''], ['table', '| a | b ||---|---|| 1 | 2 || 3 | 4 |'], ['text', 'a'], ['text', 'b'], @@ -1015,6 +1016,7 @@ br ['list_item', '- list'], ['text', 'list'], ['text', 'list'], + ['space', ''], ['html', '
    html
    '], ['paragraph', '[link](https://example.com)'], ['link', '[link](https://example.com)'], From 7d31421058557fe2d10772c5ac03338741e28bac Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Fri, 6 Aug 2021 00:24:21 -0400 Subject: [PATCH 18/20] Finish rebase --- src/Lexer.js | 148 +++++-------------- src/Tokenizer.js | 173 +++++++++++++---------- test/specs/new/list_paren_delimiter.html | 12 +- 3 files changed, 142 insertions(+), 191 deletions(-) diff --git a/src/Lexer.js b/src/Lexer.js index acfcb3f2d7..d34aa47bc0 100644 --- a/src/Lexer.js +++ b/src/Lexer.js @@ -55,6 +55,13 @@ module.exports = class Lexer { this.options.tokenizer = this.options.tokenizer || new Tokenizer(); this.tokenizer = this.options.tokenizer; this.tokenizer.options = this.options; + this.tokenizer.lexer = this; + this.inlineQueue = []; + this.state = { + inLink: false, + inRawBlock: false, + top: true + }; const rules = { block: block.normal, @@ -109,9 +116,12 @@ module.exports = class Lexer { .replace(/\r\n|\r/g, '\n') .replace(/\t/g, ' '); - this.blockTokens(src, this.tokens, true); + this.blockTokens(src, this.tokens); - this.inline(this.tokens); + let next; + while (next = this.inlineQueue.shift()) { + this.inlineTokens(next.src, next.tokens); + } return this.tokens; } @@ -119,17 +129,17 @@ module.exports = class Lexer { /** * Lexing */ - blockTokens(src, tokens = [], top = true) { + blockTokens(src, tokens = []) { if (this.options.pedantic) { src = src.replace(/^ +$/gm, ''); } - let token, i, l, lastToken, cutSrc, lastParagraphClipped; + let token, lastToken, cutSrc, lastParagraphClipped; while (src) { if (this.options.extensions && this.options.extensions.block && this.options.extensions.block.some((extTokenizer) => { - if (token = extTokenizer.call(this, src, tokens)) { + if (token = extTokenizer.call({ lexer: this }, src, tokens)) { src = src.substring(token.raw.length); tokens.push(token); return true; @@ -156,6 +166,7 @@ module.exports = class Lexer { if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) { lastToken.raw += '\n' + token.raw; lastToken.text += '\n' + token.text; + this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text; } else { tokens.push(token); } @@ -176,13 +187,6 @@ module.exports = class Lexer { continue; } - // table no leading pipe (gfm) - if (token = this.tokenizer.nptable(src)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - // hr if (token = this.tokenizer.hr(src)) { src = src.substring(token.raw.length); @@ -193,7 +197,6 @@ module.exports = class Lexer { // blockquote if (token = this.tokenizer.blockquote(src)) { src = src.substring(token.raw.length); - token.tokens = this.blockTokens(token.text, [], top); tokens.push(token); continue; } @@ -201,14 +204,6 @@ module.exports = class Lexer { // list if (token = this.tokenizer.list(src)) { src = src.substring(token.raw.length); - l = token.items.length; - for (i = 0; i < l; i++) { - token.items[i].tokens = this.blockTokens(token.items[i].text, [], false); - if (token.items[i].tokens.some(t => t.type === 'space')) { - token.loose = true; - token.items[i].loose = true; - } - } tokens.push(token); continue; } @@ -227,13 +222,12 @@ module.exports = class Lexer { if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) { lastToken.raw += '\n' + token.raw; lastToken.text += '\n' + token.raw; - } else { - if (!this.tokens.links[token.tag]) { - this.tokens.links[token.tag] = { - href: token.href, - title: token.title - }; - } + this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text; + } else if (!this.tokens.links[token.tag]) { + this.tokens.links[token.tag] = { + href: token.href, + title: token.title + }; } continue; } @@ -260,18 +254,20 @@ module.exports = class Lexer { const tempSrc = src.slice(1); let tempStart; this.options.extensions.startBlock.forEach(function(getStartIndex) { - tempStart = getStartIndex.call(this, tempSrc); + tempStart = getStartIndex.call({ lexer: this }, tempSrc); if (typeof tempStart === 'number' && tempStart >= 0) { startIndex = Math.min(startIndex, tempStart); } }); if (startIndex < Infinity && startIndex >= 0) { cutSrc = src.substring(0, startIndex + 1); } } - if (top && (token = this.tokenizer.paragraph(cutSrc))) { + if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) { lastToken = tokens[tokens.length - 1]; if (lastParagraphClipped && lastToken.type === 'paragraph') { lastToken.raw += '\n' + token.raw; lastToken.text += '\n' + token.text; + this.inlineQueue.pop(); + this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text; } else { tokens.push(token); } @@ -287,6 +283,8 @@ module.exports = class Lexer { if (lastToken && lastToken.type === 'text') { lastToken.raw += '\n' + token.raw; lastToken.text += '\n' + token.text; + this.inlineQueue.pop(); + this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text; } else { tokens.push(token); } @@ -304,78 +302,18 @@ module.exports = class Lexer { } } + this.state.top = true; return tokens; } - inline(tokens) { - let i, - j, - k, - l2, - row, - token; - - const l = tokens.length; - for (i = 0; i < l; i++) { - token = tokens[i]; - switch (token.type) { - case 'paragraph': - case 'text': - case 'heading': { - token.tokens = []; - this.inlineTokens(token.text, token.tokens); - break; - } - case 'table': { - token.tokens = { - header: [], - cells: [] - }; - - // header - l2 = token.header.length; - for (j = 0; j < l2; j++) { - token.tokens.header[j] = []; - this.inlineTokens(token.header[j], token.tokens.header[j]); - } - - // cells - l2 = token.cells.length; - for (j = 0; j < l2; j++) { - row = token.cells[j]; - token.tokens.cells[j] = []; - for (k = 0; k < row.length; k++) { - token.tokens.cells[j][k] = []; - this.inlineTokens(row[k], token.tokens.cells[j][k]); - } - } - - break; - } - case 'blockquote': { - this.inline(token.tokens); - break; - } - case 'list': { - l2 = token.items.length; - for (j = 0; j < l2; j++) { - this.inline(token.items[j].tokens); - } - break; - } - default: { - // do nothing - } - } - } - - return tokens; + inline(src, tokens) { + this.inlineQueue.push({ src, tokens }); } /** * Lexing/Compiling */ - inlineTokens(src, tokens = [], inLink = false, inRawBlock = false) { + inlineTokens(src, tokens = []) { let token, lastToken, cutSrc; // String with links masked to avoid interference with em and strong @@ -414,7 +352,7 @@ module.exports = class Lexer { if (this.options.extensions && this.options.extensions.inline && this.options.extensions.inline.some((extTokenizer) => { - if (token = extTokenizer.call(this, src, tokens)) { + if (token = extTokenizer.call({ lexer: this }, src, tokens)) { src = src.substring(token.raw.length); tokens.push(token); return true; @@ -432,10 +370,8 @@ module.exports = class Lexer { } // tag - if (token = this.tokenizer.tag(src, inLink, inRawBlock)) { + if (token = this.tokenizer.tag(src)) { src = src.substring(token.raw.length); - inLink = token.inLink; - inRawBlock = token.inRawBlock; lastToken = tokens[tokens.length - 1]; if (lastToken && token.type === 'text' && lastToken.type === 'text') { lastToken.raw += token.raw; @@ -449,9 +385,6 @@ module.exports = class Lexer { // link if (token = this.tokenizer.link(src)) { src = src.substring(token.raw.length); - if (token.type === 'link') { - token.tokens = this.inlineTokens(token.text, [], true, inRawBlock); - } tokens.push(token); continue; } @@ -460,10 +393,7 @@ module.exports = class Lexer { if (token = this.tokenizer.reflink(src, this.tokens.links)) { src = src.substring(token.raw.length); lastToken = tokens[tokens.length - 1]; - if (token.type === 'link') { - token.tokens = this.inlineTokens(token.text, [], true, inRawBlock); - tokens.push(token); - } else if (lastToken && token.type === 'text' && lastToken.type === 'text') { + if (lastToken && token.type === 'text' && lastToken.type === 'text') { lastToken.raw += token.raw; lastToken.text += token.text; } else { @@ -475,7 +405,6 @@ module.exports = class Lexer { // em & strong if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) { src = src.substring(token.raw.length); - token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); tokens.push(token); continue; } @@ -497,7 +426,6 @@ module.exports = class Lexer { // del (gfm) if (token = this.tokenizer.del(src)) { src = src.substring(token.raw.length); - token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); tokens.push(token); continue; } @@ -510,7 +438,7 @@ module.exports = class Lexer { } // url (gfm) - if (!inLink && (token = this.tokenizer.url(src, mangle))) { + if (!this.state.inLink && (token = this.tokenizer.url(src, mangle))) { src = src.substring(token.raw.length); tokens.push(token); continue; @@ -524,14 +452,14 @@ module.exports = class Lexer { const tempSrc = src.slice(1); let tempStart; this.options.extensions.startInline.forEach(function(getStartIndex) { - tempStart = getStartIndex.call(this, tempSrc); + tempStart = getStartIndex.call({ lexer: this }, tempSrc); if (typeof tempStart === 'number' && tempStart >= 0) { startIndex = Math.min(startIndex, tempStart); } }); if (startIndex < Infinity && startIndex >= 0) { cutSrc = src.substring(0, startIndex + 1); } } - if (token = this.tokenizer.inlineText(cutSrc, inRawBlock, smartypants)) { + if (token = this.tokenizer.inlineText(cutSrc, smartypants)) { src = src.substring(token.raw.length); if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started prevChar = token.raw.slice(-1); diff --git a/src/Tokenizer.js b/src/Tokenizer.js index 2a7cf83fa1..53135aa62a 100644 --- a/src/Tokenizer.js +++ b/src/Tokenizer.js @@ -6,18 +6,20 @@ const { findClosingBracket } = require('./helpers.js'); -function outputLink(cap, link, raw) { +function outputLink(cap, link, raw, lexer) { const href = link.href; const title = link.title ? escape(link.title) : null; const text = cap[1].replace(/\\([\[\]])/g, '$1'); if (cap[0].charAt(0) !== '!') { + lexer.state.inLink = true; return { type: 'link', raw, href, title, - text + text, + tokens: lexer.inlineTokens(text, []) }; } else { return { @@ -125,48 +127,15 @@ module.exports = class Tokenizer { } } - return { + const token = { type: 'heading', raw: cap[0], depth: cap[1].length, - text: text - }; - } - } - - nptable(src) { - const cap = this.rules.block.nptable.exec(src); - if (cap) { - const item = { - type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), - align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [], - raw: cap[0] + text: text, + tokens: [] }; - - if (item.header.length === item.align.length) { - let l = item.align.length; - let i; - for (i = 0; i < l; i++) { - if (/^ *-+: *$/.test(item.align[i])) { - item.align[i] = 'right'; - } else if (/^ *:-+: *$/.test(item.align[i])) { - item.align[i] = 'center'; - } else if (/^ *:-+ *$/.test(item.align[i])) { - item.align[i] = 'left'; - } else { - item.align[i] = null; - } - } - - l = item.cells.length; - for (i = 0; i < l; i++) { - item.cells[i] = splitCells(item.cells[i], item.header.length); - } - - return item; - } + this.lexer.inline(token.text, token.tokens); + return token; } } @@ -188,6 +157,7 @@ module.exports = class Tokenizer { return { type: 'blockquote', raw: cap[0], + tokens: this.lexer.blockTokens(text, []), text }; } @@ -196,7 +166,7 @@ module.exports = class Tokenizer { list(src) { let cap = this.rules.block.list.exec(src); if (cap) { - let raw, istask, ischecked, indent, i, blankLine, endsWithBlankLine, + let raw, istask, ischecked, indent, i, l, blankLine, endsWithBlankLine, line, lines, itemContents; let bull = cap[1].trim(); @@ -321,11 +291,23 @@ module.exports = class Tokenizer { src = src.slice(raw.length); } - // Do not consume ending newlines. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic + // Do not consume newlines at end of final item. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic list.items[list.items.length - 1].raw = raw.trimRight(); list.items[list.items.length - 1].text = itemContents.trimRight(); list.raw = list.raw.trimRight(); + l = list.items.length; + + // Item child tokens handled here at end because we needed to have the final item to trim it first + for (i = 0; i < l; i++) { + this.lexer.state.top = false; + list.items[i].tokens = this.lexer.blockTokens(list.items[i].text, []); + if (list.items[i].tokens.some(t => t.type === 'space')) { + list.loose = true; + list.items[i].loose = true; + } + } + return list; } } @@ -333,15 +315,20 @@ module.exports = class Tokenizer { html(src) { const cap = this.rules.block.html.exec(src); if (cap) { - return { - type: this.options.sanitize - ? 'paragraph' - : 'html', + const token = { + type: 'html', raw: cap[0], pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), - text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0] + text: cap[0] }; + if (this.options.sanitize) { + token.type = 'paragraph'; + token.text = this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0]); + token.tokens = []; + this.lexer.inline(token.text, token.tokens); + } + return token; } } @@ -374,7 +361,7 @@ module.exports = class Tokenizer { item.raw = cap[0]; let l = item.align.length; - let i; + let i, j, k, row; for (i = 0; i < l; i++) { if (/^ *-+: *$/.test(item.align[i])) { item.align[i] = 'right'; @@ -389,9 +376,31 @@ module.exports = class Tokenizer { l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells( - item.cells[i].replace(/^ *\| *| *\| *$/g, ''), - item.header.length); + item.cells[i] = splitCells(item.cells[i], item.header.length); + } + + // parse child tokens inside headers and cells + item.tokens = { + header: [], + cells: [] + }; + + // header child tokens + l = item.header.length; + for (j = 0; j < l; j++) { + item.tokens.header[j] = []; + this.lexer.inlineTokens(item.header[j], item.tokens.header[j]); + } + + // cell child tokens + l = item.cells.length; + for (j = 0; j < l; j++) { + row = item.cells[j]; + item.tokens.cells[j] = []; + for (k = 0; k < row.length; k++) { + item.tokens.cells[j][k] = []; + this.lexer.inlineTokens(row[k], item.tokens.cells[j][k]); + } } return item; @@ -402,36 +411,45 @@ module.exports = class Tokenizer { lheading(src) { const cap = this.rules.block.lheading.exec(src); if (cap) { - return { + const token = { type: 'heading', raw: cap[0], depth: cap[2].charAt(0) === '=' ? 1 : 2, - text: cap[1] + text: cap[1], + tokens: [] }; + this.lexer.inline(token.text, token.tokens); + return token; } } paragraph(src) { const cap = this.rules.block.paragraph.exec(src); if (cap) { - return { + const token = { type: 'paragraph', raw: cap[0], text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) - : cap[1] + : cap[1], + tokens: [] }; + this.lexer.inline(token.text, token.tokens); + return token; } } text(src) { const cap = this.rules.block.text.exec(src); if (cap) { - return { + const token = { type: 'text', raw: cap[0], - text: cap[0] + text: cap[0], + tokens: [] }; + this.lexer.inline(token.text, token.tokens); + return token; } } @@ -446,18 +464,18 @@ module.exports = class Tokenizer { } } - tag(src, inLink, inRawBlock) { + tag(src) { const cap = this.rules.inline.tag.exec(src); if (cap) { - if (!inLink && /^/i.test(cap[0])) { - inLink = false; + if (!this.lexer.state.inLink && /^/i.test(cap[0])) { + this.lexer.state.inLink = false; } - if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { - inRawBlock = true; - } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { - inRawBlock = false; + if (!this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + this.lexer.state.inRawBlock = true; + } else if (this.lexer.state.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + this.lexer.state.inRawBlock = false; } return { @@ -465,8 +483,8 @@ module.exports = class Tokenizer { ? 'text' : 'html', raw: cap[0], - inLink, - inRawBlock, + inLink: this.lexer.state.inLink, + inRawBlock: this.lexer.state.inRawBlock, text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) @@ -528,7 +546,7 @@ module.exports = class Tokenizer { return outputLink(cap, { href: href ? href.replace(this.rules.inline._escapes, '$1') : href, title: title ? title.replace(this.rules.inline._escapes, '$1') : title - }, cap[0]); + }, cap[0], this.lexer); } } @@ -546,7 +564,7 @@ module.exports = class Tokenizer { text }; } - return outputLink(cap, link, cap[0]); + return outputLink(cap, link, cap[0], this.lexer); } } @@ -595,18 +613,22 @@ module.exports = class Tokenizer { // Create `em` if smallest delimiter has odd char count. *a*** if (Math.min(lLength, rLength) % 2) { + const text = src.slice(1, lLength + match.index + rLength); return { type: 'em', raw: src.slice(0, lLength + match.index + rLength + 1), - text: src.slice(1, lLength + match.index + rLength) + text, + tokens: this.lexer.inlineTokens(text, []) }; } // Create 'strong' if smallest delimiter has even char count. **a*** + const text = src.slice(2, lLength + match.index + rLength - 1); return { type: 'strong', raw: src.slice(0, lLength + match.index + rLength + 1), - text: src.slice(2, lLength + match.index + rLength - 1) + text, + tokens: this.lexer.inlineTokens(text, []) }; } } @@ -646,7 +668,8 @@ module.exports = class Tokenizer { return { type: 'del', raw: cap[0], - text: cap[2] + text: cap[2], + tokens: this.lexer.inlineTokens(cap[2], []) }; } } @@ -716,11 +739,11 @@ module.exports = class Tokenizer { } } - inlineText(src, inRawBlock, smartypants) { + inlineText(src, smartypants) { const cap = this.rules.inline.text.exec(src); if (cap) { let text; - if (inRawBlock) { + if (this.lexer.state.inRawBlock) { text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0]; } else { text = escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]); diff --git a/test/specs/new/list_paren_delimiter.html b/test/specs/new/list_paren_delimiter.html index 5f06396fab..ba8ecf36dd 100644 --- a/test/specs/new/list_paren_delimiter.html +++ b/test/specs/new/list_paren_delimiter.html @@ -1,13 +1,13 @@
      -
    1. one
    2. -
    3. two
    4. -
    5. three
    6. +
    7. one
    8. +
    9. two
    10. +
    11. three

      -
    1. two
    2. -
    3. three
    4. -
    5. four
    6. +
    7. two
    8. +
    9. three
    10. +
    11. four
    From 823dccffac2d5f970b6db0e3f968d2f0ca06a647 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Fri, 6 Aug 2021 00:25:21 -0400 Subject: [PATCH 19/20] lint --- src/Tokenizer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tokenizer.js b/src/Tokenizer.js index 53135aa62a..85bdb33675 100644 --- a/src/Tokenizer.js +++ b/src/Tokenizer.js @@ -166,7 +166,7 @@ module.exports = class Tokenizer { list(src) { let cap = this.rules.block.list.exec(src); if (cap) { - let raw, istask, ischecked, indent, i, l, blankLine, endsWithBlankLine, + let raw, istask, ischecked, indent, i, blankLine, endsWithBlankLine, line, lines, itemContents; let bull = cap[1].trim(); @@ -296,7 +296,7 @@ module.exports = class Tokenizer { list.items[list.items.length - 1].text = itemContents.trimRight(); list.raw = list.raw.trimRight(); - l = list.items.length; + const l = list.items.length; // Item child tokens handled here at end because we needed to have the final item to trim it first for (i = 0; i < l; i++) { From 2dfda0e1ec5c69c7d0b9b37e50cd2bd01e0df16f Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Fri, 6 Aug 2021 00:29:39 -0400 Subject: [PATCH 20/20] update packaged lib --- lib/marked.esm.js | 343 +++++++++++++++++----------------------- lib/marked.js | 391 ++++++++++++++++++++-------------------------- 2 files changed, 315 insertions(+), 419 deletions(-) diff --git a/lib/marked.esm.js b/lib/marked.esm.js index 4c00523bd1..a1144f1b83 100644 --- a/lib/marked.esm.js +++ b/lib/marked.esm.js @@ -212,6 +212,10 @@ function splitCells(tableRow, count) { cells = row.split(/ \|/); let i = 0; + // First/last cell in a row cannot be empty if it has no leading/trailing pipe + if (!cells[0].trim()) { cells.shift(); } + if (!cells[cells.length - 1].trim()) { cells.pop(); } + if (cells.length > count) { cells.splice(count); } else { @@ -319,18 +323,20 @@ const { findClosingBracket: findClosingBracket$1 } = helpers; -function outputLink(cap, link, raw) { +function outputLink(cap, link, raw, lexer) { const href = link.href; const title = link.title ? escape$1(link.title) : null; const text = cap[1].replace(/\\([\[\]])/g, '$1'); if (cap[0].charAt(0) !== '!') { + lexer.state.inLink = true; return { type: 'link', raw, href, title, - text + text, + tokens: lexer.inlineTokens(text, []) }; } else { return { @@ -438,48 +444,15 @@ var Tokenizer_1 = class Tokenizer { } } - return { + const token = { type: 'heading', raw: cap[0], depth: cap[1].length, - text: text - }; - } - } - - nptable(src) { - const cap = this.rules.block.nptable.exec(src); - if (cap) { - const item = { - type: 'table', - header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), - align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [], - raw: cap[0] + text: text, + tokens: [] }; - - if (item.header.length === item.align.length) { - let l = item.align.length; - let i; - for (i = 0; i < l; i++) { - if (/^ *-+: *$/.test(item.align[i])) { - item.align[i] = 'right'; - } else if (/^ *:-+: *$/.test(item.align[i])) { - item.align[i] = 'center'; - } else if (/^ *:-+ *$/.test(item.align[i])) { - item.align[i] = 'left'; - } else { - item.align[i] = null; - } - } - - l = item.cells.length; - for (i = 0; i < l; i++) { - item.cells[i] = splitCells$1(item.cells[i], item.header.length); - } - - return item; - } + this.lexer.inline(token.text, token.tokens); + return token; } } @@ -501,6 +474,7 @@ var Tokenizer_1 = class Tokenizer { return { type: 'blockquote', raw: cap[0], + tokens: this.lexer.blockTokens(text, []), text }; } @@ -634,11 +608,23 @@ var Tokenizer_1 = class Tokenizer { src = src.slice(raw.length); } - // Do not consume ending newlines. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic + // Do not consume newlines at end of final item. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic list.items[list.items.length - 1].raw = raw.trimRight(); list.items[list.items.length - 1].text = itemContents.trimRight(); list.raw = list.raw.trimRight(); + const l = list.items.length; + + // Item child tokens handled here at end because we needed to have the final item to trim it first + for (i = 0; i < l; i++) { + this.lexer.state.top = false; + list.items[i].tokens = this.lexer.blockTokens(list.items[i].text, []); + if (list.items[i].tokens.some(t => t.type === 'space')) { + list.loose = true; + list.items[i].loose = true; + } + } + return list; } } @@ -646,15 +632,20 @@ var Tokenizer_1 = class Tokenizer { html(src) { const cap = this.rules.block.html.exec(src); if (cap) { - return { - type: this.options.sanitize - ? 'paragraph' - : 'html', + const token = { + type: 'html', raw: cap[0], pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), - text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$1(cap[0])) : cap[0] + text: cap[0] }; + if (this.options.sanitize) { + token.type = 'paragraph'; + token.text = this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$1(cap[0]); + token.tokens = []; + this.lexer.inline(token.text, token.tokens); + } + return token; } } @@ -687,7 +678,7 @@ var Tokenizer_1 = class Tokenizer { item.raw = cap[0]; let l = item.align.length; - let i; + let i, j, k, row; for (i = 0; i < l; i++) { if (/^ *-+: *$/.test(item.align[i])) { item.align[i] = 'right'; @@ -702,9 +693,31 @@ var Tokenizer_1 = class Tokenizer { l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells$1( - item.cells[i].replace(/^ *\| *| *\| *$/g, ''), - item.header.length); + item.cells[i] = splitCells$1(item.cells[i], item.header.length); + } + + // parse child tokens inside headers and cells + item.tokens = { + header: [], + cells: [] + }; + + // header child tokens + l = item.header.length; + for (j = 0; j < l; j++) { + item.tokens.header[j] = []; + this.lexer.inlineTokens(item.header[j], item.tokens.header[j]); + } + + // cell child tokens + l = item.cells.length; + for (j = 0; j < l; j++) { + row = item.cells[j]; + item.tokens.cells[j] = []; + for (k = 0; k < row.length; k++) { + item.tokens.cells[j][k] = []; + this.lexer.inlineTokens(row[k], item.tokens.cells[j][k]); + } } return item; @@ -715,36 +728,45 @@ var Tokenizer_1 = class Tokenizer { lheading(src) { const cap = this.rules.block.lheading.exec(src); if (cap) { - return { + const token = { type: 'heading', raw: cap[0], depth: cap[2].charAt(0) === '=' ? 1 : 2, - text: cap[1] + text: cap[1], + tokens: [] }; + this.lexer.inline(token.text, token.tokens); + return token; } } paragraph(src) { const cap = this.rules.block.paragraph.exec(src); if (cap) { - return { + const token = { type: 'paragraph', raw: cap[0], text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) - : cap[1] + : cap[1], + tokens: [] }; + this.lexer.inline(token.text, token.tokens); + return token; } } text(src) { const cap = this.rules.block.text.exec(src); if (cap) { - return { + const token = { type: 'text', raw: cap[0], - text: cap[0] + text: cap[0], + tokens: [] }; + this.lexer.inline(token.text, token.tokens); + return token; } } @@ -759,18 +781,18 @@ var Tokenizer_1 = class Tokenizer { } } - tag(src, inLink, inRawBlock) { + tag(src) { const cap = this.rules.inline.tag.exec(src); if (cap) { - if (!inLink && /^
    /i.test(cap[0])) { - inLink = false; + if (!this.lexer.state.inLink && /^/i.test(cap[0])) { + this.lexer.state.inLink = false; } - if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { - inRawBlock = true; - } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { - inRawBlock = false; + if (!this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + this.lexer.state.inRawBlock = true; + } else if (this.lexer.state.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + this.lexer.state.inRawBlock = false; } return { @@ -778,8 +800,8 @@ var Tokenizer_1 = class Tokenizer { ? 'text' : 'html', raw: cap[0], - inLink, - inRawBlock, + inLink: this.lexer.state.inLink, + inRawBlock: this.lexer.state.inRawBlock, text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) @@ -841,7 +863,7 @@ var Tokenizer_1 = class Tokenizer { return outputLink(cap, { href: href ? href.replace(this.rules.inline._escapes, '$1') : href, title: title ? title.replace(this.rules.inline._escapes, '$1') : title - }, cap[0]); + }, cap[0], this.lexer); } } @@ -859,7 +881,7 @@ var Tokenizer_1 = class Tokenizer { text }; } - return outputLink(cap, link, cap[0]); + return outputLink(cap, link, cap[0], this.lexer); } } @@ -908,18 +930,22 @@ var Tokenizer_1 = class Tokenizer { // Create `em` if smallest delimiter has odd char count. *a*** if (Math.min(lLength, rLength) % 2) { + const text = src.slice(1, lLength + match.index + rLength); return { type: 'em', raw: src.slice(0, lLength + match.index + rLength + 1), - text: src.slice(1, lLength + match.index + rLength) + text, + tokens: this.lexer.inlineTokens(text, []) }; } // Create 'strong' if smallest delimiter has even char count. **a*** + const text = src.slice(2, lLength + match.index + rLength - 1); return { type: 'strong', raw: src.slice(0, lLength + match.index + rLength + 1), - text: src.slice(2, lLength + match.index + rLength - 1) + text, + tokens: this.lexer.inlineTokens(text, []) }; } } @@ -959,7 +985,8 @@ var Tokenizer_1 = class Tokenizer { return { type: 'del', raw: cap[0], - text: cap[2] + text: cap[2], + tokens: this.lexer.inlineTokens(cap[2], []) }; } } @@ -1029,11 +1056,11 @@ var Tokenizer_1 = class Tokenizer { } } - inlineText(src, inRawBlock, smartypants) { + inlineText(src, smartypants) { const cap = this.rules.inline.text.exec(src); if (cap) { let text; - if (inRawBlock) { + if (this.lexer.state.inRawBlock) { text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$1(cap[0])) : cap[0]; } else { text = escape$1(this.options.smartypants ? smartypants(cap[0]) : cap[0]); @@ -1075,7 +1102,6 @@ const block = { + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag + ')', def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, - nptable: noopTest$1, table: noopTest$1, lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/, // regex template, placeholders will be replaced according to different paragraph @@ -1141,25 +1167,11 @@ block.normal = merge$1({}, block); */ block.gfm = merge$1({}, block.normal, { - nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header - + ' {0,3}([-:]+ *\\|[-| :]*)' // Align - + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', // Cells - table: '^ *\\|(.+)\\n' // Header - + ' {0,3}\\|?( *[-:]+[-| :]*)' // Align + table: '^ *([^\\n ].*\\|.*)\\n' // Header + + ' {0,3}(?:\\| *)?(:?-+:? *(?:\\| *:?-+:? *)*)\\|?' // Align + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells }); -block.gfm.nptable = edit$1(block.gfm.nptable) - .replace('hr', block.hr) - .replace('heading', ' {0,3}#{1,6} ') - .replace('blockquote', ' {0,3}>') - .replace('code', ' {4}[^\\n]') - .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') - .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt - .replace('html', ')|<(?:script|pre|style|textarea|!--)') - .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks - .getRegex(); - block.gfm.table = edit$1(block.gfm.table) .replace('hr', block.hr) .replace('heading', ' {0,3}#{1,6} ') @@ -1409,6 +1421,13 @@ var Lexer_1 = class Lexer { this.options.tokenizer = this.options.tokenizer || new Tokenizer_1(); this.tokenizer = this.options.tokenizer; this.tokenizer.options = this.options; + this.tokenizer.lexer = this; + this.inlineQueue = []; + this.state = { + inLink: false, + inRawBlock: false, + top: true + }; const rules = { block: block$1.normal, @@ -1463,9 +1482,12 @@ var Lexer_1 = class Lexer { .replace(/\r\n|\r/g, '\n') .replace(/\t/g, ' '); - this.blockTokens(src, this.tokens, true); + this.blockTokens(src, this.tokens); - this.inline(this.tokens); + let next; + while (next = this.inlineQueue.shift()) { + this.inlineTokens(next.src, next.tokens); + } return this.tokens; } @@ -1473,17 +1495,17 @@ var Lexer_1 = class Lexer { /** * Lexing */ - blockTokens(src, tokens = [], top = true) { + blockTokens(src, tokens = []) { if (this.options.pedantic) { src = src.replace(/^ +$/gm, ''); } - let token, i, l, lastToken, cutSrc, lastParagraphClipped; + let token, lastToken, cutSrc, lastParagraphClipped; while (src) { if (this.options.extensions && this.options.extensions.block && this.options.extensions.block.some((extTokenizer) => { - if (token = extTokenizer.call(this, src, tokens)) { + if (token = extTokenizer.call({ lexer: this }, src, tokens)) { src = src.substring(token.raw.length); tokens.push(token); return true; @@ -1510,6 +1532,7 @@ var Lexer_1 = class Lexer { if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) { lastToken.raw += '\n' + token.raw; lastToken.text += '\n' + token.text; + this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text; } else { tokens.push(token); } @@ -1530,13 +1553,6 @@ var Lexer_1 = class Lexer { continue; } - // table no leading pipe (gfm) - if (token = this.tokenizer.nptable(src)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - // hr if (token = this.tokenizer.hr(src)) { src = src.substring(token.raw.length); @@ -1547,7 +1563,6 @@ var Lexer_1 = class Lexer { // blockquote if (token = this.tokenizer.blockquote(src)) { src = src.substring(token.raw.length); - token.tokens = this.blockTokens(token.text, [], top); tokens.push(token); continue; } @@ -1555,14 +1570,6 @@ var Lexer_1 = class Lexer { // list if (token = this.tokenizer.list(src)) { src = src.substring(token.raw.length); - l = token.items.length; - for (i = 0; i < l; i++) { - token.items[i].tokens = this.blockTokens(token.items[i].text, [], false); - if (token.items[i].tokens.some(t => t.type === 'space')) { - token.loose = true; - token.items[i].loose = true; - } - } tokens.push(token); continue; } @@ -1581,13 +1588,12 @@ var Lexer_1 = class Lexer { if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) { lastToken.raw += '\n' + token.raw; lastToken.text += '\n' + token.raw; - } else { - if (!this.tokens.links[token.tag]) { - this.tokens.links[token.tag] = { - href: token.href, - title: token.title - }; - } + this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text; + } else if (!this.tokens.links[token.tag]) { + this.tokens.links[token.tag] = { + href: token.href, + title: token.title + }; } continue; } @@ -1614,18 +1620,20 @@ var Lexer_1 = class Lexer { const tempSrc = src.slice(1); let tempStart; this.options.extensions.startBlock.forEach(function(getStartIndex) { - tempStart = getStartIndex.call(this, tempSrc); + tempStart = getStartIndex.call({ lexer: this }, tempSrc); if (typeof tempStart === 'number' && tempStart >= 0) { startIndex = Math.min(startIndex, tempStart); } }); if (startIndex < Infinity && startIndex >= 0) { cutSrc = src.substring(0, startIndex + 1); } } - if (top && (token = this.tokenizer.paragraph(cutSrc))) { + if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) { lastToken = tokens[tokens.length - 1]; if (lastParagraphClipped && lastToken.type === 'paragraph') { lastToken.raw += '\n' + token.raw; lastToken.text += '\n' + token.text; + this.inlineQueue.pop(); + this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text; } else { tokens.push(token); } @@ -1641,6 +1649,8 @@ var Lexer_1 = class Lexer { if (lastToken && lastToken.type === 'text') { lastToken.raw += '\n' + token.raw; lastToken.text += '\n' + token.text; + this.inlineQueue.pop(); + this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text; } else { tokens.push(token); } @@ -1658,75 +1668,18 @@ var Lexer_1 = class Lexer { } } + this.state.top = true; return tokens; } - inline(tokens) { - let i, - j, - k, - l2, - row, - token; - - const l = tokens.length; - for (i = 0; i < l; i++) { - token = tokens[i]; - switch (token.type) { - case 'paragraph': - case 'text': - case 'heading': { - token.tokens = []; - this.inlineTokens(token.text, token.tokens); - break; - } - case 'table': { - token.tokens = { - header: [], - cells: [] - }; - - // header - l2 = token.header.length; - for (j = 0; j < l2; j++) { - token.tokens.header[j] = []; - this.inlineTokens(token.header[j], token.tokens.header[j]); - } - - // cells - l2 = token.cells.length; - for (j = 0; j < l2; j++) { - row = token.cells[j]; - token.tokens.cells[j] = []; - for (k = 0; k < row.length; k++) { - token.tokens.cells[j][k] = []; - this.inlineTokens(row[k], token.tokens.cells[j][k]); - } - } - - break; - } - case 'blockquote': { - this.inline(token.tokens); - break; - } - case 'list': { - l2 = token.items.length; - for (j = 0; j < l2; j++) { - this.inline(token.items[j].tokens); - } - break; - } - } - } - - return tokens; + inline(src, tokens) { + this.inlineQueue.push({ src, tokens }); } /** * Lexing/Compiling */ - inlineTokens(src, tokens = [], inLink = false, inRawBlock = false) { + inlineTokens(src, tokens = []) { let token, lastToken, cutSrc; // String with links masked to avoid interference with em and strong @@ -1765,7 +1718,7 @@ var Lexer_1 = class Lexer { if (this.options.extensions && this.options.extensions.inline && this.options.extensions.inline.some((extTokenizer) => { - if (token = extTokenizer.call(this, src, tokens)) { + if (token = extTokenizer.call({ lexer: this }, src, tokens)) { src = src.substring(token.raw.length); tokens.push(token); return true; @@ -1783,10 +1736,8 @@ var Lexer_1 = class Lexer { } // tag - if (token = this.tokenizer.tag(src, inLink, inRawBlock)) { + if (token = this.tokenizer.tag(src)) { src = src.substring(token.raw.length); - inLink = token.inLink; - inRawBlock = token.inRawBlock; lastToken = tokens[tokens.length - 1]; if (lastToken && token.type === 'text' && lastToken.type === 'text') { lastToken.raw += token.raw; @@ -1800,9 +1751,6 @@ var Lexer_1 = class Lexer { // link if (token = this.tokenizer.link(src)) { src = src.substring(token.raw.length); - if (token.type === 'link') { - token.tokens = this.inlineTokens(token.text, [], true, inRawBlock); - } tokens.push(token); continue; } @@ -1811,10 +1759,7 @@ var Lexer_1 = class Lexer { if (token = this.tokenizer.reflink(src, this.tokens.links)) { src = src.substring(token.raw.length); lastToken = tokens[tokens.length - 1]; - if (token.type === 'link') { - token.tokens = this.inlineTokens(token.text, [], true, inRawBlock); - tokens.push(token); - } else if (lastToken && token.type === 'text' && lastToken.type === 'text') { + if (lastToken && token.type === 'text' && lastToken.type === 'text') { lastToken.raw += token.raw; lastToken.text += token.text; } else { @@ -1826,7 +1771,6 @@ var Lexer_1 = class Lexer { // em & strong if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) { src = src.substring(token.raw.length); - token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); tokens.push(token); continue; } @@ -1848,7 +1792,6 @@ var Lexer_1 = class Lexer { // del (gfm) if (token = this.tokenizer.del(src)) { src = src.substring(token.raw.length); - token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); tokens.push(token); continue; } @@ -1861,7 +1804,7 @@ var Lexer_1 = class Lexer { } // url (gfm) - if (!inLink && (token = this.tokenizer.url(src, mangle))) { + if (!this.state.inLink && (token = this.tokenizer.url(src, mangle))) { src = src.substring(token.raw.length); tokens.push(token); continue; @@ -1875,14 +1818,14 @@ var Lexer_1 = class Lexer { const tempSrc = src.slice(1); let tempStart; this.options.extensions.startInline.forEach(function(getStartIndex) { - tempStart = getStartIndex.call(this, tempSrc); + tempStart = getStartIndex.call({ lexer: this }, tempSrc); if (typeof tempStart === 'number' && tempStart >= 0) { startIndex = Math.min(startIndex, tempStart); } }); if (startIndex < Infinity && startIndex >= 0) { cutSrc = src.substring(0, startIndex + 1); } } - if (token = this.tokenizer.inlineText(cutSrc, inRawBlock, smartypants)) { + if (token = this.tokenizer.inlineText(cutSrc, smartypants)) { src = src.substring(token.raw.length); if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started prevChar = token.raw.slice(-1); @@ -2238,7 +2181,7 @@ var Parser_1 = class Parser { // Run any renderer extensions if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) { - ret = this.options.extensions.renderers[token.type].call(this, token); + ret = this.options.extensions.renderers[token.type].call({ parser: this }, token); if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'html', 'paragraph', 'text'].includes(token.type)) { out += ret || ''; continue; @@ -2394,7 +2337,7 @@ var Parser_1 = class Parser { // Run any renderer extensions if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) { - ret = this.options.extensions.renderers[token.type].call(this, token); + ret = this.options.extensions.renderers[token.type].call({ parser: this }, token); if (ret !== false || !['escape', 'html', 'link', 'image', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(token.type)) { out += ret || ''; continue; diff --git a/lib/marked.js b/lib/marked.js index 882ee312d6..8f43b0b880 100644 --- a/lib/marked.js +++ b/lib/marked.js @@ -293,7 +293,15 @@ } }), cells = row.split(/ \|/); - var i = 0; + var i = 0; // First/last cell in a row cannot be empty if it has no leading/trailing pipe + + if (!cells[0].trim()) { + cells.shift(); + } + + if (!cells[cells.length - 1].trim()) { + cells.pop(); + } if (cells.length > count) { cells.splice(count); @@ -412,18 +420,20 @@ _escape = helpers.escape, findClosingBracket$1 = helpers.findClosingBracket; - function outputLink(cap, link, raw) { + function outputLink(cap, link, raw, lexer) { var href = link.href; var title = link.title ? _escape(link.title) : null; var text = cap[1].replace(/\\([\[\]])/g, '$1'); if (cap[0].charAt(0) !== '!') { + lexer.state.inLink = true; return { type: 'link', raw: raw, href: href, title: title, - text: text + text: text, + tokens: lexer.inlineTokens(text, []) }; } else { return { @@ -535,51 +545,15 @@ } } - return { + var token = { type: 'heading', raw: cap[0], depth: cap[1].length, - text: text + text: text, + tokens: [] }; - } - }; - - _proto.nptable = function nptable(src) { - var cap = this.rules.block.nptable.exec(src); - - if (cap) { - var item = { - type: 'table', - header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), - align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [], - raw: cap[0] - }; - - if (item.header.length === item.align.length) { - var l = item.align.length; - var i; - - for (i = 0; i < l; i++) { - if (/^ *-+: *$/.test(item.align[i])) { - item.align[i] = 'right'; - } else if (/^ *:-+: *$/.test(item.align[i])) { - item.align[i] = 'center'; - } else if (/^ *:-+ *$/.test(item.align[i])) { - item.align[i] = 'left'; - } else { - item.align[i] = null; - } - } - - l = item.cells.length; - - for (i = 0; i < l; i++) { - item.cells[i] = splitCells$1(item.cells[i], item.header.length); - } - - return item; - } + this.lexer.inline(token.text, token.tokens); + return token; } }; @@ -602,6 +576,7 @@ return { type: 'blockquote', raw: cap[0], + tokens: this.lexer.blockTokens(text, []), text: text }; } @@ -737,12 +712,26 @@ }); list.raw += raw; src = src.slice(raw.length); - } // Do not consume ending newlines. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic + } // Do not consume newlines at end of final item. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic list.items[list.items.length - 1].raw = raw.trimRight(); list.items[list.items.length - 1].text = itemContents.trimRight(); list.raw = list.raw.trimRight(); + var l = list.items.length; // Item child tokens handled here at end because we needed to have the final item to trim it first + + for (i = 0; i < l; i++) { + this.lexer.state.top = false; + list.items[i].tokens = this.lexer.blockTokens(list.items[i].text, []); + + if (list.items[i].tokens.some(function (t) { + return t.type === 'space'; + })) { + list.loose = true; + list.items[i].loose = true; + } + } + return list; } }; @@ -751,12 +740,21 @@ var cap = this.rules.block.html.exec(src); if (cap) { - return { - type: this.options.sanitize ? 'paragraph' : 'html', + var token = { + type: 'html', raw: cap[0], pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), - text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0] + text: cap[0] }; + + if (this.options.sanitize) { + token.type = 'paragraph'; + token.text = this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]); + token.tokens = []; + this.lexer.inline(token.text, token.tokens); + } + + return token; } }; @@ -790,7 +788,7 @@ if (item.header.length === item.align.length) { item.raw = cap[0]; var l = item.align.length; - var i; + var i, j, k, row; for (i = 0; i < l; i++) { if (/^ *-+: *$/.test(item.align[i])) { @@ -807,7 +805,33 @@ l = item.cells.length; for (i = 0; i < l; i++) { - item.cells[i] = splitCells$1(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length); + item.cells[i] = splitCells$1(item.cells[i], item.header.length); + } // parse child tokens inside headers and cells + + + item.tokens = { + header: [], + cells: [] + }; // header child tokens + + l = item.header.length; + + for (j = 0; j < l; j++) { + item.tokens.header[j] = []; + this.lexer.inlineTokens(item.header[j], item.tokens.header[j]); + } // cell child tokens + + + l = item.cells.length; + + for (j = 0; j < l; j++) { + row = item.cells[j]; + item.tokens.cells[j] = []; + + for (k = 0; k < row.length; k++) { + item.tokens.cells[j][k] = []; + this.lexer.inlineTokens(row[k], item.tokens.cells[j][k]); + } } return item; @@ -819,12 +843,15 @@ var cap = this.rules.block.lheading.exec(src); if (cap) { - return { + var token = { type: 'heading', raw: cap[0], depth: cap[2].charAt(0) === '=' ? 1 : 2, - text: cap[1] + text: cap[1], + tokens: [] }; + this.lexer.inline(token.text, token.tokens); + return token; } }; @@ -832,11 +859,14 @@ var cap = this.rules.block.paragraph.exec(src); if (cap) { - return { + var token = { type: 'paragraph', raw: cap[0], - text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1] + text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1], + tokens: [] }; + this.lexer.inline(token.text, token.tokens); + return token; } }; @@ -844,11 +874,14 @@ var cap = this.rules.block.text.exec(src); if (cap) { - return { + var token = { type: 'text', raw: cap[0], - text: cap[0] + text: cap[0], + tokens: [] }; + this.lexer.inline(token.text, token.tokens); + return token; } }; @@ -864,27 +897,27 @@ } }; - _proto.tag = function tag(src, inLink, inRawBlock) { + _proto.tag = function tag(src) { var cap = this.rules.inline.tag.exec(src); if (cap) { - if (!inLink && /^/i.test(cap[0])) { - inLink = false; + if (!this.lexer.state.inLink && /^/i.test(cap[0])) { + this.lexer.state.inLink = false; } - if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { - inRawBlock = true; - } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { - inRawBlock = false; + if (!this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + this.lexer.state.inRawBlock = true; + } else if (this.lexer.state.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + this.lexer.state.inRawBlock = false; } return { type: this.options.sanitize ? 'text' : 'html', raw: cap[0], - inLink: inLink, - inRawBlock: inRawBlock, + inLink: this.lexer.state.inLink, + inRawBlock: this.lexer.state.inRawBlock, text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0] }; } @@ -950,7 +983,7 @@ return outputLink(cap, { href: href ? href.replace(this.rules.inline._escapes, '$1') : href, title: title ? title.replace(this.rules.inline._escapes, '$1') : title - }, cap[0]); + }, cap[0], this.lexer); } }; @@ -970,7 +1003,7 @@ }; } - return outputLink(cap, link, cap[0]); + return outputLink(cap, link, cap[0], this.lexer); } }; @@ -1021,18 +1054,23 @@ rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal); // Create `em` if smallest delimiter has odd char count. *a*** if (Math.min(lLength, rLength) % 2) { + var _text = src.slice(1, lLength + match.index + rLength); + return { type: 'em', raw: src.slice(0, lLength + match.index + rLength + 1), - text: src.slice(1, lLength + match.index + rLength) + text: _text, + tokens: this.lexer.inlineTokens(_text, []) }; } // Create 'strong' if smallest delimiter has even char count. **a*** + var text = src.slice(2, lLength + match.index + rLength - 1); return { type: 'strong', raw: src.slice(0, lLength + match.index + rLength + 1), - text: src.slice(2, lLength + match.index + rLength - 1) + text: text, + tokens: this.lexer.inlineTokens(text, []) }; } } @@ -1077,7 +1115,8 @@ return { type: 'del', raw: cap[0], - text: cap[2] + text: cap[2], + tokens: this.lexer.inlineTokens(cap[2], []) }; } }; @@ -1151,13 +1190,13 @@ } }; - _proto.inlineText = function inlineText(src, inRawBlock, smartypants) { + _proto.inlineText = function inlineText(src, smartypants) { var cap = this.rules.inline.text.exec(src); if (cap) { var text; - if (inRawBlock) { + if (this.lexer.state.inRawBlock) { text = this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]; } else { text = _escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]); @@ -1200,7 +1239,6 @@ + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag + ')', def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, - nptable: noopTest$1, table: noopTest$1, lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/, // regex template, placeholders will be replaced according to different paragraph @@ -1232,18 +1270,11 @@ */ block.gfm = merge$1({}, block.normal, { - nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header - + ' {0,3}([-:]+ *\\|[-| :]*)' // Align - + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', - // Cells - table: '^ *\\|(.+)\\n' // Header - + ' {0,3}\\|?( *[-:]+[-| :]*)' // Align + table: '^ *([^\\n ].*\\|.*)\\n' // Header + + ' {0,3}(?:\\| *)?(:?-+:? *(?:\\| *:?-+:? *)*)\\|?' // Align + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells }); - block.gfm.nptable = edit$1(block.gfm.nptable).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt - .replace('html', ')|<(?:script|pre|style|textarea|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks - .getRegex(); block.gfm.table = edit$1(block.gfm.table).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt .replace('html', ')|<(?:script|pre|style|textarea|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks .getRegex(); @@ -1420,6 +1451,13 @@ this.options.tokenizer = this.options.tokenizer || new Tokenizer_1(); this.tokenizer = this.options.tokenizer; this.tokenizer.options = this.options; + this.tokenizer.lexer = this; + this.inlineQueue = []; + this.state = { + inLink: false, + inRawBlock: false, + top: true + }; var rules = { block: block$1.normal, inline: inline$1.normal @@ -1470,8 +1508,13 @@ _proto.lex = function lex(src) { src = src.replace(/\r\n|\r/g, '\n').replace(/\t/g, ' '); - this.blockTokens(src, this.tokens, true); - this.inline(this.tokens); + this.blockTokens(src, this.tokens); + var next; + + while (next = this.inlineQueue.shift()) { + this.inlineTokens(next.src, next.tokens); + } + return this.tokens; } /** @@ -1479,26 +1522,24 @@ */ ; - _proto.blockTokens = function blockTokens(src, tokens, top) { + _proto.blockTokens = function blockTokens(src, tokens) { var _this = this; if (tokens === void 0) { tokens = []; } - if (top === void 0) { - top = true; - } - if (this.options.pedantic) { src = src.replace(/^ +$/gm, ''); } - var token, i, l, lastToken, cutSrc, lastParagraphClipped; + var token, lastToken, cutSrc, lastParagraphClipped; while (src) { if (this.options.extensions && this.options.extensions.block && this.options.extensions.block.some(function (extTokenizer) { - if (token = extTokenizer.call(_this, src, tokens)) { + if (token = extTokenizer.call({ + lexer: _this + }, src, tokens)) { src = src.substring(token.raw.length); tokens.push(token); return true; @@ -1528,6 +1569,7 @@ if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) { lastToken.raw += '\n' + token.raw; lastToken.text += '\n' + token.text; + this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text; } else { tokens.push(token); } @@ -1547,13 +1589,6 @@ src = src.substring(token.raw.length); tokens.push(token); continue; - } // table no leading pipe (gfm) - - - if (token = this.tokenizer.nptable(src)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; } // hr @@ -1566,7 +1601,6 @@ if (token = this.tokenizer.blockquote(src)) { src = src.substring(token.raw.length); - token.tokens = this.blockTokens(token.text, [], top); tokens.push(token); continue; } // list @@ -1574,19 +1608,6 @@ if (token = this.tokenizer.list(src)) { src = src.substring(token.raw.length); - l = token.items.length; - - for (i = 0; i < l; i++) { - token.items[i].tokens = this.blockTokens(token.items[i].text, [], false); - - if (token.items[i].tokens.some(function (t) { - return t.type === 'space'; - })) { - token.loose = true; - token.items[i].loose = true; - } - } - tokens.push(token); continue; } // html @@ -1606,13 +1627,12 @@ if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) { lastToken.raw += '\n' + token.raw; lastToken.text += '\n' + token.raw; - } else { - if (!this.tokens.links[token.tag]) { - this.tokens.links[token.tag] = { - href: token.href, - title: token.title - }; - } + this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text; + } else if (!this.tokens.links[token.tag]) { + this.tokens.links[token.tag] = { + href: token.href, + title: token.title + }; } continue; @@ -1643,7 +1663,9 @@ var tempStart = void 0; _this.options.extensions.startBlock.forEach(function (getStartIndex) { - tempStart = getStartIndex.call(this, tempSrc); + tempStart = getStartIndex.call({ + lexer: this + }, tempSrc); if (typeof tempStart === 'number' && tempStart >= 0) { startIndex = Math.min(startIndex, tempStart); @@ -1656,12 +1678,14 @@ })(); } - if (top && (token = this.tokenizer.paragraph(cutSrc))) { + if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) { lastToken = tokens[tokens.length - 1]; if (lastParagraphClipped && lastToken.type === 'paragraph') { lastToken.raw += '\n' + token.raw; lastToken.text += '\n' + token.text; + this.inlineQueue.pop(); + this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text; } else { tokens.push(token); } @@ -1679,6 +1703,8 @@ if (lastToken && lastToken.type === 'text') { lastToken.raw += '\n' + token.raw; lastToken.text += '\n' + token.text; + this.inlineQueue.pop(); + this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text; } else { tokens.push(token); } @@ -1698,97 +1724,28 @@ } } + this.state.top = true; return tokens; }; - _proto.inline = function inline(tokens) { - var i, j, k, l2, row, token; - var l = tokens.length; - - for (i = 0; i < l; i++) { - token = tokens[i]; - - switch (token.type) { - case 'paragraph': - case 'text': - case 'heading': - { - token.tokens = []; - this.inlineTokens(token.text, token.tokens); - break; - } - - case 'table': - { - token.tokens = { - header: [], - cells: [] - }; // header - - l2 = token.header.length; - - for (j = 0; j < l2; j++) { - token.tokens.header[j] = []; - this.inlineTokens(token.header[j], token.tokens.header[j]); - } // cells - - - l2 = token.cells.length; - - for (j = 0; j < l2; j++) { - row = token.cells[j]; - token.tokens.cells[j] = []; - - for (k = 0; k < row.length; k++) { - token.tokens.cells[j][k] = []; - this.inlineTokens(row[k], token.tokens.cells[j][k]); - } - } - - break; - } - - case 'blockquote': - { - this.inline(token.tokens); - break; - } - - case 'list': - { - l2 = token.items.length; - - for (j = 0; j < l2; j++) { - this.inline(token.items[j].tokens); - } - - break; - } - } - } - - return tokens; + _proto.inline = function inline(src, tokens) { + this.inlineQueue.push({ + src: src, + tokens: tokens + }); } /** * Lexing/Compiling */ ; - _proto.inlineTokens = function inlineTokens(src, tokens, inLink, inRawBlock) { + _proto.inlineTokens = function inlineTokens(src, tokens) { var _this2 = this; if (tokens === void 0) { tokens = []; } - if (inLink === void 0) { - inLink = false; - } - - if (inRawBlock === void 0) { - inRawBlock = false; - } - var token, lastToken, cutSrc; // String with links masked to avoid interference with em and strong var maskedSrc = src; @@ -1825,7 +1782,9 @@ keepPrevChar = false; // extensions if (this.options.extensions && this.options.extensions.inline && this.options.extensions.inline.some(function (extTokenizer) { - if (token = extTokenizer.call(_this2, src, tokens)) { + if (token = extTokenizer.call({ + lexer: _this2 + }, src, tokens)) { src = src.substring(token.raw.length); tokens.push(token); return true; @@ -1844,10 +1803,8 @@ } // tag - if (token = this.tokenizer.tag(src, inLink, inRawBlock)) { + if (token = this.tokenizer.tag(src)) { src = src.substring(token.raw.length); - inLink = token.inLink; - inRawBlock = token.inRawBlock; lastToken = tokens[tokens.length - 1]; if (lastToken && token.type === 'text' && lastToken.type === 'text') { @@ -1863,11 +1820,6 @@ if (token = this.tokenizer.link(src)) { src = src.substring(token.raw.length); - - if (token.type === 'link') { - token.tokens = this.inlineTokens(token.text, [], true, inRawBlock); - } - tokens.push(token); continue; } // reflink, nolink @@ -1877,10 +1829,7 @@ src = src.substring(token.raw.length); lastToken = tokens[tokens.length - 1]; - if (token.type === 'link') { - token.tokens = this.inlineTokens(token.text, [], true, inRawBlock); - tokens.push(token); - } else if (lastToken && token.type === 'text' && lastToken.type === 'text') { + if (lastToken && token.type === 'text' && lastToken.type === 'text') { lastToken.raw += token.raw; lastToken.text += token.text; } else { @@ -1893,7 +1842,6 @@ if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) { src = src.substring(token.raw.length); - token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); tokens.push(token); continue; } // code @@ -1915,7 +1863,6 @@ if (token = this.tokenizer.del(src)) { src = src.substring(token.raw.length); - token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); tokens.push(token); continue; } // autolink @@ -1928,7 +1875,7 @@ } // url (gfm) - if (!inLink && (token = this.tokenizer.url(src, mangle))) { + if (!this.state.inLink && (token = this.tokenizer.url(src, mangle))) { src = src.substring(token.raw.length); tokens.push(token); continue; @@ -1945,7 +1892,9 @@ var tempStart = void 0; _this2.options.extensions.startInline.forEach(function (getStartIndex) { - tempStart = getStartIndex.call(this, tempSrc); + tempStart = getStartIndex.call({ + lexer: this + }, tempSrc); if (typeof tempStart === 'number' && tempStart >= 0) { startIndex = Math.min(startIndex, tempStart); @@ -1958,7 +1907,7 @@ })(); } - if (token = this.tokenizer.inlineText(cutSrc, inRawBlock, smartypants)) { + if (token = this.tokenizer.inlineText(cutSrc, smartypants)) { src = src.substring(token.raw.length); if (token.raw.slice(-1) !== '_') { @@ -2337,7 +2286,9 @@ token = tokens[i]; // Run any renderer extensions if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) { - ret = this.options.extensions.renderers[token.type].call(this, token); + ret = this.options.extensions.renderers[token.type].call({ + parser: this + }, token); if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'html', 'paragraph', 'text'].includes(token.type)) { out += ret || ''; @@ -2515,7 +2466,9 @@ token = tokens[i]; // Run any renderer extensions if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) { - ret = this.options.extensions.renderers[token.type].call(this, token); + ret = this.options.extensions.renderers[token.type].call({ + parser: this + }, token); if (ret !== false || !['escape', 'html', 'link', 'image', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(token.type)) { out += ret || '';