编译并捆绑您的MDX文件及其依赖项。快速地。
您使用的是一串MDX和各种TS/JS文件,并希望在浏览器中获得这些文件的捆绑版本。
这是一个异步函数,它将编译和捆绑您的MDX文件及其依赖项。它使用MDX V3和Esbuild,因此非常快,并且支持打字稿文件(对于MDX文件的依赖项)。
您的源文件可以在远程GITHUB存储库中,在CMS中或其他地方进行本地文件,这并不重要。所有mdx-bundler
都在乎您将其传递给所有文件和源代码所需的所有文件,这将为您捆绑所有内容。
MDX使您可以将内容的Terse Markdown语法与React组件的功能相结合。对于内容丰富的网站,用直接的HTML编写内容可能是令人讨厌的详细信息。人们通常会使用Wsywig编辑来解决此问题,但通常这些编辑在将作者映射到HTML方面却很少。许多人更喜欢使用Markdown表达其内容源,并将其解析为HTML进行渲染。
将Markdown用于内容的问题是,如果您想将一些互动性嵌入到内容中,那么您非常有限。您要么需要插入JavaScript定位的元素(这是令人讨厌的间接),要么可以使用iframe
或其他内容。
如前所述,MDX使您可以将内容的简短标记语法与React组件的功能结合在一起。因此,您可以导入React组件并将其呈现在降价本身中。这是两全其美的最好的。
next-mdx-remote
有何不同?” mdx-bundler
实际上捆绑了MDX文件的依赖关系。例如,这与next-mdx-remote
无法使用,但它将与mdx-bundler
:
---标题:示例推迟:2021-02-13Description:这是一些描述---#Wahooimport演示来自'./demo' 这是一个**整洁的**演示: <演示 />
next-mdx-remote
在该导入中窒息,因为它不是捆绑器,而只是编译器。 mdx-bundler
是MDX编译器和Bundler。那是区别。
这些工具旨在“在构建时间”运行,然后您部署文件的构建版本。这意味着,如果您在MDX中有一些内容并想进行错别字更改,则必须重建和重新部署整个网站。这也意味着,您添加到网站的每个MDX页面都会增加您的构建时间,因此它不能很好地扩展。
mdx-bundler
肯定可以在构建时间使用,但是它更有力地用作运行时捆绑器。一个常见的用例是为您的MDX内容提供一条路由,当该请求进来时,您可以加载MDX内容并将其交给mdx-bundler
进行捆绑。这意味着mdx-bundler
是无限可扩展的。无论您拥有多少MDX内容,您的构建都将不再是。另外, mdx-bundler
非常快,但是要使此按需捆绑更快,您可以使用适当的缓存标头来避免不必要的重新捆绑。
webpack/lollup/etc还要求您所有的MDX文件都在本地文件系统上工作。如果您想将MDX内容存储在单独的存储库中或CMS中,那么您有点不幸,或者必须进行一些构建的体操以获取构建文件的文件。
使用mdx-bundler
,您的MDX内容来自何处,您可以从任何地方捆绑文件,您只是有责任将内容纳入内存,然后将其交给mdx-bundler
以捆绑。
完全。它可以与任何这些工具一起使用。根据您的元框架是否支持服务器端渲染,您将以不同的方式实施。您可能会决定采用建筑时间的方法(对于Gatsby/CRA),但是如前所述, mdx-bundler
的真正力量以按需捆绑的形式出现。因此,它最适合SSR框架,例如Remix/Next。
为什么不呢?
Esbuild提供了与之互动的GO书面的服务。此服务的一个实例一次可以运行,并且必须具有与NPM软件包相同的版本。如果这是一个困难的依赖性,则只能使用Esbuild版本MDX-Bundler用途。
安装
用法
选项
返回
类型
组件替代
前肌和const
访问命名的出口
图像捆绑
捆绑文件。
下游文件中的自定义组件
已知问题
灵感
其他解决方案
问题
?错误
功能请求
贡献者
执照
该模块是通过NPM分发的,该模块与节点捆绑在一起,应作为您项目的dependencies
项之一安装:
npm install --save mdx-bundler esbuild
MDX-Bundler的依赖项之一需要一个工作的节点GYP设置才能正确安装。
import {bundleMDX} from 'mdx-bundler'const mdxSource = `---title: Example Postpublished: 2021-02-13description: This is some description---# Wahooimport Demo from './demo'Here's a **neat* *演示:<demo /> trim() conconst result =等待bundlemdx({{ 资料来源:mdxsource, 文件:{'./demo.tsx':`import * as as react as react'react'function demo(){return <div> neat demo!</div!</div>}导出default default default demo`, },})const {code,frontMatter} =结果
从那里,您将code
发送给客户,然后:
导入 *作为从'react'import {getmdxcomponent}的react *,从'mdx-bundler/client'function post({code,frontMatter}){ //通常是一个好主意 //避免重新创建每个渲染的组件。 const component = react.usemo((()=> getmdxcomponent(代码),[代码]) 返回(<> <Header> <h1> {frontMatter.title} </h1> <p> {frontMatter.description} </p> </header> <ain> <ain> <main> <component/> </> </main> </> </> )
最终,这将被渲染(基本上):
<Header> <h1>这是标题</h1> <p>这是一些描述</p> </header> <ain> <div> <h1> wahoo </h1> <p>这是<strong>整洁</strong>演示:</p> <div>整洁的演示!</div!</div> </div> </main>
MDX的string
源。
如果设置file
,则无法设置
使用MDX IN上的磁盘上文件的路径。您可能也需要设置CWD。
如果设置source
无法设置
files
配置是您要捆绑的所有文件的对象。关键是文件的路径(相对于MDX源),值是文件源代码的字符串。您可以从文件系统或远程数据库中获取这些内容。如果您的MDX不参考其他文件(或仅从node_modules
导入内容),则可以完全省略此内容。
这使您可以修改内置的MDX配置(传递到@mdx-js/esbuild
)。这可能有助于指定您自己的杰尔普格斯/rehypeplugins。
该函数通过默认的mdxoptions和前肌。
bundlemdx({ 资料来源:mdxsource, mdxoptions(选项,frontMatter){//这是添加自定义备注/rehype插件的推荐方法://语法可能看起来很奇怪,但是如果我们将来添加/添加/删除插件,它可以保护您= [...(options.remarkplugins ?? []),myremarkplugin] options.rehypeplugins = [...(options.hrepplugins ?? []),myrehypeplugin]返回选项 },}))
您可以使用该选项esbuildOptions
自定义任何Esbuild选项。这采用了传递默认的ESBUILD选项和前模的函数,并期望返回一个选项对象。
bundlemdx({ 资料来源:mdxsource, esbuildoptions(选项,frontMatter){options.minify = falseptions.target = ['es2020','chrome58','firefox57','safari11','edge16','edge16','node12',]返回选项 },}))
有关可用选项的更多信息,请参见Esbuild文档。
建议使用此功能将target
配置为所需的输出,否则,Esbuild默认为esnext
即它不会编译任何标准化功能,以便较旧的浏览器的用户可能会遇到错误。
这告诉Esbuild,给定的模块在外部可用。例如,如果您的MDX文件使用D3库,并且您已经在应用程序中使用了D3库,则最终将两次将d3
运送到用户(一次用于您的应用程序,一次用于此MDX组件)。这是浪费的,您最好只告诉Esbuild不要捆绑d3
,当您致电getMDXComponent
时,您可以将其传递给组件。
全局外部配置选项:https://www.npmjs.com/package/@fal-works/esbuild-plugin-global-externals
这是一个例子:
//在节点中运行的服务器列或构建时间代码:import {bundlemdx}来自'mdx-bundler'Conconst mdxSource =`#这是'left-pad'<div> {leftpad(“ neateat demo)的titleimport leftpad(” ! 资料来源:mdxsource, //注意:如果您想在MDX之间共享DEP时,这是 *只有 * //文件捆绑包和主机应用。否则,所有二端都将被捆绑在一起。 //因此,无论哪种方式都可以正常工作,这只是避免发送的优化 //给您的用户的同一库的多个副本。 Globals:{'left-pad':'myleftpad'},}),)
//可以在浏览器或节点中运行的Server-rendered和/或客户端代码:导入 *作为从'react'import'react as react as react'从'react'import leftpad中的'left-pad'import {getMdxComponent}从'mdx-bundler/client'function mdxpage({code}:{code:string}){ const component = react.usemo((()=> getmdxcomponent(result.code,{myleftpad:leftpad})),[result.code,leftpad],leftpad], ) 返回(<ain> <component /> < /main> )
将cwd
(当前工作目录)设置为目录将允许Esbuild解决导入。该目录可能是从读取MDX内容的目录,也可以是应使用ddisk MDX的目录。
内容/页面/demo.tsx
导入 *作为从'react'function demo(){ 返回<div>整洁的演示!</div>}导出默认演示
src/build.ts
import {bundleMDX} from 'mdx-bundler'const mdxSource = `---title: Example Postpublished: 2021-02-13description: This is some description---# Wahooimport Demo from './demo'Here's a **neat* *演示:<demo /> trim() conconst result =等待bundlemdx({{ 资料来源:mdxsource, cwd:'/user/you/site/_content/pages',})const {code,frontMatter} =结果
这使您可以配置灰色 - 象征选项。
您的功能传递了当前的灰色 - 模糊配置,供您修改。将修改的配置对象返回灰质。
bundlemdx({ GrayMatterOptions:options => {options.excerpt = trueReturn选项 },}))
这使您可以将捆绑包和公共URL的输出目录设置为目录。如果设置一个选项,则必须也必须是。
JavaScript捆绑包并未写入此目录,并且仍以bundleMDX
的字符串返回。
此功能最好与mdxOptions
和esbuildOptions
进行调整。在下面的示例中, .png
文件写入磁盘,然后从/file/
使用。
这使您可以使用MDX存储资产,然后将Esbuild像其他任何东西一样。
建议每个捆绑包都有自己的bundleDirectory
以便多个捆绑包不会覆盖彼此的资产。
const {code} =等待bundlemdx({{ 文件:'/path/to/site/content/file.mdx', CWD:'/path/to/site/content', BundleDirectory:'/path/to/to/site/public/file', 束:'/file/', mdxoptions:options => {options.remarkplugins = [dempressmdximages]返回选项 },, esbuildoptions:options => {options.loader = {... options.loader,'.png':'file',}返回选项 },}))
bundleMDX
返回具有以下属性的对象的承诺。
code
- MDX的捆绑包作为string
。
frontmatter
- 灰色物体的前肌object
。
matter
- 灰色返回的整个对象
mdx-bundler
在其自己的软件包中提供完整的打字。
bundleMDX
具有单一类型参数,该参数是您的前胶的类型。它默认为{[key: string]: any}
,必须是一个对象。然后将其用于键入返回的frontmatter
,然后将前染色传递给esbuildOptions
和mdxOptions
。
const {frontMatter} = bundlemdx <{title:string}>({source})frontmatter.title //有类型字符串
MDX Bundler传递了MDX通过getMDXComponent
返回的组件的components
支柱替换组件的能力。
这是一个示例,可以从图像周围删除P标签。
导入 *作为从'react'import {getmdxcomponent}的react *从'mdx-bundler/client'Conconst段落:react.fc = props => { if(typeof props.children!=='string'&& props.children.type ==='img'){return <> {props.children} </> } 返回<p {... props} />}函数mdxpage({code}:{code:string}){ const component = react.usemo((()=> getmdxcomponent(代码),[代码]) return(<ain> <component components = {{p:paragraph}}} /> < /main> )
您可以在MDX内容中引用FrontMatter Meta或const。
---标题:示例帖子--- export const示例Image ='https://example.com/image.jpg'# {frontMatter.title} <img src = {exampleimage} alt alt =“ image alt alt =” image alt text“/>
您可以使用getMDXExport
而不是getMDXComponent
将MDX文件视为模块而不是组件。它具有与getMDXComponent
相同的参数。
---标题:示例帖子---导出const toc = [{depth:1,value:'the Title'}]#标题
导入 *作为从'react'import {getmdxexport}的react *从'mdx-bundler/client'function mdxpage({code}:{code:string}){ const mdxexport = getmdxexport(代码) console.log(mdxexport.toc)// [{depth:1,value:'the title'}] const component = react.usemo((()=> mdxexport.default,[code]) 返回<组件 />}
借助CWD和备注插件备注mdx-images,您可以将图像捆绑在MDX中!
Esbuild中有两个装载机可以在这里使用。最简单的是dataurl
,它将图像作为返回代码中的内联数据URL输出。
import {dempressmdximages}来自'dempress-mdx-images'const {code} =等待bundlemdx({{{ 资料来源:mdxsource, CWD:'/user/you/site/_content/pages', mdxoptions:options => {options.remarkplugins = [...(options.remarkplugins ?? []),备注mdximages]返回选项 },, esbuildoptions:options => {options.loader = {... options.loader,'.png':'dataurl',}返回选项 },}))
file
加载程序需要更多的配置才能工作。使用file
加载器,您的图像将复制到输出目录,因此需要设置Esbuild来编写文件,并需要知道将其放置在哪里以及要在图像源中使用的文件夹的URL。
每个对
bundleMDX
的呼叫都与其他呼吁隔离。如果将目录设置为相同的所有内容,则bundleMDX
将覆盖图像而不会警告。结果,每个捆绑包都需要自己的输出目录。
//对于文件`_content/pages/about.mdx`const {code} =等待bundlemdx({{ 资料来源:mdxsource, CWD:'/user/you/site/_content/pages', mdxoptions:options => {options.remarkplugins = [...(options.remarkplugins ?? []),备注mdximages]返回选项 },, esbuildoptions:options => {//将`otdir`设置为bumpuly.options.options.outdir ='/users/you/site/public/public/img/img/about'options.loader = {... options.loader.loader ,//告诉Esbuild使用pngs'.png':'file',} //将公共路径设置为/img/aboutoptions.publicpath ='/img/of true写入true true true true true true true true file'':'file':'file'。因此,Esbuild将输出files.options.write = trueReturn选项 },}))
如果您的MDX文件在您的磁盘上,则可以通过让mdx-bundler
为您读取文件来节省一些时间和代码。而不是提供source
字符串,您可以将file
设置为磁盘上MDX的路径。将cwd
设置为其文件夹,以便相对导入工作。
从'mdx-bundler'const {code,frontMatter} =等待bundlemdx({{{{ 文件:'/users/you/site/content/file.mdx', CWD:'/user/you/site/content/',})
为了确保在下游MDX文件中可以访问自定义组件,您可以使用@mdx-js/react
的MDXProvider
将自定义组件传递给嵌套导入。
npm install --save @mdx-js/react
const globals = { '@mdx-js/react':{varname:'mdxjsreact',名为exports:['usemdxcomponents'],defaultExport:false,false,false, },}; const {code} = bundlemdx({ 来源, 全球, mdxoptions(选项:record <字符串,任何>){return {... options,providerimportsource:'@mdx-js/react',},}; }});
从那里,您将code
发送给客户,然后:
import {mdxprovider,usemdxcomponents}来自'@mdx-js/react'; const mdx_global_config = {{ mdxjsreact:{usemdxcomponents, },};导出const mdxcomponent:react.fc <{ 代码:字符串; frontMatter:record <字符串,任何>;}> =({code})=> { const component = usememo(()=> getmdxcomponent(代码,mdx_global_config),[代码],, ); return(<mdxprovider组件= {{text:({children})=> <p> {children} </p>}}}}> <component/> </> </> </mdxprovider> );};
我们希望这能在Cloudflare工人中工作。不幸的是,Cloudflares有两个限制,可以防止mdx-bundler
在该环境中工作:
工人不能运行二进制文件。 bundleMDX
使用esbuild
(二进制)来捆绑您的MDX代码。
工人无法运行eval
或类似。 getMDXComponent
使用new Function
评估捆绑的代码。
解决此问题的方法是将与MDX捆绑的代码放在不同的环境中,并从Cloudflare工作人员中调用环境。 IMO,这打败了使用Cloudflare工人的目的。另一个潜在的解决方法是使用工人内部的WASM。有esbuild-wasm
,但是该链接中解释了该软件包的一些问题。然后是wasm-jseval
,但是我无法将其运行,该代码是从mdx-bundler
输出而没有错误的代码。
如果有人想挖掘这一点,那将是出色的,但是不幸的是,我不太可能做到这一点。
Esbuild依靠__dirname
来确定可执行文件的位置。
在bundleMDX
之前添加以下代码将直接指向您平台的正确执行操作。
导入路径从'path'if(process.platform ==='win32'){ process.env.esbuild_binary_path = path.join(process.cwd(),'node_modules','esbuild','esbuild.exe','esbuild',' )} 别的 { process.env.esbuild_binary_path = path.join(process.cwd(),'node_modules','esbuild','bin','bin','esbuild',' )
有关此问题的更多信息,请参见本文。
当我将kentcdodds.com重写为混音时,我决定要将博客文章保留为MDX,但是我不想在构建时间进行编译,或者每次修复错字时都需要重新启动。因此,我做了此操作,使我的服务器可以按需编译。
有下一步的mdx示例,但它更像是一个MDX兼容器,而不是捆绑器(无法将MDX捆绑在依赖项中)。另外,它专注于Next.js,而这是Meta-Framework不可知论。
想要贡献?寻找良好的第一期标签。
请提交错误,缺少文档或意外行为的问题。
请参阅错误
请提交问题以建议新功能。通过添加A?对功能请求进行投票。这有助于维护者优先考虑工作。
请参阅功能请求
谢谢这些人(表情符号密钥):
肯特·多德斯(Kent C. Dodds) ? | 本维斯 ? ? | 亚当·莱考克(Adam Laycock) | 泰特斯 ? ? | 克里斯蒂安·墨菲(Christian Murphy) ? | 佩德罗·杜阿尔特(Pedro Duarte) | 埃里克·拉斯穆森(Erik Rasmussen) |
奥马尔·赛克斯(Omar Syx) ? | GaëlHaméon | GabrielLoiácono | 斯宾塞·米斯科维亚克(Spencer Miskoviak) | 卡斯珀 | Apostolos Christodoulou | Yordis Prieto |
XOUMI | Yasin | Mohammed'Mo'Mulazada | 可以 | Hosenur Rahaman | Maciek Sitkowski | Priyang |
莫萨德 | Stefanprobst | 弗拉德·莫罗兹(Vlad Moroz) |
该项目遵循全企业规范。欢迎任何形式的贡献!
麻省理工学院