Compilez et regroupez vos fichiers MDX et leurs dépendances. RAPIDE.
Vous disposez d'une chaîne de fichiers MDX et de divers fichiers TS / JS qu'il utilise et vous souhaitez obtenir une version groupée de ces fichiers à évaluer dans le navigateur.
Il s'agit d'une fonction asynchrone qui compile et regroupera vos fichiers MDX et leurs dépendances. Il utilise MDX V3 et ESBUILD, il est donc très rapide et prend en charge les fichiers TypeScript (pour les dépendances de vos fichiers MDX).
Vos fichiers source peuvent être locaux, dans un repo github distant, dans un CMS, ou partout où et cela n'a pas d'importance. Tout ce qui se soucie de mdx-bundler
, c'est que vous le transmettez tous les fichiers et le code source nécessaire et qu'il prendra en charge tout pour vous.
MDX vous permet de combiner la syntaxe Markdown laconique pour votre contenu avec la puissance des composants React. Pour les sites lourds de contenu, l'écriture du contenu avec du HTML droit peut être ennuyeusement verbeux. Souvent, les gens résolvent cela en utilisant un éditeur de WSYWIG, mais trop souvent ceux qui ne sont pas à la cartographie de l'intention de l'écrivain à HTML. Beaucoup de gens préfèrent utiliser Markdown pour exprimer leur source de contenu et ont analysé ce HTML à être rendu.
Le problème avec l'utilisation de Markdown pour votre contenu est que si vous voulez avoir une interactivité intégrée dans votre contenu, vous êtes assez limité. Vous devez soit insérer un élément que JavaScript cible (ce qui est indirect de manière ennuyeuse), ou vous pouvez utiliser un iframe
ou quelque chose.
Comme indiqué précédemment, MDX vous permet de combiner la syntaxe Markdown TERSE pour votre contenu avec la puissance des composants React. Ainsi, vous pouvez importer un composant React et le rendre dans le Markdown lui-même. C'est le meilleur des deux mondes.
next-mdx-remote
?" mdx-bundler
regorge de dépendances de vos fichiers MDX. Par exemple, cela ne fonctionnera pas avec next-mdx-remote
, mais il le sera avec mdx-bundler
:
--- Titre: Exemple repose publié: 2021-02-13Description: Ceci est une description --- # Demo wahooimport de './demo' Voici une démo ** soignée **: <Démo />
next-mdx-remote
s'étouffe sur cette importation parce que ce n'est pas un bundler, c'est juste un compilateur. mdx-bundler
est un compilateur MDX et Bundler. C'est la différence.
Ces outils sont destinés à être exécutés "au moment de la construction", puis vous déployez la version construite de vos fichiers. Cela signifie que si vous avez du contenu dans MDX et que vous souhaitez faire un changement de faute de frappe, vous devez reconstruire et redéployer tout le site. Cela signifie également que chaque page MDX que vous ajoutez à votre site augmentera vos temps de construction, de sorte que cela ne s'allonge pas bien.
mdx-bundler
peut certainement être utilisé à la construction, mais il est plus puissamment utilisé comme bundler d'exécution. Un cas d'utilisation courant consiste à avoir un itinéraire pour votre contenu MDX et lorsque cette demande entre, vous chargez le contenu MDX et remettez-le à mdx-bundler
pour le regroupement. Cela signifie que mdx-bundler
est infiniment évolutif. Votre version ne sera plus plus que votre contenu MDX dont vous disposez. En outre, mdx-bundler
est assez rapide, mais pour rendre ce regroupement à la demande encore plus rapidement, vous pouvez utiliser des en-têtes de cache appropriés pour éviter une récompense inutile.
WebPack / Rollup / etc nécessite également que tous vos fichiers MDX soient sur le système de fichiers local pour fonctionner. Si vous souhaitez stocker votre contenu MDX dans un référentiel séparé ou CMS, vous n'avez pas de chance ou vous devez faire de la gymnastique de construction pour mettre en place les fichiers pour la construction.
Avec mdx-bundler
, peu importe d'où vient votre contenu MDX, vous pouvez regrouper des fichiers de n'importe où, vous êtes simplement responsable de mettre le contenu en mémoire, puis vous remettez cela à mdx-bundler
pour le regroupement.
Totalement. Il fonctionne avec l'un de ces outils. Selon que votre méta-tramework prenne en charge le rendu côté serveur, vous l'implémenterez différemment. Vous pourriez décider d'aller avec une approche de temps de construction (pour Gatsby / CRA), mais comme mentionné, la véritable puissance de mdx-bundler
se présente sous la forme d'un regroupement à la demande. Il est donc mieux adapté aux frameworks SSR comme Remix / Suivant.
Pourquoi pas?
Esbuild fournit un service écrit en Go avec lequel il interagit. Une seule instance de ce service peut s'exécuter à la fois et elle doit avoir une version identique au package NPM. S'il s'agissait d'une dépendance difficile, vous ne pourriez utiliser que la version Esbuild MDX-Bundler utilise.
Installation
Usage
Options
Rendements
Types
Substitution des composants
Frontmatter et const
Accéder aux exportations nommées
Regroupement d'images
Regrouper un fichier.
Composants personnalisés dans des fichiers en aval
Problèmes connus
Inspiration
Autres solutions
Problèmes
? Insectes
Demandes de fonctionnalités
Contributeurs
LICENCE
Ce module est distribué via le NPM qui est regroupé de nœud et doit être installé comme l'une des dependencies
de votre projet:
npm install --save mdx-bundler esbuild
L'une des dépendances de MDX-Bundler nécessite une configuration de gyp nœud de travail pour pouvoir installer correctement.
Import {bundlemdx} de 'mdx-bundler'const mdxsource = `--- Titre: Exemple postpublié: 2021-02-13Description: Ceci est une description --- # wahooimport Demo from' ./demo'here's a ** net * * Demo: <Demo />`.trim()Const result = attente bundlemdx ({ Source: mdxsource, Fichiers: {'./demo.tsx': `import * en tant que réagir à partir de 'react'function démo () {return <div> net démo! </div>} Export Demo par défaut`, },}) const {code, frontmatter} = résultat
De là, vous envoyez le code
à votre client, puis:
Importer * en tant que réagir à partir de 'react'import {getMDxComponent} de' mdx-bundler / client'function post ({code, frontmatter}) { // C'est généralement une bonne idée de mémoriser cet appel de fonction à // Évitez de recréer le composant à chaque rendu. const composant = react.UseMemo (() => getMDxComponent (code), [code]) return (<> <e-header> <h1> {frontmatter.title} </h1> <p> {frontmatter.description} </p> </ header> <Main> <composant /> </-main> </> )}
En fin de compte, cela est rendu (en gros):
<dique> <h1> c'est le titre </h1> <p> Ceci est une description </p> </-header> <Main> <div> <h1> wahoo </h1> <p> Voici une démo <strong> soignée </strong>: </p> <div> Demo net! </div> </div> </-main>
La source string
de votre MDX.
Ne peut pas être défini si file
est défini
Le chemin d'accès vers le fichier de votre disque avec le MDX dans. Vous voudrez probablement également définir CWD.
Ne peut pas être défini si source
est définie
La configuration files
est un objet de tous les fichiers que vous regroupez. La clé est le chemin d'accès au fichier (par rapport à la source MDX) et la valeur est la chaîne du code source de fichier. Vous pouvez les obtenir du système de fichiers ou d'une base de données distante. Si votre MDX ne fait pas référence à d'autres fichiers (ou n'importe que des choses à partir de node_modules
), vous pouvez l'omettre entièrement.
Cela vous permet de modifier la configuration MDX intégrée (transmise à @mdx-js/esbuild
). Cela peut être utile pour spécifier vos propres bouchons de bouchons / reflets.
La fonction est transmise les mdxoptions par défaut et le frontmatter.
bundlemdx ({ Source: mdxsource, mdxoptions (options, frontmatter) {// c'est le moyen recommandé d'ajouter des plugins de remarques / réchype personnalisés: // La syntaxe peut sembler bizarre, mais elle vous protège au cas où nous ajouterons / en supprimant // les plugins dans le futur.options.remarkplugins = [... (options.remarkPlugins ?? []), myRemarkPlugin] options.rehypePlugins = [... (options.rehypePlugins ?? []), myrehypePlugin] Options de retour },})
Vous pouvez personnaliser l'une des options Esbuild avec l'option esbuildOptions
. Cela prend une fonction qui est transmise les options Esbuild par défaut et le frontmatter et s'attend à ce qu'un objet d'options soit renvoyé.
bundlemdx ({ Source: mdxsource, EsBuildOptions (Options, FrontMatter) {Options.Minify = FalseOptions.target = ['ES2020', 'Chrome58', 'Firefox57', 'Safari11', 'Edge16', 'Node12',] Return Options Options Options },})
Plus d'informations sur les options disponibles peuvent être trouvées dans la documentation Esbuild.
Il est recommandé d'utiliser cette fonctionnalité pour configurer la target
à la sortie souhaitée, sinon, Esbuild par défaut est à esnext
ce qui veut dire qu'il ne compile aucune fonctionnalité standardisée, il est donc possible que les utilisateurs de navigateurs plus anciens subissent des erreurs.
Cela indique à Esbuild qu'un module donné est disponible en externe. Par exemple, si votre fichier MDX utilise la bibliothèque D3 et que vous utilisez déjà la bibliothèque D3 dans votre application, vous finirez par expédier d3
à l'utilisateur deux fois (une fois pour votre application et une fois pour ce composant MDX). C'est un gaspillage et vous feriez mieux de simplement dire à Esbuild de ne pas regrouper d3
et vous pouvez le transmettre au composant vous-même lorsque vous appelez getMDXComponent
.
Options de configuration externe globale: https://www.npmjs.com/package/@fal-works/esbuild-plugin-global-externals
Voici un exemple:
// Code de serveur ou de build-time qui s'exécute dans Node: import {bundlemdx} de 'mdx-bundler'const mdxsource = `# Ceci est le title import Leftpad de' Left-pad '<div> {LeftPad (" Demo net ! ", 12, '! Source: mdxsource, // Remarque: ce n'est * que * nécessaire si vous souhaitez partager les DEP entre votre MDX // File Bundle et l'application hôte. Sinon, tous les DEP seront simplement regroupés. // donc cela fonctionnera dans les deux cas, c'est juste une optimisation pour éviter d'envoyer // plusieurs copies de la même bibliothèque à vos utilisateurs. Globals: {'Left-pad': 'myleftpad'},})
// Code de serveur rendu et / ou côté client qui peut s'exécuter dans le navigateur ou le nœud: Importer * comme réagir à partir de 'React'Import LeftPad à partir de' Left-Pad'import {getMDxComponent} à partir de 'mdx-bundler / client'function Mdxpage ({code}: {code: chaîne}) { const composant = react.UseMemo (() => getMDxComponent (result.code, {myleftpad: LeftPad}), [result.code, LeftPad], ) return (<Main> <composant /> </-main> )}
La définition cwd
( répertoire de travail actuel ) dans un répertoire permettra à Esbuild de résoudre les importations. Ce répertoire pourrait être le répertoire dans lequel le contenu MDX a été lu ou un répertoire dans lequel MDX OFF-Disk devrait être exécuté .
Contenu / Pages / Demo.tsx
import * comme react à partir de 'react'Function Demo () { retour <div> Demo soigné! </div>} Démo par défaut d'exportation
src / build.ts
Import {bundlemdx} de 'mdx-bundler'const mdxsource = `--- Titre: Exemple postpublié: 2021-02-13Description: Ceci est une description --- # wahooimport Demo from' ./demo'here's a ** net * * Demo: <Demo />`.trim()Const result = attente bundlemdx ({ Source: mdxsource, cwd: '/ utilisateurs / vous / site / _content / pages',}) const {code, frontmatter} = résultat
Cela vous permet de configurer les options de matière grise.
Votre fonction est passé la configuration actuelle de la matière grise pour que vous puissiez modifier. Renvoyez votre objet de configuration modifié pour la matière grise.
bundlemdx ({ GrayMatterOptions: Options => {options.excerpt = TrueReturn Options },})
Cela vous permet de définir le répertoire de sortie du bundle et de l'URL public sur le répertoire. Si une option est définie, l'autre doit également être.
Le bundle JavaScript n'est pas écrit dans ce répertoire et est toujours renvoyé en tant que chaîne de bundleMDX
.
Cette fonctionnalité est mieux utilisée avec des ajustements pour mdxOptions
et esbuildOptions
. Dans l'exemple ci-dessous, les fichiers .png
sont écrits sur le disque puis servis à partir de /file/
.
Cela vous permet de stocker des actifs avec votre MDX, puis de les traiter comme tout autre chose.
Il est recommandé que chaque bundle ait son propre bundleDirectory
afin que plusieurs faisceaux ne se remplacent pas les actifs les uns des autres.
const {code} = attendre bundlemdx ({ Fichier: '/path/to/site/content/file.mdx', cwd: '/ path / to / site / contenu', Bundledirectory: '/ path / to / site / public / fichier', bundlepath: '/ file /', mdxOptions: options => {options.remarkPlugins = [RemarquemdIx] Options de retour }, esbuildOptions: options => {options.loadher = {... options.loader, '.png': 'file',} return options },})
bundleMDX
renvoie une promesse pour un objet avec les propriétés suivantes.
code
- Le bundle de votre MDX en tant que string
.
frontmatter
- L' object
de frontmatter de Gray-Matter.
matter
- L'objet entier renvoyé par Gray-Matter
mdx-bundler
fournit des tapis complètes dans son propre package.
bundleMDX
a un paramètre de type unique qui est le type de votre frontmatter. Il est par défaut à {[key: string]: any}
et doit être un objet. Ceci est ensuite utilisé pour taper la frontmatter
retournée et le frontmatter est passé à esbuildOptions
et mdxOptions
.
const {frontmatter} = bundlemdx <{title: string}> ({source}) frontmatter.title // a type string
MDX Bundler transmet la capacité de MDX à remplacer les composants via l'hélice components
sur le composant renvoyé par getMDXComponent
.
Voici un exemple qui supprime les balises P autour d'images.
import * en tant que réagir à partir de 'react'import {getmdxcomponent} de' mdx-bundler / client'const paragraph: react.fc = props => { if (typeof props.children! == 'string' && props.children.type === 'img') {return <> {props.children} </> } return <p {... props} />} fonction mdxpage ({code}: {code: string}) { const composant = react.UseMemo (() => getMDxComponent (code), [code]) return (<Main> <composant composants = {{p: paragraphe}} /> </-main> )}
Vous pouvez référencer Frontmatter Meta ou constants dans le contenu MDX.
--- Titre: Exemple de publication --- Export const EXABLEIMAGE = 'https://example.com/image.jpg'# {frontmatter.title} <img src = {exampleMage} alt = "image alt text" />
Vous pouvez utiliser getMDXExport
au lieu de getMDXComponent
pour traiter le fichier MDX comme un module au lieu d'un simple composant. Il faut les mêmes arguments que fait getMDXComponent
.
--- Titre: Exemple de post --- Export const toc = [{Depth: 1, valeur: 'le titre'}] # Le titre
Importer * en tant que réagir à partir de 'react'import {getmdxexport} à partir de' mdx-bundler / client'function mdxpage ({code}: {code: string}) { const mdxexport = getmdxexport (code) console.log (mdxexport.toc) // [{Depth: 1, valeur: 'le titre'}] const Component = react.UseMemo (() => mdxExport.default, [code]) retour <composant />}
Avec le CWD et le plugin Remarch-MDX-images, vous pouvez regrouper des images dans votre MDX!
Il y a deux chargeurs dans Esbuild qui peuvent être utilisés ici. Le plus simple est dataurl
qui publie les images en tant qu'URL de données en ligne dans le code renvoyé.
Import {RemarkMdIxhages} de 'Remark-Mdx-Images'Const {Code} = Await Bundlemdx ({{ Source: mdxsource, CWD: «/ Users / You / Site / _Content / Pages», mdxOptions: Options => {options.remarkPlugins = [... (options.remarkPlugins? }, EsBuildOptions: Options => {options.loadher = {... options.loader, '.png': 'dataUrl',} Return Options },})
Le chargeur file
nécessite un peu plus de configuration pour fonctionner. Avec le chargeur file
, vos images sont copiées dans le répertoire de sortie, donc Esbuild doit être défini pour écrire des fichiers et doit savoir où les placer ainsi que l'URL du dossier à utiliser dans les sources d'image.
Chaque appel à
bundleMDX
est isolé des autres. Si vous définissez le répertoire de la même manière pour tout quebundleMDX
écrasera les images sans avertissement. En conséquence, chaque bundle a besoin de son propre répertoire de sortie.
// pour le fichier `_content / pages / about.mdx`const {code} = attendre bundlemdx ({ Source: mdxsource, CWD: «/ Users / You / Site / _Content / Pages», mdxOptions: Options => {options.remarkPlugins = [... (options.remarkPlugins? }, EsBuildOptions: Options => {// Définissez le `OutDir` sur un emplacement public pour ce bundle.options.outdir = '/users/you/site/public/img/about'options.loadher = {... Options.Loadier. , // Dites à Esbuild d'utiliser le chargeur `fichier` pour pngs '.png': 'file',} // Définissez le chemin public vers /img/aboutoptions.publicpath = '/ img / about' // Définissez l'écriture sur true sur true afin que Esbuild sortira les fichiers.options.write = TrueReturn Options },})
Si votre fichier MDX est sur votre disque, vous pouvez gagner du temps et du code en faisant lire le fichier mdx-bundler
. Au lieu de fournir une chaîne source
, vous pouvez définir file
sur le chemin du chemin du disque MDX. Définissez cwd
sur son dossier afin que les importations relatives fonctionnent.
import {bundlemdx} de 'mdx-bundler'const {code, frontmatter} = attendre bundlemdx ({{ Fichier: '/users/you/site/content/file.mdx', cwd: '/ utilisateurs / vous / site / contenu /',})
Pour vous assurer que les composants personnalisés sont accessibles dans les fichiers MDX en aval, vous pouvez utiliser le MDXProvider
à partir de @mdx-js/react
pour passer les composants personnalisés à vos importations imbriquées.
npm install --save @mdx-js/react
const Globals = { '@ mdx-js / react': {varname: 'mdxjsreact', nomméxports: ['usemdxcomponents'], defaultExport: false, },}; const {code} = bundlemdx ({ source, Globals, mdxOptions (options: enregistrer <chaîne, any>) {return {... options, providerimportSource: '@ mdx-js / react',}; }});
De là, vous envoyez le code
à votre client, puis:
import {mdxprovider, usemdxcomponents} de '@ mdx-js / react'; const mdx_global_config = { Mdxjsreact: {usemdxcomponents, },}; export const mdxcomponent: react.fc <{ code: chaîne; frontmatter: enregistrer <string, any>;}> = ({code}) => { const composant = useMemo (() => getMDxComponent (code, mdx_global_config), [code], )); return (<mdxprovider composants = {{text: ({enfants}) => <p> {enfants} </p>}}> <composant /> </ mdxprovider> );};
Nous aimerions que cela fonctionne chez les travailleurs de Cloudflare. Malheureusement, CloudFlares a deux limitations qui empêchent mdx-bundler
de travailler dans cet environnement:
Les travailleurs ne peuvent pas faire de binaires. bundleMDX
utilise esbuild
(un binaire) pour regrouper votre code MDX.
Les travailleurs ne peuvent pas exécuter eval
ou similaire. getMDXComponent
évalue le code groupé à l'aide new Function
.
Une solution de contournement à cela consiste à mettre votre code lié à Bundler MDX dans un environnement différent et à appeler cet environnement au sein du travailleur CloudFlare. OMI, cela va à la défaite de l'utilisation des travailleurs de Cloudflare. Une autre solution de contournement potentielle consiste à utiliser WASM au sein du travailleur. Il y a esbuild-wasm
, mais il y a des problèmes avec ce package expliqué sur ce lien. Ensuite, il y a wasm-jseval
, mais je n'ai pas pu faire en sorte que le code exécute de mdx-bundler
sans erreur.
Si quelqu'un souhaitait y creuser, ce serait stellaire, mais malheureusement, il est peu probable que je travaillerai jamais dessus.
Esbuild s'appuie sur __dirname
pour déterminer où est exécutable, ensuite.js et webpack peuvent parfois casser cela et Esbuild doit être dit manuellement où chercher.
L'ajout du code suivant avant votre bundleMDX
pointera directement Esbuild à l'exécutable correct pour votre plate-forme.
Importer un chemin à partir de 'path'if (process.platform ===' win32 ') { process.env.esbuild_binary_path = path.join (process.cwd (), 'node_modules', 'esbuild', 'esbuild.exe', )} autre { process.env.esbuild_binary_path = path.join (process.cwd (), 'node_modules', 'esbuild', 'bin', 'esbuild', )}
Plus d'informations sur cette question peuvent être trouvées dans cet article.
Alors que je réécrivais kentcdodds.com pour remix, j'ai décidé que je voulais garder mes articles de blog en tant que MDX, mais je ne voulais pas avoir à les compiler tous au moment de la construction ou être tenu de redéployer chaque fois que je corrige une faute de frappe. J'ai donc fait cela qui permet à mon serveur de compiler à la demande.
Il y a le prochain MDX-Remote mais c'est plus un compositeur MDX qu'un bundler (je ne peux pas regrouper votre MDX pour les dépendances). Il est également axé sur Next.js alors que c'est un agnostique de méta-trame.
Vous cherchez à contribuer? Recherchez le bon étiquette de premier numéro.
Veuillez déposer un problème pour les bogues, la documentation manquante ou un comportement inattendu.
Voir les bogues
Veuillez déposer un problème pour suggérer de nouvelles fonctionnalités. Votez sur les demandes de fonctionnalités en ajoutant un ?. Cela aide les mainteneurs à hiérarchiser sur quoi travailler.
Voir les demandes de fonctionnalités
Merci à ces gens (clé emoji):
Kent C. Dodds ? | Benwis ? ? | Adam Laycock | Tirus ? ? | Christian Murphy ? | Pedro Duarte | Erik Rasmussen |
Omar Syx ? | Gaël Haméon | Gabriel Loiácono | Spencer Miskoviak | Casper | APOSTOLOS CHRISTODOULOU | Yordis Prieto |
xoumi | Yasin | Mohammed 'mo' mulazada | Peut rau | Hosenur Rahaman | Maciek Sitkowski | Priyang |
Mosaad | stefanprobst | Vlad Moroz |
Ce projet suit les spécifications de tous les contributeurs. Contributions de toute nature bienvenue!
Mit