8
0
Просмотр исходного кода

Merge pull request #7307 from ckeditor/i/5988

Feature (markdown-gfm): The markdown data processor has been totally revamped and updated to the most recent conversion libraries. Closes #5988.
Maciej 5 лет назад
Родитель
Сommit
3881349eae
25 измененных файлов с 858 добавлено и 2710 удалено
  1. 4 1
      packages/ckeditor5-markdown-gfm/package.json
  2. 17 14
      packages/ckeditor5-markdown-gfm/src/gfmdataprocessor.js
  3. 93 0
      packages/ckeditor5-markdown-gfm/src/html2markdown/html2markdown.js
  4. 0 1286
      packages/ckeditor5-markdown-gfm/src/lib/marked/marked.js
  5. 0 175
      packages/ckeditor5-markdown-gfm/src/lib/marked/renderer.js
  6. 0 68
      packages/ckeditor5-markdown-gfm/src/lib/to-markdown/converters.js
  7. 0 785
      packages/ckeditor5-markdown-gfm/src/lib/to-markdown/to-markdown.js
  8. 32 0
      packages/ckeditor5-markdown-gfm/src/markdown2html/markdown2html.js
  9. 26 3
      packages/ckeditor5-markdown-gfm/tests/_utils/utils.js
  10. 9 9
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/blockquotes.js
  11. 54 18
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/code.js
  12. 41 7
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/escaping.js
  13. 12 2
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/headers.js
  14. 26 26
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/horizontal-rules.js
  15. 26 0
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/html.js
  16. 3 3
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/images.js
  17. 47 125
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/links.js
  18. 42 0
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/lists.js
  19. 6 3
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/manual/gfmdataprocessor.js
  20. 3 42
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/paragraphs.js
  21. 2 2
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/strikethrough.js
  22. 12 0
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/strong-emphasis.js
  23. 2 2
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/tables.js
  24. 50 0
      packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/text.js
  25. 351 139
      yarn.lock

+ 4 - 1
packages/ckeditor5-markdown-gfm/package.json

@@ -10,7 +10,10 @@
     "ckeditor5-plugin"
   ],
   "dependencies": {
-    "@ckeditor/ckeditor5-engine": "^21.0.0"
+    "@ckeditor/ckeditor5-engine": "^21.0.0",
+    "marked": "^0.7.0",
+    "turndown": "^6.0.0",
+    "turndown-plugin-gfm": "^1.0.2"
   },
   "engines": {
     "node": ">=12.0.0",

+ 17 - 14
packages/ckeditor5-markdown-gfm/src/gfmdataprocessor.js

@@ -7,11 +7,10 @@
  * @module markdown-gfm/gfmdataprocessor
  */
 
-import marked from './lib/marked/marked';
-import toMarkdown from './lib/to-markdown/to-markdown';
 import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor';
-import GFMRenderer from './lib/marked/renderer';
-import converters from './lib/to-markdown/converters';
+
+import markdown2html from './markdown2html/markdown2html';
+import html2markdown, { turndownService } from './html2markdown/html2markdown';
 
 /**
  * This data processor implementation uses GitHub Flavored Markdown as input/output data.
@@ -36,6 +35,18 @@ export default class GFMDataProcessor {
 		this._htmlDP = new HtmlDataProcessor( document );
 	}
 
+	/**
+	 * Keeps the specified element in the output as HTML. This is useful if the editor contains
+	 * features that produce HTML that are not part of the markdon standards.
+	 *
+	 * By default, all HTML tags are removed.
+	 *
+	 * @param element {String} The element name to be kept.
+	 */
+	keepHtml( element ) {
+		turndownService.keep( [ element ] );
+	}
+
 	/**
 	 * Converts the provided Markdown string to view tree.
 	 *
@@ -43,14 +54,7 @@ export default class GFMDataProcessor {
 	 * @returns {module:engine/view/documentfragment~DocumentFragment} The converted view element.
 	 */
 	toView( data ) {
-		const html = marked.parse( data, {
-			gfm: true,
-			breaks: true,
-			tables: true,
-			xhtml: true,
-			renderer: new GFMRenderer()
-		} );
-
+		const html = markdown2html( data );
 		return this._htmlDP.toView( html );
 	}
 
@@ -63,7 +67,6 @@ export default class GFMDataProcessor {
 	 */
 	toData( viewFragment ) {
 		const html = this._htmlDP.toData( viewFragment );
-
-		return toMarkdown( html, { gfm: true, converters } );
+		return html2markdown( html );
 	}
 }

+ 93 - 0
packages/ckeditor5-markdown-gfm/src/html2markdown/html2markdown.js

@@ -0,0 +1,93 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module markdown-gfm/html2markdown
+ */
+
+import TurndownService from 'turndown';
+import { gfm } from 'turndown-plugin-gfm';
+
+// Override the original escape method by not escaping links.
+const originalEscape = TurndownService.prototype.escape;
+
+function escape( string ) {
+	string = originalEscape( string );
+
+	// Escape "<".
+	string = string.replace( /</g, '\\<' );
+
+	return string;
+}
+
+// eslint-disable-next-line max-len
+const regex = /\b(?:https?:\/\/|www\.)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()[\]{};:'".,<>?«»“”‘’])/g;
+
+TurndownService.prototype.escape = function( string ) {
+	// Urls should not be escaped. Our strategy is using a regex to find them and escape everything
+	// which is out of the matches parts.
+
+	let escaped = '';
+	let lastLinkEnd = 0;
+
+	for ( const match of string.matchAll( regex ) ) {
+		const index = match.index;
+
+		// Append the substring between the last match and the current one (if anything).
+		if ( index > lastLinkEnd ) {
+			escaped += escape( string.substring( lastLinkEnd, index ) );
+		}
+
+		const matchedURL = match[ 0 ];
+
+		escaped += matchedURL;
+
+		lastLinkEnd = index + matchedURL.length;
+	}
+
+	// Add text after the last link or at the string start if no matches.
+	if ( lastLinkEnd < string.length ) {
+		escaped += escape( string.substring( lastLinkEnd, string.length ) );
+	}
+
+	return escaped;
+};
+
+const turndownService = new TurndownService( {
+	codeBlockStyle: 'fenced',
+	hr: '---',
+	headingStyle: 'atx'
+} );
+
+turndownService.use( [
+	gfm,
+	todoList
+] );
+
+/**
+ * Parses HTML to a markdown.
+ *
+ * @param {String} html
+ * @returns {String}
+ */
+export default function html2markdown( html ) {
+	return turndownService.turndown( html );
+}
+
+export { turndownService };
+
+// This is a copy of the original taskListItems rule from turdown-plugin-gfm, with minor changes.
+function todoList( turndownService ) {
+	turndownService.addRule( 'taskListItems', {
+		filter( node ) {
+			return node.type === 'checkbox' &&
+				// Changes here as CKEditor outputs a deeper structure.
+				( node.parentNode.nodeName === 'LI' || node.parentNode.parentNode.nodeName === 'LI' );
+		},
+		replacement( content, node ) {
+			return ( node.checked ? '[x]' : '[ ]' ) + ' ';
+		}
+	} );
+}

+ 0 - 1286
packages/ckeditor5-markdown-gfm/src/lib/marked/marked.js

@@ -1,1286 +0,0 @@
-/**
- * marked - a markdown parser
- * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
- * https://github.com/chjj/marked
- */
-
-;(function() {
-
-/**
- * Block-Level Grammar
- */
-
-var block = {
-  newline: /^\n+/,
-  code: /^( {4}[^\n]+\n*)+/,
-  fences: noop,
-  hr: /^( *[-*_]){3,} *(?:\n+|$)/,
-  heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
-  nptable: noop,
-  lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
-  blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
-  list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
-  html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
-  def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
-  table: noop,
-  paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
-  text: /^[^\n]+/
-};
-
-block.bullet = /(?:[*+-]|\d+\.)/;
-block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
-block.item = replace(block.item, 'gm')
-  (/bull/g, block.bullet)
-  ();
-
-block.list = replace(block.list)
-  (/bull/g, block.bullet)
-  ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
-  ('def', '\\n+(?=' + block.def.source + ')')
-  ();
-
-block.blockquote = replace(block.blockquote)
-  ('def', block.def)
-  ();
-
-block._tag = '(?!(?:'
-  + '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';
-
-block.html = replace(block.html)
-  ('comment', /<!--[\s\S]*?-->/)
-  ('closed', /<(tag)[\s\S]+?<\/\1>/)
-  ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
-  (/tag/g, block._tag)
-  ();
-
-block.paragraph = replace(block.paragraph)
-  ('hr', block.hr)
-  ('heading', block.heading)
-  ('lheading', block.lheading)
-  ('blockquote', block.blockquote)
-  ('tag', '<' + block._tag)
-  ('def', block.def)
-  ();
-
-/**
- * Normal Block Grammar
- */
-
-block.normal = merge({}, block);
-
-/**
- * GFM Block Grammar
- */
-
-block.gfm = merge({}, block.normal, {
-  fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
-  paragraph: /^/,
-  heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
-});
-
-block.gfm.paragraph = replace(block.paragraph)
-  ('(?!', '(?!'
-    + block.gfm.fences.source.replace('\\1', '\\2') + '|'
-    + block.list.source.replace('\\1', '\\3') + '|')
-  ();
-
-/**
- * GFM + Tables Block Grammar
- */
-
-block.tables = merge({}, block.gfm, {
-  nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
-  table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
-});
-
-/**
- * Block Lexer
- */
-
-function Lexer(options) {
-  this.tokens = [];
-  this.tokens.links = {};
-  this.options = options || marked.defaults;
-  this.rules = block.normal;
-
-  if (this.options.gfm) {
-    if (this.options.tables) {
-      this.rules = block.tables;
-    } else {
-      this.rules = block.gfm;
-    }
-  }
-}
-
-/**
- * Expose Block Rules
- */
-
-Lexer.rules = block;
-
-/**
- * Static Lex Method
- */
-
-Lexer.lex = function(src, options) {
-  var lexer = new Lexer(options);
-  return lexer.lex(src);
-};
-
-/**
- * Preprocessing
- */
-
-Lexer.prototype.lex = function(src) {
-  src = src
-    .replace(/\r\n|\r/g, '\n')
-    .replace(/\t/g, '    ')
-    .replace(/\u00a0/g, ' ')
-    .replace(/\u2424/g, '\n');
-
-  return this.token(src, true);
-};
-
-/**
- * Lexing
- */
-
-Lexer.prototype.token = function(src, top, bq) {
-  var src = src.replace(/^ +$/gm, '')
-    , next
-    , loose
-    , cap
-    , bull
-    , b
-    , item
-    , space
-    , i
-    , l;
-
-  while (src) {
-    // newline
-    if (cap = this.rules.newline.exec(src)) {
-      src = src.substring(cap[0].length);
-      if (cap[0].length > 1) {
-        this.tokens.push({
-          type: 'space'
-        });
-      }
-    }
-
-    // code
-    if (cap = this.rules.code.exec(src)) {
-      src = src.substring(cap[0].length);
-      cap = cap[0].replace(/^ {4}/gm, '');
-      this.tokens.push({
-        type: 'code',
-        text: !this.options.pedantic
-          ? cap.replace(/\n+$/, '')
-          : cap
-      });
-      continue;
-    }
-
-    // fences (gfm)
-    if (cap = this.rules.fences.exec(src)) {
-      src = src.substring(cap[0].length);
-      this.tokens.push({
-        type: 'code',
-        lang: cap[2],
-        text: cap[3] || ''
-      });
-      continue;
-    }
-
-    // heading
-    if (cap = this.rules.heading.exec(src)) {
-      src = src.substring(cap[0].length);
-      this.tokens.push({
-        type: 'heading',
-        depth: cap[1].length,
-        text: cap[2]
-      });
-      continue;
-    }
-
-    // table no leading pipe (gfm)
-    if (top && (cap = this.rules.nptable.exec(src))) {
-      src = src.substring(cap[0].length);
-
-      item = {
-        type: 'table',
-        header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
-        align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
-        cells: cap[3].replace(/\n$/, '').split('\n')
-      };
-
-      for (i = 0; i < item.align.length; 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;
-        }
-      }
-
-      for (i = 0; i < item.cells.length; i++) {
-        item.cells[i] = item.cells[i].split(/ *\| */);
-      }
-
-      this.tokens.push(item);
-
-      continue;
-    }
-
-    // lheading
-    if (cap = this.rules.lheading.exec(src)) {
-      src = src.substring(cap[0].length);
-      this.tokens.push({
-        type: 'heading',
-        depth: cap[2] === '=' ? 1 : 2,
-        text: cap[1]
-      });
-      continue;
-    }
-
-    // hr
-    if (cap = this.rules.hr.exec(src)) {
-      src = src.substring(cap[0].length);
-      this.tokens.push({
-        type: 'hr'
-      });
-      continue;
-    }
-
-    // blockquote
-    if (cap = this.rules.blockquote.exec(src)) {
-      src = src.substring(cap[0].length);
-
-      this.tokens.push({
-        type: 'blockquote_start'
-      });
-
-      cap = cap[0].replace(/^ *> ?/gm, '');
-
-      // Pass `top` to keep the current
-      // "toplevel" state. This is exactly
-      // how markdown.pl works.
-      this.token(cap, top, true);
-
-      this.tokens.push({
-        type: 'blockquote_end'
-      });
-
-      continue;
-    }
-
-    // list
-    if (cap = this.rules.list.exec(src)) {
-      src = src.substring(cap[0].length);
-      bull = cap[2];
-
-      this.tokens.push({
-        type: 'list_start',
-        ordered: bull.length > 1
-      });
-
-      // Get each top-level item.
-      cap = cap[0].match(this.rules.item);
-
-      next = false;
-      l = cap.length;
-      i = 0;
-
-      for (; i < l; i++) {
-        item = cap[i];
-
-        // 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, '');
-        }
-
-        // Determine whether the next list item belongs here.
-        // Backpedal if it does not belong in this list.
-        if (this.options.smartLists && i !== l - 1) {
-          b = block.bullet.exec(cap[i + 1])[0];
-          if (bull !== b && !(bull.length > 1 && b.length > 1)) {
-            src = cap.slice(i + 1).join('\n') + src;
-            i = l - 1;
-          }
-        }
-
-        // Determine whether item is loose or not.
-        // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
-        // for discount behavior.
-        loose = next || /\n\n(?!\s*$)/.test(item);
-        if (i !== l - 1) {
-          next = item.charAt(item.length - 1) === '\n';
-          if (!loose) loose = next;
-        }
-
-        this.tokens.push({
-          type: loose
-            ? 'loose_item_start'
-            : 'list_item_start'
-        });
-
-        // Recurse.
-        this.token(item, false, bq);
-
-        this.tokens.push({
-          type: 'list_item_end'
-        });
-      }
-
-      this.tokens.push({
-        type: 'list_end'
-      });
-
-      continue;
-    }
-
-    // html
-    if (cap = this.rules.html.exec(src)) {
-      src = src.substring(cap[0].length);
-      this.tokens.push({
-        type: this.options.sanitize
-          ? 'paragraph'
-          : 'html',
-        pre: !this.options.sanitizer
-          && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
-        text: cap[0]
-      });
-      continue;
-    }
-
-    // def
-    if ((!bq && top) && (cap = this.rules.def.exec(src))) {
-      src = src.substring(cap[0].length);
-      this.tokens.links[cap[1].toLowerCase()] = {
-        href: cap[2],
-        title: cap[3]
-      };
-      continue;
-    }
-
-    // table (gfm)
-    if (top && (cap = this.rules.table.exec(src))) {
-      src = src.substring(cap[0].length);
-
-      item = {
-        type: 'table',
-        header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
-        align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
-        cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
-      };
-
-      for (i = 0; i < item.align.length; 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;
-        }
-      }
-
-      for (i = 0; i < item.cells.length; i++) {
-        item.cells[i] = item.cells[i]
-          .replace(/^ *\| *| *\| *$/g, '')
-          .split(/ *\| */);
-      }
-
-      this.tokens.push(item);
-
-      continue;
-    }
-
-    // top-level paragraph
-    if (top && (cap = this.rules.paragraph.exec(src))) {
-      src = src.substring(cap[0].length);
-      this.tokens.push({
-        type: 'paragraph',
-        text: cap[1].charAt(cap[1].length - 1) === '\n'
-          ? cap[1].slice(0, -1)
-          : cap[1]
-      });
-      continue;
-    }
-
-    // text
-    if (cap = this.rules.text.exec(src)) {
-      // Top-level should never reach here.
-      src = src.substring(cap[0].length);
-      this.tokens.push({
-        type: 'text',
-        text: cap[0]
-      });
-      continue;
-    }
-
-    if (src) {
-      throw new
-        Error('Infinite loop on byte: ' + src.charCodeAt(0));
-    }
-  }
-
-  return this.tokens;
-};
-
-/**
- * Inline-Level Grammar
- */
-
-var inline = {
-  escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
-  autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
-  url: noop,
-  tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
-  link: /^!?\[(inside)\]\(href\)/,
-  reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
-  nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
-  strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
-  em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
-  code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
-  br: /^ {2,}\n(?!\s*$)/,
-  del: noop,
-  text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
-};
-
-inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
-inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
-
-inline.link = replace(inline.link)
-  ('inside', inline._inside)
-  ('href', inline._href)
-  ();
-
-inline.reflink = replace(inline.reflink)
-  ('inside', inline._inside)
-  ();
-
-/**
- * Normal Inline Grammar
- */
-
-inline.normal = merge({}, inline);
-
-/**
- * Pedantic Inline Grammar
- */
-
-inline.pedantic = merge({}, inline.normal, {
-  strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
-  em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
-});
-
-/**
- * GFM Inline Grammar
- */
-
-inline.gfm = merge({}, inline.normal, {
-  escape: replace(inline.escape)('])', '~|])')(),
-  url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
-  del: /^~~(?=\S)([\s\S]*?\S)~~/,
-  text: replace(inline.text)
-    (']|', '~]|')
-    ('|', '|https?://|')
-    ()
-});
-
-/**
- * GFM + Line Breaks Inline Grammar
- */
-
-inline.breaks = merge({}, inline.gfm, {
-  br: replace(inline.br)('{2,}', '*')(),
-  text: replace(inline.gfm.text)('{2,}', '*')()
-});
-
-/**
- * Inline Lexer & Compiler
- */
-
-function InlineLexer(links, options) {
-  this.options = options || marked.defaults;
-  this.links = links;
-  this.rules = inline.normal;
-  this.renderer = this.options.renderer || new Renderer;
-  this.renderer.options = this.options;
-
-  if (!this.links) {
-    throw new
-      Error('Tokens array requires a `links` property.');
-  }
-
-  if (this.options.gfm) {
-    if (this.options.breaks) {
-      this.rules = inline.breaks;
-    } else {
-      this.rules = inline.gfm;
-    }
-  } else if (this.options.pedantic) {
-    this.rules = inline.pedantic;
-  }
-}
-
-/**
- * Expose Inline Rules
- */
-
-InlineLexer.rules = inline;
-
-/**
- * Static Lexing/Compiling Method
- */
-
-InlineLexer.output = function(src, links, options) {
-  var inline = new InlineLexer(links, options);
-  return inline.output(src);
-};
-
-/**
- * Lexing/Compiling
- */
-
-InlineLexer.prototype.output = function(src) {
-  var out = ''
-    , link
-    , text
-    , href
-    , cap;
-
-  while (src) {
-    // escape
-    if (cap = this.rules.escape.exec(src)) {
-      src = src.substring(cap[0].length);
-      out += cap[1];
-      continue;
-    }
-
-    // autolink
-    if (cap = this.rules.autolink.exec(src)) {
-      src = src.substring(cap[0].length);
-      if (cap[2] === '@') {
-        text = cap[1].charAt(6) === ':'
-          ? this.mangle(cap[1].substring(7))
-          : this.mangle(cap[1]);
-        href = this.mangle('mailto:') + text;
-      } else {
-        text = escape(cap[1]);
-        href = text;
-      }
-      out += this.renderer.link(href, null, text);
-      continue;
-    }
-
-    // url (gfm)
-    if (!this.inLink && (cap = this.rules.url.exec(src))) {
-      src = src.substring(cap[0].length);
-      text = escape(cap[1]);
-      href = text;
-      out += this.renderer.link(href, null, text);
-      continue;
-    }
-
-    // tag
-    if (cap = this.rules.tag.exec(src)) {
-      if (!this.inLink && /^<a /i.test(cap[0])) {
-        this.inLink = true;
-      } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
-        this.inLink = false;
-      }
-      src = src.substring(cap[0].length);
-      out += this.options.sanitize
-        ? this.options.sanitizer
-          ? this.options.sanitizer(cap[0])
-          : escape(cap[0])
-        : cap[0]
-      continue;
-    }
-
-    // link
-    if (cap = this.rules.link.exec(src)) {
-      src = src.substring(cap[0].length);
-      this.inLink = true;
-      out += this.outputLink(cap, {
-        href: cap[2],
-        title: cap[3]
-      });
-      this.inLink = false;
-      continue;
-    }
-
-    // reflink, nolink
-    if ((cap = this.rules.reflink.exec(src))
-        || (cap = this.rules.nolink.exec(src))) {
-      src = src.substring(cap[0].length);
-      link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
-      link = this.links[link.toLowerCase()];
-      if (!link || !link.href) {
-        out += cap[0].charAt(0);
-        src = cap[0].substring(1) + src;
-        continue;
-      }
-      this.inLink = true;
-      out += this.outputLink(cap, link);
-      this.inLink = false;
-      continue;
-    }
-
-    // strong
-    if (cap = this.rules.strong.exec(src)) {
-      src = src.substring(cap[0].length);
-      out += this.renderer.strong(this.output(cap[2] || cap[1]));
-      continue;
-    }
-
-    // em
-    if (cap = this.rules.em.exec(src)) {
-      src = src.substring(cap[0].length);
-      out += this.renderer.em(this.output(cap[2] || cap[1]));
-      continue;
-    }
-
-    // code
-    if (cap = this.rules.code.exec(src)) {
-      src = src.substring(cap[0].length);
-      out += this.renderer.codespan(escape(cap[2], true));
-      continue;
-    }
-
-    // br
-    if (cap = this.rules.br.exec(src)) {
-      src = src.substring(cap[0].length);
-      out += this.renderer.br();
-      continue;
-    }
-
-    // del (gfm)
-    if (cap = this.rules.del.exec(src)) {
-      src = src.substring(cap[0].length);
-      out += this.renderer.del(this.output(cap[1]));
-      continue;
-    }
-
-    // text
-    if (cap = this.rules.text.exec(src)) {
-      src = src.substring(cap[0].length);
-      out += this.renderer.text(escape(this.smartypants(cap[0])));
-      continue;
-    }
-
-    if (src) {
-      throw new
-        Error('Infinite loop on byte: ' + src.charCodeAt(0));
-    }
-  }
-
-  return out;
-};
-
-/**
- * Compile Link
- */
-
-InlineLexer.prototype.outputLink = function(cap, link) {
-  var href = escape(link.href)
-    , title = link.title ? escape(link.title) : null;
-
-  return cap[0].charAt(0) !== '!'
-    ? this.renderer.link(href, title, this.output(cap[1]))
-    : this.renderer.image(href, title, escape(cap[1]));
-};
-
-/**
- * Smartypants Transformations
- */
-
-InlineLexer.prototype.smartypants = function(text) {
-  if (!this.options.smartypants) return text;
-  return text
-    // em-dashes
-    .replace(/---/g, '\u2014')
-    // en-dashes
-    .replace(/--/g, '\u2013')
-    // opening singles
-    .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
-    // closing singles & apostrophes
-    .replace(/'/g, '\u2019')
-    // opening doubles
-    .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
-    // closing doubles
-    .replace(/"/g, '\u201d')
-    // ellipses
-    .replace(/\.{3}/g, '\u2026');
-};
-
-/**
- * Mangle Links
- */
-
-InlineLexer.prototype.mangle = function(text) {
-  if (!this.options.mangle) return text;
-  var out = ''
-    , l = text.length
-    , i = 0
-    , ch;
-
-  for (; i < l; i++) {
-    ch = text.charCodeAt(i);
-    if (Math.random() > 0.5) {
-      ch = 'x' + ch.toString(16);
-    }
-    out += '&#' + ch + ';';
-  }
-
-  return out;
-};
-
-/**
- * Renderer
- */
-
-function Renderer(options) {
-  this.options = options || { };
-}
-
-Renderer.prototype.code = function(code, lang, escaped) {
-  if (this.options.highlight) {
-    var out = this.options.highlight(code, lang);
-    if (out != null && out !== code) {
-      escaped = true;
-      code = out;
-    }
-  }
-
-  if (!lang) {
-    return '<pre><code>'
-      + (escaped ? code : escape(code, true))
-      + '\n</code></pre>';
-  }
-
-  return '<pre><code class="'
-    + this.options.langPrefix
-    + escape(lang, true)
-    + '">'
-    + (escaped ? code : escape(code, true))
-    + '\n</code></pre>\n';
-};
-
-Renderer.prototype.blockquote = function(quote) {
-  return '<blockquote>\n' + quote + '</blockquote>\n';
-};
-
-Renderer.prototype.html = function(html) {
-  return html;
-};
-
-Renderer.prototype.heading = function(text, level, raw) {
-  return '<h'
-    + level
-    + ' id="'
-    + this.options.headerPrefix
-    + raw.toLowerCase().replace(/[^\w]+/g, '-')
-    + '">'
-    + text
-    + '</h'
-    + level
-    + '>\n';
-};
-
-Renderer.prototype.hr = function() {
-  return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
-};
-
-Renderer.prototype.list = function(body, ordered) {
-  var type = ordered ? 'ol' : 'ul';
-  return '<' + type + '>\n' + body + '</' + type + '>\n';
-};
-
-Renderer.prototype.listitem = function(text) {
-  return '<li>' + text + '</li>\n';
-};
-
-Renderer.prototype.paragraph = function(text) {
-  return '<p>' + text + '</p>\n';
-};
-
-Renderer.prototype.table = function(header, body) {
-  return '<table>\n'
-    + '<thead>\n'
-    + header
-    + '</thead>\n'
-    + '<tbody>\n'
-    + body
-    + '</tbody>\n'
-    + '</table>\n';
-};
-
-Renderer.prototype.tablerow = function(content) {
-  return '<tr>\n' + content + '</tr>\n';
-};
-
-Renderer.prototype.tablecell = function(content, flags) {
-  var type = flags.header ? 'th' : 'td';
-  var tag = flags.align
-    ? '<' + type + ' style="text-align:' + flags.align + '">'
-    : '<' + type + '>';
-  return tag + content + '</' + type + '>\n';
-};
-
-// span level renderer
-Renderer.prototype.strong = function(text) {
-  return '<strong>' + text + '</strong>';
-};
-
-Renderer.prototype.em = function(text) {
-  return '<em>' + text + '</em>';
-};
-
-Renderer.prototype.codespan = function(text) {
-  return '<code>' + text + '</code>';
-};
-
-Renderer.prototype.br = function() {
-  return this.options.xhtml ? '<br/>' : '<br>';
-};
-
-Renderer.prototype.del = function(text) {
-  return '<del>' + text + '</del>';
-};
-
-Renderer.prototype.link = function(href, title, text) {
-  if (this.options.sanitize) {
-    try {
-      var prot = decodeURIComponent(unescape(href))
-        .replace(/[^\w:]/g, '')
-        .toLowerCase();
-    } catch (e) {
-      return '';
-    }
-    if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
-      return '';
-    }
-  }
-  var out = '<a href="' + href + '"';
-  if (title) {
-    out += ' title="' + title + '"';
-  }
-  out += '>' + text + '</a>';
-  return out;
-};
-
-Renderer.prototype.image = function(href, title, text) {
-  var out = '<img src="' + href + '" alt="' + text + '"';
-  if (title) {
-    out += ' title="' + title + '"';
-  }
-  out += this.options.xhtml ? '/>' : '>';
-  return out;
-};
-
-Renderer.prototype.text = function(text) {
-  return text;
-};
-
-/**
- * Parsing & Compiling
- */
-
-function Parser(options) {
-  this.tokens = [];
-  this.token = null;
-  this.options = options || marked.defaults;
-  this.options.renderer = this.options.renderer || new Renderer;
-  this.renderer = this.options.renderer;
-  this.renderer.options = this.options;
-}
-
-/**
- * Static Parse Method
- */
-
-Parser.parse = function(src, options, renderer) {
-  var parser = new Parser(options, renderer);
-  return parser.parse(src);
-};
-
-/**
- * Parse Loop
- */
-
-Parser.prototype.parse = function(src) {
-  this.inline = new InlineLexer(src.links, this.options, this.renderer);
-  this.tokens = src.reverse();
-
-  var out = '';
-  while (this.next()) {
-    out += this.tok();
-  }
-
-  return out;
-};
-
-/**
- * Next Token
- */
-
-Parser.prototype.next = function() {
-  return this.token = this.tokens.pop();
-};
-
-/**
- * Preview Next Token
- */
-
-Parser.prototype.peek = function() {
-  return this.tokens[this.tokens.length - 1] || 0;
-};
-
-/**
- * Parse Text Tokens
- */
-
-Parser.prototype.parseText = function() {
-  var body = this.token.text;
-
-  while (this.peek().type === 'text') {
-    body += '\n' + this.next().text;
-  }
-
-  return this.inline.output(body);
-};
-
-/**
- * Parse Current Token
- */
-
-Parser.prototype.tok = function() {
-  switch (this.token.type) {
-    case 'space': {
-      return '';
-    }
-    case 'hr': {
-      return this.renderer.hr();
-    }
-    case 'heading': {
-      return this.renderer.heading(
-        this.inline.output(this.token.text),
-        this.token.depth,
-        this.token.text);
-    }
-    case 'code': {
-      return this.renderer.code(this.token.text,
-        this.token.lang,
-        this.token.escaped);
-    }
-    case 'table': {
-      var header = ''
-        , body = ''
-        , i
-        , row
-        , cell
-        , flags
-        , j;
-
-      // header
-      cell = '';
-      for (i = 0; i < this.token.header.length; i++) {
-        flags = { header: true, align: this.token.align[i] };
-        cell += this.renderer.tablecell(
-          this.inline.output(this.token.header[i]),
-          { header: true, align: this.token.align[i] }
-        );
-      }
-      header += this.renderer.tablerow(cell);
-
-      for (i = 0; i < this.token.cells.length; i++) {
-        row = this.token.cells[i];
-
-        cell = '';
-        for (j = 0; j < row.length; j++) {
-          cell += this.renderer.tablecell(
-            this.inline.output(row[j]),
-            { header: false, align: this.token.align[j] }
-          );
-        }
-
-        body += this.renderer.tablerow(cell);
-      }
-      return this.renderer.table(header, body);
-    }
-    case 'blockquote_start': {
-      var body = '';
-
-      while (this.next().type !== 'blockquote_end') {
-        body += this.tok();
-      }
-
-      return this.renderer.blockquote(body);
-    }
-    case 'list_start': {
-      var body = ''
-        , ordered = this.token.ordered;
-
-      while (this.next().type !== 'list_end') {
-        body += this.tok();
-      }
-
-      return this.renderer.list(body, ordered);
-    }
-    case 'list_item_start': {
-      var body = '';
-
-      while (this.next().type !== 'list_item_end') {
-        body += this.token.type === 'text'
-          ? this.parseText()
-          : this.tok();
-      }
-
-      return this.renderer.listitem(body);
-    }
-    case 'loose_item_start': {
-      var body = '';
-
-      while (this.next().type !== 'list_item_end') {
-        body += this.tok();
-      }
-
-      return this.renderer.listitem(body);
-    }
-    case 'html': {
-      var html = !this.token.pre && !this.options.pedantic
-        ? this.inline.output(this.token.text)
-        : this.token.text;
-      return this.renderer.html(html);
-    }
-    case 'paragraph': {
-      return this.renderer.paragraph(this.inline.output(this.token.text));
-    }
-    case 'text': {
-      return this.renderer.paragraph(this.parseText());
-    }
-  }
-};
-
-/**
- * Helpers
- */
-
-function escape(html, encode) {
-  return html
-    .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
-    .replace(/</g, '&lt;')
-    .replace(/>/g, '&gt;')
-    .replace(/"/g, '&quot;')
-    .replace(/'/g, '&#39;');
-}
-
-function unescape(html) {
-	// explicitly match decimal, hex, and named HTML entities
-  return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
-    n = n.toLowerCase();
-    if (n === 'colon') return ':';
-    if (n.charAt(0) === '#') {
-      return n.charAt(1) === 'x'
-        ? String.fromCharCode(parseInt(n.substring(2), 16))
-        : String.fromCharCode(+n.substring(1));
-    }
-    return '';
-  });
-}
-
-function replace(regex, opt) {
-  regex = regex.source;
-  opt = opt || '';
-  return function self(name, val) {
-    if (!name) return new RegExp(regex, opt);
-    val = val.source || val;
-    val = val.replace(/(^|[^\[])\^/g, '$1');
-    regex = regex.replace(name, val);
-    return self;
-  };
-}
-
-function noop() {}
-noop.exec = noop;
-
-function merge(obj) {
-  var i = 1
-    , target
-    , key;
-
-  for (; i < arguments.length; i++) {
-    target = arguments[i];
-    for (key in target) {
-      if (Object.prototype.hasOwnProperty.call(target, key)) {
-        obj[key] = target[key];
-      }
-    }
-  }
-
-  return obj;
-}
-
-
-/**
- * Marked
- */
-
-function marked(src, opt, callback) {
-  if (callback || typeof opt === 'function') {
-    if (!callback) {
-      callback = opt;
-      opt = null;
-    }
-
-    opt = merge({}, marked.defaults, opt || {});
-
-    var highlight = opt.highlight
-      , tokens
-      , pending
-      , i = 0;
-
-    try {
-      tokens = Lexer.lex(src, opt)
-    } catch (e) {
-      return callback(e);
-    }
-
-    pending = tokens.length;
-
-    var done = function(err) {
-      if (err) {
-        opt.highlight = highlight;
-        return callback(err);
-      }
-
-      var out;
-
-      try {
-        out = Parser.parse(tokens, opt);
-      } catch (e) {
-        err = e;
-      }
-
-      opt.highlight = highlight;
-
-      return err
-        ? callback(err)
-        : callback(null, out);
-    };
-
-    if (!highlight || highlight.length < 3) {
-      return done();
-    }
-
-    delete opt.highlight;
-
-    if (!pending) return done();
-
-    for (; i < tokens.length; i++) {
-      (function(token) {
-        if (token.type !== 'code') {
-          return --pending || done();
-        }
-        return highlight(token.text, token.lang, function(err, code) {
-          if (err) return done(err);
-          if (code == null || code === token.text) {
-            return --pending || done();
-          }
-          token.text = code;
-          token.escaped = true;
-          --pending || done();
-        });
-      })(tokens[i]);
-    }
-
-    return;
-  }
-  try {
-    if (opt) opt = merge({}, marked.defaults, opt);
-    return Parser.parse(Lexer.lex(src, opt), opt);
-  } catch (e) {
-    e.message += '\nPlease report this to https://github.com/chjj/marked.';
-    if ((opt || marked.defaults).silent) {
-      return '<p>An error occured:</p><pre>'
-        + escape(e.message + '', true)
-        + '</pre>';
-    }
-    throw e;
-  }
-}
-
-/**
- * Options
- */
-
-marked.options =
-marked.setOptions = function(opt) {
-  merge(marked.defaults, opt);
-  return marked;
-};
-
-marked.defaults = {
-  gfm: true,
-  tables: true,
-  breaks: false,
-  pedantic: false,
-  sanitize: false,
-  sanitizer: null,
-  mangle: true,
-  smartLists: false,
-  silent: false,
-  highlight: null,
-  langPrefix: 'lang-',
-  smartypants: false,
-  headerPrefix: '',
-  renderer: new Renderer,
-  xhtml: false
-};
-
-/**
- * Expose
- */
-
-marked.Parser = Parser;
-marked.parser = Parser.parse;
-
-marked.Renderer = Renderer;
-
-marked.Lexer = Lexer;
-marked.lexer = Lexer.lex;
-
-marked.InlineLexer = InlineLexer;
-marked.inlineLexer = InlineLexer.output;
-
-marked.parse = marked;
-
-if (typeof module !== 'undefined' && typeof exports === 'object') {
-  module.exports = marked;
-} else if (typeof define === 'function' && define.amd) {
-  define(function() { return marked; });
-} else {
-  this.marked = marked;
-}
-
-}).call(function() {
-  return this || (typeof window !== 'undefined' ? window : global);
-}());

+ 0 - 175
packages/ckeditor5-markdown-gfm/src/lib/marked/renderer.js

@@ -1,175 +0,0 @@
-/**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
- */
-
-/**
- * Original marked.js library renderer with fixes:
- * - No formatting for output HTML string &mdash; all newlines between tags are removed to create clean output.
- * - Changed long string concatenations to ES5 template strings.
- * - Changed code style.
- *
- * @see {@link https://github.com/chjj/marked#renderer} Methods description.
- * @param options
- * @constructor
- */
-function Renderer( options ) {
-	this.options = options || {};
-}
-
-Renderer.prototype.code = function( code, lang, escaped ) {
-	if ( this.options.highlight ) {
-		const out = this.options.highlight( code, lang );
-
-		if ( out !== null && out !== code ) {
-			escaped = true;
-			code = out;
-		}
-	}
-
-	if ( !lang ) {
-		return `<pre><code>${ escaped ? code : escape( code, true ) }</code></pre>`;
-	}
-
-	const cssClass = this.options.langPrefix + escape( lang, true );
-
-	return `<pre><code class="${ cssClass }">${ escaped ? code : escape( code, true ) }</code></pre>`;
-};
-
-Renderer.prototype.blockquote = function( quote ) {
-	return `<blockquote>${ quote }</blockquote>`;
-};
-
-Renderer.prototype.html = function( html ) {
-	return html;
-};
-
-Renderer.prototype.heading = function( text, level, raw ) {
-	return `<h${ level }>${ text }</h${ level }>`;
-};
-
-Renderer.prototype.hr = function() {
-	return this.options.xhtml ? '<hr/>' : '<hr>';
-};
-
-Renderer.prototype.list = function( body, ordered ) {
-	const type = ordered ? 'ol' : 'ul';
-
-	return `<${ type }>${ body }</${ type }>`;
-};
-
-Renderer.prototype.listitem = function( text ) {
-	return `<li>${ text }</li>`;
-};
-
-Renderer.prototype.paragraph = function( text ) {
-	return `<p>${ text }</p>`;
-};
-
-Renderer.prototype.table = function( header, body ) {
-	return `<table><thead>${ header }</thead><tbody>${ body }</tbody></table>`;
-};
-
-Renderer.prototype.tablerow = function( content ) {
-	return '<tr>' + content + '</tr>';
-};
-
-Renderer.prototype.tablecell = function( content, flags ) {
-	const type = flags.header ? 'th' : 'td';
-	const tag = flags.align ? `<${ type } align="${ flags.align }">` : `<${ type }>`;
-
-	return tag + content + `</${ type }>`;
-};
-
-// span level renderer
-Renderer.prototype.strong = function( text ) {
-	return `<strong>${ text }</strong>`;
-};
-
-Renderer.prototype.em = function( text ) {
-	return `<em>${ text }</em>`;
-};
-
-Renderer.prototype.codespan = function( text ) {
-	return `<code>${ text.trim() }</code>`;
-};
-
-Renderer.prototype.br = function() {
-	return this.options.xhtml ? '<br/>' : '<br>';
-};
-
-Renderer.prototype.del = function( text ) {
-	return `<del>${ text }</del>`;
-};
-
-Renderer.prototype.link = function( href, title, text ) {
-	if ( this.options.sanitize ) {
-		let prot;
-
-		try {
-			prot = decodeURIComponent( unescape( href ) )
-				.replace( /[^\w:]/g, '' )
-				.toLowerCase();
-		} catch ( e ) {
-			return '';
-		}
-
-		if ( prot.indexOf( 'javascript:' ) === 0 || prot.indexOf( 'vbscript:' ) === 0 ) { // jshint ignore:line
-			return '';
-		}
-	}
-
-	let out = '<a href="' + href + '"';
-
-	if ( title ) {
-		out += ' title="' + title + '"';
-	}
-	out += '>' + text + '</a>';
-
-	return out;
-};
-
-Renderer.prototype.image = function( href, title, text ) {
-	let out = '<img src="' + href + '" alt="' + text + '"';
-
-	if ( title ) {
-		out += ' title="' + title + '"';
-	}
-	out += this.options.xhtml ? '/>' : '>';
-
-	return out;
-};
-
-Renderer.prototype.text = function( text ) {
-	return text;
-};
-
-export default Renderer;
-
-function escape( html, encode ) {
-	return html
-		.replace( !encode ? /&(?!#?\w+;)/g : /&/g, '&amp;' )
-		.replace( /</g, '&lt;' )
-		.replace( />/g, '&gt;' )
-		.replace( /"/g, '&quot;' )
-		.replace( /'/g, '&#39;' );
-}
-
-function unescape( html ) {
-	// explicitly match decimal, hex, and named HTML entities
-	return html.replace( /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function( _, n ) {
-		n = n.toLowerCase();
-
-		if ( n === 'colon' ) {
-			return ':';
-		}
-
-		if ( n.charAt( 0 ) === '#' ) {
-			return n.charAt( 1 ) === 'x' ?
-				String.fromCharCode( parseInt( n.substring( 2 ), 16 ) ) :
-				String.fromCharCode( +n.substring( 1 ) ); // jscs:ignore
-		}
-
-		return '';
-	} );
-}

+ 0 - 68
packages/ckeditor5-markdown-gfm/src/lib/to-markdown/converters.js

@@ -1,68 +0,0 @@
-/**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
- */
-
-// Exports an array with custom converters used by to-markdown library.
-export default [
-	// Converting code blocks with class name matching output from marked library.
-	{
-		filter: ( node ) =>  {
-			const regexp = /lang-(.+)/;
-
-			return node.nodeName === 'PRE' &&
-				node.firstChild &&
-				node.firstChild.nodeName === 'CODE' &&
-				regexp.test( node.firstChild.className );
-		},
-		replacement: ( content, node ) => {
-			const regexp = /lang-(.+)/;
-			const lang = regexp.exec( node.firstChild.className )[ 1 ];
-
-			return '\n\n``` ' + lang + '\n' + node.firstChild.textContent + '\n```\n\n';
-		}
-	},
-	// Converting empty links.
-	{
-		filter: ( node ) => {
-			return node.nodeName === 'A' && !node.getAttribute( 'href' );
-		},
-
-		replacement: ( content, node ) => {
-			const title = node.title ? `"${node.title}"` : '';
-
-			return `[${ content }](${ title })`;
-		}
-	},
-	// Headers - fixing newline at the beginning.
-	{
-		filter: [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ],
-		replacement: ( content, node ) => {
-			const hLevel = node.nodeName.charAt( 1 );
-			let hPrefix = '';
-
-			for ( let i = 0; i < hLevel; i++ ) {
-				hPrefix += '#';
-			}
-
-			return hPrefix + ' ' + content + '\n';
-		}
-	},
-	// Inline code - fixing backticks inside code blocks.
-	{
-		filter: ( node ) => {
-			const hasSiblings = node.previousSibling || node.nextSibling;
-			const isCodeBlock = node.parentNode.nodeName === 'PRE' && !hasSiblings;
-
-			return node.nodeName === 'CODE' && !isCodeBlock;
-		},
-		replacement: ( content ) => {
-			// If content starts or ends with backtick - use double backtick.
-			if ( content.indexOf( '`' ) > -1 ) {
-				return '`` ' + content + ' ``';
-			}
-
-			return '`' + content + '`';
-		}
-	}
-];

+ 0 - 785
packages/ckeditor5-markdown-gfm/src/lib/to-markdown/to-markdown.js

@@ -1,785 +0,0 @@
-(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.toMarkdown = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
-	/*
-	 * to-markdown - an HTML to Markdown converter
-	 *
-	 * Copyright 2011+, Dom Christie
-	 * Licenced under the MIT licence
-	 *
-	 */
-
-	'use strict'
-
-	var toMarkdown
-	var converters
-	var mdConverters = require('./lib/md-converters')
-	var gfmConverters = require('./lib/gfm-converters')
-	var HtmlParser = require('./lib/html-parser')
-	var collapse = require('collapse-whitespace')
-
-	/*
-	 * Utilities
-	 */
-
-	var blocks = ['address', 'article', 'aside', 'audio', 'blockquote', 'body',
-		'canvas', 'center', 'dd', 'dir', 'div', 'dl', 'dt', 'fieldset', 'figcaption',
-		'figure', 'footer', 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
-		'header', 'hgroup', 'hr', 'html', 'isindex', 'li', 'main', 'menu', 'nav',
-		'noframes', 'noscript', 'ol', 'output', 'p', 'pre', 'section', 'table',
-		'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'ul'
-	]
-
-	function isBlock (node) {
-		return blocks.indexOf(node.nodeName.toLowerCase()) !== -1
-	}
-
-	var voids = [
-		'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input',
-		'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'
-	]
-
-	function isVoid (node) {
-		return voids.indexOf(node.nodeName.toLowerCase()) !== -1
-	}
-
-	function htmlToDom (string) {
-		var tree = new HtmlParser().parseFromString(string, 'text/html')
-		collapse(tree.documentElement, isBlock)
-		return tree
-	}
-
-	/*
-	 * Flattens DOM tree into single array
-	 */
-
-	function bfsOrder (node) {
-		var inqueue = [node]
-		var outqueue = []
-		var elem
-		var children
-		var i
-
-		while (inqueue.length > 0) {
-			elem = inqueue.shift()
-			outqueue.push(elem)
-			children = elem.childNodes
-			for (i = 0; i < children.length; i++) {
-				if (children[i].nodeType === 1) inqueue.push(children[i])
-			}
-		}
-		outqueue.shift()
-		return outqueue
-	}
-
-	/*
-	 * Contructs a Markdown string of replacement text for a given node
-	 */
-
-	function getContent (node) {
-		var text = ''
-		for (var i = 0; i < node.childNodes.length; i++) {
-			if (node.childNodes[i].nodeType === 1) {
-				text += node.childNodes[i]._replacement
-			} else if (node.childNodes[i].nodeType === 3) {
-				text += node.childNodes[i].data
-			} else continue
-		}
-		return text
-	}
-
-	/*
-	 * Returns the HTML string of an element with its contents converted
-	 */
-
-	function outer (node, content) {
-		return node.cloneNode(false).outerHTML.replace('><', '>' + content + '<')
-	}
-
-	function canConvert (node, filter) {
-		if (typeof filter === 'string') {
-			return filter === node.nodeName.toLowerCase()
-		}
-		if (Array.isArray(filter)) {
-			return filter.indexOf(node.nodeName.toLowerCase()) !== -1
-		} else if (typeof filter === 'function') {
-			return filter.call(toMarkdown, node)
-		} else {
-			throw new TypeError('`filter` needs to be a string, array, or function')
-		}
-	}
-
-	function isFlankedByWhitespace (side, node) {
-		var sibling
-		var regExp
-		var isFlanked
-
-		if (side === 'left') {
-			sibling = node.previousSibling
-			regExp = / $/
-		} else {
-			sibling = node.nextSibling
-			regExp = /^ /
-		}
-
-		if (sibling) {
-			if (sibling.nodeType === 3) {
-				isFlanked = regExp.test(sibling.nodeValue)
-			} else if (sibling.nodeType === 1 && !isBlock(sibling)) {
-				isFlanked = regExp.test(sibling.textContent)
-			}
-		}
-		return isFlanked
-	}
-
-	function flankingWhitespace (node) {
-		var leading = ''
-		var trailing = ''
-
-		if (!isBlock(node)) {
-			var hasLeading = /^[ \r\n\t]/.test(node.innerHTML)
-			var hasTrailing = /[ \r\n\t]$/.test(node.innerHTML)
-
-			if (hasLeading && !isFlankedByWhitespace('left', node)) {
-				leading = ' '
-			}
-			if (hasTrailing && !isFlankedByWhitespace('right', node)) {
-				trailing = ' '
-			}
-		}
-
-		return { leading: leading, trailing: trailing }
-	}
-
-	/*
-	 * Finds a Markdown converter, gets the replacement, and sets it on
-	 * `_replacement`
-	 */
-
-	function process (node) {
-		var replacement
-		var content = getContent(node)
-
-		// Remove blank nodes
-		if (!isVoid(node) && !/A|TH|TD/.test(node.nodeName) && /^\s*$/i.test(content)) {
-			node._replacement = ''
-			return
-		}
-
-		for (var i = 0; i < converters.length; i++) {
-			var converter = converters[i]
-
-			if (canConvert(node, converter.filter)) {
-				if (typeof converter.replacement !== 'function') {
-					throw new TypeError(
-						'`replacement` needs to be a function that returns a string'
-					)
-				}
-
-				var whitespace = flankingWhitespace(node)
-
-				if (whitespace.leading || whitespace.trailing) {
-					content = content.trim()
-				}
-				replacement = whitespace.leading +
-					converter.replacement.call(toMarkdown, content, node) +
-					whitespace.trailing
-				break
-			}
-		}
-
-		node._replacement = replacement
-	}
-
-	toMarkdown = function (input, options) {
-		options = options || {}
-
-		if (typeof input !== 'string') {
-			throw new TypeError(input + ' is not a string')
-		}
-
-		// Escape potential ol triggers
-		input = input.replace(/(\d+)\. /g, '$1\\. ')
-
-		var clone = htmlToDom(input).body
-		var nodes = bfsOrder(clone)
-		var output
-
-		converters = mdConverters.slice(0)
-		if (options.gfm) {
-			converters = gfmConverters.concat(converters)
-		}
-
-		if (options.converters) {
-			converters = options.converters.concat(converters)
-		}
-
-		// Process through nodes in reverse (so deepest child elements are first).
-		for (var i = nodes.length - 1; i >= 0; i--) {
-			process(nodes[i])
-		}
-		output = getContent(clone)
-
-		return output.replace(/^[\t\r\n]+|[\t\r\n\s]+$/g, '')
-			.replace(/\n\s+\n/g, '\n\n')
-			.replace(/\n{3,}/g, '\n\n')
-	}
-
-	toMarkdown.isBlock = isBlock
-	toMarkdown.isVoid = isVoid
-	toMarkdown.outer = outer
-
-	module.exports = toMarkdown
-
-},{"./lib/gfm-converters":2,"./lib/html-parser":3,"./lib/md-converters":4,"collapse-whitespace":7}],2:[function(require,module,exports){
-	'use strict'
-
-	function cell (content, node) {
-		var index = Array.prototype.indexOf.call(node.parentNode.childNodes, node)
-		var prefix = ' '
-		if (index === 0) prefix = '| '
-		return prefix + content + ' |'
-	}
-
-	var highlightRegEx = /highlight highlight-(\S+)/
-
-	module.exports = [
-		{
-			filter: 'br',
-			replacement: function () {
-				return '\n'
-			}
-		},
-		{
-			filter: ['del', 's', 'strike'],
-			replacement: function (content) {
-				return '~~' + content + '~~'
-			}
-		},
-
-		{
-			filter: function (node) {
-				return node.type === 'checkbox' && node.parentNode.nodeName === 'LI'
-			},
-			replacement: function (content, node) {
-				return (node.checked ? '[x]' : '[ ]') + ' '
-			}
-		},
-
-		{
-			filter: ['th', 'td'],
-			replacement: function (content, node) {
-				return cell(content, node)
-			}
-		},
-
-		{
-			filter: 'tr',
-			replacement: function (content, node) {
-				var borderCells = ''
-				var alignMap = { left: ':--', right: '--:', center: ':-:' }
-
-				if (node.parentNode.nodeName === 'THEAD') {
-					for (var i = 0; i < node.childNodes.length; i++) {
-						var align = node.childNodes[i].attributes.align
-						var border = '---'
-
-						if (align) border = alignMap[align.value] || border
-
-						borderCells += cell(border, node.childNodes[i])
-					}
-				}
-				return '\n' + content + (borderCells ? '\n' + borderCells : '')
-			}
-		},
-
-		{
-			filter: 'table',
-			replacement: function (content) {
-				return '\n\n' + content + '\n\n'
-			}
-		},
-
-		{
-			filter: ['thead', 'tbody', 'tfoot'],
-			replacement: function (content) {
-				return content
-			}
-		},
-
-		// Fenced code blocks
-		{
-			filter: function (node) {
-				return node.nodeName === 'PRE' &&
-					node.firstChild &&
-					node.firstChild.nodeName === 'CODE'
-			},
-			replacement: function (content, node) {
-				return '\n\n```\n' + node.firstChild.textContent + '\n```\n\n'
-			}
-		},
-
-		// Syntax-highlighted code blocks
-		{
-			filter: function (node) {
-				return node.nodeName === 'PRE' &&
-					node.parentNode.nodeName === 'DIV' &&
-					highlightRegEx.test(node.parentNode.className)
-			},
-			replacement: function (content, node) {
-				var language = node.parentNode.className.match(highlightRegEx)[1]
-				return '\n\n```' + language + '\n' + node.textContent + '\n```\n\n'
-			}
-		},
-
-		{
-			filter: function (node) {
-				return node.nodeName === 'DIV' &&
-					highlightRegEx.test(node.className)
-			},
-			replacement: function (content) {
-				return '\n\n' + content + '\n\n'
-			}
-		}
-	]
-
-},{}],3:[function(require,module,exports){
-	/*
-	 * Set up window for Node.js
-	 */
-
-	var _window = (typeof window !== 'undefined' ? window : this)
-
-	/*
-	 * Parsing HTML strings
-	 */
-
-	function canParseHtmlNatively () {
-		var Parser = _window.DOMParser
-		var canParse = false
-
-		// Adapted from https://gist.github.com/1129031
-		// Firefox/Opera/IE throw errors on unsupported types
-		try {
-			// WebKit returns null on unsupported types
-			if (new Parser().parseFromString('', 'text/html')) {
-				canParse = true
-			}
-		} catch (e) {}
-
-		return canParse
-	}
-
-	function createHtmlParser () {
-		var Parser = function () {}
-
-		// For Node.js environments
-		if (typeof document === 'undefined') {
-			var jsdom = require('jsdom')
-			Parser.prototype.parseFromString = function (string) {
-				return jsdom.jsdom(string, {
-					plugins: {
-						FetchExternalResources: [],
-						ProcessExternalResources: false
-					}
-				})
-			}
-		} else {
-			if (!shouldUseActiveX()) {
-				Parser.prototype.parseFromString = function (string) {
-					var doc = document.implementation.createHTMLDocument('')
-					doc.open()
-					doc.write(string)
-					doc.close()
-					return doc
-				}
-			} else {
-				Parser.prototype.parseFromString = function (string) {
-					var doc = new window.ActiveXObject('htmlfile')
-					doc.designMode = 'on' // disable on-page scripts
-					doc.open()
-					doc.write(string)
-					doc.close()
-					return doc
-				}
-			}
-		}
-		return Parser
-	}
-
-	function shouldUseActiveX () {
-		var useActiveX = false
-
-		try {
-			document.implementation.createHTMLDocument('').open()
-		} catch (e) {
-			if (window.ActiveXObject) useActiveX = true
-		}
-
-		return useActiveX
-	}
-
-	module.exports = canParseHtmlNatively() ? _window.DOMParser : createHtmlParser()
-
-},{"jsdom":6}],4:[function(require,module,exports){
-	'use strict'
-
-	module.exports = [
-		{
-			filter: 'p',
-			replacement: function (content) {
-				return '\n\n' + content + '\n\n'
-			}
-		},
-
-		{
-			filter: 'br',
-			replacement: function () {
-				return '  \n'
-			}
-		},
-
-		{
-			filter: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
-			replacement: function (content, node) {
-				var hLevel = node.nodeName.charAt(1)
-				var hPrefix = ''
-				for (var i = 0; i < hLevel; i++) {
-					hPrefix += '#'
-				}
-				return '\n\n' + hPrefix + ' ' + content + '\n\n'
-			}
-		},
-
-		{
-			filter: 'hr',
-			replacement: function () {
-				return '\n\n* * *\n\n'
-			}
-		},
-
-		{
-			filter: ['em', 'i'],
-			replacement: function (content) {
-				return '_' + content + '_'
-			}
-		},
-
-		{
-			filter: ['strong', 'b'],
-			replacement: function (content) {
-				return '**' + content + '**'
-			}
-		},
-
-		// Inline code
-		{
-			filter: function (node) {
-				var hasSiblings = node.previousSibling || node.nextSibling
-				var isCodeBlock = node.parentNode.nodeName === 'PRE' && !hasSiblings
-
-				return node.nodeName === 'CODE' && !isCodeBlock
-			},
-			replacement: function (content) {
-				return '`' + content + '`'
-			}
-		},
-
-		{
-			filter: function (node) {
-				return node.nodeName === 'A' && node.getAttribute('href')
-			},
-			replacement: function (content, node) {
-				var titlePart = node.title ? ' "' + node.title + '"' : ''
-				return '[' + content + '](' + node.getAttribute('href') + titlePart + ')'
-			}
-		},
-
-		{
-			filter: 'img',
-			replacement: function (content, node) {
-				var alt = node.alt || ''
-				var src = node.getAttribute('src') || ''
-				var title = node.title || ''
-				var titlePart = title ? ' "' + title + '"' : ''
-				return src ? '![' + alt + ']' + '(' + src + titlePart + ')' : ''
-			}
-		},
-
-		// Code blocks
-		{
-			filter: function (node) {
-				return node.nodeName === 'PRE' && node.firstChild.nodeName === 'CODE'
-			},
-			replacement: function (content, node) {
-				return '\n\n    ' + node.firstChild.textContent.replace(/\n/g, '\n    ') + '\n\n'
-			}
-		},
-
-		{
-			filter: 'blockquote',
-			replacement: function (content) {
-				content = content.trim()
-				content = content.replace(/\n{3,}/g, '\n\n')
-				content = content.replace(/^/gm, '> ')
-				return '\n\n' + content + '\n\n'
-			}
-		},
-
-		{
-			filter: 'li',
-			replacement: function (content, node) {
-				content = content.replace(/^\s+/, '').replace(/\n/gm, '\n    ')
-				var prefix = '*   '
-				var parent = node.parentNode
-				var index = Array.prototype.indexOf.call(parent.children, node) + 1
-
-				prefix = /ol/i.test(parent.nodeName) ? index + '.  ' : '*   '
-				return prefix + content
-			}
-		},
-
-		{
-			filter: ['ul', 'ol'],
-			replacement: function (content, node) {
-				var strings = []
-				for (var i = 0; i < node.childNodes.length; i++) {
-					strings.push(node.childNodes[i]._replacement)
-				}
-
-				if (/li/i.test(node.parentNode.nodeName)) {
-					return '\n' + strings.join('\n')
-				}
-				return '\n\n' + strings.join('\n') + '\n\n'
-			}
-		},
-
-		{
-			filter: function (node) {
-				return this.isBlock(node)
-			},
-			replacement: function (content, node) {
-				return '\n\n' + this.outer(node, content) + '\n\n'
-			}
-		},
-
-		// Anything else!
-		{
-			filter: function () {
-				return true
-			},
-			replacement: function (content, node) {
-				return this.outer(node, content)
-			}
-		}
-	]
-
-},{}],5:[function(require,module,exports){
-	/**
-	 * This file automatically generated from `build.js`.
-	 * Do not manually edit.
-	 */
-
-	module.exports = [
-		"address",
-		"article",
-		"aside",
-		"audio",
-		"blockquote",
-		"canvas",
-		"dd",
-		"div",
-		"dl",
-		"fieldset",
-		"figcaption",
-		"figure",
-		"footer",
-		"form",
-		"h1",
-		"h2",
-		"h3",
-		"h4",
-		"h5",
-		"h6",
-		"header",
-		"hgroup",
-		"hr",
-		"main",
-		"nav",
-		"noscript",
-		"ol",
-		"output",
-		"p",
-		"pre",
-		"section",
-		"table",
-		"tfoot",
-		"ul",
-		"video"
-	];
-
-},{}],6:[function(require,module,exports){
-
-},{}],7:[function(require,module,exports){
-	'use strict';
-
-	var voidElements = require('void-elements');
-	Object.keys(voidElements).forEach(function (name) {
-		voidElements[name.toUpperCase()] = 1;
-	});
-
-	var blockElements = {};
-	require('block-elements').forEach(function (name) {
-		blockElements[name.toUpperCase()] = 1;
-	});
-
-	/**
-	 * isBlockElem(node) determines if the given node is a block element.
-	 *
-	 * @param {Node} node
-	 * @returns {Boolean}
-	 */
-	function isBlockElem(node) {
-		return !!(node && blockElements[node.nodeName]);
-	}
-
-	/**
-	 * isVoid(node) determines if the given node is a void element.
-	 *
-	 * @param {Node} node
-	 * @returns {Boolean}
-	 */
-	function isVoid(node) {
-		return !!(node && voidElements[node.nodeName]);
-	}
-
-	/**
-	 * whitespace(elem [, isBlock]) removes extraneous whitespace from an
-	 * the given element. The function isBlock may optionally be passed in
-	 * to determine whether or not an element is a block element; if none
-	 * is provided, defaults to using the list of block elements provided
-	 * by the `block-elements` module.
-	 *
-	 * @param {Node} elem
-	 * @param {Function} blockTest
-	 */
-	function collapseWhitespace(elem, isBlock) {
-		if (!elem.firstChild || elem.nodeName === 'PRE') return;
-
-		if (typeof isBlock !== 'function') {
-			isBlock = isBlockElem;
-		}
-
-		var prevText = null;
-		var prevVoid = false;
-
-		var prev = null;
-		var node = next(prev, elem);
-
-		while (node !== elem) {
-			if (node.nodeType === 3) {
-				// Node.TEXT_NODE
-				var text = node.data.replace(/[ \r\n\t]+/g, ' ');
-
-				if ((!prevText || / $/.test(prevText.data)) && !prevVoid && text[0] === ' ') {
-					text = text.substr(1);
-				}
-
-				// `text` might be empty at this point.
-				if (!text) {
-					node = remove(node);
-					continue;
-				}
-
-				node.data = text;
-				prevText = node;
-			} else if (node.nodeType === 1) {
-				// Node.ELEMENT_NODE
-				if (isBlock(node) || node.nodeName === 'BR') {
-					if (prevText) {
-						prevText.data = prevText.data.replace(/ $/, '');
-					}
-
-					prevText = null;
-					prevVoid = false;
-				} else if (isVoid(node)) {
-					// Avoid trimming space around non-block, non-BR void elements.
-					prevText = null;
-					prevVoid = true;
-				}
-			} else {
-				node = remove(node);
-				continue;
-			}
-
-			var nextNode = next(prev, node);
-			prev = node;
-			node = nextNode;
-		}
-
-		if (prevText) {
-			prevText.data = prevText.data.replace(/ $/, '');
-			if (!prevText.data) {
-				remove(prevText);
-			}
-		}
-	}
-
-	/**
-	 * remove(node) removes the given node from the DOM and returns the
-	 * next node in the sequence.
-	 *
-	 * @param {Node} node
-	 * @returns {Node} node
-	 */
-	function remove(node) {
-		var next = node.nextSibling || node.parentNode;
-
-		node.parentNode.removeChild(node);
-
-		return next;
-	}
-
-	/**
-	 * next(prev, current) returns the next node in the sequence, given the
-	 * current and previous nodes.
-	 *
-	 * @param {Node} prev
-	 * @param {Node} current
-	 * @returns {Node}
-	 */
-	function next(prev, current) {
-		if (prev && prev.parentNode === current || current.nodeName === 'PRE') {
-			return current.nextSibling || current.parentNode;
-		}
-
-		return current.firstChild || current.nextSibling || current.parentNode;
-	}
-
-	module.exports = collapseWhitespace;
-
-},{"block-elements":5,"void-elements":8}],8:[function(require,module,exports){
-	/**
-	 * This file automatically generated from `pre-publish.js`.
-	 * Do not manually edit.
-	 */
-
-	module.exports = {
-		"area": true,
-		"base": true,
-		"br": true,
-		"col": true,
-		"embed": true,
-		"hr": true,
-		"img": true,
-		"input": true,
-		"keygen": true,
-		"link": true,
-		"menuitem": true,
-		"meta": true,
-		"param": true,
-		"source": true,
-		"track": true,
-		"wbr": true
-	};
-
-},{}]},{},[1])(1)
-});

+ 32 - 0
packages/ckeditor5-markdown-gfm/src/markdown2html/markdown2html.js

@@ -0,0 +1,32 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module markdown-gfm/markdown2html
+ */
+
+import marked from 'marked';
+
+/**
+ * Parses markdown string to an HTML.
+ *
+ * @param {String} markdown
+ * @returns {String}
+ */
+export default function markdown2html( markdown ) {
+	return marked.parse( markdown, {
+		gfm: true,
+		breaks: true,
+		tables: true,
+		xhtml: true,
+		headerIds: false
+	} );
+}
+
+export { marked };
+
+// Disable the autolink rule in the lexer (point it to a regex that always fail).
+marked.InlineLexer.rules.breaks.autolink = /^\b$/;
+marked.InlineLexer.rules.breaks.url = /^\b$/;

+ 26 - 3
packages/ckeditor5-markdown-gfm/tests/_utils/utils.js

@@ -14,18 +14,41 @@ import { StylesProcessor } from '@ckeditor/ckeditor5-engine/src/view/stylesmap';
  * @param {String} markdown Markdown to be processed to view.
  * @param {String} viewString Expected view structure.
  * @param {String} [normalizedMarkdown] When converting back to the markdown it might be different than provided input
+ * @param {Object} [options] Additional options.
+ * @param {Function} [options.setup] A function that receives the data processor instance before its execution.
  * markdown string (which will be used if this parameter is not provided).
  */
-export function testDataProcessor( markdown, viewString, normalizedMarkdown ) {
+export function testDataProcessor( markdown, viewString, normalizedMarkdown, options ) {
 	const viewDocument = new ViewDocument( new StylesProcessor() );
+
 	const dataProcessor = new MarkdownDataProcessor( viewDocument );
+
+	if ( options && options.setup ) {
+		options.setup( dataProcessor );
+	}
 	const viewFragment = dataProcessor.toView( markdown );
 
+	const html = cleanHtml( stringify( viewFragment ) );
+
 	// Check if view has correct data.
-	expect( stringify( viewFragment ) ).to.equal( viewString );
+	expect( html ).to.equal( viewString );
 
 	// Check if converting back gives the same result.
 	const normalized = typeof normalizedMarkdown !== 'undefined' ? normalizedMarkdown : markdown;
 
-	expect( dataProcessor.toData( viewFragment ) ).to.equal( normalized );
+	expect( cleanMarkdown( dataProcessor.toData( viewFragment ) ) ).to.equal( normalized );
+}
+
+function cleanHtml( html ) {
+	// Space between table elements.
+	html = html.replace( /(th|td|tr)>\s+<(\/?(?:th|td|tr))/g, '$1><$2' );
+	return html;
+}
+
+function cleanMarkdown( markdown ) {
+	// Trim spaces at the end of the lines.
+	markdown = markdown.replace( / +$/gm, '' );
+	// Trim linebreak at the very beginning.
+	markdown = markdown.replace( /^\s+/g, '' );
+	return markdown;
 }

+ 9 - 9
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/blockquotes.js

@@ -23,9 +23,9 @@ describe( 'GFMDataProcessor', () => {
 		it( 'should process nested blockquotes', () => {
 			testDataProcessor(
 				'> foo\n' +
-				'> \n' +
+				'>\n' +
 				'> > bar\n' +
-				'> \n' +
+				'>\n' +
 				'> foo',
 
 				// GitHub is rendering as:
@@ -51,7 +51,7 @@ describe( 'GFMDataProcessor', () => {
 		it( 'should process list within a blockquote', () => {
 			testDataProcessor(
 				'> A list within a blockquote:\n' +
-				'> \n' +
+				'>\n' +
 				'> *   asterisk 1\n' +
 				'> *   asterisk 2\n' +
 				'> *   asterisk 3',
@@ -80,13 +80,13 @@ describe( 'GFMDataProcessor', () => {
 		it( 'should process blockquotes with code inside with ```', () => {
 			testDataProcessor(
 				'> Example 1:\n' +
-				'> \n' +
+				'>\n' +
 				'> ```\n' +
 				'> code 1\n' +
 				'> ```\n' +
-				'> \n' +
+				'>\n' +
 				'> Example 2:\n' +
-				'> \n' +
+				'>\n' +
 				'> ```\n' +
 				'> code 2\n' +
 				'> ```',
@@ -159,13 +159,13 @@ describe( 'GFMDataProcessor', () => {
 
 				// When converting back to data, DataProcessor will normalize tabs to ```.
 				'> Example 1:\n' +
-				'> \n' +
+				'>\n' +
 				'> ```\n' +
 				'> code 1\n' +
 				'> ```\n' +
-				'> \n' +
+				'>\n' +
 				'> Example 2:\n' +
-				'> \n' +
+				'>\n' +
 				'> ```\n' +
 				'> code 2\n' +
 				'> ```'

+ 54 - 18
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/code.js

@@ -45,7 +45,7 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should properly process backticks inside code spans #2', () => {
 			testDataProcessor(
-				'`` some `backticks` inside ``',
+				'``some `backticks` inside``',
 
 				'<p><code>some `backticks` inside</code></p>'
 			);
@@ -142,17 +142,17 @@ describe( 'GFMDataProcessor', () => {
 				'<pre><code>the lines in this block  \n' +
 				'all contain trailing spaces  </code></pre>',
 
-				// When converting back tabs are normalized to ```.
+				// When converting back tabs are normalized to ```, while the test function remove trailing spaces.
 				'```\n' +
-				'the lines in this block  \n' +
-				'all contain trailing spaces  \n' +
+				'the lines in this block\n' +
+				'all contain trailing spaces\n' +
 				'```'
 			);
 		} );
 
 		it( 'should process code block with language name', () => {
 			testDataProcessor(
-				'``` js\n' +
+				'```js\n' +
 				'var a = \'hello\';\n' +
 				'console.log(a + \' world\');\n' +
 				'```',
@@ -160,7 +160,7 @@ describe( 'GFMDataProcessor', () => {
 				// GitHub is rendering as special html with syntax highlighting.
 				// We will need to handle this separately by some feature.
 
-				'<pre><code class="lang-js">var a = \'hello\';\n' +
+				'<pre><code class="language-js">var a = \'hello\';\n' +
 				'console.log(a + \' world\');</code></pre>'
 			);
 		} );
@@ -174,11 +174,11 @@ describe( 'GFMDataProcessor', () => {
 				// GitHub is rendering as special html with syntax highlighting.
 				// We will need to handle this separately by some feature.
 
-				'<pre><code class="lang-bash">#!/bin/bash</code></pre>',
+				'<pre><code class="language-bash">#!/bin/bash</code></pre>',
 
 				// When converting back ~~~ are normalized to ```.
 
-				'``` bash\n' +
+				'```bash\n' +
 				'#!/bin/bash\n' +
 				'```'
 			);
@@ -186,7 +186,7 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process code block with language name and using ``````` as delimiter', () => {
 			testDataProcessor(
-				'``````` js\n' +
+				'```````js\n' +
 				'var a = \'hello\';\n' +
 				'console.log(a + \' world\');\n' +
 				'```````',
@@ -194,12 +194,12 @@ describe( 'GFMDataProcessor', () => {
 				// GitHub is rendering as special html with syntax highlighting.
 				// We will need to handle this separately by some feature.
 
-				'<pre><code class="lang-js">var a = \'hello\';\n' +
+				'<pre><code class="language-js">var a = \'hello\';\n' +
 				'console.log(a + \' world\');</code></pre>',
 
 				// When converting back ``````` are normalized to ```.
 
-				'``` js\n' +
+				'```js\n' +
 				'var a = \'hello\';\n' +
 				'console.log(a + \' world\');\n' +
 				'```'
@@ -216,12 +216,12 @@ describe( 'GFMDataProcessor', () => {
 				// GitHub is rendering as special html with syntax highlighting.
 				// We will need to handle this separately by some feature.
 
-				'<pre><code class="lang-js">var a = \'hello\';\n' +
+				'<pre><code class="language-js">var a = \'hello\';\n' +
 				'console.log(a + \' world\');</code></pre>',
 
 				// When converting back ~~~~~~~~~~ are normalized to ```.
 
-				'``` js\n' +
+				'```js\n' +
 				'var a = \'hello\';\n' +
 				'console.log(a + \' world\');\n' +
 				'```'
@@ -230,13 +230,13 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process empty code block', () => {
 			testDataProcessor(
-				'``` js\n' +
+				'```js\n' +
 				'```',
 
 				// GitHub is rendering as special html with syntax highlighting.
 				// We will need to handle this separately by some feature.
 
-				'<pre><code class="lang-js"></code></pre>',
+				'<pre><code class="language-js"></code></pre>',
 
 				// When converting back, empty code blocks will be removed.
 				// This might be an issue when switching from source to editor
@@ -247,14 +247,14 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process code block with empty line', () => {
 			testDataProcessor(
-				'``` js\n' +
+				'```js\n' +
 				'\n' +
 				'```',
 
 				// GitHub is rendering as special html with syntax highlighting.
 				// We will need to handle this separately by some feature.
 
-				'<pre><code class="lang-js"></code></pre>',
+				'<pre><code class="language-js"></code></pre>',
 
 				// When converting back, empty code blocks will be removed.
 				// This might be an issue when switching from source to editor
@@ -273,7 +273,43 @@ describe( 'GFMDataProcessor', () => {
 				'<p><code>code `` code ```</code></p>',
 
 				// When converting back ````` will be normalized to ``.
-				'`` code `` code ``` ``'
+				'`code `` code ``` `'
+			);
+		} );
+
+		it( 'should handle triple ticks inside code', () => {
+			testDataProcessor(
+				'````\n' +
+				'```\n' +
+				'Code\n' +
+				'```\n' +
+				'````',
+
+				'<pre><code>' +
+				'```\n' +
+				'Code\n' +
+				'```' +
+				'</code></pre>'
+			);
+		} );
+
+		it( 'should handle triple and quatruple ticks inside code', () => {
+			testDataProcessor(
+				'`````\n' +
+				'````\n' +
+				'```\n' +
+				'Code\n' +
+				'```\n' +
+				'````\n' +
+				'`````',
+
+				'<pre><code>' +
+				'````\n' +
+				'```\n' +
+				'Code\n' +
+				'```\n' +
+				'````' +
+				'</code></pre>'
 			);
 		} );
 	} );

+ 41 - 7
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/escaping.js

@@ -5,6 +5,7 @@
 
 import MarkdownDataProcessor from '../../src/gfmdataprocessor';
 import { stringify } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
+import { testDataProcessor } from '../../tests/_utils/utils';
 import ViewDocument from '@ckeditor/ckeditor5-engine/src/view/document';
 import { StylesProcessor } from '@ckeditor/ckeditor5-engine/src/view/stylesmap';
 
@@ -26,15 +27,15 @@ const testCases = {
 };
 
 describe( 'GFMDataProcessor', () => {
-	let dataProcessor;
-
-	beforeEach( () => {
-		const viewDocument = new ViewDocument( new StylesProcessor() );
-		dataProcessor = new MarkdownDataProcessor( viewDocument );
-	} );
-
 	describe( 'escaping', () => {
 		describe( 'toView', () => {
+			let dataProcessor;
+
+			beforeEach( () => {
+				const viewDocument = new ViewDocument( new StylesProcessor() );
+				dataProcessor = new MarkdownDataProcessor( viewDocument );
+			} );
+
 			for ( const key in testCases ) {
 				const test = testCases[ key ].test;
 				const result = testCases[ key ].result;
@@ -70,5 +71,38 @@ describe( 'GFMDataProcessor', () => {
 				expect( stringify( documentFragment ) ).to.equal( '<pre><code>\\`</code></pre>' );
 			} );
 		} );
+
+		describe( 'HTML', () => {
+			// To note that the test util inlines entities in text nodes, hence the expected HTML in these tests
+			// contain the raw characters but we "know" that those are text nodes and therefore should be converted
+			// back to entities when outputting markdown.
+
+			it( 'should escape <', () => {
+				testDataProcessor( '\\<', '<p><</p>' );
+			} );
+
+			it( 'should escape HTML as text', () => {
+				testDataProcessor( '\\<h1>Test\\</h1>', '<p><h1>Test</h1></p>' );
+			} );
+
+			it( 'should not escape \\< inside inline code', () => {
+				testDataProcessor( '`\\<`', '<p><code>\\<</code></p>' );
+			} );
+
+			it( 'should not touch escape-like HTML inside code blocks', () => {
+				testDataProcessor(
+					'```\n' +
+					'\\<h1>Test\\</h1>\n' +
+					'```',
+					'<pre><code>' +
+					'\\<h1>Test\\</h1>' +
+					'</code></pre>' );
+			} );
+
+			// Necessary test as we're overriding Turndown's escape(). Just to be sure.
+			it( 'should still escape markdown characters', () => {
+				testDataProcessor( '\\* \\_', '<p>* _</p>' );
+			} );
+		} );
 	} );
 } );

+ 12 - 2
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/headers.js

@@ -95,7 +95,11 @@ describe( 'GFMDataProcessor', () => {
 				'# header\n' +
 				'# header',
 
-				'<h1>header</h1><h1>header</h1>'
+				'<h1>header</h1><h1>header</h1>',
+
+				'# header\n' +
+				'\n' +
+				'# header'
 			);
 		} );
 
@@ -105,7 +109,13 @@ describe( 'GFMDataProcessor', () => {
 				'## header\n' +
 				'### header',
 
-				'<h1>header</h1><h2>header</h2><h3>header</h3>'
+				'<h1>header</h1><h2>header</h2><h3>header</h3>',
+
+				'# header\n' +
+				'\n' +
+				'## header\n' +
+				'\n' +
+				'### header'
 			);
 		} );
 

+ 26 - 26
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/horizontal-rules.js

@@ -6,24 +6,24 @@
 import { testDataProcessor } from '../_utils/utils';
 
 describe( 'GFMDataProcessor', () => {
-	// Horizontal rules are are always rendered by GitHub as <hr> and normalized when converting
-	// back to * * *.
+	// Horizontal rules are always rendered by GitHub as <hr> and normalized when converting
+	// back to ---.
 	describe( 'horizontal rules', () => {
 		describe( 'dashes', () => {
 			it( '#1', () => {
-				testDataProcessor( '---', '<hr></hr>', '* * *' );
+				testDataProcessor( '---', '<hr></hr>', '---' );
 			} );
 
 			it( '#2', () => {
-				testDataProcessor( ' ---', '<hr></hr>', '* * *' );
+				testDataProcessor( ' ---', '<hr></hr>', '---' );
 			} );
 
 			it( '#3', () => {
-				testDataProcessor( '  ---', '<hr></hr>', '* * *' );
+				testDataProcessor( '  ---', '<hr></hr>', '---' );
 			} );
 
 			it( '#4', () => {
-				testDataProcessor( '   ---', '<hr></hr>', '* * *' );
+				testDataProcessor( '   ---', '<hr></hr>', '---' );
 			} );
 
 			it( '#5 - code', () => {
@@ -43,19 +43,19 @@ describe( 'GFMDataProcessor', () => {
 
 		describe( 'dashes with spaces', () => {
 			it( '#1', () => {
-				testDataProcessor( '- - -', '<hr></hr>', '* * *' );
+				testDataProcessor( '- - -', '<hr></hr>', '---' );
 			} );
 
 			it( '#2', () => {
-				testDataProcessor( ' - - -', '<hr></hr>', '* * *' );
+				testDataProcessor( ' - - -', '<hr></hr>', '---' );
 			} );
 
 			it( '#3', () => {
-				testDataProcessor( '  - - -', '<hr></hr>', '* * *' );
+				testDataProcessor( '  - - -', '<hr></hr>', '---' );
 			} );
 
 			it( '#4', () => {
-				testDataProcessor( '   - - -', '<hr></hr>', '* * *' );
+				testDataProcessor( '   - - -', '<hr></hr>', '---' );
 			} );
 
 			it( '#5 - code', () => {
@@ -75,19 +75,19 @@ describe( 'GFMDataProcessor', () => {
 
 		describe( 'asterisks', () => {
 			it( '#1', () => {
-				testDataProcessor( '***', '<hr></hr>', '* * *' );
+				testDataProcessor( '***', '<hr></hr>', '---' );
 			} );
 
 			it( '#2', () => {
-				testDataProcessor( ' ***', '<hr></hr>', '* * *' );
+				testDataProcessor( ' ***', '<hr></hr>', '---' );
 			} );
 
 			it( '#3', () => {
-				testDataProcessor( '  ***', '<hr></hr>', '* * *' );
+				testDataProcessor( '  ***', '<hr></hr>', '---' );
 			} );
 
 			it( '#4', () => {
-				testDataProcessor( '   ***', '<hr></hr>', '* * *' );
+				testDataProcessor( '   ***', '<hr></hr>', '---' );
 			} );
 
 			it( '#5 - code', () => {
@@ -107,19 +107,19 @@ describe( 'GFMDataProcessor', () => {
 
 		describe( 'asterisks with spaces', () => {
 			it( '#1', () => {
-				testDataProcessor( '* * *', '<hr></hr>', '* * *' );
+				testDataProcessor( '* * *', '<hr></hr>', '---' );
 			} );
 
 			it( '#2', () => {
-				testDataProcessor( ' * * *', '<hr></hr>', '* * *' );
+				testDataProcessor( ' * * *', '<hr></hr>', '---' );
 			} );
 
 			it( '#3', () => {
-				testDataProcessor( '  * * *', '<hr></hr>', '* * *' );
+				testDataProcessor( '  * * *', '<hr></hr>', '---' );
 			} );
 
 			it( '#4', () => {
-				testDataProcessor( '   * * *', '<hr></hr>', '* * *' );
+				testDataProcessor( '   * * *', '<hr></hr>', '---' );
 			} );
 
 			it( '#5 - code', () => {
@@ -139,19 +139,19 @@ describe( 'GFMDataProcessor', () => {
 
 		describe( 'underscores', () => {
 			it( '#1', () => {
-				testDataProcessor( '___', '<hr></hr>', '* * *' );
+				testDataProcessor( '___', '<hr></hr>', '---' );
 			} );
 
 			it( '#2', () => {
-				testDataProcessor( ' ___', '<hr></hr>', '* * *' );
+				testDataProcessor( ' ___', '<hr></hr>', '---' );
 			} );
 
 			it( '#3', () => {
-				testDataProcessor( '  ___', '<hr></hr>', '* * *' );
+				testDataProcessor( '  ___', '<hr></hr>', '---' );
 			} );
 
 			it( '#4', () => {
-				testDataProcessor( '   ___', '<hr></hr>', '* * *' );
+				testDataProcessor( '   ___', '<hr></hr>', '---' );
 			} );
 
 			it( '#5 - code', () => {
@@ -171,19 +171,19 @@ describe( 'GFMDataProcessor', () => {
 
 		describe( 'underscores with spaces', () => {
 			it( '#1', () => {
-				testDataProcessor( '_ _ _', '<hr></hr>', '* * *' );
+				testDataProcessor( '_ _ _', '<hr></hr>', '---' );
 			} );
 
 			it( '#2', () => {
-				testDataProcessor( ' _ _ _', '<hr></hr>', '* * *' );
+				testDataProcessor( ' _ _ _', '<hr></hr>', '---' );
 			} );
 
 			it( '#3', () => {
-				testDataProcessor( '  _ _ _', '<hr></hr>', '* * *' );
+				testDataProcessor( '  _ _ _', '<hr></hr>', '---' );
 			} );
 
 			it( '#4', () => {
-				testDataProcessor( '   _ _ _', '<hr></hr>', '* * *' );
+				testDataProcessor( '   _ _ _', '<hr></hr>', '---' );
 			} );
 
 			it( '#5 - code', () => {

+ 26 - 0
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/html.js

@@ -0,0 +1,26 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import { testDataProcessor } from '../_utils/utils';
+
+describe( 'GFMDataProcessor', () => {
+	describe( 'html', () => {
+		it( 'should keep html', () => {
+			testDataProcessor(
+				'test with <keep>html</keep> and <notkeep>not html</notkeep>',
+
+				'<p>test with <keep>html</keep> and <notkeep>not html</notkeep></p>',
+
+				'test with <keep>html</keep> and not html',
+
+				{
+					setup: dataProcessor => {
+						dataProcessor.keepHtml( 'keep' );
+					}
+				}
+			);
+		} );
+	} );
+} );

+ 3 - 3
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/images.js

@@ -35,7 +35,7 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process referenced images', () => {
 			testDataProcessor(
-				'![alt text][logo]\n' +
+				'![alt text][logo]\n\n' +
 				'[logo]: http://example.com/image.png "title text"',
 
 				'<p><img alt="alt text" src="http://example.com/image.png" title="title text"></img></p>',
@@ -47,7 +47,7 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process referenced images without title', () => {
 			testDataProcessor(
-				'![alt text][logo]\n' +
+				'![alt text][logo]\n\n' +
 				'[logo]: http://example.com/image.png',
 
 				'<p><img alt="alt text" src="http://example.com/image.png"></img></p>',
@@ -59,7 +59,7 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process referenced images without alt text', () => {
 			testDataProcessor(
-				'![][logo]\n' +
+				'![][logo]\n\n' +
 				'[logo]: http://example.com/image.png "title text"',
 
 				'<p><img alt="" src="http://example.com/image.png" title="title text"></img></p>',

+ 47 - 125
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/links.js

@@ -7,57 +7,34 @@ import { testDataProcessor } from '../_utils/utils';
 
 describe( 'GFMDataProcessor', () => {
 	describe( 'links', () => {
-		it( 'should autolink', () => {
-			testDataProcessor(
-				'Link: <http://example.com/>.',
-				'<p>Link: <a href="http://example.com/">http://example.com/</a>.</p>',
-
-				// When converting back it will be represented as standard markdown link.
-				'Link: [http://example.com/](http://example.com/).'
-			);
-		} );
-
-		it( 'should autolink #2', () => {
+		it( 'should not autolink', () => {
 			testDataProcessor(
 				'Link: http://example.com/.',
-				'<p>Link: <a href="http://example.com/">http://example.com/</a>.</p>',
-
-				// When converting back it will be represented as standard markdown link.
-				'Link: [http://example.com/](http://example.com/).'
+				'<p>Link: http://example.com/.</p>'
 			);
 		} );
 
-		it( 'should autolink with params', () => {
+		it( 'should not autolink with params', () => {
 			testDataProcessor(
-				'Link: <http://example.com/?foo=1&bar=2>.',
-				'<p>Link: <a href="http://example.com/?foo=1&bar=2">http://example.com/?foo=1&bar=2</a>.</p>',
-
-				// When converting back it will be represented as standard markdown link.
-				'Link: [http://example.com/?foo=1&bar=2](http://example.com/?foo=1&bar=2).'
+				'Link: http://example.com/?foo=1&bar=2.',
+				'<p>Link: http://example.com/?foo=1&bar=2.</p>'
 			);
 		} );
 
-		it( 'should autolink inside list', () => {
+		it( 'should not autolink inside list', () => {
 			testDataProcessor(
-				'* <http://example.com/>',
-
-				'<ul><li><a href="http://example.com/">http://example.com/</a></li></ul>',
-
-				// When converting back it will be represented as standard markdown link.
-				'*   [http://example.com/](http://example.com/)'
+				'*   http://example.com/',
+				'<ul><li>http://example.com/</li></ul>'
 			);
 		} );
 
-		it( 'should autolink inside blockquote', () => {
+		it( 'should not autolink inside blockquote', () => {
 			testDataProcessor(
-				'> Blockquoted: <http://example.com/>',
+				'> Blockquoted: http://example.com/',
 
 				'<blockquote>' +
-				'<p>Blockquoted: <a href="http://example.com/">http://example.com/</a></p>' +
-				'</blockquote>',
-
-				// When converting back it will be represented as standard markdown link.
-				'> Blockquoted: [http://example.com/](http://example.com/)'
+				'<p>Blockquoted: http://example.com/</p>' +
+				'</blockquote>'
 			);
 		} );
 
@@ -138,37 +115,17 @@ describe( 'GFMDataProcessor', () => {
 			);
 		} );
 
-		it( 'should process inline links with spaces in URL', () => {
-			testDataProcessor(
-				'[URL and title]( /url/has space )',
-				'<p><a href="/url/has space">URL and title</a></p>',
-
-				// When converting back unneeded spaces will be removed.
-				'[URL and title](/url/has space)'
-			);
-		} );
-
-		it( 'should process inline links with titles and spaces in URL', () => {
-			testDataProcessor(
-				'[URL and title]( /url/has space/ "url has space and title")',
-				'<p><a href="/url/has space/" title="url has space and title">URL and title</a></p>',
-
-				// When converting back unneeded spaces will be removed.
-				'[URL and title](/url/has space/ "url has space and title")'
-			);
-		} );
-
-		it( 'should process empty link', () => {
-			testDataProcessor(
-				'[Empty]()',
-
-				'<p><a href="">Empty</a></p>'
-			);
-		} );
+		// it( 'should process empty link', () => {
+		// 	testDataProcessor(
+		// 		'[Empty]()',
+		//
+		// 		'<p><a href="">Empty</a></p>'
+		// 	);
+		// } );
 
 		it( 'should process reference links', () => {
 			testDataProcessor(
-				'Foo [bar] [1].\n' +
+				'Foo [bar][1].\n\n' +
 				'[1]: /url/  "Title"',
 
 				'<p>Foo <a href="/url/" title="Title">bar</a>.</p>',
@@ -181,19 +138,7 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process reference links - without space', () => {
 			testDataProcessor(
-				'Foo [bar][1].\n' +
-				'[1]: /url/  "Title"',
-
-				'<p>Foo <a href="/url/" title="Title">bar</a>.</p>',
-
-				'Foo [bar](/url/ "Title").'
-			);
-		} );
-
-		it( 'should process reference links - with newline', () => {
-			testDataProcessor(
-				'Foo [bar]\n' +
-				'[1].\n' +
+				'Foo [bar][1].\n\n' +
 				'[1]: /url/  "Title"',
 
 				'<p>Foo <a href="/url/" title="Title">bar</a>.</p>',
@@ -204,18 +149,18 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process reference links - with embedded brackets', () => {
 			testDataProcessor(
-				'With [embedded [brackets]] [b].\n' +
+				'With [embedded [brackets]][b].\n\n' +
 				'[b]: /url/',
 
 				'<p>With <a href="/url/">embedded [brackets]</a>.</p>',
 
-				'With [embedded [brackets]](/url/).'
+				'With [embedded \\[brackets\\]](/url/).'
 			);
 		} );
 
 		it( 'should process reference links - with reference indented once', () => {
 			testDataProcessor(
-				'Indented [once][].\n' +
+				'Indented [once][].\n\n' +
 				' [once]: /url',
 
 				'<p>Indented <a href="/url">once</a>.</p>',
@@ -226,7 +171,7 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process reference links - with reference indented twice', () => {
 			testDataProcessor(
-				'Indented [twice][].\n' +
+				'Indented [twice][].\n\n' +
 				'  [twice]: /url',
 
 				'<p>Indented <a href="/url">twice</a>.</p>',
@@ -237,7 +182,7 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process reference links - with reference indented three times', () => {
 			testDataProcessor(
-				'Indented [trice][].\n' +
+				'Indented [trice][].\n\n' +
 				'   [trice]: /url',
 
 				'<p>Indented <a href="/url">trice</a>.</p>',
@@ -246,28 +191,9 @@ describe( 'GFMDataProcessor', () => {
 			);
 		} );
 
-		it( 'should NOT process reference links - with reference indented four times', () => {
-			testDataProcessor(
-				'Indented [four][].\n' +
-				'    [four]: /url',
-
-				// GitHub renders it as:
-				// <p>Indented [four][].<br>
-				// [four]: /url</p>
-				// Marked converts it to the code block.
-				'<p>Indented [four][].</p><pre><code>[four]: /url</code></pre>',
-
-				'Indented [four][].\n' +
-				'\n' +
-				'```\n' +
-				'[four]: /url\n' +
-				'```'
-			);
-		} );
-
 		it( 'should process reference links when title and reference are same #1', () => {
 			testDataProcessor(
-				'[this] [this]\n' +
+				'[this][this]\n\n' +
 				'[this]: foo',
 
 				'<p><a href="foo">this</a></p>',
@@ -278,7 +204,7 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process reference links when title and reference are same #2', () => {
 			testDataProcessor(
-				'[this][this]\n' +
+				'[this][this]\n\n' +
 				'[this]: foo',
 
 				'<p><a href="foo">this</a></p>',
@@ -289,7 +215,7 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process reference links when only title is provided and is same as reference #1', () => {
 			testDataProcessor(
-				'[this] []\n' +
+				'[this][]\n\n' +
 				'[this]: foo',
 
 				'<p><a href="foo">this</a></p>',
@@ -300,7 +226,7 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process reference links when only title is provided and is same as reference #2', () => {
 			testDataProcessor(
-				'[this][]\n' +
+				'[this][]\n\n' +
 				'[this]: foo',
 
 				'<p><a href="foo">this</a></p>',
@@ -311,7 +237,7 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process reference links when only title is provided and is same as reference #3', () => {
 			testDataProcessor(
-				'[this]\n' +
+				'[this]\n\n' +
 				'[this]: foo',
 
 				'<p><a href="foo">this</a></p>',
@@ -321,18 +247,12 @@ describe( 'GFMDataProcessor', () => {
 		} );
 
 		it( 'should not process reference links when reference is not found #1', () => {
-			testDataProcessor(
-				'[this] []',
-
-				'<p>[this] []</p>'
-			);
-		} );
-
-		it( 'should not process reference links when reference is not found #2', () => {
 			testDataProcessor(
 				'[this][]',
 
-				'<p>[this][]</p>'
+				'<p>[this][]</p>',
+
+				'\\[this\\]\\[\\]'
 			);
 		} );
 
@@ -340,35 +260,37 @@ describe( 'GFMDataProcessor', () => {
 			testDataProcessor(
 				'[this]',
 
-				'<p>[this]</p>'
+				'<p>[this]</p>',
+
+				'\\[this\\]'
 			);
 		} );
 
 		it( 'should process reference links nested in brackets #1', () => {
 			testDataProcessor(
-				'[a reference inside [this][]]\n' +
+				'[a reference inside [this][]]\n\n' +
 				'[this]: foo',
 
 				'<p>[a reference inside <a href="foo">this</a>]</p>',
 
-				'[a reference inside [this](foo)]'
+				'\\[a reference inside [this](foo)\\]'
 			);
 		} );
 
 		it( 'should process reference links nested in brackets #2', () => {
 			testDataProcessor(
-				'[a reference inside [this]]\n' +
+				'[a reference inside [this]]\n\n' +
 				'[this]: foo',
 
 				'<p>[a reference inside <a href="foo">this</a>]</p>',
 
-				'[a reference inside [this](foo)]'
+				'\\[a reference inside [this](foo)\\]'
 			);
 		} );
 
 		it( 'should not process reference links when title is same as reference but reference is different', () => {
 			testDataProcessor(
-				'[this](/something/else/)\n' +
+				'[this](/something/else/)\n\n' +
 				'[this]: foo',
 
 				'<p><a href="/something/else/">this</a></p>',
@@ -379,19 +301,19 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should not process reference links suppressed by backslashes', () => {
 			testDataProcessor(
-				'Suppress \\[this] and [this\\].\n' +
+				'Suppress \\[this] and [this\\].\n\n' +
 				'[this]: foo',
 
 				'<p>Suppress [this] and [this].</p>',
 
-				'Suppress [this] and [this].'
+				'Suppress \\[this\\] and \\[this\\].'
 			);
 		} );
 
 		it( 'should process reference links when used across multiple lines #1', () => {
 			testDataProcessor(
 				'This is [multiline\n' +
-				'reference]\n' +
+				'reference]\n\n' +
 				'[multiline reference]: foo',
 
 				'<p>This is <a href="foo">multiline<br></br>reference</a></p>',
@@ -404,7 +326,7 @@ describe( 'GFMDataProcessor', () => {
 		it( 'should process reference links when used across multiple lines #2', () => {
 			testDataProcessor(
 				'This is [multiline \n' +
-				'reference]\n' +
+				'reference]\n\n' +
 				'[multiline reference]: foo',
 
 				'<p>This is <a href="foo">multiline<br></br>reference</a></p>',
@@ -416,7 +338,7 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process reference links case-insensitive', () => {
 			testDataProcessor(
-				'[hi]\n' +
+				'[hi]\n\n' +
 				'[HI]: /url',
 
 				'<p><a href="/url">hi</a></p>',

+ 42 - 0
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/lists.js

@@ -4,6 +4,10 @@
  */
 
 import { testDataProcessor } from '../_utils/utils';
+import MarkdownDataProcessor from '../../src/gfmdataprocessor';
+import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor';
+import ViewDocument from '@ckeditor/ckeditor5-engine/src/view/document';
+import { StylesProcessor } from '@ckeditor/ckeditor5-engine/src/view/stylesmap';
 
 describe( 'GFMDataProcessor', () => {
 	describe( 'lists', () => {
@@ -340,4 +344,42 @@ describe( 'GFMDataProcessor', () => {
 			);
 		} );
 	} );
+
+	describe( 'todo lists', () => {
+		it( 'should process todo lists', () => {
+			testDataProcessor(
+				'*   [ ] Item 1\n' +
+				'*   [x] Item 2',
+
+				'<ul>' +
+					'<li><input disabled="" type="checkbox"></input>Item 1</li>' +
+					'<li><input checked="" disabled="" type="checkbox"></input>Item 2</li>' +
+				'</ul>' );
+		} );
+
+		it( 'should process the HTML produced by the todo list feature', () => {
+			const viewDocument = new ViewDocument( new StylesProcessor() );
+
+			const htmlDataProcessor = new HtmlDataProcessor( viewDocument );
+			const mdDataProcessor = new MarkdownDataProcessor( viewDocument );
+
+			const viewFragment = htmlDataProcessor.toView(
+				'<ul class="todo-list">' +
+					'<li><label class="todo-list__label">' +
+						'<input type="checkbox" disabled="disabled">' +
+						'<span class="todo-list__label__description">Item 1</span>' +
+					'</label></li>' +
+					'<li><label class="todo-list__label">' +
+						'<input type="checkbox" disabled="disabled" checked="checked">' +
+						'<span class="todo-list__label__description">Item 2</span>' +
+					'</label></li>' +
+				'</ul>'
+			);
+
+			expect( mdDataProcessor.toData( viewFragment ) ).to.equal(
+				'*   [ ] Item 1\n' +
+				'*   [x] Item 2'
+			);
+		} );
+	} );
 } );

+ 6 - 3
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/manual/gfmdataprocessor.js

@@ -5,17 +5,20 @@
 
 /* globals document */
 
-import MarkdownDataProcessor from '../../../src/gfmdataprocessor';
+import Document from '@ckeditor/ckeditor5-engine/src/view/document';
+import { StylesProcessor } from '@ckeditor/ckeditor5-engine/src/view/stylesmap';
 import { stringify, parse } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
 
+import MarkdownDataProcessor from '../../../src/gfmdataprocessor';
+
 const markdownTextArea = document.getElementById( 'markdown' );
 const viewTextArea = document.getElementById( 'view' );
-const dataProcessor = new MarkdownDataProcessor();
+const dataProcessor = new MarkdownDataProcessor( new Document( new StylesProcessor() ) );
 
 document.getElementById( 'button_to_view' ).addEventListener( 'click', convertToView );
 document.getElementById( 'button_to_md' ).addEventListener( 'click', convertToMarkdown );
 
-markdownTextArea.value = '### Header\n\nFoo bar baz biz.';
+markdownTextArea.value = '### Header 3\n\nTodo:\n\n* [ ] Test me';
 convertToView();
 
 function convertToView() {

+ 3 - 42
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/paragraphs.js

@@ -40,25 +40,6 @@ describe( 'GFMDataProcessor', () => {
 				// <h1>header</h1>
 				'<p>single line</p><h1>header</h1>',
 
-				// To-markdown always put 2 empty lines after paragraph.
-				'single line\n\n' +
-				'# header'
-			);
-		} );
-
-		it( 'with header after #2', () => {
-			testDataProcessor(
-				'single line\n' +
-				'header\n' +
-				'===',
-
-				// GitHub is rendering as:
-				// <p>single line</p>
-				//
-				// <h1>header</h1>
-				'<p>single line</p><h1>header</h1>',
-
-				// To-markdown always put 2 empty lines after paragraph and normalize header to #.
 				'single line\n' +
 				'\n' +
 				'# header'
@@ -78,17 +59,16 @@ describe( 'GFMDataProcessor', () => {
 				// </blockquote>
 				'<p>single line</p><blockquote><p>quote</p></blockquote>',
 
-				// To-markdown always put 2 empty lines after paragraph.
-				'single line\n' +
+				'single line' +
 				'\n' +
-				'> quote'
+				'\n> quote'
 			);
 		} );
 
 		it( 'with list after', () => {
 			testDataProcessor(
 				'single line\n' +
-				'* item',
+				'*   item',
 
 				// GitHub is rendering as:
 				// <p>single line</p>
@@ -98,29 +78,10 @@ describe( 'GFMDataProcessor', () => {
 				// </ul>
 				'<p>single line</p><ul><li>item</li></ul>',
 
-				// To-markdown always put 2 empty lines after paragraph.
 				'single line\n' +
 				'\n' +
 				'*   item'
 			);
 		} );
-
-		it( 'with div element after', () => {
-			testDataProcessor(
-				'single line\n' +
-				'<div>div element</div>',
-
-				// GitHub is rendering as:
-				// <p>single line</p>
-				//
-				// <div>div element</div>
-				'<p>single line</p><div>div element</div>',
-
-				// To-markdown always put 2 empty lines after paragraph.
-				'single line\n' +
-				'\n' +
-				'<div>div element</div>'
-			);
-		} );
 	} );
 } );

+ 2 - 2
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/strikethrough.js

@@ -9,7 +9,7 @@ describe( 'GFMDataProcessor', () => {
 	describe( 'Strikethrough', () => {
 		it( 'should process strikethrough text', () => {
 			testDataProcessor(
-				'~~deleted~~',
+				'~deleted~',
 
 				'<p><del>deleted</del></p>'
 			);
@@ -17,7 +17,7 @@ describe( 'GFMDataProcessor', () => {
 
 		it( 'should process strikethrough inside text', () => {
 			testDataProcessor(
-				'This is ~~deleted content~~.',
+				'This is ~deleted content~.',
 
 				'<p>This is <del>deleted content</del>.</p>'
 			);

+ 12 - 0
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/strong-emphasis.js

@@ -78,6 +78,18 @@ describe( 'GFMDataProcessor', () => {
 			testDataProcessor(
 				'This should_not_be_emp.',
 
+				'<p>This should_not_be_emp.</p>',
+
+				// Turndow escape markdown markup characters used inside text.
+				'This should\\_not\\_be\\_emp.'
+			);
+		} );
+
+		it( 'should not render escape marks', () => {
+			testDataProcessor(
+				// Following the previous test.
+				'This should\\_not\\_be\\_emp.',
+
 				'<p>This should_not_be_emp.</p>'
 			);
 		} );

+ 2 - 2
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/tables.js

@@ -119,7 +119,7 @@ describe( 'GFMDataProcessor', () => {
 			testDataProcessor(
 				'Header 1|Header 2|Header 3|Header 4\n' +
 				':-------|:------:|-------:|--------\n' +
-				'*Cell 1*  |**Cell 2**  |~~Cell 3~~  |Cell 4',
+				'*Cell 1*  |**Cell 2**  |~Cell 3~  |Cell 4',
 
 				'<table>' +
 					'<thead>' +
@@ -151,7 +151,7 @@ describe( 'GFMDataProcessor', () => {
 				// After converting back it will be normalized.
 				'| Header 1 | Header 2 | Header 3 | Header 4 |\n' +
 				'| :-- | :-: | --: | --- |\n' +
-				'| _Cell 1_ | **Cell 2** | ~~Cell 3~~ | Cell 4 |'
+				'| _Cell 1_ | **Cell 2** | ~Cell 3~ | Cell 4 |'
 			);
 		} );
 	} );

+ 50 - 0
packages/ckeditor5-markdown-gfm/tests/gfmdataprocessor/text.js

@@ -0,0 +1,50 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import { testDataProcessor } from '../_utils/utils';
+
+describe( 'GFMDataProcessor', () => {
+	describe( 'text', () => {
+		describe( 'urls', () => {
+			it( 'should not escape urls', () => {
+				testDataProcessor(
+					'escape\\_this https://test.com/do_[not]-escape escape\\_this',
+					'<p>escape_this https://test.com/do_[not]-escape escape_this</p>'
+				);
+			} );
+
+			it( 'should not escape urls (data escaped between urls)', () => {
+				testDataProcessor(
+					'escape\\_this https://test.com/do_[not]-escape escape\\_this https://test.com/do_[not]-escape',
+					'<p>escape_this https://test.com/do_[not]-escape escape_this https://test.com/do_[not]-escape</p>'
+				);
+			} );
+
+			it( 'should not escape urls (at start)', () => {
+				testDataProcessor(
+					'https://test.com/do_[not]-escape escape\\_this',
+					'<p>https://test.com/do_[not]-escape escape_this</p>'
+				);
+			} );
+
+			it( 'should not escape urls (at end)', () => {
+				testDataProcessor(
+					'escape\\_this https://test.com/do_[not]-escape',
+					'<p>escape_this https://test.com/do_[not]-escape</p>'
+				);
+			} );
+
+			[
+				'https://test.com/do_[not]-escape',
+				'http://test.com/do_[not]-escape',
+				'www.test.com/do_[not]-escape'
+			].forEach( url => {
+				it( `should not escape urls (${ url })`, () => {
+					testDataProcessor( url, `<p>${ url }</p>` );
+				} );
+			} );
+		} );
+	} );
+} );

+ 351 - 139
yarn.lock

@@ -259,9 +259,9 @@
     js-tokens "^4.0.0"
 
 "@babel/parser@^7.10.3", "@babel/parser@^7.10.4", "@babel/parser@^7.11.0", "@babel/parser@^7.11.1", "@babel/parser@^7.6.0", "@babel/parser@^7.7.7", "@babel/parser@^7.9.6":
-  version "7.11.1"
-  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.1.tgz#d91a387990b21e5d20047b336bb19b0553f02ff5"
-  integrity sha512-u9QMIRdKVF7hfEkb3nu2LgZDIzCQPv+yHD9Eg6ruoJLjkrQ9fFz4IBSlF/9XwoNri9+2F1IY+dYuOfZrXq8t3w==
+  version "7.11.3"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.3.tgz#9e1eae46738bcd08e23e867bab43e7b95299a8f9"
+  integrity sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==
 
 "@babel/plugin-proposal-async-generator-functions@^7.10.4":
   version "7.10.5"
@@ -799,9 +799,9 @@
     esutils "^2.0.2"
 
 "@babel/runtime@^7.8.4":
-  version "7.11.1"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.1.tgz#087afc57e7bf1073e792fe54f8fb3cfa752f9230"
-  integrity sha512-nH5y8fLvVl3HAb+ezbgcgwrH8QbClWo8xzkOu7+oyqngo3EVorwpWJQaqXPjGRpfj7mQvsJCl/S8knkfkPWqrw==
+  version "7.11.2"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
+  integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
   dependencies:
     regenerator-runtime "^0.13.4"
 
@@ -1258,9 +1258,9 @@
     "@types/node" ">= 8"
 
 "@octokit/types@^5.0.0", "@octokit/types@^5.0.1", "@octokit/types@^5.2.0":
-  version "5.2.1"
-  resolved "https://registry.yarnpkg.com/@octokit/types/-/types-5.2.1.tgz#c212f03b0492faf215fa2ae506d5ec18038c2a36"
-  integrity sha512-PugtgEw8u++zAyBpDpSkR8K1OsT2l8QWp3ECL6bZHFoq9PfHDoKeGFWSuX2Z+Ghy93k1fkKf8tsmqNBv+8dEfQ==
+  version "5.4.0"
+  resolved "https://registry.yarnpkg.com/@octokit/types/-/types-5.4.0.tgz#25f2f8e24fec09214553168c41c06383c9d0f529"
+  integrity sha512-D/uotqF69M50OIlwMqgyIg9PuLT2daOiBAYF0P40I2ekFA2ESwwBY5dxZe/UhXdPvIbNKDzuZmQrO7rMpuFbcg==
   dependencies:
     "@types/node" ">= 8"
 
@@ -1339,10 +1339,10 @@
     "@sinonjs/commons" "^1"
     "@sinonjs/samsam" "^5.0.2"
 
-"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.0.3":
-  version "5.0.3"
-  resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.0.3.tgz#86f21bdb3d52480faf0892a480c9906aa5a52938"
-  integrity sha512-QucHkc2uMJ0pFGjJUDP3F9dq5dx8QIaqISl9QgwLOh6P9yv877uONPGXh/OH/0zmM3tW1JjuJltAZV2l7zU+uQ==
+"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.1.0":
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.1.0.tgz#3afe719232b541bb6cf3411a4c399a188de21ec0"
+  integrity sha512-42nyaQOVunX5Pm6GRJobmzbS7iLI+fhERITnETXzzwDZh+TtDr/Au3yAvXVjFmZ4wEUaE4Y3NFZfKv0bV0cbtg==
   dependencies:
     "@sinonjs/commons" "^1.6.0"
     lodash.get "^4.4.2"
@@ -1634,6 +1634,11 @@ a-sync-waterfall@^1.0.0:
   resolved "https://registry.yarnpkg.com/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz#75b6b6aa72598b497a125e7a2770f14f4c8a1fa7"
   integrity sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==
 
+abab@^2.0.3:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.4.tgz#6dfa57b417ca06d21b2478f0e638302f99c2405c"
+  integrity sha512-Eu9ELJWCz/c1e9gTiCY+FceWxcqzjYEbqMgtndnuSqZSUCOL73TWNK2mHfIj4Cw2E/ongOp+JISVNCmovt2KYQ==
+
 abbrev@1, abbrev@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@@ -1654,6 +1659,14 @@ acorn-globals@^3.0.0:
   dependencies:
     acorn "^4.0.4"
 
+acorn-globals@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45"
+  integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==
+  dependencies:
+    acorn "^7.1.1"
+    acorn-walk "^7.1.1"
+
 acorn-jsx@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
@@ -1671,6 +1684,11 @@ acorn-walk@^6.2.0:
   resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c"
   integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==
 
+acorn-walk@^7.1.1:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
+  integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
+
 acorn@^3.0.4, acorn@^3.1.0, acorn@^3.3.0:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
@@ -1945,24 +1963,20 @@ arrify@^1.0.1:
   resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
   integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
 
-arrify@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa"
-  integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==
-
 asap@^2.0.3, asap@~2.0.3:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
   integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
 
-asn1.js@^4.0.0:
-  version "4.10.1"
-  resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
-  integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==
+asn1.js@^5.2.0:
+  version "5.4.1"
+  resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
+  integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==
   dependencies:
     bn.js "^4.0.0"
     inherits "^2.0.1"
     minimalistic-assert "^1.0.0"
+    safer-buffer "^2.1.0"
 
 asn1@~0.2.3:
   version "0.2.4"
@@ -2070,9 +2084,9 @@ aws-sign2@~0.7.0:
   integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
 
 aws4@^1.8.0:
-  version "1.10.0"
-  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2"
-  integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==
+  version "1.10.1"
+  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428"
+  integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==
 
 babel-code-frame@^6.26.0:
   version "6.26.0"
@@ -2434,6 +2448,11 @@ brorand@^1.0.1:
   resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
   integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
 
+browser-process-hrtime@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
+  integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
+
 browser-stdout@1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
@@ -2501,14 +2520,14 @@ browserify-zlib@^0.2.0:
     pako "~1.0.5"
 
 browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.8.5:
-  version "4.13.0"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.13.0.tgz#42556cba011e1b0a2775b611cba6a8eca18e940d"
-  integrity sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ==
+  version "4.14.0"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.0.tgz#2908951abfe4ec98737b72f34c3bcedc8d43b000"
+  integrity sha512-pUsXKAF2lVwhmtpeA3LJrZ76jXuusrNyhduuQs7CDFf9foT4Y38aQOserd2lMe5DSSrjf3fx34oHwryuvxAUgQ==
   dependencies:
-    caniuse-lite "^1.0.30001093"
-    electron-to-chromium "^1.3.488"
-    escalade "^3.0.1"
-    node-releases "^1.1.58"
+    caniuse-lite "^1.0.30001111"
+    electron-to-chromium "^1.3.523"
+    escalade "^3.0.2"
+    node-releases "^1.1.60"
 
 buffer-from@^1.0.0:
   version "1.1.1"
@@ -2692,11 +2711,6 @@ camelcase@^5.0.0, camelcase@^5.3.1:
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
   integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
 
-camelcase@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e"
-  integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==
-
 caniuse-api@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
@@ -2707,10 +2721,10 @@ caniuse-api@^3.0.0:
     lodash.memoize "^4.1.2"
     lodash.uniq "^4.5.0"
 
-caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001109:
-  version "1.0.30001111"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001111.tgz#dd0ce822c70eb6c7c068e4a55c22e19ec1501298"
-  integrity sha512-xnDje2wchd/8mlJu8sXvWxOGvMgv+uT3iZ3bkIAynKOzToCssWCmkz/ZIkQBs/2pUB4uwnJKVORWQ31UkbVjOg==
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001111:
+  version "1.0.30001113"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001113.tgz#22016ab55b5a8b04fa00ca342d9ee1b98df48065"
+  integrity sha512-qMvjHiKH21zzM/VDZr6oosO6Ri3U0V2tC015jRXjOecwQCJtsU5zklTNTk31jQbIOP8gha0h1ccM/g0ECP+4BA==
 
 caseless@~0.12.0:
   version "0.12.0"
@@ -2895,9 +2909,9 @@ chokidar@^2.1.8:
     fsevents "^1.2.7"
 
 chokidar@^3.0.0, chokidar@^3.3.0, chokidar@^3.4.0, chokidar@^3.4.1:
-  version "3.4.1"
-  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.1.tgz#e905bdecf10eaa0a0b1db0c664481cc4cbc22ba1"
-  integrity sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==
+  version "3.4.2"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d"
+  integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==
   dependencies:
     anymatch "~3.1.1"
     braces "~3.0.2"
@@ -3164,14 +3178,6 @@ commonmark@^0.29.1:
     minimist "~1.2.0"
     string.prototype.repeat "^0.2.0"
 
-compare-func@^1.3.1:
-  version "1.3.4"
-  resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-1.3.4.tgz#6b07c4c5e8341119baf44578085bda0f4a823516"
-  integrity sha512-sq2sWtrqKPkEXAC8tEJA1+BqAH9GbFkGBtUOqrUX57VSfwp8xyktctk+uLoRy5eccTdxzDcVIztlYDpKs3Jv1Q==
-  dependencies:
-    array-ify "^1.0.0"
-    dot-prop "^3.0.0"
-
 compare-func@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3"
@@ -3287,11 +3293,11 @@ content-type@~1.0.4:
   integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
 
 conventional-changelog-writer@^4.0.16:
-  version "4.0.16"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.16.tgz#ca10f2691a8ea6d3c2eb74bd35bcf40aa052dda5"
-  integrity sha512-jmU1sDJDZpm/dkuFxBeRXvyNcJQeKhGtVcFFkwTphUAzyYWcwz2j36Wcv+Mv2hU3tpvLMkysOPXJTLO55AUrYQ==
+  version "4.0.17"
+  resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.17.tgz#4753aaa138bf5aa59c0b274cb5937efcd2722e21"
+  integrity sha512-IKQuK3bib/n032KWaSb8YlBFds+aLmzENtnKtxJy3+HqDq5kohu3g/UdNbIHeJWygfnEbZjnCKFxAW0y7ArZAw==
   dependencies:
-    compare-func "^1.3.1"
+    compare-func "^2.0.0"
     conventional-commits-filter "^2.0.6"
     dateformat "^3.0.0"
     handlebars "^4.7.6"
@@ -3663,6 +3669,23 @@ csso@^4.0.2:
   dependencies:
     css-tree "1.0.0-alpha.39"
 
+cssom@^0.4.4:
+  version "0.4.4"
+  resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10"
+  integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==
+
+cssom@~0.3.6:
+  version "0.3.8"
+  resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
+  integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
+
+cssstyle@^2.2.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852"
+  integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==
+  dependencies:
+    cssom "~0.3.6"
+
 cuid@^2.1.4:
   version "2.1.8"
   resolved "https://registry.yarnpkg.com/cuid/-/cuid-2.1.8.tgz#cbb88f954171e0d5747606c0139fb65c5101eac0"
@@ -3697,6 +3720,15 @@ dashdash@^1.12.0:
   dependencies:
     assert-plus "^1.0.0"
 
+data-urls@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b"
+  integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==
+  dependencies:
+    abab "^2.0.3"
+    whatwg-mimetype "^2.3.0"
+    whatwg-url "^8.0.0"
+
 date-fns@^1.29.0:
   version "1.30.1"
   resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
@@ -3763,6 +3795,11 @@ decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0:
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
   integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
 
+decimal.js@^10.2.0:
+  version "10.2.0"
+  resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.0.tgz#39466113a9e036111d02f82489b5fd6b0b5ed231"
+  integrity sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==
+
 decode-uri-component@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
@@ -4021,6 +4058,13 @@ domelementtype@^2.0.1:
   resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d"
   integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==
 
+domexception@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304"
+  integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==
+  dependencies:
+    webidl-conversions "^5.0.0"
+
 domhandler@^2.3.0:
   version "2.4.2"
   resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
@@ -4060,13 +4104,6 @@ domutils@^2.0.0:
     domelementtype "^2.0.1"
     domhandler "^3.0.0"
 
-dot-prop@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177"
-  integrity sha1-G3CK8JSknJoOfbyteQq6U52sEXc=
-  dependencies:
-    is-obj "^1.0.0"
-
 dot-prop@^5.1.0, dot-prop@^5.2.0:
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
@@ -4082,9 +4119,9 @@ dreamopt@~0.6.0:
     wordwrap ">=0.0.2"
 
 duplexer@^0.1.1:
-  version "0.1.1"
-  resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
-  integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
+  integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
 
 duplexify@^3.4.2, duplexify@^3.6.0:
   version "3.7.1"
@@ -4129,10 +4166,10 @@ ee-first@1.1.1:
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
   integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
 
-electron-to-chromium@^1.3.488:
-  version "1.3.520"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.520.tgz#dfda0a14a4aed785cbddfdb505ea122f75978392"
-  integrity sha512-q6H9E1sXDCjRHP+X06vcP+N0ki8ZvYoRPZfKnDuiRX10WWXxEHzKFVf4O9rBFMpuPtR3M+2KAdJnugJoBBp3Rw==
+electron-to-chromium@^1.3.523:
+  version "1.3.533"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.533.tgz#d7e5ca4d57e9bc99af87efbe13e7be5dde729b0f"
+  integrity sha512-YqAL+NXOzjBnpY+dcOKDlZybJDCOzgsq4koW3fvyty/ldTmsb4QazZpOWmVvZ2m0t5jbBf7L0lIGU3BUipwG+A==
 
 elliptic@^6.5.3:
   version "6.5.3"
@@ -4295,7 +4332,7 @@ es6-error@^4.0.1:
   resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
   integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
 
-escalade@^3.0.1:
+escalade@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4"
   integrity sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==
@@ -4315,7 +4352,7 @@ escape-string-regexp@^2.0.0:
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
   integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
 
-escodegen@^1.9.0:
+escodegen@^1.14.1, escodegen@^1.9.0:
   version "1.14.3"
   resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503"
   integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==
@@ -4460,9 +4497,9 @@ estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
   integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
 
 estraverse@^5.1.0:
-  version "5.1.0"
-  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642"
-  integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
+  integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==
 
 esutils@^2.0.2:
   version "2.0.3"
@@ -4840,9 +4877,9 @@ flush-write-stream@^1.0.0:
     readable-stream "^2.3.6"
 
 follow-redirects@^1.0.0:
-  version "1.12.1"
-  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.12.1.tgz#de54a6205311b93d60398ebc01cf7015682312b6"
-  integrity sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
+  integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
 
 for-in@^1.0.1, for-in@^1.0.2:
   version "1.0.2"
@@ -5041,9 +5078,9 @@ get-stream@^4.0.0:
     pump "^3.0.0"
 
 get-stream@^5.0.0:
-  version "5.1.0"
-  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9"
-  integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
+  integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
   dependencies:
     pump "^3.0.0"
 
@@ -5639,6 +5676,13 @@ html-comment-regex@^1.1.0:
   resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7"
   integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==
 
+html-encoding-sniffer@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3"
+  integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==
+  dependencies:
+    whatwg-encoding "^1.0.5"
+
 html-escaper@^2.0.0:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
@@ -5920,6 +5964,11 @@ invariant@^2.2.2, invariant@^2.2.4:
   dependencies:
     loose-envify "^1.0.0"
 
+ip-regex@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
+  integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=
+
 is-absolute-url@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
@@ -6182,7 +6231,7 @@ is-number@^7.0.0:
   resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
   integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
 
-is-obj@^1.0.0, is-obj@^1.0.1:
+is-obj@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
   integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
@@ -6234,6 +6283,11 @@ is-posix-bracket@^0.1.0:
   resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
   integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=
 
+is-potential-custom-element-name@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397"
+  integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c=
+
 is-primitive@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
@@ -6476,9 +6530,9 @@ javascript-stringify@^2.0.1:
   integrity sha512-yV+gqbd5vaOYjqlbk16EG89xB5udgjqQF3C5FAORDg4f/IS1Yc5ERCv5e/57yBcfJYw05V5JyIXabhwb75Xxow==
 
 jest-worker@^26.2.1:
-  version "26.2.1"
-  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.2.1.tgz#5d630ab93f666b53f911615bc13e662b382bd513"
-  integrity sha512-+XcGMMJDTeEGncRb5M5Zq9P7K4sQ1sirhjdOxsN1462h6lFo9w59bl2LVQmdGEEeU3m+maZCkS2Tcc9SfCHO4A==
+  version "26.3.0"
+  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.3.0.tgz#7c8a97e4f4364b4f05ed8bca8ca0c24de091871f"
+  integrity sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw==
   dependencies:
     "@types/node" "*"
     merge-stream "^2.0.0"
@@ -6495,14 +6549,14 @@ js-base64@^2.1.8:
   integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==
 
 js-beautify@^1.11.0:
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.11.0.tgz#afb873dc47d58986360093dcb69951e8bcd5ded2"
-  integrity sha512-a26B+Cx7USQGSWnz9YxgJNMmML/QG2nqIaL7VVYPCXbqiKz8PN0waSNvroMtvAK6tY7g/wPdNWGEP+JTNIBr6A==
+  version "1.12.0"
+  resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.12.0.tgz#6c7e6a47a6075a7c8e60c861e850440a5479d36e"
+  integrity sha512-hZCm93+sWHqrsB2ac38cPX4A9t6mfReq13ZUr/0dk6rCXNLIq0R4lu0EiuJc0Ip6RiWNtE0vECjXOhcy/jMt9Q==
   dependencies:
     config-chain "^1.1.12"
     editorconfig "^0.15.3"
     glob "^7.1.3"
-    mkdirp "~1.0.3"
+    mkdirp "^1.0.4"
     nopt "^4.0.3"
 
 js-stringify@^1.0.1, js-stringify@^1.0.2:
@@ -6589,6 +6643,38 @@ jsbn@~0.1.0:
     taffydb "2.6.2"
     underscore "~1.8.3"
 
+jsdom@^16.2.0:
+  version "16.4.0"
+  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.4.0.tgz#36005bde2d136f73eee1a830c6d45e55408edddb"
+  integrity sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==
+  dependencies:
+    abab "^2.0.3"
+    acorn "^7.1.1"
+    acorn-globals "^6.0.0"
+    cssom "^0.4.4"
+    cssstyle "^2.2.0"
+    data-urls "^2.0.0"
+    decimal.js "^10.2.0"
+    domexception "^2.0.1"
+    escodegen "^1.14.1"
+    html-encoding-sniffer "^2.0.1"
+    is-potential-custom-element-name "^1.0.0"
+    nwsapi "^2.2.0"
+    parse5 "5.1.1"
+    request "^2.88.2"
+    request-promise-native "^1.0.8"
+    saxes "^5.0.0"
+    symbol-tree "^3.2.4"
+    tough-cookie "^3.0.1"
+    w3c-hr-time "^1.0.2"
+    w3c-xmlserializer "^2.0.0"
+    webidl-conversions "^6.1.0"
+    whatwg-encoding "^1.0.5"
+    whatwg-mimetype "^2.3.0"
+    whatwg-url "^8.0.0"
+    ws "^7.2.3"
+    xml-name-validator "^3.0.0"
+
 jsesc@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
@@ -6934,9 +7020,9 @@ lint-staged@^10.2.6:
     stringify-object "^3.3.0"
 
 listr2@^2.1.0:
-  version "2.4.1"
-  resolved "https://registry.yarnpkg.com/listr2/-/listr2-2.4.1.tgz#006fc94ae77b3195403cbf3a4a563e2d6366224f"
-  integrity sha512-8pYsCZCztr5+KAjReLyBeGhLV0vaQ2Du/eMe/ux9QAfQl7efiWejM1IWjALh0zHIRYuIbhQ8N2KztZ4ci56pnQ==
+  version "2.5.1"
+  resolved "https://registry.yarnpkg.com/listr2/-/listr2-2.5.1.tgz#f265dddf916c8a9b475437b34ae85a7d8f495c7a"
+  integrity sha512-qkNRW70SwfwWLD/eiaTf2tfgWT/ZvjmMsnEFJOCzac0cjcc8rYHDBr1eQhRxopj6lZO7Oa5sS/pZzS6q+BsX+w==
   dependencies:
     chalk "^4.1.0"
     cli-truncate "^2.1.0"
@@ -6944,7 +7030,7 @@ listr2@^2.1.0:
     indent-string "^4.0.0"
     log-update "^4.0.0"
     p-map "^4.0.0"
-    rxjs "^6.6.0"
+    rxjs "^6.6.2"
     through "^2.3.8"
 
 load-json-file@^1.0.0:
@@ -7091,6 +7177,11 @@ lodash.some@^4.4.0:
   resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
   integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=
 
+lodash.sortby@^4.7.0:
+  version "4.7.0"
+  resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
+  integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
+
 lodash.template@^4.0.2:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab"
@@ -7370,6 +7461,11 @@ markdown@~0.5.0:
   dependencies:
     nopt "~2.1.1"
 
+marked@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/marked/-/marked-0.7.0.tgz#b64201f051d271b1edc10a04d1ae9b74bb8e5c0e"
+  integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==
+
 marked@^0.8.2:
   version "0.8.2"
   resolved "https://registry.yarnpkg.com/marked/-/marked-0.8.2.tgz#4faad28d26ede351a7a1aaa5fec67915c869e355"
@@ -7464,17 +7560,15 @@ meow@^3.7.0:
     trim-newlines "^1.0.0"
 
 meow@^7.0.0, meow@^7.0.1:
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/meow/-/meow-7.0.1.tgz#1ed4a0a50b3844b451369c48362eb0515f04c1dc"
-  integrity sha512-tBKIQqVrAHqwit0vfuFPY3LlzJYkEOFyKa3bPgxzNl6q/RtN8KQ+ALYEASYuFayzSAsjlhXj/JZ10rH85Q6TUw==
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/meow/-/meow-7.1.0.tgz#50ecbcdafa16f8b58fb7eb9675b933f6473b3a59"
+  integrity sha512-kq5F0KVteskZ3JdfyQFivJEj2RaA8NFsS4+r9DaMKLcUHpk5OcHS3Q0XkCXONB1mZRPsu/Y/qImKri0nwSEZog==
   dependencies:
     "@types/minimist" "^1.2.0"
-    arrify "^2.0.1"
-    camelcase "^6.0.0"
     camelcase-keys "^6.2.2"
     decamelize-keys "^1.1.0"
     hard-rejection "^2.1.0"
-    minimist-options "^4.0.2"
+    minimist-options "4.1.0"
     normalize-package-data "^2.5.0"
     read-pkg-up "^7.0.1"
     redent "^3.0.0"
@@ -7610,7 +7704,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
   dependencies:
     brace-expansion "^1.1.7"
 
-minimist-options@^4.0.2:
+minimist-options@4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619"
   integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==
@@ -7696,7 +7790,7 @@ mkdirp@0.5.5, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkd
   dependencies:
     minimist "^1.2.5"
 
-mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.3:
+mkdirp@^1.0.3, mkdirp@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
   integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
@@ -7836,7 +7930,7 @@ nice-try@^1.0.4:
   resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
   integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
 
-nise@^4.0.1:
+nise@^4.0.4:
   version "4.0.4"
   resolved "https://registry.yarnpkg.com/nise/-/nise-4.0.4.tgz#d73dea3e5731e6561992b8f570be9e363c4512dd"
   integrity sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A==
@@ -7922,7 +8016,7 @@ node-preload@^0.2.1:
   dependencies:
     process-on-spawn "^1.0.0"
 
-node-releases@^1.1.58:
+node-releases@^1.1.60:
   version "1.1.60"
   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084"
   integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==
@@ -8078,6 +8172,11 @@ nunjucks@^3.1.3:
   optionalDependencies:
     chokidar "^3.3.0"
 
+nwsapi@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
+  integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
+
 nyc@^15.0.1:
   version "15.1.0"
   resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.1.0.tgz#1335dae12ddc87b6e249d5a1994ca4bdaea75f02"
@@ -8210,9 +8309,9 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
     wrappy "1"
 
 onetime@^5.1.0:
-  version "5.1.1"
-  resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.1.tgz#5c8016847b0d67fcedb7eef254751cfcdc7e9418"
-  integrity sha512-ZpZpjcJeugQfWsfyQlshVoowIIQ1qBGSVll4rfDq6JJVO//fesjoX808hXWfBjY+ROZgpKDI5TRSRBSoJiZ8eg==
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+  integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
   dependencies:
     mimic-fn "^2.1.0"
 
@@ -8373,13 +8472,12 @@ parent-module@^1.0.0:
     callsites "^3.0.0"
 
 parse-asn1@^5.0.0, parse-asn1@^5.1.5:
-  version "5.1.5"
-  resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e"
-  integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==
+  version "5.1.6"
+  resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4"
+  integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==
   dependencies:
-    asn1.js "^4.0.0"
+    asn1.js "^5.2.0"
     browserify-aes "^1.0.0"
-    create-hash "^1.1.0"
     evp_bytestokey "^1.0.0"
     pbkdf2 "^3.0.3"
     safe-buffer "^5.1.1"
@@ -8446,6 +8544,11 @@ parse5@1.0.0:
   resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.0.0.tgz#d53ec053a2054e7538424ea9a6eeb9b265261279"
   integrity sha1-1T7AU6IFTnU4Qk6ppu65smUmEnk=
 
+parse5@5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
+  integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
+
 parseqs@0.0.5:
   version "0.0.5"
   resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
@@ -8647,9 +8750,9 @@ posix-character-classes@^0.1.0:
   integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
 
 postcss-calc@^7.0.1:
-  version "7.0.2"
-  resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.2.tgz#504efcd008ca0273120568b0792b16cdcde8aac1"
-  integrity sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.3.tgz#d65cca92a3c52bf27ad37a5f732e0587b74f1623"
+  integrity sha512-IB/EAEmZhIMEIhG7Ov4x+l47UaXOS1n2f4FBUk/aKllQhtSCxWhTzn0nJgkqN7fo/jcWySvWTSB6Syk9L+31bA==
   dependencies:
     postcss "^7.0.27"
     postcss-selector-parser "^6.0.2"
@@ -9111,9 +9214,9 @@ pretty-hrtime@^1.0.3:
   integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=
 
 prismjs@^1.17.1:
-  version "1.20.0"
-  resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.20.0.tgz#9b685fc480a3514ee7198eac6a3bf5024319ff03"
-  integrity sha512-AEDjSrVNkynnw6A+B1DsFkd6AVdTnp+/WoUixFRULlCLZVRZlVQMVWio/16jv7G1FscUxQxOQhWwApgbnxr6kQ==
+  version "1.21.0"
+  resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.21.0.tgz#36c086ec36b45319ec4218ee164c110f9fc015a3"
+  integrity sha512-uGdSIu1nk3kej2iZsLyDoJ7e9bnPzIgY0naW/HdknGj61zScaprVEVGHrPoXqI+M9sP0NDnTK2jpkvmldpuqDw==
   optionalDependencies:
     clipboard "^2.0.0"
 
@@ -9875,6 +9978,22 @@ replace-ext@1.0.0:
   resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb"
   integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=
 
+request-promise-core@1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f"
+  integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==
+  dependencies:
+    lodash "^4.17.19"
+
+request-promise-native@^1.0.8:
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28"
+  integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==
+  dependencies:
+    request-promise-core "1.1.4"
+    stealthy-require "^1.1.1"
+    tough-cookie "^2.3.3"
+
 request@^2.51.0, request@^2.87.0, request@^2.88.0, request@^2.88.2:
   version "2.88.2"
   resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
@@ -10063,7 +10182,7 @@ run-queue@^1.0.0, run-queue@^1.0.3:
   dependencies:
     aproba "^1.1.1"
 
-rxjs@^6.6.0:
+rxjs@^6.6.0, rxjs@^6.6.2:
   version "6.6.2"
   resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2"
   integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==
@@ -10112,6 +10231,13 @@ sax@^1.2.4, sax@~1.2.4:
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
   integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
 
+saxes@^5.0.0:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d"
+  integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==
+  dependencies:
+    xmlchars "^2.2.0"
+
 scheduler@^0.19.1:
   version "0.19.1"
   resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
@@ -10198,13 +10324,6 @@ semver@~5.3.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
   integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8=
 
-serialize-javascript@^3.1.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.1.0.tgz#8bf3a9170712664ef2561b44b691eafe399214ea"
-  integrity sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==
-  dependencies:
-    randombytes "^2.1.0"
-
 serialize-javascript@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa"
@@ -10313,16 +10432,16 @@ sinon-chai@^3.5.0:
   integrity sha512-IifbusYiQBpUxxFJkR3wTU68xzBN0+bxCScEaKMjBvAQERg6FnTTc1F17rseLb1tjmkJ23730AXpFI0c47FgAg==
 
 sinon@^9.0.2:
-  version "9.0.2"
-  resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.0.2.tgz#b9017e24633f4b1c98dfb6e784a5f0509f5fd85d"
-  integrity sha512-0uF8Q/QHkizNUmbK3LRFqx5cpTttEVXudywY9Uwzy8bTfZUhljZ7ARzSxnRHWYWtVTeh4Cw+tTb3iU21FQVO9A==
+  version "9.0.3"
+  resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.0.3.tgz#bffc3ec28c936332cd2a41833547b9eed201ecff"
+  integrity sha512-IKo9MIM111+smz9JGwLmw5U1075n1YXeAq8YeSFlndCLhAL5KGn6bLgu7b/4AYHTV/LcEMcRm2wU2YiL55/6Pg==
   dependencies:
     "@sinonjs/commons" "^1.7.2"
     "@sinonjs/fake-timers" "^6.0.1"
     "@sinonjs/formatio" "^5.0.1"
-    "@sinonjs/samsam" "^5.0.3"
+    "@sinonjs/samsam" "^5.1.0"
     diff "^4.0.2"
-    nise "^4.0.1"
+    nise "^4.0.4"
     supports-color "^7.1.0"
 
 sitemap@^6.1.6:
@@ -10663,6 +10782,11 @@ stdout-stream@^1.4.0:
   dependencies:
     readable-stream "^2.0.1"
 
+stealthy-require@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
+  integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
+
 stream-browserify@^2.0.1:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
@@ -11057,6 +11181,11 @@ swig-templates@^2.0.3:
     optimist "~0.6"
     uglify-js "2.6.0"
 
+symbol-tree@^3.2.4:
+  version "3.2.4"
+  resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
+  integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
+
 table@^5.2.3, table@^5.4.6:
   version "5.4.6"
   resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
@@ -11099,15 +11228,15 @@ tar@^6.0.2:
     yallist "^4.0.0"
 
 terser-webpack-plugin@^1.4.3:
-  version "1.4.4"
-  resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.4.tgz#2c63544347324baafa9a56baaddf1634c8abfc2f"
-  integrity sha512-U4mACBHIegmfoEe5fdongHESNJWqsGU+W0S/9+BmYGVQDw1+c2Ow05TpMhxjPK1sRb7cuYq1BPl1e5YHJMTCqA==
+  version "1.4.5"
+  resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b"
+  integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==
   dependencies:
     cacache "^12.0.2"
     find-cache-dir "^2.1.0"
     is-wsl "^1.1.0"
     schema-utils "^1.0.0"
-    serialize-javascript "^3.1.0"
+    serialize-javascript "^4.0.0"
     source-map "^0.6.1"
     terser "^4.1.2"
     webpack-sources "^1.4.0"
@@ -11285,7 +11414,7 @@ token-stream@1.0.0:
   resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-1.0.0.tgz#cc200eab2613f4166d27ff9afc7ca56d49df6eb4"
   integrity sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=
 
-tough-cookie@~2.5.0:
+tough-cookie@^2.3.3, tough-cookie@~2.5.0:
   version "2.5.0"
   resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
   integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
@@ -11293,6 +11422,22 @@ tough-cookie@~2.5.0:
     psl "^1.1.28"
     punycode "^2.1.1"
 
+tough-cookie@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2"
+  integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==
+  dependencies:
+    ip-regex "^2.1.0"
+    psl "^1.1.28"
+    punycode "^2.1.1"
+
+tr46@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479"
+  integrity sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==
+  dependencies:
+    punycode "^2.1.1"
+
 tree-model@^1.0.7:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/tree-model/-/tree-model-1.0.7.tgz#45b2307aa2dfc2da8ceb6a631c1e2b15550dce88"
@@ -11360,6 +11505,18 @@ tunnel-agent@^0.6.0:
   dependencies:
     safe-buffer "^5.0.1"
 
+turndown-plugin-gfm@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/turndown-plugin-gfm/-/turndown-plugin-gfm-1.0.2.tgz#6f8678a361f35220b2bdf5619e6049add75bf1c7"
+  integrity sha512-vwz9tfvF7XN/jE0dGoBei3FXWuvll78ohzCZQuOb+ZjWrs3a0XhQVomJEb2Qh4VHTPNRO4GPZh0V7VRbiWwkRg==
+
+turndown@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/turndown/-/turndown-6.0.0.tgz#c083d6109a9366be1b84b86b20af09140ea4b413"
+  integrity sha512-UVJBhSyRHCpNKtQ00mNWlYUM/i+tcipkb++F0PrOpt0L7EhNd0AX9mWEpL2dRFBu7LWXMp4HgAMA4OeKKnN7og==
+  dependencies:
+    jsdom "^16.2.0"
+
 tweetnacl@^0.14.3, tweetnacl@~0.14.0:
   version "0.14.5"
   resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
@@ -11852,6 +12009,20 @@ vue-template-compiler@^2.6.11:
     de-indent "^1.0.2"
     he "^1.1.0"
 
+w3c-hr-time@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
+  integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==
+  dependencies:
+    browser-process-hrtime "^1.0.0"
+
+w3c-xmlserializer@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a"
+  integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==
+  dependencies:
+    xml-name-validator "^3.0.0"
+
 walkdir@^0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39"
@@ -11887,6 +12058,16 @@ watchpack@^1.7.4:
     chokidar "^3.4.1"
     watchpack-chokidar2 "^2.0.0"
 
+webidl-conversions@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff"
+  integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==
+
+webidl-conversions@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
+  integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
+
 webpack-cli@^3.3.11:
   version "3.3.12"
   resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a"
@@ -11960,6 +12141,27 @@ webpack@^4.43.0:
     watchpack "^1.7.4"
     webpack-sources "^1.4.1"
 
+whatwg-encoding@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
+  integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==
+  dependencies:
+    iconv-lite "0.4.24"
+
+whatwg-mimetype@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
+  integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
+
+whatwg-url@^8.0.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.1.0.tgz#c628acdcf45b82274ce7281ee31dd3c839791771"
+  integrity sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw==
+  dependencies:
+    lodash.sortby "^4.7.0"
+    tr46 "^2.0.2"
+    webidl-conversions "^5.0.0"
+
 which-module@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
@@ -11997,9 +12199,9 @@ window-size@0.1.0:
   integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=
 
 windows-release@^3.1.0:
-  version "3.3.1"
-  resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.3.1.tgz#cb4e80385f8550f709727287bf71035e209c4ace"
-  integrity sha512-Pngk/RDCaI/DkuHPlGTdIkDiTAnAkyMjoQMZqRsxydNl1qGXNIoZrB7RK8g53F2tEgQBMqQJHQdYZuQEEAu54A==
+  version "3.3.3"
+  resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.3.3.tgz#1c10027c7225743eec6b89df160d64c2e0293999"
+  integrity sha512-OSOGH1QYiW5yVor9TtmXKQvt2vjQqbYS+DqmsZw+r7xDwLXEeT3JGW0ZppFmHx4diyXmxt238KFR3N9jzevBRg==
   dependencies:
     execa "^1.0.0"
 
@@ -12088,7 +12290,7 @@ write@1.0.3:
   dependencies:
     mkdirp "^0.5.1"
 
-ws@^7.1.2:
+ws@^7.1.2, ws@^7.2.3:
   version "7.3.1"
   resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
   integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==
@@ -12100,6 +12302,16 @@ ws@~6.1.0:
   dependencies:
     async-limiter "~1.0.0"
 
+xml-name-validator@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
+  integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
+
+xmlchars@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
+  integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
+
 xmlcreate@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-2.0.3.tgz#df9ecd518fd3890ab3548e1b811d040614993497"