Qubyte Codes

Custom markdown blocks with marked

Published

I use marked to do the markdown rendering for this blog. A recent feature makes it possible to create custom block types with a little hacking. In this post I show you how!

I'll be using mathematics (TeX) blocks for my example. The marked setup code looks like this:

marked.use({
  walkTokens(token) {
    const { type, raw } = token;

    // Modify paragraph blocks beginning and ending with $$.
    if (type === 'paragraph' && raw.startsWith('$$\n') && raw.endsWith('\n$$')) {
      token.type = 'code';
      token.lang = 'mathematics';
      token.text = token.raw.slice(3, -3); // Remove the $$ boundaries.
      token.tokens.length = 0; // Remove child tokens.
    }
  },
  renderer: {
    code(code, language) {
      // Use custom mathematics renderer.
      if (language === 'mathematics') {
        return renderMathematics(code);
      }

      // Use default code renderer.
      return false;
    }
  }
});

I'm passing two things to marked to configure it here. The first is a token walker function, which is the recent feature which makes this all possible. It is called for each token, traversing the children of a token before it progresses to its siblings (so it's sort of depth-first).

The idea is for blocks of text with a $$ above and below them to be handled as mathematics. To a person this looks like a fenced code block with two dollar symbols in the place of the three backticks. For example:

Some example text with some mathematics to render below:

$$
a^2 = b^2 + c^2
$$

Some example text below.

It's a common extension to place LaTeX code inside $$ delimited blocks. Even if you're not familiar, the dollar symbols above and below are a little like a fenced code block. To marked the block looks like a paragraph. This means that the token walker will receive some paragraph tokens which need to be modified.

To know the difference, paragraph tokens are checked by the token walker to see if they begin with $$\n and end with \n$$. When they do, the block is modified to look like a code block with a special 'markdown' language. Child tokens are removed because the content shouldn't be treated as markdown, and the text property is set by snipping the leading and trailing dollar symbols and newlines off.

The second part of this trick is in the renderer option. The renderer for code blocks is modified with special handling for the 'mathematics' language. The code parameter received by it is the text we set on the token, so it's ready to be rendered to mathematics. The rendering itself is beyond the scope of this post, but I use MathJax. When code is of any other language the custom code renderer returns false to instruct marked to use the default code rendering behaviour.