如何使用组件将 MDX2 编译为静态 HTML"/>
如何使用组件将 MDX2 编译为静态 HTML
我正在尝试编写一个 MDX2 到 HTML 的编译器。我已经完成了大部分工作(也就是说,我可以将一个简单的
.mdx
文件编译为HTML),但没有可导入的组件。
我已经通读了整个 MDX2 网站,但它对我来说并不是很吸引人。我有一堆成功的实验,做了不同的部分,但我还没有完全看到。
目标
我想将一组 MDX2 字符串(一个主要片段和一组组件)编译成静态 HTML 组件——最好不访问磁盘.
(这不是绝对要求,但我想在 AWS lambda 中运行它,并且我无法在部署时访问所有文件。我可以使用 lambda 的
/tmp
目录,但宁愿不是因为各种原因。)
我试过的
这是我目前所拥有的。 这行得通;它在不接触磁盘的情况下将 MDX2 编译为 HTML(除了在此示例中加载入口点,但它可以很容易地是来自其他地方的字符串)。但它不允许我做诸如导入 MDX 组件之类的事情,而且我正在做的字符串操作感觉真的很脏。
首先是我的
.mdx
档案:
//example.mdx
# Title
export const Thing = ({foo}) => <>{foo}</>
# Hello, <Thing foo="bar"/>!
This is the content.
然后我有一个
.js
文件:
// example.js
import fs from 'node:fs/promises'
import { compile } from '@mdx-js/mdx'
import { renderToString } from 'react-dom/server';
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
/**
* Using a vm to compile the mdx. Why? It seems like a way I can add
* components accessible to the entry markdown file. Is there a better way?
*
* This is the only way I could figure out how to _use_ the compiled module
* without touching the filesystem. Is a vfile a better choice? I haven't
* used them yet.
*/
import {NodeVM} from 'vm2';
/**
* Load and compile the ./example.mdx file. For reasons I don't quite
* understand, I have to remove any import statements and the default
* export. If I don't do this, it throws an error (this could be a VM
* issue and a reason not to use it).
*/
let compiled = String(await compile(await fs.readFile('example.mdx')))
.replace(/import .*;/, '')
.replace(/export default MDXContent;/, '')
;
/**
* Here, grab the named export from the module.
*/
let namedExport = compiled.match(/export const (\w+)/);
if (namedExport) {
namedExport = namedExport[1];
}
// Get the compiled version and remove the `export const`
compiled = compiled.replace(/export const (\w+)/, "$1");
/**
* The compild version of this module needs to export MDXContent and the
* named export (if there is one). We do that here.
*/
compiled += `
module.exports = {
MDXContent,
${namedExport || "_void: ''"},
}
`;
/**
* Set up our NodeVM.
*/
const vm = new NodeVM({
console: 'inherit',
sandbox: {
// React resources needed by the module.
_Fragment,
_jsx,
_jsxs,
},
require: {
external : true,
builtin : ['fs','path'],
root : './',
external : true,
mock: {},
},
});
// Now we've got a component.
let component = vm.run(compiled);
/**
* For reasons I don't understand, when I render this I get empty HTML
* comments. This removes them.
*/
console.log(
renderToString(component.MDXContent())
.replace(/<!-- -->/igsm,'')
);
这是我的
package.json
:
{
"name": "mdx",
"version": "1.0.0",
"description": "",
"type": "module",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@mdx-js/loader": "^2.3.0",
"@mdx-js/mdx": "^2.3.0",
"@mdx-js/node-loader": "^2.3.0",
"@mdx-js/react": "^2.3.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-jsx": "^1.0.0",
"vm2": "^3.9.17"
}
}
这行得通——但感觉太老套了。由于我们在后台使用统一,我当然可以只呈现为静态 HTML 吗?据我了解,MDX2(通过
@mdx-js/mdx
)基本上做了类似的事情:
mdx → hast → esast
从 MDX2 到编译组件。我知道
esast
步骤是解决进口等问题。我觉得我应该能够从那里back到hast
并只渲染片段,但我不完全明白它。
下一步...?
我很乐意使用一组 MDX2(也许还有 MD)strings 并将所有内容编译成片段而无需访问磁盘。我看到了 mdx-bundler(链接自 mdx 文档中的here),但我无法让它工作。不过,它看起来很有希望。
这是我想要开始工作的一个例子:
组件A
Component A says: {props.data}
组件B
<ul>{props.items.map(item => (<li>{item}</li>))}</ul>
入口点降价
# Entrypoint
<ComponentA data="foo"/>
<ComponentB items={1,2,3}/>
输出:
<h1>Entrypoint</h1>
<p>Component A says: foo</p>
<ul><li>1</li><li>2</li><li>3</li></ul>
使用一些看起来像这样的 javascript:
...
const ComponentA = 'Component A says: {foo}';
const ComponentB = componentBFromSomewhere;
const entrypoint = entrypointFromSomewhere;
function mdxToHtml (e, opt) {
// ... magic!
}
console.log(mdxToHtml(entrypoint, {components: {ComponentA, ComponentB}}));
或类似的东西。帮忙吗?
回答如下:更多推荐
如何使用组件将 MDX2 编译为静态 HTML
发布评论