-
I am working on a richtext editor for MDX here. My main goal is to use regular standard ASTs (instead of slate AST for example) and give users lots of control for overriding how stuff fits together, with reasonable & simple defaults and examples. The main thing I am having trouble with is turning the HTML from the editor into MDX, and I'm a bit lost in the vast unified/syntax-tree/mdx ecosystem, having trouble figuring out even what libraries I should be using. I made a simplified sandbox that illustrates the problem, here. The core of it is this: // remark plugin to insert the editor UI for react-components
function remarkPluginSetupEditUi() {
return (tree) =>
visit(tree, (node) => {
if (node.type === "mdxJsxFlowElement") {
node.type = "html";
node.value = `
<div data-tag=${JSON.stringify(node.name)} data-props="${encodeProps(node)}" class="slim-component flex gap-2 items-center justify-end">
<div class="grow">${node.name}</div>
<button onClick="alert('Here I would popup editor for ${
node.name
} in parent')">EDIT</button>
<button onClick="alert('Here I would trigger delete in parent')">DELETE</button>
</div>
`;
delete node.children;
}
});
}
// rehype plugin to turn HTML from editor into mdast (for turning into MDX)
function rehypePluginEditUiToJsx() {
return (tree) =>
visit(tree, (node) => {
if (
node.type === "element" &&
(node?.properties?.className || []).includes("slim-component")
) {
const tag = node?.properties?.dataTag;
const props = decodeProps(node);
// What to do here to turn into JSX?
console.log("found your component-editor", { tag, props });
}
});
}
// Turns MDX into HTML (for editor)
export const mdx2html = (input, options = {}) => {
return String(
unified()
.use(remarkParse, { fragment: true })
.use(remarkMdx)
.use(remarkPluginSetupEditUi, options)
.use(remarkHtml, { sanitize: false })
.processSync(input)
);
};
// Turns HTML (for editor) into MDX
// XXX: this is the one having trouble!
export const html2mdx = (input, options = {}) => {
return String(
unified()
.use(rehypeParse)
.use(rehypePluginEditUiToJsx, options)
.use(rehypeRemark)
.use(remarkStringify)
.processSync(input)
);
};
<div data-tag="Tester" data-props="ENCODED_PROPS" class="slim-component">
<div>Tester</div>
<button>EDIT</button>
<button>DELETE</button>
</div> I may be going about it wrong, but I basically turn the MDX into HTML, with a On the other side, I want to turn the RTE's HTML AST (with |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 4 replies
-
Hi David! MDX is about JSX. Not about HTML. You notice this with Also: don’t inject strings of HTML into an AST.
That’s what components can also do?
Why do you want to turn MDX into HTML? I thought Slate has an AST
If you have an AST, transform that AST. Don’t serialize it as HTML. If you are working on an editor, you are wasting time by with all the parse/serialize/parse/serialize iterations I recommend taking a step back and reformulating the question. What is it you want to do? You might want to take a look at: |
Beta Was this translation helpful? Give feedback.
-
@wooorm I appreciate the help, but I think you might be misunderstanding my question. I will try to clarify.
I am trying to make my own richtext editor, not use slate. I don't like how slate's config works, and it has a totally different, less standard AST. If you are using slate, it takes a great deal of configuration code to get it working in some way that isn't in the examples (even the examples take a lot of config.) Mine is not like that. It uses regular AST stuff (so you can use syntax-tree/unified plugins to do more with it, etc.) And config is very simple. To add or change the toolbar for example, you can just make a very simple react toolbar, and you're not locked into using their more rigid setup with lots of overrides and configuration.
I am not really trying to make HTML, like as my final-form. I could do that easily, as a sidenote, with renderToStaticNodeStream or similar. It's very doable, but that is not what I am trying to do. I am trying to make HTML that represents the MDX in the RTE, since that is how richtext editors typically work (they are not JSX, or MDX, MD, or an AST, only editable HTML, represented by a regular DOM) and then convert that back to MDX string. If you look at the example, I don't have a problem doing that part. I can make a more complete example, if that is helpful, but have a look at the part that renders the HTML. This part is working fine, and I don't think there is any other way to output HTML other than strings (other HTML AST stuff is stripped, there) in that exact spot in the pipeline (when converting the original MDX source to HTML to be used in the RTE.) I've already got a working richtext editor that does all the parts except children of JSX elements, in the MDX. I am happy to accept that is the wrong way to make the HTML for the editor (as I said, I am no unified expert and although I have worked on a few RTE, it's not my area of expertise, either) but I think I would need to see an example of what you mean, since as I said, mine is working great. There are 3 parts to the MDX RTE, in this solution:
The last part is what I am needing help on, or I need to understand why this is the wrong approach. This seems like the only good way to do this to me, and slate itself works very similar, just without using rehype/remark. |
Beta Was this translation helpful? Give feedback.
-
I dunno if this will clarify, but here is a fork of the sandbox that rebuilds the AST coming back from the RTE's HTML in the The last piece I am missing is "how do I turn the mdast, which includes export const html2mdx = (input, options = {}) => {
return String(
unified()
.use(rehypeParse)
.use(rehypePluginEditUiToJsx, options) // my plugin that outputs mdxJsxFlowElement
.use(rehypeRemark)
.use(remarkMdx) // does this work here? I don't think it does.
.use(remarkStringify)
.processSync(input)
);
}; It doesn't understand |
Beta Was this translation helpful? Give feedback.
With the first (
renderToStaticNodeStream
) you are evaluating things.With the second, you are not.
MDX includes JavaScript. Such as
{1 + 1}
. What do you want in your output?2
or{1 + 1}
? What about:How do you want to represent that in HTML?
And how do you want to convert that back?
You can completely do this. I am just not sure how you want to go about it.