marp-team / marp-cli

A CLI interface for Marp and Marpit based converters
MIT License
1.85k stars 105 forks source link

Allow async engine.render in converter #540

Closed GuillaumeDesforges closed 1 year ago

GuillaumeDesforges commented 1 year ago

Related to discussion https://github.com/orgs/marp-team/discussions/466

The whole context is async already, but the engine.render method is not treated as possibly async. Using await here allows to use either non-async and async render method, which is helpful for custom engines.

GuillaumeDesforges commented 1 year ago

The following marp.config.mjs works for me as expected:

import { Marp } from "@marp-team/marp-core"

async function marpMermaidPlugin(md) {
    // special render for mermaid fence code blocks
    const { fence } = md.renderer.rules
    md.renderer.rules.fence = (tokens, idx, options, env, self) => {
        const info = md.utils.unescapeAll(tokens[idx].info).trim()
        if (info) {
            const [_, lang, divOptions] = info.match(/(\w+)\s*(?:\{\s*(.+)\s*\})?/)
            if (lang == "mermaid") {
                const graphDefinition = tokens[idx].content

                // <marp-auto-scaling> is working only with Marp Core v3
                return `<p><marp-auto-scaling data-downscale-only><div class="mermaid-unprocessed">${graphDefinition}</div></marp-auto-scaling></p>`
            }
        }

        return fence.call(self, tokens, idx, options, env, self)
    }
}

class PostprocessMarpitEngine extends Marp {
    constructor(options, postprocess) {
        super(options)
        this.postprocess = postprocess
    }

    withPostprocess(postprocess) {
        this.postprocess = postprocess
        return this
    }

    async render(markdown, env = {}) {
        const { html, css, comments } = super.render(markdown, env)
        if (this.postprocess) {
            return await this.postprocess(markdown, env, html, css, comments)
        }
        return { html, css, comments }
    }
}

export default {
    engine: async (constructorOptions) =>
        new PostprocessMarpitEngine(constructorOptions)
            .use(marpMermaidPlugin)
            .withPostprocess(async (markdown, env, html, css, comments) => {
                // do things on HTML (in my case call mermaid-cli API)
                return { html, css, comments }
            }),
}