UNPKG

dox-draft-js-import-markdown

Version:
840 lines (692 loc) 20.4 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _syntheticDom2 = require('synthetic-dom'); var hasOwnProperty = Object.prototype.hasOwnProperty; /** * Ported from: * https://github.com/chjj/marked/blob/49b7eaca/lib/marked.js * TODO: * Use ES6 classes * Add flow annotations */ /* eslint-disable no-spaced-func */ var assign = Object.assign || function (obj) { var i = 1; for (; i < arguments.length; i++) { var target = arguments[i]; for (var key in target) { if (hasOwnProperty.call(target, key)) { obj[key] = target[key]; } } } return obj; }; var noop = function noop() {}; noop.exec = noop; var defaults = { gfm: true, headings: true, underlines: true, breaks: false, pedantic: false, smartLists: false, silent: false, langPrefix: 'lang-', renderer: new Renderer(), xhtml: false }; /** * 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*$)/, def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/, paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|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.paragraph = replace(block.paragraph)('hr', block.hr)('heading', block.heading)('lheading', block.lheading)('blockquote', block.blockquote)('def', block.def)(); /** * Normal Block Grammar */ block.normal = assign({}, block); /** * GFM Block Grammar */ block.gfm = assign({}, 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') + '|')(); /** * Block Lexer */ function Lexer(options) { this.tokens = []; this.tokens.links = {}; this.options = assign({}, options || defaults); this.rules = block.normal; if (this.options.gfm) { this.rules = block.gfm; } } /** * Expose Block Rules */ Lexer.rules = block; /** * Static Lex Method */ Lexer.parse = function (src, options) { var lexer = new Lexer(options); return lexer.parse(src); }; /** * Preprocessing */ Lexer.prototype.parse = 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 next; var loose; var cap; var bull; var b; var item; var space; var i; var l; src = src.replace(/^ +$/gm, ''); 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); if (this.options.headings) { this.tokens.push({ type: 'heading', depth: cap[1].length, text: cap[2] }); } else { this.tokens.push({ type: 'paragraph', text: cap[0] }); } 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; } // 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; } // 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: /^\\([\\`*{}\[\]()#+\-.!_>])/, link: /^!?\[(inside)\]\(href\)/, reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/, nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/, strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/, em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/, code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/, br: /^ {2,}\n(?!\s*$)/, del: noop, ins: 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 = assign({}, inline); /** * Pedantic Inline Grammar */ inline.pedantic = assign({}, 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 = assign({}, inline.normal, { escape: replace(inline.escape)('])', '~|])')(), del: /^~~(?=\S)([\s\S]*?\S)~~/, ins: /^\+\+(?=\S)([\s\S]*?\S)\+\+/, text: replace(inline.text)(']|', '~+]|')() }); /** * GFM + Line Breaks Inline Grammar */ inline.breaks = assign({}, inline.gfm, { br: replace(inline.br)('{2,}', '*')(), text: replace(inline.gfm.text)('{2,}', '*')() }); /** * Inline Lexer & Compiler */ function InlineLexer(links, options) { this.options = assign({}, options || 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.parse = function (src, links, options) { var inline = new InlineLexer(links, options); return inline.parse(src); }; /** * Lexing/Compiling */ InlineLexer.prototype.parse = function (src) { var out = new _syntheticDom2.FragmentNode(); var link; var cap; while (src) { // escape if (cap = this.rules.escape.exec(src)) { src = src.substring(cap[0].length); out.appendChild(new _syntheticDom2.TextNode(cap[1])); continue; } // link if (cap = this.rules.link.exec(src)) { src = src.substring(cap[0].length); this.inLink = true; out.appendChild(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.appendChild(new _syntheticDom2.TextNode(cap[0].charAt(0))); src = cap[0].substring(1) + src; continue; } this.inLink = true; out.appendChild(this.outputLink(cap, link)); this.inLink = false; continue; } // strong if (cap = this.rules.strong.exec(src)) { src = src.substring(cap[0].length); out.appendChild(this.renderer.strong(this.parse(cap[2] || cap[1]))); continue; } // em if (cap = this.rules.em.exec(src)) { src = src.substring(cap[0].length); out.appendChild(this.renderer.em(this.parse(cap[2] || cap[1]))); continue; } // code if (cap = this.rules.code.exec(src)) { src = src.substring(cap[0].length); out.appendChild(this.renderer.codespan(cap[2])); continue; } // br if (cap = this.rules.br.exec(src)) { src = src.substring(cap[0].length); out.appendChild(this.renderer.br()); continue; } // del (gfm) if (cap = this.rules.del.exec(src)) { src = src.substring(cap[0].length); out.appendChild(this.renderer.del(this.parse(cap[1]))); continue; } // ins (gfm extended) if (cap = this.rules.ins.exec(src)) { src = src.substring(cap[0].length); out.appendChild(this.renderer.ins(this.parse(cap[1]))); continue; } // text if (cap = this.rules.text.exec(src)) { src = src.substring(cap[0].length); out.appendChild(this.renderer.text(new _syntheticDom2.TextNode(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 = link.href; var title = link.title; return cap[0].charAt(0) !== '!' ? this.renderer.link(href, title, this.parse(cap[1])) : this.renderer.image(href, title, cap[1]); }; /** * Renderer */ function Renderer(options) { this.options = options || {}; } Renderer.prototype.code = function (childNode, lang) { var attributes = []; if (lang) { attributes.push({ name: 'class', value: this.options.langPrefix + lang }); } var codeNode = new _syntheticDom2.ElementNode('code', attributes, [childNode]); return new _syntheticDom2.ElementNode('pre', [], [codeNode]); }; Renderer.prototype.blockquote = function (childNode) { return new _syntheticDom2.ElementNode('blockquote', [], [childNode]); }; Renderer.prototype.heading = function (childNode, level) { return new _syntheticDom2.ElementNode('h' + level, [], [childNode]); }; Renderer.prototype.hr = function () { return new _syntheticDom2.ElementNode('hr', [], _syntheticDom2.SELF_CLOSING); }; Renderer.prototype.list = function (childNode, isOrdered) { return new _syntheticDom2.ElementNode(isOrdered ? 'ol' : 'ul', [], [childNode]); }; Renderer.prototype.listitem = function (childNode) { return new _syntheticDom2.ElementNode('li', [], [childNode]); }; Renderer.prototype.paragraph = function (childNode) { return new _syntheticDom2.ElementNode('p', [], [childNode]); }; // span level renderer Renderer.prototype.strong = function (childNode) { return new _syntheticDom2.ElementNode('strong', [], [childNode]); }; Renderer.prototype.em = function (childNode) { return new _syntheticDom2.ElementNode('em', [], [childNode]); }; Renderer.prototype.codespan = function (text) { return new _syntheticDom2.ElementNode('code', [], [new _syntheticDom2.TextNode(text)]); }; Renderer.prototype.br = function () { return new _syntheticDom2.ElementNode('br', [], _syntheticDom2.SELF_CLOSING); }; Renderer.prototype.del = function (childNode) { return new _syntheticDom2.ElementNode('del', [], [childNode]); }; Renderer.prototype.ins = function (childNode) { if (this.options.underlines) { return new _syntheticDom.ElementNode('ins', [], [childNode]); } else { return childNode; } }; Renderer.prototype.link = function (href, title, childNode) { var attributes = [{ name: 'href', value: href }]; if (title) { attributes.push({ name: 'title', value: title }); } return new _syntheticDom2.ElementNode('a', attributes, [childNode]); }; Renderer.prototype.image = function (href, title, alt) { var attributes = [{ name: 'src', value: href }]; if (title) { attributes.push({ name: 'title', value: title }); } if (alt) { attributes.push({ name: 'alt', value: alt }); } return new _syntheticDom2.ElementNode('img', attributes, _syntheticDom2.SELF_CLOSING); }; Renderer.prototype.text = function (childNode) { return childNode; }; /** * Parsing & Compiling */ function Parser(options) { this.tokens = []; this.token = null; this.options = assign({}, options || 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.slice().reverse(); var out = new _syntheticDom2.FragmentNode(); while (this.next()) { out.appendChild(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.parse(body); }; /** * Parse Current Token */ Parser.prototype.tok = function () { switch (this.token.type) { case 'space': { return new _syntheticDom2.TextNode(''); } case 'hr': { return this.renderer.hr(); } case 'heading': { return this.renderer.heading(this.inline.parse(this.token.text), this.token.depth); } case 'code': { return this.renderer.code(this.token.text, this.token.lang); } case 'blockquote_start': { var body = new _syntheticDom2.FragmentNode(); while (this.next().type !== 'blockquote_end') { body.appendChild(this.tok()); } return this.renderer.blockquote(body); } case 'list_start': { var _body = new _syntheticDom2.FragmentNode(); var ordered = this.token.ordered; while (this.next().type !== 'list_end') { _body.appendChild(this.tok()); } return this.renderer.list(_body, ordered); } case 'list_item_start': { var _body2 = new _syntheticDom2.FragmentNode(); while (this.next().type !== 'list_item_end') { _body2.appendChild(this.token.type === 'text' ? this.parseText() : this.tok()); } return this.renderer.listitem(_body2); } case 'loose_item_start': { var _body3 = new _syntheticDom2.FragmentNode(); while (this.next().type !== 'list_item_end') { _body3.appendChild(this.tok()); } return this.renderer.listitem(_body3); } case 'paragraph': { return this.renderer.paragraph(this.inline.parse(this.token.text)); } case 'text': { return this.renderer.paragraph(this.parseText()); } } }; /** * Helpers */ function replace(regex, options) { regex = regex.source; options = options || ''; return function self(name, val) { if (!name) { return new RegExp(regex, options); } val = val.source || val; val = val.replace(/(^|[^\[])\^/g, '$1'); regex = regex.replace(name, val); return self; }; } var MarkdownParser = { parse: function parse(src, options) { options = assign({}, defaults, options); try { var fragment = Parser.parse(Lexer.parse(src, options), options); } catch (e) { if (options.silent) { fragment = new _syntheticDom2.FragmentNode([new _syntheticDom2.ElementNode('p', [], [new _syntheticDom2.TextNode('An error occured:')]), new _syntheticDom2.ElementNode('pre', [], [new _syntheticDom2.TextNode(e.message)])]); } else { throw e; } } if (options.getAST) { return new _syntheticDom2.ElementNode('body', [], [fragment]); } else { return fragment.toString(this.options.xhtml); } } }; exports.default = MarkdownParser;