Thanks for creating this amazing package! This is a really awesome idea.
I would like to ask for some help with plugins. For some reason the don't do anything.
My code
import React from "react";
import { render } from "react-dom";
const ReactMarkdown = require("react-markdown");
const toc = require("remark-toc");
const input = `
# Alpha
## Table of Contents
## Bravo
### Charlie
## Delta
`;
const App = () => <ReactMarkdown source={input} plugins={[toc]} />;
render(<App />, document.getElementById("root"));
codesandbox: https://codesandbox.io/s/340pq1r2l6
The output:
<div id="root">
<div>
<h1>Alpha</h1>
<h2>Table of Contents</h2>
<h2>Bravo</h2>
<h3>Charlie</h3>
<h2>Delta</h2>
</div>
</div>
The table of contents is not created also slugs are not added to headings.
Can't get remark-autolink-headings to work either. The resulting AST transform function in the plugin is never called.
So, turned out this version support only _parser_ plugins. Basically, if you take a look at this diagram only first stage (namely, parse()) is called during the processing and most of the react-* are actually transformers that are applied in the second run() stage.
Probably, it could be fixed by just running run() in the ReactMarkdown function itself after initial parse call. I haven't tried that.
For me the workaround that did the job was to use astPlugins parameter (which is not documented for some reason, may be even a good one). It takes plugins in a very similar format:
import linker from '../src/Documentation/remark-linker'
...
astPlugins={[linker()]}
Note, that function call is made to actually get the transformer from the plugin description.
I'm not sure this is 100% compatible with all the plugins. In my case I wrote the plugin I needed for the dvc documentation site:
function linker() {
function transformer(tree) {
visit(tree, 'inlineCode', function(node, index, parent) {
if (parent.type !== 'link' && /dvc [a-z-.]+/.test(node.value)) {
parent.children[index] = {
type: 'link',
url: 'https://dvc.org/doc/commands-reference/' + node.value.split(' ')[1],
children: [node],
position: node.position
};
}
})
return tree
}
return transformer
}
@rexxars Any thoughts on a solution or workaround for this? @shcheklein 's analysis is correct--transformer plugins do not work with react-markdown.
I ran into this while trying to integrate remark-ping for @mention syntax. It doesn't work with react-markdown, so I ended up using remark/rehype directly.
// render-markdown.js
import remark from "remark";
import remarkPing from "remark-ping";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";
import rehypeHighlight from "rehype-highlight";
export default function renderMarkdown(source) {
const plugins = [
[
remarkPing,
{
//..config...
},
],
remarkRehype,
rehypeStringify,
rehypeHighlight
];
const remarkInst = remark().use(plugins);
return new Promise((resolve, reject) => {
remarkInst.process(source, (err, rendered) => {
err ? reject(err) : resolve(rendered.contents);
});
});
}
// Markdown.jsx
import React from "react";
import t from "prop-types";
import renderMarkdown from "./render-markdown";
export default class Markdown extends React.Component {
constructor(props) {
super(props);
this.state = {
rendered: ""
};
}
componentDidMount() {
const { source } = this.props;
renderMarkdown(source).then(rendered => {
this.setState({ rendered });
});
}
render() {
const { source, ...rest } = this.props;
return (
<div
dangerouslySetInnerHTML={{ __html: this.state.rendered }}
{...rest}
/>
);
}
}
Markdown.propTypes = {
source: t.string.isRequired
};
This really isn't ideal, if only for the impact on bundle size (rehype-highlight alone ends up adding 190 kb to the min/gzip bundle).
The bundle size issue is the reason why I wanted to not include the full remark suite. If we are able to come up with a solution without adding too many bytes to the mix, that would be great. I unfortunately don't have time to dig into this right now, but would be very happy if someone else were to take a stab at it.
I needed to run a plugin for my react project so I dug into this issue a bit. @shcheklein was on the right track. I added the parser.runSync() function to get plugin transforms working. The other issue I came across was that transform generated nodes did not have position which would cause astToReact() to throw an error. I patched that by giving nodes' their parent's positioning. Not sure if that's the best idea, but it got things working. My fork is available here: https://github.com/frankieali/react-markdown. I updated the demo as well to show some third party plugins (remark-behead,remark-toc,remark-sectionize,remark-captions, and remark-linkify-regex) at work. I'll make a PR if you think this works.
My fix adds nothing to the bundle size (excepting the demo, which does not have to be PR'ed in).
Anyone knows why the remark-slug plugin doesn鈥檛 work? According to the above PR, looks like it should... See https://github.com/rexxars/react-markdown/issues/397
Did anyone manage to get remark-autolink-headings plugin working?
I鈥檓 also having trouble getting remark-external-links working. This is
important because it works around a security hole; the hole is properly
fixed by #350, but that PR hasn鈥檛 been merged.
Repro: https://codesandbox.io/s/silly-cherry-bznd3?file=/src/index.js
Note that the pre-rendered output properly includes both target and
rel attributes, whereas the react-markdown output has neither
attribute, suggesting that the plugin is not being called at all. Yet no
error is shown in the UI or console.
import React from "react"; // v16.13.1
import {render} from "react-dom"; // v16.13.1
import ReactMarkdown from "react-markdown"; // v4.3.1
import remark from "remark"; // v12.0.0
import externalLinks from "remark-external-links"; // v6.1.0
import html from "remark-html"; // v11.0.2
const input = "Here is [an example link](https://example.com).";
async function main() {
const rendered = await new Promise((resolve, reject) => {
remark()
.use(externalLinks)
.use(html)
.process(input, (err, file) => {
if (err != null) {
reject(err);
}
resolve(file);
});
});
const App = () => (
<div>
<p>Pre-rendered:</p>
<pre>
<code>{rendered}</code>
</pre>
<p>
With <code>ReactMarkdown</code>:
</p>
<ReactMarkdown source={input} plugins={[externalLinks]} />
</div>
);
render(<App />, document.getElementById("root"));
}
main();
Perhaps this issue should be reopened?
@shcheklein I'm trying to follow your example, I'm pretty new in AST can you point me out from where you are importing the visit function in your code?
visit(tree, 'inlineCode', function(node, index, parent) {
馃檹
@nahumzs hi! sure here is the link - https://github.com/iterative/dvc.org/blob/7f88227d131c5fcf8eead8d7d6c62876152fb395/src/Documentation/Markdown/utils/remark-linker.js (we've updated the plugin since then). I'll edit the existing comment.
Many of these are related to #384, in that they depend on hProperties being supported to add HTML attributes, this will be resolved by #428
Most helpful comment
So, turned out this version support only _parser_ plugins. Basically, if you take a look at this diagram only first stage (namely,
parse()) is called during the processing and most of thereact-*are actually transformers that are applied in the secondrun()stage.Probably, it could be fixed by just running
run()in theReactMarkdownfunction itself after initial parse call. I haven't tried that.For me the workaround that did the job was to use
astPluginsparameter (which is not documented for some reason, may be even a good one). It takes plugins in a very similar format:Note, that function call is made to actually get the transformer from the plugin description.
I'm not sure this is 100% compatible with all the plugins. In my case I wrote the plugin I needed for the dvc documentation site: