'Problem Transpiling HTML to JSX of HTML code generated by showdown
I'm using showdown to create some objects using the Markdown editor and in particular I want to include React javascript code in the markdown using the backtick ` ` characters, but my code is not working.
If I look at the markdown preview it's working fine, but when I try to convert it to JSX it breaks.
I can't figure out if it is an escaping error or something else. It seems it's trying to parse the code but it shoudn't, since it's inside a <code> </code> tag in the generated HTML.
Here is my code:
import { observer } from "mobx-react-lite"
import React, {useMemo} from "react"
import Showdown from 'showdown'
import { useStore } from "../store/Provider"
const buble = require('buble');
const MarkdownToJSX = ({ md }) => {
// if (typeof md !== 'string') return null;
const makeComponent = useMemo(() => {
const converter = new Showdown.Converter({
tables: true,
simplifiedAutoLink: true,
strikethrough: true,
tasklists: true,
});
// wrap converted HTML in closures
// const html = <>${converter.makeHtml(md)}</>
const html = converter.makeHtml(md)
const htmlWrapped = '<>'.concat(html).concat('</>')
const code = buble.transform(htmlWrapped).code;
// eslint-disable-next-line
const makeComponent = Function('React', 'return ' + code)
console.log(makeComponent)
return makeComponent;
}, [md]);
return makeComponent(React);
};
function Bounties() {
const store = useStore()
return (
store.bounties.map( bounty => (
<div className="bounties" key={bounty.id}>
<h2>{bounty.title}</h2>
<div >
<MarkdownToJSX md={bounty.body}/>
</div>
</div>
))
)
}
export default observer(Bounties)
Here is an example of the problems I'm facing: https://stackblitz.com/edit/react-8fcgyb
Using the code form de Demo provided on the comments:
import { observer } from "mobx-react-lite"
import React, {useMemo} from "react"
import Showdown from 'showdown'
import "primereact/resources/themes/lara-light-indigo/theme.css"; //theme
import "primereact/resources/primereact.min.css"; //core css
import "primeicons/primeicons.css";
import { Button } from 'primereact/button';
import { useStore } from "../store/Provider"
// import Bounty from "./Bounty";
const buble = require('buble');
const MarkdownToJSX = ({ md }) => {
// if (typeof md !== 'string') return null;
const reg = /(?<=<code>).+(?=<\/code>)/gim;
const makeComponent = useMemo(() => {
const converter = new Showdown.Converter({
tables: true,
simplifiedAutoLink: true,
strikethrough: true,
tasklists: true,
});
// wrap converted HTML in closures
let html = `<>${converter.makeHtml(md)}</>`;
//Extra step to sanitize wrong text content
html = html.replace(reg, (match) => `{"${match}"}`);
console.log("HTML: ",html);
const code = buble.transform(html).code;
// eslint-disable-next-line
const makeComponent = Function('React', 'return ' + code)
console.log(makeComponent)
return makeComponent;
}, [md, reg]);
return makeComponent(React);
};
function selectBounty( event, bountyId) {
console.log(bountyId)
}
function Bounties() {
const store = useStore()
return (
store.bounties.map( bounty => (
<div className="bounties" key={bounty.id}>
<Button
className="p-button-outlined"
onClick={(event) => selectBounty(event, bounty.id)}>{bounty.title}</Button>
<div >
<MarkdownToJSX md={bounty.body}/>
</div>
</div>
))
)
}
export default observer(Bounties)
If I enter console.log(`Hello ${world}` ) on the editor and try to convert, I get this correct HTML:
HTML: <><h1 id="typeyourmarkdownbountyhere">Type your markdown bounty here…</h1>
<pre><code>console.log(`Hello ${world}` )
</code></pre></>
But I get this error.
Uncaught ReferenceError: world is not defined
at eval (eval at <anonymous> (Bounties.js:32:1), <anonymous>:4:94)
at MarkdownToJSX (Bounties.js:37:1)
at renderWithHooks (react-dom.development.js:16141:1)
at mountIndeterminateComponent (react-dom.development.js:20838:1)
at beginWork (react-dom.development.js:22342:1)
at HTMLUnknownElement.callCallback (react-dom.development.js:4157:1)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4206:1)
at invokeGuardedCallback (react-dom.development.js:4270:1)
at beginWork$1 (react-dom.development.js:27243:1)
at performUnitOfWork (react-dom.development.js:26392:1)
at workLoopSync (react-dom.development.js:26303:1)
at renderRootSync (react-dom.development.js:26271:1)
at recoverFromConcurrentError (react-dom.development.js:25689:1)
at performSyncWorkOnRoot (react-dom.development.js:25935:1)
at flushSyncCallbacks (react-dom.development.js:11982:1)
at react-dom.development.js:25490:1
eval @ VM103:4
MarkdownToJSX @ Bounties.js:37
renderWithHooks @ react-dom.development.js:16141
mountIndeterminateComponent @ react-dom.development.js:20838
beginWork @ react-dom.development.js:22342
callCallback @ react-dom.development.js:4157
invokeGuardedCallbackDev @ react-dom.development.js:4206
invokeGuardedCallback @ react-dom.development.js:4270
beginWork$1 @ react-dom.development.js:27243
performUnitOfWork @ react-dom.development.js:26392
workLoopSync @ react-dom.development.js:26303
renderRootSync @ react-dom.development.js:26271
recoverFromConcurrentError @ react-dom.development.js:25689
performSyncWorkOnRoot @ react-dom.development.js:25935
flushSyncCallbacks @ react-dom.development.js:11982
(anonymous) @ react-dom.development.js:25490
index.js:1 The above error occurred in the <MarkdownToJSX> component:
at MarkdownToJSX (http://localhost:3000/static/js/main.chunk.js:830:5)
at div
at div
at observerComponent (http://localhost:3000/static/js/0.chunk.js:18376:76)
at div
at observerComponent (http://localhost:3000/static/js/0.chunk.js:18376:76)
at StoreProvider (http://localhost:3000/static/js/main.chunk.js:1387:5)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
Solution 1:[1]
The problem is that you are trying to write { observer } as a string but without quotes, and JSX can't know that, since in JSX when you put something between curly brackets, it means it's JS code, no matter what tag embraces it, buble will transform it to:
React.createElement("code", null, "import", observer , "etc...")
And observer is obviously undefined when you parse that function.
This should fix it:
body: `#Title1<br/> \`{"import { observer } from 'mobx-react-lite'"}\`<br/>*Line2*`
Since you ned to explicitly tell JSX that you want everything inside those code tags to be evaluated as a JavaScript string type. This is just lean JSX not something related to buble or markdown escape.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Cesare Polonara |
