withastro / astro

The web framework for content-driven websites. ⭐️ Star to support our work!
https://astro.build
Other
45.52k stars 2.39k forks source link

Markdown mermaid is not supported #4433

Closed Stratis-Dermanoutsos closed 2 years ago

Stratis-Dermanoutsos commented 2 years ago

What version of astro are you using?

1.0.7

Are you using an SSR adapter? If so, which one?

None

What package manager are you using?

npm

What operating system are you using?

Windows

Describe the Bug

When I am added a mermaid code block, it only renders as plaintext.

To be more specific, when I visit the Markdown page that has any mermaid code block, the terminal logs the following:

The language "mermaid" doesn't exist, falling back to plaintext.

Example code block:

```mermaid
 graph TD;
   AppName-->app/view/main/pages;
   app/view/main/pages-->PageName;
   PageName-->PageNameModel.js;
\```

(The closing ` is escaped to get printed here, in the Github preview. I don't escape it in the actual code.)

Link to Minimal Reproducible Example

https://stackblitz.com/edit/github-xn8csz

Participation

bholmesdev commented 2 years ago

Thanks for reporting @Stratis-Dermanoutsos! Yes, it looks like mermaid is included in Shiki's default language list. I'll investigate why we're missing this 👍

JuanM04 commented 2 years ago

@Stratis-Dermanoutsos, #4519 should address your issue. If you are looking for a implementation of Mermaid in Astro like GitHub does, you can check out my webpage, where I've done that: mermaid.ts, Layout.astro

jasekt commented 1 year ago

@JuanM04 Thank you man! Your comment was extremely helpful in implementing Mermaid in my personal Astro site :) Note for anyone trying to replicate - this implementation works for mermaid versions 9. but breaks for versions 10.

xandermann commented 1 year ago

I downgraded mermaid version and it is working too. If anyone has a fix for mermaid 10 I'm interested !

JuanM04 commented 1 year ago

Just for the record, I've migrated to Mermaid 10 successfully! This is the remark plugin and this is the dynamic script.

xandermann commented 1 year ago

It is working for me ! Thank you very much @JuanM04 ! ❤️

jzanecook commented 1 year ago

@JuanM04 You're awesome, bro!

LoneExile commented 1 year ago

Thanks, @JuanM04, for the solution.

However, I want to optimize it by ensuring it only loads when required. More specifically, I want it to run only when the '.mermaid' elements become visible in the viewport.

Here the approach: ```html ```

The above code works, but it triggers all of my scripts for other elements that use Client Directives (client:visible).

My question is: Is there a way to run this script during the build process instead of on the client-side?

Currently, I'm running a script with Partytown

like this: [slug].astro ```html ``` mermaid.ts ```ts import type {RemarkPlugin} from '@astrojs/markdown-remark' import {visit} from 'unist-util-visit' import dedent from 'ts-dedent' export const mermaid: RemarkPlugin<[]> = () => (tree) => { visit(tree, 'code', (node) => { if (node.lang !== 'mermaid') return // @ts-ignore node.type = 'html' node.value = dedent`
        ${node.value}
      
` }) } ``` However, this solution I don't know how to initialize mermaidAPI theme.
Nomango commented 8 months ago

Not working on Astro v4 ToT. Always shows [ERROR] __vite_ssr_import_1__.default is not a function

Nomango commented 8 months ago

Not working on Astro v4 ToT. Always shows [ERROR] __vite_ssr_import_1__.default is not a function

Resolved by removing ts-dedent. Follow at https://github.com/tamino-martinius/node-ts-dedent/issues/40.

tamaracha commented 8 months ago

As another approach, I use rehype-mermaid and rehype-shikiji plugins and disable astro's built-in syntax highlighting. This renders the diagrams to svg via playwright, no client-side js needed. The remaining code blocks get highlighted afterwards. You could also use e.g. rehype-pretty-code for highlighting.

import { defineConfig } from 'astro/config'
import rehypeMermaid from 'rehype-mermaid'
import rehypeShikiji from 'rehype-shikiji'

// https://astro.build/config
export default defineConfig({
  markdown: {
    rehypePlugins: [rehypeMermaid, [rehypeShikiji, { theme: 'github-dark' }]],
    syntaxHighlight: false
  },
})

In my md files, I can have mermaid syntax in code fences and they get rendered to inlined svg diagram.

nvkhuy commented 6 months ago

The SVG render with size 24x24 only, cannot change it. Any one has solution

87xie commented 6 months ago

The SVG render with size 24x24 only, cannot change it. Any one has solution

Overriding the default style using the themeCSS option in the Mermaid initialize API or the global CSS.

mermaid.initialize({
  themeCSS: 'width: 100%; height: auto;', 
  // skip
});

or

.mermaid svg {
  width: 100%;
  height: auto;
}
tamaracha commented 6 months ago

Since Astro v4.5, syntax highlighting uses shiki again instead of shikiji, and internal syntax highlighting has been migrated to use rehype plugins instead of remark plugins. These plugins still run before custom plugins, so sequence matters. What has changed is that rehype-shikiji is not needed anymore and we can use the internal shiki plugin.

# Upgrade astro
npx @astrojs/upgrade
# Remove rehype-shikiji plugin
npm rm rehype-shikiji

The markdown section in astro.config.js looks like this (shortened):

import { defineConfig } from 'astro/config'
import { rehypeShiki } from '@astrojs/markdown-remark'
import rehypeMermaid from 'rehype-mermaid'

// https://astro.build/config
export default defineConfig({
  markdown: {
    rehypePlugins: [
      rehypeMermaid,
      rehypeShiki,
    ],
    syntaxHighlight: false,
  },
})

Is this still a recommended way of adding Mermaid, or am I missing some implications from the newer changes?

stephengrice commented 4 months ago

Just for the record, I've migrated to Mermaid 10 successfully! This is the remark plugin and this is the dynamic script.

Thanks so much for this. It worked for me after I made a few tweaks. Sharing in case it helps anyone else.

For me, using data-content was removing line breaks, so I had to change it to dump the mermaid source into a <pre>:

mermaid.ts

import type { RemarkPlugin } from "@astrojs/markdown-remark"
import { visit } from "unist-util-visit"

const escapeMap: Record<string, string> = {
  "&": "&amp;",
  "<": "&lt;",
  ">": "&gt;",
  '"': "&quot;",
  "'": "&#39;",
}

const escapeHtml = (str: string) => str.replace(/[&<>"']/g, c => escapeMap[c])

export const mermaid: RemarkPlugin<[]> = () => tree => {
  visit(tree, "code", node => {
    if (node.lang !== "mermaid") return

    // @ts-ignore
    node.type = "html"
    node.value = `
      <div class="mermaid">
        <p>Loading graph...</p>
        <pre class="mermaid-src">${escapeHtml(node.value)}</pre>
        </div>
    `
  })
}

<script> in Layout.astro

      <script>
        // Source: https://github.com/JuanM04/portfolio/blob/983b0ed0eabdac37bf8b7912d3e8128a443192b9/src/pages/docs/%5B...documentSlug%5D.astro#L74-L103
        // From this comment: https://github.com/withastro/astro/issues/4433#issuecomment-1584019991
        /**
         * @params {HTMLCollectionOf<HTMLElement>} graphs
         */
        async function renderDiagrams(graphs) {
          const {default: mermaid} = await import("mermaid")
          mermaid.initialize({
            startOnLoad: false,
            fontFamily: "var(--sans-font)",
            // @ts-ignore This works, but TS expects a enum for some reason
            theme: window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "default",
          })

          for (const graph of graphs) {
            const content = graph.querySelector(".mermaid-src").innerText
            if (!content) continue
            let svg = document.createElement("svg")
            const id = (svg.id = "mermaid-" + Math.round(Math.random() * 100000))
            graph.appendChild(svg)
            mermaid.render(id, content).then(result => {
              graph.innerHTML = result.svg
            })
          }
        }

        const graphs = document.getElementsByClassName("mermaid")
        if (document.getElementsByClassName("mermaid").length > 0) {
          renderDiagrams(graphs);
        }
      </script>

astro.config.mjs

import { mermaid } from "./src/plugins/mermaid";
// ...
  markdown: {
    remarkPlugins: [
      // ...
      mermaid,
    ],
// ...

Used in this repo in case you're curious.

Thanks again for the solution!

pengjeck commented 4 months ago

@stephengrice Thank you for the detailed description in your answer, it saved me a lot of time.

After successfully rendering the mermaid diagram following the steps mentioned above, I noticed that when I use the browser's back button to return to the blog, the mermaid rendering does not work.