Compilar e agrupar seus arquivos MDX e suas dependências. RÁPIDO.
Você tem uma sequência de MDX e vários arquivos TS/JS que ele usa e deseja obter uma versão em agrupamento desses arquivos para avaliar no navegador.
Esta é uma função assíncrona que compilará e agrupará seus arquivos MDX e suas dependências. Ele usa o MDX V3 e o ESBUILL, por isso é muito rápido e suporta arquivos tipscript (para as dependências dos seus arquivos MDX).
Seus arquivos de origem podem ser locais, em um repositório remoto do GitHub, em um CMS, ou em qualquer outro lugar, e isso não importa. Tudo o que mdx-bundler
se importa é que você passa por todos os arquivos e código-fonte necessário e ele cuidará de agrupar tudo para você.
O MDX permite combinar a sintaxe concisa de marcação para o seu conteúdo com o poder dos componentes do React. Para sites pesados de conteúdo, escrever o conteúdo com HTML direto pode ser irritantemente verboso. Muitas vezes, as pessoas resolvem isso usando um editor do WSYWIG, mas muitas vezes aquelas ficam aquém do mapeamento da intenção do escritor de HTML. Muitas pessoas preferem usar o Markdown para expressar sua fonte de conteúdo e que isso analisou o HTML a ser renderizado.
O problema de usar o Markdown para o seu conteúdo é que, se você deseja ter alguma interatividade incorporada ao seu conteúdo, você é bastante limitado. Você precisa inserir um elemento que o JavaScript alvos (que é irritantemente indireto) ou pode usar um iframe
ou algo assim.
Como afirmado anteriormente, o MDX permite combinar sintaxe concisa de marcação para o seu conteúdo com o poder dos componentes do React. Assim, você pode importar um componente React e renderizá -lo dentro do próprio Markdown. É o melhor dos dois mundos.
next-mdx-remote
?" mdx-bundler
realmente agrupa dependências dos seus arquivos MDX. Por exemplo, isso não funcionará com next-mdx-remote
, mas será com mdx-bundler
:
--- Título: Exemplo Pós-publicado: 2021-02-13Description: Esta é uma descrição ---# wahooimport demo de './demo' Aqui está uma demonstração ** legal **: <Demonstração />
next-mdx-remote
Garoga nessa importação, porque não é um empacota, é apenas um compilador. mdx-bundler
é um compilador MDX e Poundler. Essa é a diferença.
Essas ferramentas devem ser executadas "no horário de construção" e você implanta a versão construída de seus arquivos. Isso significa que, se você tiver algum conteúdo no MDX e deseja fazer uma mudança de digitação, precisará reconstruir e reimplantar o site inteiro. Isso também significa que cada página do MDX que você adiciona ao seu site aumentará o seu tempo de construção, para que não escala tudo assim.
mdx-bundler
pode definitivamente ser usado no momento da construção, mas é mais poderosamente usado como um empacota de tempo de execução. Um caso de uso comum é ter uma rota para o seu conteúdo MDX e, quando a solicitação chegar, você carrega o conteúdo MDX e entrega isso ao mdx-bundler
para agrupamento. Isso significa que mdx-bundler
é infinitamente escalável. Sua compilação não será mais mais independentemente de quanto conteúdo MDX você possui. Além disso, mdx-bundler
é bastante rápido, mas, para tornar esse agrupamento sob demanda ainda mais rápido, você pode usar cabeçalhos de cache apropriados para evitar reformulações desnecessárias.
Webpack/rollup/etc também exige que todos os seus arquivos MDX estejam no sistema de arquivos local para funcionar. Se você deseja armazenar seu conteúdo MDX em um repositório ou CMS separado, está sem sorte ou precisa fazer algumas ginásticas em tempo de construção para obter os arquivos para a compilação.
Com mdx-bundler
, não importa de onde vem o seu conteúdo MDX, você pode agrupar arquivos de qualquer lugar, você só é responsável por colocar o conteúdo na memória e, em seguida, entrega isso ao mdx-bundler
para agrupamento.
Totalmente. Funciona com qualquer uma dessas ferramentas. Dependendo de o seu meta-quadro suporta a renderização do lado do servidor, você a implementará de maneira diferente. Você pode decidir seguir uma abordagem de tempo construído (para Gatsby/CRA), mas, como mencionado, o verdadeiro poder do mdx-bundler
vem na forma de agrupamento sob demanda. Portanto, é mais adequado para estruturas SSR como Remix/Next.
Por que não?
A Esbuild fornece um serviço escrito em Go com o qual interage. Apenas uma instância desse serviço pode ser executada por vez e deve ter uma versão idêntica ao pacote NPM. Se fosse uma dependência difícil, você só seria capaz de usar a versão Esbuild que o MDX-Bundler usa.
Instalação
Uso
Opções
Retorna
Tipos
Substituição de componentes
Frontmatter e const
Acessando exportações nomeadas
Bundling de imagem
Agrupar um arquivo.
Componentes personalizados em arquivos a jusante
Questões conhecidas
Inspiração
Outras soluções
Problemas
? Bugs
Solicitações de recursos
Colaboradores
LICENÇA
Este módulo é distribuído via NPM, empacotado com nó e deve ser instalado como uma das dependencies
do seu projeto:
npm install --save mdx-bundler esbuild
Uma das dependências do MDX-Bundler requer uma configuração de Node GYP em funcionamento para poder instalar corretamente.
Importar {Bundlemdx} de 'Mdx-Bundler'Const MdxSource = `--- Título: Exemplo Pós-publicado: 2021-02-13Description: Esta é uma descrição ---# Wahooimport Demo de' ./Demo'here's A ** put* * Demo: <Demo />`.trim()Const Result = Wait Bundlemdx ({ Fonte: MDXSource, Arquivos: {'./demo.tsx': `importar * como reação de 'react'function Demo () {return <iv> Demo puro! </div>} exportar demonstração padrão`, },}) const {code, frontMatter} = resultado
A partir daí, você envia o code
para o seu cliente e depois:
importar * como reagir de 'react'import {getMdxComponent} de' mdx-bundler/client'function post ({code, frontmatter}) { // geralmente é uma boa ideia memorizar esta chamada de função para // Evite recriar o componente a cada renderização. const component = react.useememo (() => getMdxComponent (code), [código]) Return (<> <> header> <h1> {FrontMatter.title} </h1> <p> {FrontMatter.Description} </p> </wewgeer> <main> <componente/> </main> </> )}
Por fim, isso é renderizado (basicamente):
<header> <H1> Este é o título </h1> <p> Esta é uma descrição </p> </weweler> <main> <div> <h1> wahoo </h1> <p> Aqui está uma <strong> put </strong> demo: </p> <div> Demo puro! </div> </div> </rain>
A fonte string
do seu MDX.
Não pode ser definido se file
estiver definido
O caminho para o arquivo no seu disco com o MDX in. Você provavelmente desejará definir CWD também.
Não pode ser definido se source
estiver definida
A configuração files
é um objeto de todos os arquivos que você está agrupando. A chave é o caminho para o arquivo (em relação à fonte MDX) e o valor é a sequência do código -fonte do arquivo. Você pode obtê -los no sistema de arquivos ou em um banco de dados remoto. Se o seu MDX não referenciar outros arquivos (ou importar apenas coisas de node_modules
), você poderá omitir isso completamente.
Isso permite modificar a configuração MDX interna (passada para @mdx-js/esbuild
). Isso pode ser útil para especificar seus próprios comunha -PLUGINS/REHYPEPLUGINS.
A função é passada as MDXOPTions padrão e o frontMatter.
Bundlemdx ({ Fonte: MDXSource, MdxOptions (Opções, FrontMatter) {// Esta é a maneira recomendada de adicionar plugins de observar/reprodução personalizados: // A sintaxe pode parecer estranha, mas protege você caso adicione/remova // plugins no futuro.Options.RemarkPlugins = [... (options.RemarkPlugins? },})
Você pode personalizar qualquer uma das opções ESBuild com a opção esbuildOptions
. Isso leva uma função que é aprovada pelas opções Esbuild padrão e o frontMatter e espera que um objeto de opções seja retornado.
Bundlemdx ({ Fonte: MDXSource, EsbuildOptions (Opções, FrontMatter) {options.Minify = falsetions.target = ['ES2020', 'Chrome58', 'Firefox57', 'Safari11', 'Edge16', 'Node12',] Opções de retorno },})
Mais informações sobre as opções disponíveis podem ser encontradas na documentação da Esbuild.
É recomendável usar esse recurso para configurar o target
para a saída desejada, caso contrário, os padrões ESBUILD para esnext
o que é dizer que ele não compila nenhum recurso padronizado, por isso é possível que os usuários de navegadores mais antigos experimentem erros.
Isso diz à Esbuild que um determinado módulo está disponível externamente. Por exemplo, se o seu arquivo MDX usar a biblioteca D3 e você já estiver usando a biblioteca D3 em seu aplicativo, você acabará enviando d3
para o usuário duas vezes (uma vez para o seu aplicativo e uma vez para este componente MDX). Isso é um desperdício e você estaria melhor dizendo ao Esbuild para não agrupar d3
e você pode passar para o componente quando ligar para getMDXComponent
.
Opções globais de configuração externa: https://www.npmjs.com/package/@fal-works/esbuild-plugin-global-externals
Aqui está um exemplo:
// Código de tempo do servidor ou tempo de construção que é executado no nó: importar {bundlemdx} de 'mdx-bundler'const mdxSource = `# Este é o titleImport de esquerda do' esquerdo-pad '<div> {leftpad (" puro Demo ! ", 12, '!')} </div>` .Trim () const resultado = aguarda bundlemdx ({ Fonte: MDXSource, // Nota: isso é * apenas * necessário se você quiser compartilhar deíceivos entre o seu MDX // pacote de arquivos e o aplicativo host. Caso contrário, todos os deíceps serão apenas agrupados. // para que funcione de qualquer maneira, isso é apenas uma otimização para evitar o envio // Várias cópias da mesma biblioteca para seus usuários. Globals: {'esquerda-pad': 'myleftpad'},})
// Código de servidor renderizado e/ou do lado do cliente que pode ser executado no navegador ou nó: importar * como reagir de 'React'import LeftPad de' esquerda-Pad'import {getMdxComponent} de 'mdx-bundler/client'function' Mdxpage ({code}: {code: string}) { const component = react.useMemo (() => getMdxComponent (resultado.code, {myleftpad: leftpad}), [resultado.code, leftpad], ) Retornar (<Main> <componente /> </rain> )}
Definir cwd
( diretório de trabalho atual ) para um diretório permitirá que a ESBuild resolva as importações. Esse diretório pode ser o diretório do qual o conteúdo do MDX foi lido ou um diretório em que o MDX fora do disco deve ser executado .
Conteúdo/Páginas/Demo.tsx
importar * como reagir da demonstração de 'React'Function () { Retornar <div> Demo puro! </div>} exportar demonstração padrão
src/build.ts
Importar {Bundlemdx} de 'Mdx-Bundler'Const MdxSource = `--- Título: Exemplo Pós-publicado: 2021-02-13Description: Esta é uma descrição ---# Wahooimport Demo de' ./Demo'here's A ** put* * Demo: <Demo />`.trim()Const Result = Wait Bundlemdx ({ Fonte: MDXSource, CWD: '/Users/you/site/_content/páginas',}) const {code, FrontMatter} = resultado
Isso permite que você configure as opções de matéria cinza.
Sua função é passada a configuração atual de mata cinza para você modificar. Retorne seu objeto de configuração modificado para substância cinzenta.
Bundlemdx ({ GraymatterOptions: Opções => {Options.excerpt = Opções de Truereturn },})
Isso permite definir o diretório de saída para o pacote e o URL público para o diretório. Se uma opção estiver definida, a outra também deve ser.
O pacote JavaScript não é gravado para este diretório e ainda é retornado como uma string do bundleMDX
.
Esse recurso é melhor usado com ajustes para mdxOptions
e esbuildOptions
. No exemplo abaixo, os arquivos .png
são gravados no disco e servidos em /file/
.
Isso permite que você armazene ativos com seu MDX e depois que a ESBUILD os processe como qualquer outra coisa.
Recomenda -se que cada pacote tenha seu próprio bundleDirectory
, para que vários pacotes não substituam os ativos um do outro.
const {code} = aguarda bundlemdx ({ Arquivo: '/path/to/site/content/file.mdx', CWD: '/path/to/site/content', BundledDirectory: '/path/to/site/public/file', bundlePath: '/file/', mdxOptions: options => {options.RemarkPlugins = [comworkMDIMAGES] Opções de retorno }, EsbuildOptions: options => {options.loader = {... options.loader, '.png': 'arquivo',} opções de retorno },})
bundleMDX
retorna uma promessa para um objeto com as seguintes propriedades.
code
- o pacote do seu MDX como uma string
.
frontmatter
- o object
frontmatter da M -Matter.
matter
- todo o objeto devolvido por cinza -mate
mdx-bundler
fornece times completos em seu próprio pacote.
bundleMDX
possui um parâmetro de tipo único, que é o tipo do seu frontMatter. Padrão para {[key: string]: any}
e deve ser um objeto. Isso é usado para digitar o frontmatter
retornado e o frontmatter passou para esbuildOptions
e mdxOptions
.
const {FrontMatter} = bundleMdx <{title: string}> ({origem}) frontmatter.title // tem string type
O MDX Bundler transmite a capacidade do MDX de substituir os componentes através do suporte components
no componente retornado pelo getMDXComponent
.
Aqui está um exemplo que remove as tags P de imagens em torno.
importar * como reagir de 'react'import {getMdxComponent} de' mdx-bundler/client'const parágrafo: react.fc = props => { if (typeof props.children! == 'string' && props.children.type === 'img') {return <> {props.children} </> } retornar <p {... props} />} função mdxpage ({code}: {code: string}) { const component = react.useememo (() => getMdxComponent (code), [código]) return (<main> <componente componentes = {{p: parágrafo}} /> </rain> )}
Você pode fazer referência à meta ou constituição do FrontMatter no conteúdo MDX.
--- Título: Exemplo de post --- Exportar const ExpletImage = 'https://example.com/image.jpg'# {FrontMatter.title} <img src = {ExpletImage} alt = "Image Alt Text"/>
Você pode usar getMDXExport
em vez do getMDXComponent
para tratar o arquivo MDX como um módulo em vez de apenas um componente. São necessários os mesmos argumentos que getMDXComponent
.
--- Título: Exemplo de posta
importar * como reagir de 'react'import {getMdxExport} de' mdx-bundler/client'function mdxpage ({code}: {code: string}) { const mdxExport = getMdxExport (código) console.log (mdxExport.toc) // [{profundidade: 1, valor: 'o título'}] const component = react.useMemo (() => mdxExport.default, [código]) retornar <componente />}
Com o CWD e o Plugin de observação do Plugin MDX-Imagens, você pode agrupar imagens em seu MDX!
Existem dois carregadores no ESBUILD que podem ser usados aqui. O mais fácil é dataurl
, que gera as imagens como URLs de dados embutidos no código retornado.
importar {comworkMDIMAGES} de 'Comuncida-mdx-images'Const {code} = Aguarda Bundlemdx ({ Fonte: MDXSource, CWD: '/Usuários/you/site/_content/páginas', mdxOptions: options => {options.RemarkPlugins = [... (options.RemarkPlugins? }, EsbuildOptions: options => {options.loader = {... options.loader, '.png': 'dataurl',} Opções de retorno },})
O carregador file
requer um pouco mais de configuração para trabalhar. Com o carregador file
, suas imagens são copiadas para o diretório de saída, para que o ESBuild precisa ser definido para escrever arquivos e precisa saber onde colocá -las mais o URL da pasta para ser usado em fontes de imagem.
Cada chamada para
bundleMDX
é isolada dos outros. Se você definir o diretório, o mesmo para tudo obundleMDX
substituirá sem aviso prévio. Como resultado, cada pacote precisa de seu próprio diretório de saída.
// para o arquivo `_content/páginas/about.mdx`const {code} = aguarda bundlemdx ({ Fonte: MDXSource, CWD: '/Usuários/you/site/_content/páginas', mdxOptions: options => {options.RemarkPlugins = [... (options.RemarkPlugins? }, EsbuildOptions: Options => {// Defina o `udo 'como um local público para este bundle.options.outdir =' /users/you/site/public/img/about'options.loader = {... options.loader , // Diga a Esbuild para usar o `file` carregador para pngs '.png': 'arquivo',} // Defina o caminho público para /img/aboutOptions.publicpath = '/img/about' // Definir gravação como true para que a esbuild produza os arquivos.options.write = truereturn options },})
Se o seu arquivo MDX estiver no seu disco, você poderá economizar algum tempo e código fazendo com que mdx-bundler
leia o arquivo para você. Em vez de fornecer uma sequência source
, você pode definir file
para o caminho do MDX no disco. Defina cwd
em sua pasta para que as importações relativas funcionem.
importar {bundlemdx} de 'mdx-bundler'const {code, frontmatter} = aguarda bundlemdx ({ Arquivo: '/users/you/site/content/file.mdx', CWD: '/Usuários/you/site/content/',})
Para garantir que os componentes personalizados estejam acessíveis em arquivos MDX downstream, você pode usar o MDXProvider
de @mdx-js/react
para passar componentes personalizados para suas importações aninhadas.
npm install --save @mdx-js/react
const globals = { '@mdx-js/react': {Varname: 'mdxjsReact', chamadoExports: ['useemdxComponents'], defaultExport: false, },}; const {code} = bundlemdx ({ fonte, globais, mdxOptions (opções: registro <string, qualquer>) {return {... opções, providerImportSource: '@mdx-js/react',}; }});
A partir daí, você envia o code
para o seu cliente e depois:
importar {mdxProvider, usEmdxComponents} de '@mdx-js/react'; const mdx_global_config = { MdxjsReact: {useMemdxComponents, },}; exportar const mdxComponent: react.fc <{ código: string; FrontMatter: registro <string, qualquer>;}> = ({code}) => { const component = useememo (() => getMdxComponent (código, mdx_global_config), [código], ); return (<mdxprovider components = {{text: ({crianças}) => <p> {crianças} </p>}}> <componente/> </mdxProvider> );};
Adoraríamos que isso funcione em trabalhadores de Cloudflare. Infelizmente, o Cloudflares tem duas limitações que impedem mdx-bundler
de trabalhar nesse ambiente:
Os trabalhadores não podem correr binários. bundleMDX
usa esbuild
(um binário) para agrupar seu código MDX.
Os trabalhadores não podem correr eval
ou similares. getMDXComponent
Avalia o código agrupado usando new Function
.
Uma solução alternativa para isso é colocar seu código relacionado ao MDX-Bundler em um ambiente diferente e chamar esse ambiente de dentro do Cloudflare Worker. IMO, isso derrota o objetivo de usar os trabalhadores do Cloudflare. Outra solução alternativa em potencial é usar o WASM de dentro do trabalhador. Há esbuild-wasm
, mas há alguns problemas com esse pacote explicado nesse link. Depois, há wasm-jseval
, mas eu não consegui que isso execute o código que foi emitido do mdx-bundler
sem erro.
Se alguém gostaria de aproveitar isso, isso seria estelar, mas infelizmente é improvável que eu trabalhe nisso.
A Esbuild conta com __dirname
para descobrir onde é executável, o próximo.js e o webpack às vezes podem quebrar isso e o Esbuild precisa ser informado manualmente onde procurar.
Adicionar o código a seguir antes de seu bundleMDX
apontar o ESBuild diretamente para o executável correto para sua plataforma.
Importar caminho de 'path'if (process.platform ===' win32 ') { process.env.esbuild_binary_path = path.join (process.cwd (), 'node_modules', 'esbuild', 'esbuild.exe', )} outro { process.env.esbuild_binary_path = path.join (process.cwd (), 'node_modules', 'esbuild', 'bin', 'esbuild', ' )}
Mais informações sobre esse assunto podem ser encontradas neste artigo.
Enquanto eu estava reescrevendo o KentcDodds.com para remix, decidi que queria manter minhas postagens no blog como MDX, mas não queria compilá -las todas elas no horário de construção ou ser obrigada a se reimplementar toda vez que conserto um erro de digitação. Então, fiz isso, o que permite que meu servidor compile sob demanda.
Existe o próximo-MDX-Remote, mas é mais um MDX-Compiler do que um empacota (não consigo agrupar seu MDX para dependências). Também está focado no Next.js, enquanto isso é o meta-quadro-frame agnóstico.
Procurando contribuir? Procure o bom rótulo da primeira edição.
Por favor, arquive um problema para bugs, documentação ausente ou comportamento inesperado.
Veja bugs
Por favor, arquive um problema para sugerir novos recursos. Vote nas solicitações de recursos adicionando A ?. Isso ajuda os mantenedores a priorizar o que trabalhar.
Veja solicitações de recursos
Obrigado a essas pessoas (key emoji):
Kent C. Dodds ? | Benwis ? ? | Adam Laycock | Titus ? ? | Christian Murphy ? | Pedro Duarte | Erik Rasmussen |
Omar Syx ? | Gaël Haméon | Gabriel Loiácono | Spencer Miskovik | Casper | Apostolos Christodoulou | Yordis Prieto |
Xoumi | Yasin | Mohammed 'Mo' Mulazada | Pode rau | Hosenur Rahaman | Maciek Sitkowski | Priyang |
Mosaad | StefanProbst | Vlad Moroz |
Este projeto segue a especificação de todos os contribuintes. Contribuições de qualquer tipo de boas -vindas!
Mit