KevinGimbel / eleventy-plugin-mermaid

Integrate Mermaid with eleventy (11ty)
https://kevingimbel.github.io/eleventy-plugin-mermaid/
ISC License
26 stars 3 forks source link

Server-side rendering #7

Open connelhooley opened 5 months ago

connelhooley commented 5 months ago

Hi

I noticed in the future ideas section of the readme it mentioned "generate SVG server-side during build". I've been playing around with 11ty and I'm not sure if I'm going to continue with it but thought I'd share how I got server side rendering working in a prototype.

It uses the mermaid-cli. The CLI in turn uses a puppeteer instance as mermaid requires a browser to render.

import { renderMermaid } from "@mermaid-js/mermaid-cli";
import puppeteer from "puppeteer";

export const mermaidPlugin = (eleventyConfig, options) => {
  let browser;
  eleventyConfig.on("eleventy.before", async () => {
    if (!browser) {
      browser = await puppeteer.launch({headless: "new"});
    };
  });
  eleventyConfig.on("eleventy.after", async () => {
    if (browser) {
      browser.close();
      browser = undefined;
    }
  });

  eleventyConfig.htmlTransformer.addPosthtmlPlugin("html", () => {
    return async (tree) => {
      const transformTag = async (node) => {
        let content;
        if (Array.isArray(node.content)) {
          content = node.content.join("");
        } else {
          content = node.content;
        }
        const { data } = await renderMermaid(browser, content, "svg", {
          mermaidConfig: options.mermaidConfig,
        });
        const htmlString = data.toString();
        node.tag = false;
        node.content = tree.parser(htmlString);
      };  
      const promises = [];
      tree.match({ tag: "pre", attrs: { class: "mermaid" } }, node => {
        promises.push(transformTag(node));
        return node;
      });  
      await Promise.all(promises);
      return tree;
    };
  });
};

It's probably very unperformant but appears to work. Usage would be like this:

eleventyConfig.addPlugin(mermaidPlugin, {
  mermaidConfig: {
    theme: "base",
    themeVariables: {
      primaryColor: "#BB2528",
      primaryTextColor: "#fff",
      primaryBorderColor: "#7C0000",
      lineColor: "#F8B229",
      secondaryColor: "#006100",
      tertiaryColor: "#fff",
    }
  },
});

It asumes/requires you have set up the syntax highlighter to ignore mermaid code blocks and instead return a <pre class="mermaid"> tag, like you already do here.

KevinGimbel commented 5 months ago

Wow, thanks a lot! This is a great starting point to get this feature going, if I find the time I'll give it a try!

Some questions I have (for myself):

Can puppeteer and mermaid-cli be hidden behind a feature flag? Don't want to pull in big dependencies unless people wanna use them.

Split plugin into browser and ssr? Maybe it makes sense to create a new plugin which implements the SSR. This plugin could re-use the existing plugin as dependency, people who want SSR can then use the SSR plugin.