m5p3nc3r

TL;DR;

I can now add code blocks such as:

```svgbob
      .---.
      /-o-/--
   .-/ / /->
  ( *  \/
   '-.  \
      \ /
       '
```.

That will generate an inline SVG element like this:

..

This should make it a lot easier to add simple diagrams into my posts on this site.

Why Svgbob?

I was looking for a way to render simple diagrams generated from text from markdown when I stumbled across Svgbob. This looked ideal as it translates plain ASCII art into SVG. I just needed to find a way to embed this as markdown. I explored using a custom react component where the child content would be the Svgbob markdown, and whilst this worked, the prettier plugin for vscode didn't understand how the Svgbob markdown should be formatted, and with format-on-save configured, it really messed with the diagrams!

The ideal solution would be to use a markdown code block, as text formatters such as prettier know not to touch the embedded text, and this is when I stumbled across remark-svgbob. There was however a slight issue with the use of the plugin, as it worked by replacing the code block with raw html. This requires the use of dangerouslySetInnerHTML in react and adding the rehype-raw plugin in the processing pipeline.

Also, the use of raw html was causing problems for my specific use-case because I am using dynamic processing of markdown in a server component. This is required as this site uses dynamic routes for generating the pages from static assets available on the server. As a result, I was getting the following error:

Error: page.mdx:Error: Cannot compile mdxjsEsm node. It looks like you are using MDX nodes with hast-util-raw (or rehype-raw). If you use this because you are using remark or rehype plugins that inject 'html' nodes, then please raise an issue with that plugin, as its a bad and slow idea. If you use this because you are using markdown syntax, then you have to configure this utility (or plugin) to pass through these nodes (see passThrough in docs), but you can also migrate to use the MDX syntax.

I raised this issue with the upstream remark-svgbob project, but as a true supporter of Open Source development, I thought I would not wait for somebody else to fix it - so I gave it a go myself!

What ideal looks like

I wanted to find a way to embed the SVG content without having to add special configuration to my project. The more obtuse the configuration process is, the harder it would be for others to make use of the resulting code withough stumbling upon edge cases.

The processing pipeline make use of an Abstract Syntax Tree hast to streamline the transformation of markdown into HTML through the use of remark plugins for processing the markdown and rehype plugins for processing the html generation.

An example from the remark documentation for how to convert markdown into HTML:

import rehypeSanitize from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import {unified} from 'unified'

const file = await unified()
  .use(remarkParse)
  .use(remarkRehype)
  .use(rehypeSanitize)
  .use(rehypeStringify)
  .process('# Hello, Neptune!')

console.log(String(file))

yields the output:

<h1>Hello, Neptune!</h1>

What I want to be able to do is add the following

import rehypeSanitize from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
+import remarkSvgbob from 'remark-svgbob'
import {unified} from 'unified'

const file = await unified()
  .use(remarkParse)
  .use(remarkRehype)
+  .use(remarkSvgbob)
  .use(rehypeSanitize)
  .use(rehypeStringify)
  .process('# Hello, Neptune!')

console.log(String(file))

With just this simple configuration change, I want to be able to have inline svgbob code blocks converted into SVG in the output HTML document.

The implementation

The implementation can be found in this PR. It shows how to create a hast tree from the SVG generated from the Svgbob library. The SVG node of the hast tree is traversed to create a node structure compatible with an Mdx pipeline. The original code block is then replaced with the new node structure.

How to use it yourself

If you want to make use of the remark-svgbob plugin yourself, please make sure you are using at least v2.0.0. So your package.json should include the following:

{
  "dependencies": {
    "remark-svgbob": "^2.0.0"
  }
}

The implementation is not yet documented upstream, but using nextjs as an example, you can add something like this to your next.config.js:

const withMDX = nextMDX({
  extension: /\.mdx?$/,
  options: {
    remarkPlugins: [[remarkSVGBob, { useMdx: true }]],
    rehypePlugins: [],
  },
});

Note that the important part here is the useMdx: true as this will enable the new codepath in remark-svgbob.

You will also need to add the svgbob css to your project. An example of the default styling can be found at svgbob.css.

Conclusion

We'll let the plugin talk for itself:

Statistical Charts

EDCBA5101520253035404550 EDCBA5101520253035404550

Plot diagrams

UinUdc500msInactiveActiveCpu.QonUdc_OK