Fermer

février 27, 2020

AST, Markdown et MDX


Markdown pour les documents, React pour l'interaction, MDX pour les deux! Mais comment Markdown et MDX arrivent-ils à HTML et JSX? La réponse est des arbres de syntaxe abstraits.

Markdown est le format parfait pour écrire des documents, de la documentation, des articles de blog, du contenu statique, etc. React, d'autre part, est idéal pour créer des interfaces interactives. Cela dit, avez-vous déjà essayé d'écrire un article de blog en React / HTML? Il y a une raison pour laquelle Markdown existe! Mais que faire si vous souhaitez ajouter des éléments interactifs à un document Markdown? Peut-être une vidéo YouTube intégrée ou peut-être un graphique qui extrait des données dynamiques? Ou peut-être un formulaire pour collecter des informations de contact sur une page de vente?

MDX vous offre le meilleur des deux mondes. Écrivez vos documents dans Markdown, mais n'hésitez pas à importer et à utiliser des composants React directement à l'intérieur de votre document.

Dans cet article, nous allons aller au-delà du niveau de la surface et plonger dans le fonctionnement interne de Markdown et MDX. Comment un fichier avec Markdown est-il converti en HTML et comment MDX est-il converti en JSX?

Nous allons explorer Abstract Syntax Trees (AST) et ce que Markdown et MDX ont à voir avec eux . Les exemples de code de cet article peuvent être trouvés ici .

Utilisation MDX dans le monde réel (avertissement)

Les exemples de cet article sont destinés à fournir un aperçu de ce que MDX fait derrière le scènes et à quoi ressemblent et utilisent les AST. Si vous souhaitez utiliser MDX dans Gatsby Next.js ou Create React App le site Web MDX fournit des exemples et documentation sur la façon de l'utiliser facilement dans votre application.

Arbres de syntaxe

La ​​possibilité d'afficher le code sous forme de données – plutôt que simplement du texte dans un fichier – ouvre un monde de possibilités. Prenons Prettier par exemple. Il est capable de prendre du JavaScript ou du Markdown mal formaté et de vous donner quelque chose de bien formaté en retour. Vous pouvez penser que la conversion passe de Markdown laid directement à Markdown formaté, mais la clé de ce processus est l'étape intermédiaire, une structure de données appelée un arbre de syntaxe abstraite (AST).

Pensez à ce que vous pouvez produire avec un fichier Markdown . Oui, vous pouvez produire du HTML, mais vous pouvez également produire un Markdown formaté (comme ce que fait Prettier), ou il peut être vérifié pour les erreurs de linter, afficher le nombre de mots qu'il contient, entre autres choses.

 Markdown -> AST - > HTML
Markdown -> AST -> Markdown formaté
Markdown -> AST -> Erreurs de charpie
Markdown -> AST -> Word Counts

C'est avec les AST que MDX est capable de combiner Markdown et React ensemble.

Arbres de syntaxe abstraite en action

Pour voir les AST en action, regardons ce petit exemple de Markdown avec un en-tête de niveau 1 et un paragraphe:

 # Bienvenue

Un paragraphe.

Si nous traitons cette démarque avec unifié avec le plug-in de remar-parse nous prendrons l'entrée Markdown et nous retrouverons avec un AST qui représente la démarque. [19659017] importer unifié de "unifié";
importation de démarque de "remarque-parse";

const input = `
# Bienvenue

Un paragraphe.
"

arbre const = unifié ()
  .use (démarque)
  .parse (entrée);

Si vous faites cela vous-même, vous verrez toutes sortes de données sur la position et la ligne des caractères, mais j'ai supprimé cela pour le rendre un peu plus digeste. Chaque nœud (un objet) dans cette arborescence contient un certain nombre de propriétés:

  • type : De quel type de données est ce nœud? En-tête, paragraphe, accentuation, fort, etc.
  • enfants : Noeuds imbriqués contenus dans l'actuel. Imaginez une image à l'intérieur d'un lien ou un lien à l'intérieur d'un paragraphe
  • profondeur : utilisé pour différencier les titres de niveau 1, 2, 3 (h1, h2, h3)
  • valeur : les nœuds de texte ont un attribut de valeur qui contient leur valeur textuelle réelle
 {
  "type": "root",
  "enfants": [
    {
      "type": "heading",
      "depth": 1,
      "children": [
        {
          "type": "text",
          "value": "Welcome"
        }
      ]
    },
    {
      "type": "paragraphe",
      "enfants": [
        {
          "type": "text",
          "value": "A paragraph."
        }
      ]
    }
  ]
}

Utilisation de l'AST pour les calculs

Nous pouvons traiter l'AST pour compter le nombre de chaque type que nous voyons (alerte de fonction récursive):

 nombre de fonctions (acc, nœud) {
  // ajoute 1 à une valeur initiale ou existante
  acc [node.type] = (acc [node.type] || 0) + 1;

  // recherche et additionne les comptes de tous les enfants de ce noeud
  return (node.children || []). réduire (
    (childAcc, childNode) => comptes (childAcc, childNode),
    acc
  );
}

Ce qui, selon votre saisie, produit quelque chose comme:

 {
  "racine": 1,
  "rubrique": 1,
  "texte": 7,
  "paragraphe": 3,
  "fort": 1,
  "accent": 1
}

Compter les mots avec l'AST

L'outil de comptage de mots que j'utilise dans VS Code compte actuellement ## Bienvenue comme 2 mots, quand on peut vraiment voir qu'il ne s'agit que d'un seul mot qui se trouve être dans une balise h2 . En utilisant un AST, nous pouvons fournir un compte de mots plus précis en comptant uniquement les valeurs du texte .

 import unified from "unified";
importation de démarque de "remarque-parse";

fonction wordCount (count, node) {
  if (node.type === "text") {
    return count + node.value.split ("") .length;
  } autre {
    return (node.children || []). réduire (
      (childCount, childNode) => wordCount (childCount, childNode),
      compter
    );
  }
}

// Notre entrée de démarque
const input = `## Welcome`;

// Convertir le démarque en AST
arbre const = unifié ()
  .use (démarque)
  .parse (entrée);

// Extraire le nombre de mots d'AST
const mots = wordCount (0, arbre);

Visualisation de l'AST

Avec cet AST, nous pouvons également créer un composant React appelé Node qui le rend, ainsi que ses enfants (en utilisant un remplissage pour afficher sa structure arborescente):

 const Node = ({node}) => (
  
{node.type}       {node.depth && (d {node.depth}) }          {node.value &&
{node.value}
}     {/ * Rendre des nœuds supplémentaires pour chaque enfant * /}     {node.children &&       node.children.map (enfant => {         const {ligne, colonne, décalage} = child.position.start;         retour ;       })}   
);

Cette sortie nous permet de voir comment l'arbre est structuré et indenté:

 root
  titre (d1)
    texte
      Bienvenue
  paragraphe
    texte
      Un paragraphe.

MDX

Si vous êtes venu ici pour MDX et non Markdown, vous avez de la chance! Nous allons maintenant passer à l'exploration du fonctionnement de MDX et de sa relation avec les exemples Markdown illustrés ci-dessus.

AST Explorer

Pour tous les apprenants visuels, il existe un excellent site Web appelé AST Explorer qui vous permet de visualiser l'AST produit par un certain nombre de formats d'entrée différents tels que Markdown et MDX. Nous allons plonger un peu plus loin dans MDX, alors jetons un œil à l'AST produit par un fichier MDX .

MDAST, HAST, MDXAST, MDXHAST … Quoi ??

Ça fait beaucoup d'acronymes! Mais que signifient-ils et qu'est-ce que cela a à voir avec Markdown et MDX? Pour convertir Markdown en AST, nous avons besoin d'une spécification ou d'un ensemble de règles à suivre afin de savoir quels types de nœuds sont disponibles (en-tête, paragraphe, lien, etc.) et quelles propriétés ils peuvent avoir (type, enfants

Cet ensemble de règles pour Markdown est appelé mdast . De même, il existe d'autres ensembles de règles pour traiter le HTML, appelés hast . Avec les deux spécifications, quelqu'un pourrait écrire du code qui convertit un Markdown AST (mdast) en HTML AST (hast), ce qui est exactement ce que remarque-rehype .

MDX est un surensemble de Markdown, ce qui signifie que tout ce que vous pouvez faire dans Markdown, vous pouvez également le faire dans MDX, ainsi que trois fonctionnalités supplémentaires, qui sont:

  • jsx (en remplacement de html)
  • instructions d'importation
  • déclarations d'exportation

Cette spécification est appelée MDXAST .

Compilation de MDX en AST

Sauf si vous développez un plug-in pour MDX vous n'aurez probablement pas besoin de traiter directement avec MDX AST, mais puisque cet article est sur l'apprentissage, écrivons du code qui produit un AST.

 const {createMdxAstCompiler} = require ("@ mdx-js / mdx");

// Un compilateur "unifié"
const compiler = createMdxAstCompiler ({remarquePlugins: []});
const input = `
importer YouTube depuis "./YouTube";

# Bienvenue


"

const ast = compiler.parse (entrée);
const astString = JSON.stringify (ast, null, 2);
console.log (astString);

Après avoir supprimé certaines des données de position, l'AST finit par ressembler aux données ci-dessous. Notez que nous voyons deux des types de nœuds MDX personnalisés: import et jsx .

 {
  "type": "root",
  "enfants": [
    {
      "type": "import",
      "value": "import YouTube from "./YouTube";"
    },
    {
      "type": "heading",
      "depth": 1,
      "children": [
        {
          "type": "text",
          "value": "Welcome"
        }
      ]
    },
    {
      "type": "jsx",
      "valeur": ""
    }
  ]
}

Compilation de MDX en JSX

Ce que nous voulons vraiment que MDX fasse, c'est de produire JSX, pas un AST. Ce code est similaire à l'exemple précédent qui a produit un AST, mais nous ajoutons la fonction utilitaire mdxHastToJsx qui reprend l'AST de l'étape précédente et produit JSX.

 const {createMdxAstCompiler} = require ("@ mdx-js / mdx");
const mdxHastToJsx = require ("@ mdx-js / mdx / mdx-hast-to-jsx");

const input = `
importer YouTube depuis "./YouTube";

# Bienvenue


"

const compiler = createMdxAstCompiler ({remarquePlugins: []}). use (mdxHastToJsx);
const jsx = compiler.processSync (entrée) .toString ();
console.log (jsx);

Ce qui est produit est un JSX valide, qui ressemble à:

 importer YouTube depuis "./YouTube";

const layoutProps = {};
const MDXLayout = "wrapper";
exporter la fonction par défaut MDXContent ({composants, ... accessoires}) {
  revenir (
    
      

{`Welcome`}

); }

Conclusion

J'espère que vous avez aimé découvrir les AST et le rôle qu'ils jouent avec Markdown et MDX. Avec les AST, nous sommes en mesure de traiter et de peaufiner notre code pour arriver au résultat souhaité. Cela peut être aussi simple que de compter le nombre de mots dans un document Markdown, ou aussi complexe que Prettier ou Babel. Ils ouvrent la porte à un certain nombre de possibilités qui, à un moment donné, pouvaient sembler farfelues. Prenez MDX lui-même par exemple. Ce n'était qu'une idée que quelques personnes avaient, et avec l'aide des AST et du travail acharné de certaines personnes intelligentes, devint une réalité.





Source link