Compile y agrupe sus archivos MDX y sus dependencias. RÁPIDO.
Tiene una cadena de archivos MDX y varios archivos TS/JS que usa y desea obtener una versión agrupada de estos archivos para evaluar en el navegador.
Esta es una función de Async que compilará y agrupará sus archivos MDX y sus dependencias. Utiliza MDX V3 y ESBuild, por lo que es muy rápido y admite archivos TypeScript (para las dependencias de sus archivos MDX).
Sus archivos de origen podrían ser locales, en un repositorio remoto de GitHub, en un CMS o en cualquier otro lugar y no importa. Todo lo que le importa mdx-bundler
es que lo pasa todos los archivos y el código fuente necesarios y se encargará de agrupar todo para usted.
MDX le permite combinar la sintaxis de Markdown para su contenido con la potencia de los componentes React. Para los sitios de contenido pesado, escribir el contenido con HTML directo puede ser molesto. A menudo, las personas resuelven esto usando un editor de WSYWIG, pero con demasiada frecuencia aquellos se quedan cortos en mapear la intención del escritor de HTML. Muchas personas prefieren usar Markdown para expresar su fuente de contenido y que se analicen en HTML para ser representados.
El problema con el uso de Markdown para su contenido es que si desea tener alguna interactividad integrada en su contenido, es bastante limitado. Necesita insertar un elemento que JavaScript se apunte (que es molesto indirecto), o puede usar un iframe
o algo.
Como se indicó anteriormente, MDX le permite combinar la sintaxis de Markdown para su contenido con la potencia de los componentes React. Por lo tanto, puede importar un componente React y renderizarlo dentro del markdown en sí. Es lo mejor de ambos mundos.
next-mdx-remote
?" mdx-bundler
en realidad envuelve las dependencias de sus archivos MDX. Por ejemplo, esto no funcionará con next-mdx-remote
, pero lo hará con mdx-bundler
:
--- Título: Ejemplo PostPublicado: 2021-02-13Descripción: Esta es una descripción ---# WahooImport Demo de './demo' Aquí hay una demostración ** ordenada **: <Demostración />
next-mdx-remote
se ahoga en esa importación porque no es un Bundler, es solo un compilador. mdx-bundler
es un compilador MDX y Bundler. Esa es la diferencia.
Esas herramientas están destinadas a ejecutarse "a tiempo de compilación" y luego implementa la versión creada de sus archivos. Esto significa que si tiene algo de contenido en MDX y desea hacer un cambio de error tipográfico, debe reconstruir y redistribuir todo el sitio. Esto también significa que cada página de MDX que agregue a su sitio aumentará sus tiempos de construcción, por lo que no escala tan bien.
mdx-bundler
definitivamente se puede usar en el tiempo de compilación, pero se usa más poderosamente como un Bundler de tiempo de ejecución. Un caso de uso común es tener una ruta para su contenido MDX y cuando entra esa solicitud, carga el contenido MDX y lo entrega a mdx-bundler
para agrupar. Esto significa que mdx-bundler
es infinitamente escalable. Su construcción no será más independientemente de la cantidad de contenido MDX que tenga. Además, mdx-bundler
es bastante rápido, pero para hacer este agrupación a pedido aún más rápido, puede usar los encabezados de caché apropiados para evitar un reincaldo innecesario.
Webpack/Rollup/etc también requiere que todos sus archivos MDX estén en el sistema de archivos local para funcionar. Si desea almacenar su contenido MDX en un repositorio o CMS separado, no tiene suerte o tiene que hacer algunas gimnasias de tiempo de compilación para poner los archivos en su lugar para la compilación.
Con mdx-bundler
, no importa de dónde provenga su contenido de MDX, puede agrupar archivos desde cualquier lugar, solo es responsable de poner el contenido en la memoria y luego lo entregará a mdx-bundler
para sujetar.
Totalmente. Funciona con cualquiera de esas herramientas. Dependiendo de si su meta-estructura admite la representación del lado del servidor, la implementará de manera diferente. Puede decidir ir con un enfoque de tiempo construido (para Gatsby/CRA), pero como se mencionó, el verdadero poder de mdx-bundler
viene en forma de agrupación a pedido. Por lo tanto, es el más adecuado para marcos SSR como Remix/Next.
¿Por qué no?
ESBuild proporciona un servicio escrito en GO con el que interactúa. Solo una instancia de este servicio puede ejecutarse a la vez y debe tener una versión idéntica al paquete NPM. Si fuera una dependencia dura, solo podría usar la versión ESBuild MDX-Bundler utiliza.
Instalación
Uso
Opción
Devolución
Tipos
Sustitución de componentes
Mattera y constante
Acceder a las exportaciones con nombre
Agrupación de imágenes
Bundling un archivo.
Componentes personalizados en archivos aguas abajo
Problemas conocidos
Inspiración
Otras soluciones
Asuntos
? Insectos
Solicitudes de funciones
Colaboradores
LICENCIA
Este módulo se distribuye a través de NPM, que se incluye con nodo y debe instalarse como una de dependencies
de su proyecto:
npm install --save mdx-bundler esbuild
Una de las dependencias de MDX-Bundler requiere una configuración de nodo-gyp de trabajo para poder instalarlo correctamente.
import {bundlemdx} de 'mdx-bundler'const mdxSource = `--- Título: Ejemplo PostPublished: 2021-02-13Description: Esta es una descripción ---# wahooimport de' ./demo'Here's a ** ordenado** * Demo: <demo />`.trim()Const result = ALEAT BUNDLEMDX ({ Fuente: MDXSource, Archivos: {'./demo.tsx': `import * como react desde 'react'function demo () {return <div> neat demo! </div>} exportar demo predeterminado`,, },}) const {código, frontmatter} = resultado
A partir de ahí, envía el code
a su cliente y luego:
import * como react desde 'react'import {getMdxComponent} de' MDX-Bundler/Client'Function Post ({Code, Frontmatter}) { // Generalmente es una buena idea memorizar esta llamada de función para // Evite recrear el componente cada renderizado. const componente = react.usememo (() => getMdxComponent (código), [código]) return (<> <Header> <h1> {frontmatter.title} </h1> <p> {frontmatter.description} </p> </weater> <inwer> <componente/> </inem> </> )}
En última instancia, esto se representa (básicamente):
<Header> <h1> Este es el título </h1> <p> Esta es una descripción </p> </weurer> <in Main> <viv> <h1> wahoo </h1> <p> Aquí hay una demostración <strong> neat </strong>: </p> <div> ordenada de demostración! </div> </div> </main>
La fuente string
de su MDX.
No se puede configurar si file
está configurado
La ruta al archivo en su disco con el MDX IN. Probablemente también desee establecer CWD.
No se puede configurar si source
está configurada
La configuración files
es un objeto de todos los archivos que está agrupando. La clave es la ruta al archivo (en relación con la fuente MDX) y el valor es la cadena del código de origen del archivo. Puede obtenerlos desde el sistema de archivos o desde una base de datos remota. Si su MDX no hace referencia a otros archivos (o solo importa cosas desde node_modules
), entonces puede omitir esto por completo.
Esto le permite modificar la configuración MDX incorporada (pasada a @mdx-js/esbuild
). Esto puede ser útil para especificar sus propias plugins/rehypePlugins.
La función se pasa por las mDXOPTions predeterminadas y la redonda.
bundlemdx ({ Fuente: MDXSource, mdxOptions (opciones, frontmatter) {// Esta es la forma recomendada de agregar complementos de observación/rehipo personalizado: // La sintaxis puede parecer extraña, pero lo protege en caso de que agregemos/eliminemos // complementos en el futuro.options.remarkPlugins = [... (options.remarkPlugins? },})
Puede personalizar cualquiera de las opciones de ESBuild con la opción esbuildOptions
. Esto toma una función que se pasa por las opciones de ESBuild predeterminadas y la parte delantera y espera que se devuelva un objeto de opciones.
bundlemdx ({ Fuente: MDXSource, EsBuildoptions (Opciones, FrontMatter) {options.minify = FALSEOPTIONS.TARGET = ['ES2020', 'Chrome58', 'Firefox57', 'Safari11', 'Edge16', 'Node12',] de retorno de opciones de retorno },})
Puede encontrar más información sobre las opciones disponibles en la documentación de ESBuild.
Se recomienda utilizar esta función para configurar el target
en su salida deseada, de lo contrario, ESBuild predeterminado a esnext
, que por decir que no compila ninguna característica estandarizada, por lo que sus posibles usuarios de navegadores más antiguos experimentan errores.
Esto le dice a EsBuild que un módulo dado está disponible externamente. Por ejemplo, si su archivo MDX usa la biblioteca D3 y ya está utilizando la biblioteca D3 en su aplicación, entonces terminará enviando d3
al usuario dos veces (una vez para su aplicación y una vez para este componente MDX). Esto es un desperdicio y sería mejor decirle a ESBuild que no agrupe d3
y puede pasarlo al componente usted mismo cuando llame getMDXComponent
.
Opciones de configuración externa global: https://www.npmjs.com/package/@fal-works/esbuild-plugin-global-externals
Aquí hay un ejemplo:
// código de tiempo de servidor o tiempo de construcción que se ejecuta en nodo: import {bundlemdx} de 'mdx-bundler'const mdxsource = `# Este es el titleimport LeftPad de' Left-Pad '<Div> {LeftPad (" ordenado ! ", 12, '!')} </div>` .trim () const date = await bundlemdx ({ Fuente: MDXSource, // Nota: Esto es * solo * necesario si desea compartir Deps entre su MDX // File Bundle y la aplicación de host. De lo contrario, todos los Deps simplemente serán agrupados. // Entonces funcionará de cualquier manera, esto es solo una optimización para evitar enviar // múltiples copias de la misma biblioteca a sus usuarios. Globals: {'Left-Pad': 'myleftpad'},})
// Código de renderizado y/o del lado del servidor que puede ejecutarse en el navegador o nodo: import * como reaccionar desde 'react'import LeftPad desde' Left-Pad'Import {getMdxComponent} de 'MDX-Bundler/Client'Function' Mdxpage ({código}: {código: string}) { const componente = react.usememo (() => getMdxComponent (result.code, {myleftpad: leftpad}), [resultado.code, leftpad], ) return (<Main> <componente /> </inter> )}
Configuración cwd
( directorio de trabajo actual ) en un directorio permitirá que ESBuild resuelva las importaciones. Este directorio podría ser el directorio que se leyó el contenido de MDX o se leyó un directorio en el que se debe ejecutar MDX fuera del disco.
contenido/páginas/demo.tsx
import * como reaccionar de 'react'function demo () { return <iv> ordenado de demostración! </div>} Exportar demostración predeterminada
src/build.ts
import {bundlemdx} de 'mdx-bundler'const mdxSource = `--- Título: Ejemplo PostPublished: 2021-02-13Description: Esta es una descripción ---# wahooimport de' ./demo'Here's a ** ordenado** * Demo: <demo />`.trim()Const result = ALEAT BUNDLEMDX ({ Fuente: MDXSource, cwd: '/users/you/sitio/_content/páginas',}) const {código, frontmatter} = resultado
Esto le permite configurar las opciones grises.
Su función se pasa por la configuración actual de gris para que usted modifique. Devuelva su objeto de configuración modificado para la materia gris.
bundlemdx ({ GrayMatterOptions: Options => {options.excerpt = TrueReturn Opciones },})
Esto le permite establecer el directorio de salida para el paquete y la URL pública en el directorio. Si se establece una opción, la otra debe ser también.
El paquete JavaScript no está escrito en este directorio y todavía se devuelve como una cadena de bundleMDX
.
Esta característica se usa mejor con ajustes a mdxOptions
y esbuildOptions
. En el ejemplo a continuación, los archivos .png
se escriben en el disco y luego se sirven desde /file/
.
Esto le permite almacenar activos con su MDX y luego hacer que ESBuild los procese como cualquier otra cosa.
Se recomienda que cada paquete tenga su propio bundleDirectory
para que múltiples paquetes no sobrescriban los activos de los demás.
const {código} = ALEA BUNDLEMDX ({ Archivo: '/path/to/site/content/file.mdx', CWD: '/Path/To/Site/Content', bundledirectory: '/path/to/site/public/file', BundlePath: '/file/', mdxoptions: options => {options.remarkPlugins = [observar las opciones de retorno }, EsBuildoptions: options => {options.loader = {... options.loader, '.png': 'file',} Opciones de retorno },})
bundleMDX
devuelve una promesa para un objeto con las siguientes propiedades.
code
: el paquete de su MDX como string
.
frontmatter
: el object
delantero de la materia gris.
matter
: todo el objeto devuelto por gris -materia
mdx-bundler
suministra tipificaciones completas dentro de su propio paquete.
bundleMDX
tiene un parámetro de tipo único que es el tipo de su frontmatter. De manera predeterminada, {[key: string]: any}
y debe ser un objeto. Luego se usa para escribir la frontmatter
devuelta y el frente de los frontal se pasó a esbuildOptions
y mdxOptions
.
const {frontmatter} = bundlemdx <{title: string}> ({fuente}) frontMatter.title // tiene una cadena de tipo
MDX Bundler pasa la capacidad de MDX para sustituir los componentes a través del accesorio components
en el componente devuelto por getMDXComponent
.
Aquí hay un ejemplo que elimina las etiquetas P de las imágenes.
import * como react desde 'react'import {getMdxComponent} de' mdx-bundler/client'const párrafo: react.fc = props => { if (typeof props.children! == 'string' && props.children.type === 'img') {return <> {props.children} </> } return <p {... props} />} function mdxpage ({código}: {código: string}) { const componente = react.usememo (() => getMdxComponent (código), [código]) return (<Rain> <componente componente = {{p: párrafo}} /> </inter> )}
Puede hacer referencia a Meta o Consts de Frontmatter en el contenido MDX.
--- Título: Ejemplo POST --- Exportar const EMPERIORMAGE = 'https://example.com/image.jpg'# {frontMatter.title} <img src = {ejemploMage} alt = "Image Alt Text"/>
Puede usar getMDXExport
en lugar de getMDXComponent
para tratar el archivo MDX como un módulo en lugar de solo un componente. Se necesitan los mismos argumentos que getMDXComponent
.
--- Título: Ejemplo Post --- Exportar const toc = [{profundidad: 1, valor: 'el título'}]# el título
import * como react desde 'react'Import {getMdxExport} de' mdx-bundler/client'function mdxpage ({código}: {código: string}) { const mdxexport = getMdxExport (código) console.log (mdxexport.toc) // [{profundidad: 1, valor: 'el título'}] const componente = react.usememo (() => mdxexport.default, [código]) return <componente />}
¡Con el CWD y el complemento de observación Observación-MDX-Images puede agrupar imágenes en su MDX!
Hay dos cargadores en ESBuild que se pueden usar aquí. El más fácil es dataurl
que genera las imágenes como URL de datos en línea en el código devuelto.
import {observación de observación} de 'observar-mdx-images'const {code} = await bundlemdx ({ Fuente: MDXSource, cwd: '/users/you/sitio/_content/páginas', mdxoptions: options => {options.remarkPlugins = [... (options.remarkPlugins ?? []), observar las opciones de retorno] }, EsBuildoptions: Options => {options.loader = {... options.loader, '.png': 'dataUrl',} Opciones de retorno },})
El cargador file
requiere un poco más de configuración para trabajar. Con el cargador file
, sus imágenes se copian en el directorio de salida, por lo que ESBuild debe configurarse para escribir archivos y debe saber dónde colocarlas más la URL de la carpeta que se utilizará en fuentes de imagen.
Cada llamada a
bundleMDX
está aislada de los demás. Si establece el directorio igual para todo lobundleMDX
sobrescribirá imágenes sin previo aviso. Como resultado, cada paquete necesita su propio directorio de salida.
// para el archivo `_content/pages/acerca.mdx`const {code} = await bundlemdx ({ Fuente: MDXSource, cwd: '/users/you/sitio/_content/páginas', mdxoptions: options => {options.remarkPlugins = [... (options.remarkPlugins ?? []), observar las opciones de retorno] }, EsBuildoptions: options => {// Establezca el `Outdir` en una ubicación pública para este paquete.options.outdir = '/users/you/site/public/img/about'options.loader = {... loader.loader , // dígale a ESBuild que use el `File` Loader para pngs '.png': 'file',} // Establezca la ruta pública a /img/aboutOptions.publicPath = '/img/Acerca de' // SET Write to True para que ESBuild emitirá las opciones files.options.write = TrueleTurn },})
Si su archivo MDX está en su disco, puede guardar algo de tiempo y código haciendo que mdx-bundler
lea el archivo por usted. En lugar de suministrar una cadena source
, puede establecer file
en la ruta del MDX en el disco. Establezca cwd
en su carpeta para que funcione relativamente las importaciones.
import {bundlemdx} de 'mdx-bundler'const {code, frontmatter} = await bundlemdx ({ Archivo: '/users/you/site/content/file.mdx', cwd: '/users/you/sitio/content/',})
Para asegurarse de que se puedan acceder a los componentes personalizados en archivos MDX aguas abajo, puede usar el MDXProvider
de @mdx-js/react
para pasar componentes personalizados a sus importaciones anidadas.
npm install --save @mdx-js/react
const globals = { '@mdx-js/react': {varname: 'mdxjsreact', namedExports: ['usemdxComponents'], defaultExport: false, },}; const {código} = bundlemdx ({ fuente, Globals, mdxoptions (opciones: registro <string, any>) {return {... options, ProviderImportSource: '@mdx-js/react',}; }});
A partir de ahí, envía el code
a su cliente y luego:
import {mdxprovider, usemdxComponents} de '@mdx-js/react'; const mdx_global_config = { Mdxjsreact: {usemdxComponents, },}; exportar const mdxcomponent: react.fc <{ código: cadena; frontmatter: registro <string, any>;}> = ({código}) => { const componente = usememo (() => getMdxComponent (código, mdx_global_config), [código], ); return (<mdxProvider components = {{text: ({niños}) => <p> {niños} </p>}}> <componente/> </mdxprovider> );};
Nos encantaría que esto funcione en los trabajadores de Cloudflare. Desafortunadamente, CloudFlares tiene dos limitaciones que impiden que mdx-bundler
trabaje en ese entorno:
Los trabajadores no pueden ejecutar binarios. bundleMDX
usa esbuild
(un binario) para agrupar su código MDX.
Los trabajadores no pueden ejecutar eval
o similar. getMDXComponent
evalúa el código agrupado utilizando new Function
.
Una solución para esto es colocar su código relacionado con el bundador MDX en un entorno diferente y llamar a ese entorno desde el trabajador de CloudFlare. En mi opinión, esto derrota el propósito de usar trabajadores de Cloudflare. Otra alternativa potencial es usar WASM desde el trabajador. Hay esbuild-wasm
, pero hay algunos problemas con ese paquete explicado en ese enlace. Luego está wasm-jseval
, pero no pude lograr que se ejecute un código que se emitió de mdx-bundler
sin error.
Si a alguien le gustaría profundizar en esto, sería estelar, pero desafortunadamente es poco probable que trabaje en eso.
Esbuild se basa en __dirname
para resolver dónde está ejecutable, Next.js y Webpack a veces pueden romper esto y a Esbuild debe decirle manualmente dónde buscar.
Agregar el siguiente código antes de su bundleMDX
señalará ESBuild directamente al ejecutable correcto para su plataforma.
Importar ruta de 'path'if (process.platform ===' win32 ') { Process.env.esbuild_binary_path = path.join (process.cwd (), 'node_modules', 'esBuild', 'esBuild.exe', )} demás { process.env.esbuild_binary_path = path.join (process.cwd (), 'node_modules', 'esBuild', 'bin', 'eSbuild,, )}
Se puede encontrar más información sobre este tema en este artículo.
Mientras reescribía kentcdodds.com para remezclar, decidí que quería mantener las publicaciones de mi blog como MDX, pero no quería tener que compilarlas a todos en el momento de la compilación o que deba reenviar cada vez que arreglo un error tipográfico. Así que hice esto, lo que permite a mi servidor compilar a pedido.
Hay Next-MDX-Remote, pero es más un compilador MDX que un Bundler (no puede agrupar su MDX para dependencias). También se centra en Next.js, mientras que este es el meta-marco de trabajo agnóstico.
¿Buscas contribuir? Busque la buena etiqueta del primer número.
Por favor presente un problema para errores, documentación faltante o comportamiento inesperado.
Ver errores
Por favor presente un problema para sugerir nuevas funciones. Vote sobre las solicitudes de funciones agregando un? Esto ayuda a los mantenedores a priorizar en qué trabajar.
Ver solicitudes de funciones
Gracias a estas personas (Key Emoji):
Kent C. Dodds ? | benwis ? ? | Adam Laycock | Tito ? ? | Christian Murphy ? | Pedro duarte | Erik Rasmussen |
Omar Syx ? | Gaël Haméon | Gabriel Loiácono | Spencer Miskoviak | Casper | Apostolos Christodoulou | Yordis Prieto |
xumi | Yasin | Mohammed 'Mo' Mulazada | Puede rau | Hosenur Rahaman | Maciek Sitkowski | Priyang |
Mosaad | stefanprobst | Vlad Moroz |
Este proyecto sigue la especificación de todos los contribuyentes. ¡Contribuciones de cualquier tipo bienvenido!
MIT