Rehype D2 Plugin

Un plugin Rehype pour convertir des diagrammes D2 en SVG ou PNG.

5 min de lecture

Comment je fais mes schémas?

Avant D2, j'utilisais excalidraw pour faire mes schémas.
Diagramme UML fait avec excalidraw
Diagramme UML fait avec excalidraw
source
Mais de plus en plus j'ai cherché à faire des diagrammes en exprimant les concepts directement dans le texte. Pour ça j'ai commencé à utiliser eraser.io
Schéma d'architecture fait avec eraser.io
Schéma d'architecture fait avec eraser.io

Ici tout ce schéma a été généré à partir de cette expression:

API gateway [icon: aws-api-gateway]
Lambda [icon: aws-lambda]
S3 [icon: aws-simple-storage-service]
VPC Subnet [icon: aws-vpc]{
  Main Server {
    Server [icon: aws-ec2]
    Data [icon: aws-rds]
  }
  Queue [icon: aws-auto-scaling]
  Compute Nodes [color: red] {
    Worker1 [icon: aws-ec2]
    Worker2 [icon: aws-ec2]
    Worker3 [icon: aws-ec2]
  }
}
Analytics [icon: aws-redshift]
 
// Define connections
API gateway > Lambda > Server > Data
Server > Queue > Worker1, Worker2, Worker3
S3 < Data
Compute Nodes > Analytics
Ce texte est simple a lire et est beaucoup plus simple a sauvegarder dans git ou a modifier.
En comparaison, un export excalidraw est un fichier json qui contient les positions et propriétés de chaque élément. Quasiment impossible a modifier manuellement.
Le soucis avec eraser.io est que c'est un outil payant... et qu'en version gratuite la customisation ou partage des schémas est limitée.
Un autre soucis (qu'on avait aussi avec excalidraw) est qu'a chaque modification il faut aller sur le site pour re-générer un schéma.

Du coup j'ai cherché un autre outil qui pourrait répondre à tous ces problèmes.

Qu'est-ce que D2?

Home | D2 Documentation

D2 is a modern DSL that turns text to diagrams.

Home | D2 Documentationhttps://d2lang.com/
D2 est un outil de création de diagrammes comparable à mermaid, graphviz, plantuml, etc. comparaison
Avec un sytaxe plus simple, mais avec des fonctionnalités avancées.

Un point intéressant de D2 est l'utilisation de classes pour styliser des elements. Et la possibilité d'importer des fichiers pour les ré-utiliser.

Pourquoi il faut un plugin?

Il existe déjà un plugin pour remark remark-d2, mais il n'est pas à jour depuis 2 ans et manque des fonctionnalités tel que les imports et la gestion d'un theme clair/sombre.

Du coup j'ai créé mon propre plugin.


Note: Entre temps un autre plugin rehype-d2 a été créé, cependant il ne supporte pas l'utilisation d'imports différents selon le theme et le format de sorti ne me convient pas

Comment ça marche?

GitHub - Vahor/rehype-d2: A Rehype plugin to convert D2 diagrams to SVG or PNG.

A Rehype plugin to convert D2 diagrams to SVG or PNG. - Vahor/rehype-d2

GitHub - Vahor/rehype-d2: A Rehype plugin to convert D2 diagrams to SVG or PNG.https://github.com/Vahor/rehype-d2
Comme j'utilise contentlayer pour gérer mon contenu, je vais vous montrer comment l'utiliser avec contentlayer.
bun add -D @vahor/rehype-d2
contentlayer.config.ts
import rehypeD2 from "@vahor/rehype-d2";
import { makeSource } from "contentlayer2/source-files";
 
...
 
export default makeSource({
    contentDirPath: contentFolder,
    documentTypes: [Post],
    mdx: {
        rehypePlugins: [[
            rehypeD2,
            {
                cwd: "public/blog/d2",
                defaultMetadata: {
                    sketch: false,
                },
            },
        ]],
    },
});
@terrastruct/d2 utilises des imports dynamiques de wasm et js, ce qui pose soucis avec contentlayer issue. Pour éviter cela il faut modifier les options esbuild du plugin:
next.config.ts
import { createContentlayerPlugin, defaultPluginOptions } from "next-contentlayer2";
import type { NextConfig } from "next";
 
const nextConfig: NextConfig = {...}
 
const withContentlayer = createContentlayerPlugin({
	...defaultPluginOptions,
	esbuildOptions: {
		external: ["@terrastruct/d2"],
	},
});
module.exports = withContentlayer(nextConfig);

Utilisation

ContentLayer se charge de convertir les fichiers mdx en HTML.
Il nous suffit donc de spécifier un bloc de code d2 comme ceci:
```d2 height=350 pad=20
a: From
b: To
a -> b: Arrow
```

Pour obtenir:

FromToArrowFromToArrow

Si on y ajoute un plugin pour importer des fichiers, on peut obtenir:

```d2 pad=10
@include "raw:../../../public/blog/d2/computer.d2"
```

ce qui donne:

INPUTOUTPUTMEMORYCENTRAL PROCESSING UNIT (CPU)MASS STORAGEINPUTOUTPUTMEMORYCENTRAL PROCESSING UNIT (CPU)MASS STORAGE

Magie! 🤯

Un petit dernier parce que c'est cool:

API GatewayLambdaS3VPC SubnetAnalyticsMainQueueComputeServerDataWorker 1Worker 2Worker 3API GatewayLambdaS3VPC SubnetAnalyticsMainQueueComputeServerDataWorker 1Worker 2Worker 3

Ça vous dit quelque chose ? En utilisant du style on peut se rapprocher du style de eraser.io.

Il reste quelques issues à corriger pour devenir un outil parfait, mais c'est un très bon début.

Source des diagrammes: