# All files / src/plugins mermaid.ts

100% Statements 12/12
100% Branches 3/3
100% Functions 4/4
100% Lines 12/12

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    1081x

    1x

    2x

    1x

    2x

    1x
    1x
    1x
    1x
    1x
    1x

    1x

     import cheerio from 'cheerio';
    import { PluginContext, FrontMatter } from './Plugin';

    const DEFAULT_CDN_ADDRESS = 'https://unpkg.com/mermaid@10/dist/mermaid.esm.min.mjs';

    function genScript(address: string) {
      return `<script type="module">
        window.mermaidPromise = null;

        const loadMermaid = () => {
          if (window.mermaidPromise === null) {
            window.mermaidPromise = import('${address || DEFAULT_CDN_ADDRESS}')
              .then(({ default: mermaid }) => {
                mermaid.initialize({
                  startOnLoad: false,
                });
                console.log('Mermaid loaded successfully');
                return mermaid;
              })
              .catch((error) => {
                console.error('Mermaid failed to load:', error);
                window.mermaidPromise = false;
                return null;
              });
          }
          return window.mermaidPromise;
        };

        const renderMermaidDiagrams = (elements) => {
          if (!elements || elements.length === 0) {
            return Promise.resolve();
          }

          return loadMermaid().then(mermaid => {
            if (!mermaid) return;
            return mermaid.run({ nodes: Array.from(elements) })
              .catch(err => console.error('Error rendering mermaid diagrams:', err));
          });
        };

        const setupMermaidObserver = () => {
          const observer = new MutationObserver((mutations) => {
            let newMermaidElements = [];

            mutations.forEach(mutation => {
              if (mutation.addedNodes.length) {
                mutation.addedNodes.forEach(node => {
                  if (node.nodeType === 1 && node.classList &&
                      node.classList.contains('mermaid')) {
                    newMermaidElements.push(node);
                  }

                  if (node.nodeType === 1 && node.querySelectorAll) {
                    const mermaidInNode = node.querySelectorAll('.mermaid');
                    if (mermaidInNode.length) {
                      newMermaidElements = [...newMermaidElements, ...mermaidInNode];
                    }
                  }
                });
              }
            });

            if (newMermaidElements.length > 0) {
              renderMermaidDiagrams(newMermaidElements);
            }
          });

          observer.observe(document.body, {
            childList: true,
            subtree: true
          });

          return observer;
        };

        // Initialize on DOM content loaded
        document.addEventListener('DOMContentLoaded', () => {
          const existingDiagrams = document.querySelectorAll('.mermaid');
          if (existingDiagrams.length > 0) {
            renderMermaidDiagrams(existingDiagrams);
          }

          setupMermaidObserver();
        });

      </script>`;
    }

    export = {
      tagConfig: {
        mermaid: {
          isSpecial: true,
        },
      },
      getScripts: (pluginContext: PluginContext) => [genScript(pluginContext.address)],
      postRender: (pluginContext: PluginContext, frontmatter: FrontMatter, content: string) => {
        const $ = cheerio.load(content);
        const mermaidTags = $('mermaid');
        if (mermaidTags.length > 0) {
          mermaidTags.each((index: number, node: cheerio.Element) => {
            const $node = $(node);
            $node.replaceWith(`<pre class="mermaid">${$node.html()}</pre>`);
          });
        }
        return $.html();
      },
    };