brillout / vite-plugin-mdx

Vite Plugin for MDX
MIT License
113 stars 36 forks source link

Rendering code demo fences #43

Closed jugglingcats closed 2 years ago

jugglingcats commented 3 years ago

I am trying to port a site using gatsby-plugin-mdx-code-demo to vite. This plugin allows you to render sample code in a normal pre together with the resulting generated component. See https://material-ui.com/components/buttons for an example of the type of thing.

I would like to be able to write MDX like:

// buttons.mdx

# Buttons

See below for an example of a button:

```jsx demo
    import {Button} from "@acme/controls"

    export default () => <Button>Click Me</Button>
```

I realise this is a very open question but any advice on the idiomatic way to achieve this using vite + vite-plugin-mdx would be appreciated!

jugglingcats commented 3 years ago

This is my solution (remark plugin) - am not sure it's the best way? Note that it adds the new jsx node and leaves the code block alone, rather than replacing it.

import {visit} from "unist-util-visit"
import crypto from "crypto";
import path, {resolve} from "path";
import fs from "fs";

const OUTDIR = ".cache/codeblock";
const PLUGIN_NAME = 'remark-codeblock';

function render(source, destination) {
    const unique = crypto.createHmac('sha1', PLUGIN_NAME).update(source).digest('hex');
    const jsxFilename = `${unique}.jsx`;
    const jsxPath = path.join(destination, jsxFilename);

    if (!fs.existsSync(jsxPath)) {
        // Write temporary file
        fs.writeFileSync(jsxPath, source);
    }

    return [jsxFilename, unique];
}

export default function remarkCodeblock() {
    fs.mkdirSync(OUTDIR, {recursive: true})
    return (tree, vFile) => {
        const importAST = []
        visit(tree, 'code', (node, index, parent) => {
            if (node.meta === "demo") {
                delete node.meta

                const source = node.value
                const [file, unique] = render(source, OUTDIR)
                const fullPath = resolve(OUTDIR + "/" + file)
                const relative = path.relative(vFile.history[0], fullPath).replace(/\\/g, '/').substr(3);

                importAST.push({
                    type: "import",
                    value: `import CodeBlock${unique} from "${relative}"`
                })

                parent.children.splice(index, 0, {
                        type: "jsx",
                        value: `<CodeBlock${unique}/>`
                    }
                )
                return index + 2
            }
        })

        tree.children = [...importAST, ...tree.children]
    }
}
silvenon commented 2 years ago

Yep, I would solve this with a remark plugin as well 👍 🙂

I'm closing this as it's not related to vite-plugin-mdx.