diff --git a/lib/markdown-to-react-loader.js b/lib/markdown-to-react-loader.js
index 73503df..f34e01e 100644
--- a/lib/markdown-to-react-loader.js
+++ b/lib/markdown-to-react-loader.js
@@ -3,24 +3,51 @@ const marked = require('marked');
const Prism = require('prismjs');
const renderer = new marked.Renderer();
+const singleReplaceChars = {
+ '\(': '(',
+ '\)': ')',
+ '\{': '{',
+ '\}': '}',
+}
+const regexString = `[${Object.keys(singleReplaceChars).join('')}]`;
+const regex = new RegExp(regexString, 'gm');
+
+function replaceReact(string) {
+ return string
+ .replace(regex, m => singleReplaceChars[m])
+ .replace(/class="/g, 'className="')
+}
+
+const reactSafe = (fn) => {
+ return function () {
+ let original = fn.apply(renderer, [...arguments]);
+ let replaced = replaceReact(original);
+ return replaced;
+ }
+}
-renderer.code = function(code, lang) {
+function processCodeBlock (code, lang) {
let className, highlighter, replaced;
// users can easily type in any language they want,
// we don't want the app to blow up in case of bad input.
try {
- require(`prismjs/components/prism-${lang}`);
- className = `language-${lang}`;
- highlighter = Prism.languages[lang];
+ if (typeof lang === 'undefined') {
+ className = '';
+ highlighter = '';
+ } else {
+ require(`prismjs/components/prism-${lang}`);
+ className = `language-${lang}`;
+ highlighter = Prism.languages[lang];
+ }
} catch (e) {
- console.warn(`Could not find PrismJS language ${lang}`);
+ console.warn(`Could not find PrismJS language ${lang}`, e);
className = '';
highlighter = '';
}
try {
- const wrapped = Prism.highlight(code, highlighter);
+ const wrapped = Prism.highlight(code, highlighter);
replaced = wrapped.replace(/\n/g, '
');
} catch (e) {
console.error(`Failed to highlight syntax for language ${lang}`);
@@ -31,15 +58,46 @@ renderer.code = function(code, lang) {
}
return `
-
-
- ${replaced}
-
-
- `;
-};
+
+
+ ${replaced}
+
+
+ `;
+}
+
+/**
+ * Block level renderer methods
+ * https://marked.js.org/#/USING_PRO.md#block-level-renderer-methods
+ */
+renderer.code = reactSafe(processCodeBlock);
+renderer.blockquote = reactSafe(renderer.blockquote);
+renderer.paragraph = reactSafe(renderer.paragraph);
+renderer.heading = reactSafe(renderer.heading);
+renderer.html = renderer.html;
+renderer.hr = reactSafe(renderer.hr);
+renderer.list = reactSafe(renderer.list);
+renderer.listitem = reactSafe(renderer.listitem);
+renderer.checkbox = reactSafe(renderer.checkbox);
+renderer.paragraph = reactSafe(renderer.paragraph);
+renderer.table = reactSafe(renderer.table);
+renderer.tablerow = reactSafe(renderer.tablerow);
+renderer.tablecell = reactSafe(renderer.tablecell);
-const extraExports = extra => {
+/**
+ * Inline level renderer methods
+ * https://marked.js.org/#/USING_PRO.md#inline-level-renderer-methods
+ */
+renderer.strong = reactSafe(renderer.strong);
+renderer.em = reactSafe(renderer.em);
+renderer.codespan = reactSafe(renderer.codespan);
+renderer.br = reactSafe(renderer.br);
+renderer.del = reactSafe(renderer.del);
+renderer.link = reactSafe(renderer.link);
+renderer.image = reactSafe(renderer.image);
+renderer.text = reactSafe(renderer.text);
+
+function extraExports(extra) {
let ret = '';
for (var key in extra) {
ret += `
@@ -66,18 +124,11 @@ module.exports = function (source, map) {
return acc;
}, {});
- const replaced = html
- .replace(/class="/g, 'className="')
- .replace(/\(/g, '(')
- .replace(/\)/g, ')')
- .replace(/\{/g, '{')
- .replace(/\}/g, '}');
-
const processed = `
import React, { Fragment } from 'react';
${parsed.attributes.imports ? parsed.attributes.imports : ''}
${extraExports(extra)}
- const Markdown = () => (Something async
++ Note: This document is itself written using Markdown; you + can{" "} + + see the source for it by adding '.text' to the URL + + . +
++ Markdown is intended to be as easy-to-read and easy-to-write as is + feasible. +
++ Readability, however, is emphasized above all else. A Markdown-formatted + document should be publishable as-is, as plain text, without looking like + it's been marked up with tags or formatting instructions. While + Markdown's syntax has been influenced by several existing text-to-HTML + filters -- including{" "} + Setext,{" "} + atx,{" "} + Textile,{" "} + reStructuredText, + Grutatext, + and EtText -- the single + biggest source of inspiration for Markdown's syntax is the format of + plain text email. +
++ A paragraph is simply one or more consecutive lines of text, separated by + one or more blank lines. (A blank line is any line that looks like a + blank line -- a line containing nothing but spaces or tabs is considered + blank.) Normal paragraphs should not be indented with spaces or tabs. +
+
+ The implication of the "one or more consecutive lines of text"
+ rule is that Markdown supports "hard-wrapped" text paragraphs.
+ This differs significantly from most other text-to-HTML formatters
+ (including Movable Type's "Convert Line Breaks"
+ option) which translate every line break character in a paragraph into
+ a <br /> tag.
+
+ When you do want to insert a <br /> break tag
+ using Markdown, you end a line with two or more spaces, then type return.
+
Markdown supports two styles of headers, [Setext] [1] and [atx] [2].
++ Optionally, you may "close" atx-style headers. This is purely + cosmetic -- you can use this if you think it looks better. The closing + hashes don't even need to match the number of hashes used to open the + header. (The number of opening hashes determines the header + level.) +
+
+ Markdown uses email-style > characters for blockquoting.
+ If you're familiar with quoting passages of text in an email message,
+ then you know how to create a blockquote in Markdown. It looks best if you
+ hard wrap the text and put a > before every line:
+
+++ This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, + consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. + Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. +
++ Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse id + sem consectetuer libero luctus adipiscing. +
+
+ Markdown allows you to be lazy and only put the > before
+ the first line of a hard-wrapped paragraph:
+
+++ This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, + consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. + Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. +
+
+++ Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse id + sem consectetuer libero luctus adipiscing. +
+
+ Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by
+ adding additional levels of >:
+
++This is the first level of quoting.
+++This is nested blockquote.
+Back to the first level.
+
+ Blockquotes can contain other Markdown elements, including headers, lists, + and code blocks: +
+++This is a header.
++
+- This is the first list item.
+- This is the second list item.
+Here's some example code:
+ ++++ return shell_exec("echo $input | $markdown_script"); ++
+ Any decent text editor should make email-style quoting easy. For example, + with BBEdit, you can make a selection and choose Increase Quote Level from + the Text menu. +
++ Markdown supports ordered (numbered) and unordered + (bulleted) lists. +
++ Unordered lists use asterisks, pluses, and hyphens -- interchangably -- as + list markers: +
+is equivalent to:
+and:
+Ordered lists use numbers followed by periods:
++ It's important to note that the actual numbers you use to mark the + list have no effect on the HTML output Markdown produces. The HTML + Markdown produces from the above list is: +
+If you instead wrote the list in Markdown like this:
+or even:
++ you'd get the exact same HTML output. The point is, if you want to, + you can use ordinal numbers in your ordered Markdown lists, so that the + numbers in your source match the numbers in your published HTML. But if + you want to be lazy, you don't have to. +
+To make lists look nice, you can wrap items with hanging indents:
+But if you want to be lazy, you don't have to:
++ List items may consist of multiple paragraphs. Each subsequent paragraph + in a list item must be indented by either 4 spaces or one tab: +
++ This is a list item with two paragraphs. Lorem ipsum dolor sit amet, + consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. +
++ Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. + Donec sit amet nisl. Aliquam semper ipsum sit amet velit. +
+Suspendisse id sem consectetuer libero luctus adipiscing.
++ It looks nice if you indent every line of the subsequent paragraphs, but + here again, Markdown will allow you to be lazy: +
+This is a list item with two paragraphs.
++ This is the second paragraph in the list item. You're only + required to indent the first line. Lorem ipsum dolor sit amet, + consectetuer adipiscing elit. +
+Another item in the same list.
+
+ To put a blockquote within a list item, the blockquote's{" "}
+ >
+ delimiters need to be indented:
+
A list item with a blockquote:
+++This is a blockquote inside a list item.
+
+ To put a code block within a list item, the code block needs to be + indented twice -- 8 spaces or two tabs: +
+A list item with a code block:
+ +
+ <code goes here>
+
+
+ Pre-formatted code blocks are used for writing about programming or markup
+ source code. Rather than forming normal paragraphs, the lines of a code
+ block are interpreted literally. Markdown wraps a code block in both{" "}
+ <pre> and <code> tags.
+
+ To produce a code block in Markdown, simply indent every line of the block + by at least 4 spaces or 1 tab. +
+This is a normal paragraph:
+ +
+ This is a code block.
+
+ Here is an example of AppleScript:
+ +
+
+ tell application "Foo"
+
beep
+
+ end tell
+
+
+ + A code block continues until it reaches a line that is not indented + (or the end of the article). +
+
+ Within a code block, ampersands (&) and angle
+ brackets (< and >) are
+ automatically converted into HTML entities. This makes it very easy to
+ include example HTML source code using Markdown -- just paste it and
+ indent it, and Markdown will handle the hassle of encoding the ampersands
+ and angle brackets. For example, this:
+
+
+ <div className="footer">
+
© 2004 Foo Corporation
+
+ </div>
+
+
+ + Regular Markdown syntax is not processed within code blocks. E.g., + asterisks are just literal asterisks within a code block. This means + it's also easy to use Markdown to write about Markdown's own + syntax. +
+ +
+
+ tell application "Foo"
+
beep
+
+ end tell
+
+
+ + Markdown supports two style of links: inline and{" "} + reference. +
+In both styles, the link text is delimited by [square brackets].
++ To create an inline link, use a set of regular parentheses immediately + after the link text's closing square bracket. Inside the parentheses, + put the URL where you want the link to point, along with an{" "} + optional + title for the link, surrounded in quotes. For example: +
++ This is an example inline link. +
++ This link has no title attribute. +
+
+ Markdown treats asterisks (*) and underscores (
+ _) as indicators of emphasis. Text wrapped with one{" "}
+ * or _ will be wrapped with an HTML{" "}
+ <em> tag; double *'s or _
+ 's will be wrapped with an HTML
+ <strong> tag. E.g., this input:
+
+ single asterisks +
++ single underscores +
++ double asterisks +
++ double underscores +
+
+ To indicate a span of code, wrap it with backtick quotes (
+ `). Unlike a pre-formatted code block, a code span
+ indicates code within a normal paragraph. For example:
+
+ Use the printf() function.
+
+
+### Code Blocks
+
+Pre-formatted code blocks are used for writing about programming or
+markup source code. Rather than forming normal paragraphs, the lines
+of a code block are interpreted literally. Markdown wraps a code block
+in both `` and `` tags.
+
+To produce a code block in Markdown, simply indent every line of the
+block by at least 4 spaces or 1 tab.
+
+This is a normal paragraph:
+
+ This is a code block.
+
+Here is an example of AppleScript:
+
+ tell application "Foo"
+ beep
+ end tell
+
+A code block continues until it reaches a line that is not indented
+(or the end of the article).
+
+Within a code block, ampersands (`&`) and angle brackets (`<` and `>`)
+are automatically converted into HTML entities. This makes it very
+easy to include example HTML source code using Markdown -- just paste
+it and indent it, and Markdown will handle the hassle of encoding the
+ampersands and angle brackets. For example, this:
+
+
+
+Regular Markdown syntax is not processed within code blocks. E.g.,
+asterisks are just literal asterisks within a code block. This means
+it's also easy to use Markdown to write about Markdown's own syntax.
+
+```
+tell application "Foo"
+ beep
+end tell
+```
+
+## Span Elements
+
+### Links
+
+Markdown supports two style of links: *inline* and *reference*.
+
+In both styles, the link text is delimited by [square brackets].
+
+To create an inline link, use a set of regular parentheses immediately
+after the link text's closing square bracket. Inside the parentheses,
+put the URL where you want the link to point, along with an *optional*
+title for the link, surrounded in quotes. For example:
+
+This is [an example](http://example.com/) inline link.
+
+[This link](http://example.net/) has no title attribute.
+
+### Emphasis
+
+Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
+emphasis. Text wrapped with one `*` or `_` will be wrapped with an
+HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML
+`` tag. E.g., this input:
+
+*single asterisks*
+
+_single underscores_
+
+**double asterisks**
+
+__double underscores__
+
+### Code
+
+To indicate a span of code, wrap it with backtick quotes (`` ` ``).
+Unlike a pre-formatted code block, a code span indicates code within a
+normal paragraph. For example:
+
+Use the `printf()` function.
diff --git a/test/io/javascript.js b/test/io/javascript.js
new file mode 100644
index 0000000..f607ac4
--- /dev/null
+++ b/test/io/javascript.js
@@ -0,0 +1,11 @@
+import React, { Fragment } from "react";
+import AsyncComponent from "AsyncComponent";
+
+let resolve = () => import("MyComponent");
+
+const Markdown = () => (
+
+
+
+);
+export default Markdown;
diff --git a/test/io/javascript.md b/test/io/javascript.md
new file mode 100644
index 0000000..008ade5
--- /dev/null
+++ b/test/io/javascript.md
@@ -0,0 +1,8 @@
+---
+imports: |
+ import AsyncComponent from 'AsyncComponent';
+
+ let resolve = () => import('MyComponent');
+---
+
+
diff --git a/test/io/replace-chars.js b/test/io/replace-chars.js
new file mode 100644
index 0000000..6d515ea
--- /dev/null
+++ b/test/io/replace-chars.js
@@ -0,0 +1,15 @@
+import React, { Fragment } from "react";
+
+const Markdown = () => (
+
+ Greater >
+ Less <
+ Open {
+ Closed }
+ Trademark (TM)
+ Happy :)
+ Sad :(
+ Emoji 👽
+
+);
+export default Markdown;
diff --git a/test/io/replace-chars.md b/test/io/replace-chars.md
new file mode 100644
index 0000000..6ab8557
--- /dev/null
+++ b/test/io/replace-chars.md
@@ -0,0 +1,15 @@
+Greater >
+
+Less <
+
+Open {
+
+Closed }
+
+Trademark (TM)
+
+Happy :)
+
+Sad :(
+
+Emoji 👽
diff --git a/test/io/table.js b/test/io/table.js
new file mode 100644
index 0000000..789d9cf
--- /dev/null
+++ b/test/io/table.js
@@ -0,0 +1,41 @@
+import React, { Fragment } from "react";
+
+const Markdown = () => (
+
+
+
+
+ column a
+ column b
+ column c
+
+
+
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+
+ 4
+ 5
+
+ code
+
+
+
+ 7
+ 8
+ 9
+
+
+
+
+);
+export default Markdown;
diff --git a/test/io/table.md b/test/io/table.md
new file mode 100644
index 0000000..0cd5e3a
--- /dev/null
+++ b/test/io/table.md
@@ -0,0 +1,5 @@
+| column a | column b | column c |
+|----------|----------|----------|
+| *1* | [2](foo) | **3** |
+| 4 | 5 | `code` |
+| 7 | 8 | 9 |