Skip to content

Latest commit

 

History

History

rehype-code-hook

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

@beoe/rehype-code-hook

Rehype plugin to make it easier to write custom processors for code e.g.:

Block-code:
<pre><code class="language-js">const x = 1;</code></pre>

Inline-code:
<code>x</code>

In markdown it corresponds to:

Block-code:

```js
const x = 1;
```

Inline-code:
`x`

This plugin is usefull if you want to create rehype plugin to:

  • highlight code, like @shikijs/rehype, rehype-prism, rehype-highlight etc.
  • render diagrams, like rehype-mermaid
  • do something else, like rehype-color-chips

Usage

Basic example looks like this:

import { rehypeCodeHook } from "@beoe/rehype-code-hook";
import { generateSvg } from "./generateSvg.js";

export const rehypeExampleDiagram = (options = {}) => {
  return rehypeCodeHook({
    ...options,
    code: ({ code }) => generateSvg(code),
  });
};

If you have code like this (it is more clear in Markdown notation, but plugin actually works at rehype level):

```js {1,10}
const x = 1;
```

code callback would be called with:

{
  code: "const x = 1;\n",
  inline: false,
  language: "js",
  meta: "{1,10}"
}

If you have code like this:

`const x = 1;`

code callback would be called with:

{
  code: "const x = 1;",
  inline: true,
  language: undefined,
  meta: undefined
}

Now it is time to render your thing:

  • you can check code, inline, language, meta and if this is not the block you are looking for you can return undefined - and block would be unchanged
  • if you decided to render something you can return string (for example SVG or HTML), you can return HAST fragment or you can return promise of string or HAST
    • if return value is HAST fragment it will replace whole code block
    • if return value is string it would be converted to HAST and then will replace whole code block
    • if return value is promise it will wait until it resolves and do one of steps above

You can configure your plugin to be called only for specific cases, for example:

  • only for language example: rehypeCodeHook({language: "example",..})
  • only for inline code: rehypeCodeHook({inline: true,..})

To enable caching you need to pass Map-like storage:

rehypeCodeHook({ code, cache: new Map(), hashTostring: true });

I checked it with @beoe/cache, but it suppose to work with any storage that has Map like interface. You may pass additional salt param to reset cache, for example when configuration of your plugin changed.

Tips

Meta string

If you need to parse meta param you can use, for example:

Astro

If you want to use rehype plugin for code elements in Astro you need to disable built-in highlighter and then make sure it will appear after your rehype plugin

import { rehypeShiki, markdownConfigDefaults } from "@astrojs/markdown-remark";

export default defineConfig({
  markdown: {
    syntaxHighlight: false,
    rehypePlugins: [
      // you rehype plugin goes here
      // re-enable default Astro code highlighter
      [rehypeShiki, markdownConfigDefaults.shikiConfig],
    ],
  },
});

Starlight

Important use v0.22+

export default defineConfig({
  markdown: {
    rehypePlugins: [
      // you rehype plugin goes here
      // after you can insert code highlighter
    ],
  },
});

Images instead of inline SVG

My main use-case is to inline SVGs, but if you want to produce images instead you can use data URLs. Something like this:

{
  code: () =>
    `<img src="data:image/png;base64,${dataBuffer.toString("base64")}">`;
}

If you want to convert SVG data URL use mini-svg-data-uri or similar.

I didn't investigate how to render images aside with vfile.

TODO

  • test async function and cache together