編譯並捆綁您的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) |
該項目遵循全企業規範。歡迎任何形式的貢獻!
麻省理工學院