async function renderMermaid (browser, definition, outputFormat, { viewport, backgroundColor = 'white', mermaidConfig = {}, myCSS, pdfFit, svgId, iconPacks = [] } = {}) {
  const page = await browser.newPage()
  page.on('console', (msg) => {
    console.warn(msg.text())
  })
  try {
    if (viewport) {
      await page.setViewport(viewport)
    }
    const mermaidHTMLPath = path.join(__dirname, '..', 'dist', 'index.html')
    await page.goto(url.pathToFileURL(mermaidHTMLPath).href)
    await page.$eval('body', (body, backgroundColor) => {
      body.style.background = backgroundColor
    }, backgroundColor)
    await Promise.all([
      page.addScriptTag({ path: mermaidIIFEPath }),
      page.addScriptTag({ path: zenumlIIFEPath })
    ])
    const metadata = await page.$eval('#container', async (container, definition, mermaidConfig, myCSS, backgroundColor, svgId, iconPacks) => {
      await Promise.all(Array.from(document.fonts, (font) => font.load()))

      /**
       * @typedef {Object} GlobalThisWithMermaid
       * We've already imported these modules in our `index.html` file (or by running `page.addScriptTag`),
       * so that they get correctly bundled.
       * @property {import("mermaid")["default"]} mermaid Already imported mermaid instance
       * @property {import("@mermaid-js/mermaid-zenuml")["default"]} mermaid-zenuml Already imported mermaid-zenuml instance
       * @property {import("@mermaid-js/layout-elk")["default"]} elkLayouts Already imported mermaid-elkLayouts instance
       */
      const { mermaid, 'mermaid-zenuml': zenuml, elkLayouts } = /** @type {GlobalThisWithMermaid & typeof globalThis} */ (globalThis)

      await mermaid.registerExternalDiagrams([zenuml])
      mermaid.registerLayoutLoaders(elkLayouts)
      // lazy load icon packs
      mermaid.registerIconPacks(
        iconPacks.map((icon) => ({
          name: icon.split('/')[1],
          loader: () =>
            fetch(`https://unpkg.com/${icon}/icons.json`)
              .then((res) => res.json())
              .catch(() => error(`Failed to fetch icon: ${icon}`))
        }))
      )
      mermaid.initialize({ startOnLoad: false, ...mermaidConfig })
      // should throw an error if mmd diagram is invalid
      const { svg: svgText } = await mermaid.render(svgId || 'my-svg', definition, container)
      container.innerHTML = svgText

      const svg = container.getElementsByTagName?.('svg')?.[0]
      if (svg?.style) {
        svg.style.backgroundColor = backgroundColor
      } else {
        warn('svg not found. Not applying background color.')
      }
      if (myCSS) {
        // add CSS as a <svg>...<style>... element
        // see https://developer.mozilla.org/en-US/docs/Web/API/SVGStyleElement
        const style = document.createElementNS('http://www.w3.org/2000/svg', 'style')
        style.appendChild(document.createTextNode(myCSS))
        svg.appendChild(style)
      }

      // Finds SVG metadata for accessibility purposes
      /** SVG title */
      let title = null
      // If <title> exists, it must be the first child Node,
      // see https://www.w3.org/TR/SVG11/struct.html#DescriptionAndTitleElements
      /* global SVGTitleElement, SVGDescElement */ // These exist in browser-based code
      if (svg.firstChild instanceof SVGTitleElement) {
        title = svg.firstChild.textContent
      }
      /** SVG description. According to SVG spec, we should use the first one we find */
      let desc = null
      for (const svgNode of svg.children) {
        if (svgNode instanceof SVGDescElement) {
          desc = svgNode.textContent
        }
      }
      return {
        title, desc
      }
    }, definition, mermaidConfig, myCSS, backgroundColor, svgId, iconPacks)

    if (outputFormat === 'svg') {
      const svgXML = await page.$eval('svg', (svg) => {
        // SVG might have HTML <foreignObject> that are not valid XML
        // E.g. <br> must be replaced with <br/>
        // Luckily the DOM Web API has the XMLSerializer for this
        // eslint-disable-next-line no-undef
        const xmlSerializer = new XMLSerializer()
        return xmlSerializer.serializeToString(svg)
      })
      return {
        ...metadata,
        data: new TextEncoder().encode(svgXML)
      }
    } else if (outputFormat === 'png') {
      const clip = await page.$eval('svg', svg => {
        const react = svg.getBoundingClientRect()
        return { x: Math.floor(react.left), y: Math.floor(react.top), width: Math.ceil(react.width), height: Math.ceil(react.height) }
      })
      await page.setViewport({ ...viewport, width: clip.x + clip.width, height: clip.y + clip.height })
      return {
        ...metadata,
        data: await page.screenshot({ clip, omitBackground: backgroundColor === 'transparent' })
      }
    } else { // pdf
      if (pdfFit) {
        const clip = await page.$eval('svg', svg => {
          const react = svg.getBoundingClientRect()
          return { x: react.left, y: react.top, width: react.width, height: react.height }
        })
        return {
          ...metadata,
          data: await page.pdf({
            omitBackground: backgroundColor === 'transparent',
            width: (Math.ceil(clip.width) + clip.x * 2) + 'px',
            height: (Math.ceil(clip.height) + clip.y * 2) + 'px',
            pageRanges: '1-1'
          })
        }
      } else {
        return {
          ...metadata,
          data: await page.pdf({
            omitBackground: backgroundColor === 'transparent'
          })
        }
      }
    }
  } finally {
    await page.close()
  }
}
Render a mermaid diagram.

@param {import("puppeteer").Browser | import("puppeteer").BrowserContext} browser - Puppeteer Browser
@param {string} definition - Mermaid diagram definition
@param {"svg" | "png" | "pdf"} outputFormat - Mermaid output format.
@param {ParseMDDOptions} [opt] - Options, see {@link ParseMDDOptions} for details.
@returns {Promise<{title: string | null, desc: string | null, data: Uint8Array}>} The output file in bytes,
with optional metadata.