How can I replace a "paragraph" node to an "html" node for MDX?
Replacing every "twitter.com" links to expanded Twitter embed HTML within an MDX content.
Reproducible CodeSandbox: https://codesandbox.io/s/dazzling-curran-2bcwe?file=/src/index.mjs:2024-3439
I have an MDX content, which contains a list of twitter links (e.g. https://twitter.com/BrendanEich/status/1151317825908166656), and replacing paragraph node causes following error
../node_modules/esbuild/lib/main.js:869:27: error: [plugin: esbuild-xdm] Cannot read property 'line' of undefined at failureErrorWithLog (/sandbox/node_modules/esbuild/lib/main.js:1449:15) at /sandbox/node_modules/esbuild/lib/main.js:1131:28 at runOnEndCallbacks (/sandbox/node_modules/esbuild/lib/main.js:921:63) at buildResponseToResult (/sandbox/node_modules/esbuild/lib/main.js:1129:7) at /sandbox/node_modules/esbuild/lib/main.js:1236:14 at /sandbox/node_modules/esbuild/lib/main.js:609:9 at handleIncomingPacket (/sandbox/node_modules/esbuild/lib/main.js:706:9) at Socket.readFromStdout (/sandbox/node_modules/esbuild/lib/main.js:576:7) at Socket.emit (events.js:315:20) at addChunk (internal/streams/readable.js:309:12)
Code in question
const remarkTwitter = (options) => {
return transformer;
async function transformer(tree) {
// gather Twitter links
visit(tree, "paragraph", (node) => {
if (isTwitterLink(node)) {
const tweetLink = node.children[0].value;
tweetNodeList.push([node, tweetLink]);
}
});
// Build Tweet embed HTML
// and replace the current node
for (let i = 0; i < tweetNodeList.length; i++) {
const twitterNode = tweetNodeList[i];
const node = twitterNode[0];
const tweetLink = twitterNode[1];
try {
const embedData = await getEmbeddedTweet(tweetLink, options);
node.type = "html";
node.value = embedData.html;
return node;
} catch (err) {
console.error(err);
}
}
}
};
Instead of my custom plugin, I used @remark-embedder/core plugin
Here is the part of the code that uses the plugin.
import remarkEmbedder from "@remark-embedder/core";
// other imports...
const getMDXOptions = (year: number, slug: string) => {
const cwd = `${getBlogRoot(year, slug)}`;
const imagesUrl = `/images/blog/${year}/${slug}`;
return {
cwd,
xdmOptions: (options: any) => {
options.format = "mdx";
options.allowDangerousRemoteMdx = true;
options.rehypePlugins = [twitterWidgetScriptPlugin];
options.remarkPlugins = [
remarkMdxImages,
[
remarkEmbedder,
{
transformers: [
githubGistTransformer,
codeSandboxTransformer,
[oembedTransformer, oembedConfig],
],
handleHTML,
},
],
];
return options;
},
esbuildOptions: (options: BuildOptions) => {
options.outdir = path.join(process.cwd(), "public", imagesUrl);
options.loader = {
...options.loader,
".png": "file",
".jpg": "file",
".gif": "file",
".svg": "file",
};
options.publicPath = imagesUrl;
options.write = true;
return options;
},
};
};