remark-code-figure

A remark plugin that wraps code blocks with a figure and adds an optional figcaption.

Downloads in past

Stats

StarsIssuesVersionUpdatedCreatedSize
remark-code-figure
120.2.23 years ago3 years agoMinified + gzip package size for remark-code-figure in KB

Readme

remark-code-figure
A remark plugin that wraps code blocks in figure elements with an optional figcaption.

Installation

npm install remark-code-figure

Usage

remark-code-figure can be used with remark or with unifiedjs.
We have the following test.md file.
~markdown
Example markdown
```js My caption text console.log("Hello, world!");
~~~

Let's look at a few ways this can be processed with tools in the [unified](https://unifiedjs.com) collective.

### To Markdown

You can process markdown input and return the transformed markdown output (the figure element will be written directly into markdown):

```js
import report from 'vfile-reporter'; // for reporting errors
import {readSync, writeSync} from 'to-vfile'; // for reading our markdown file
import { remark } from 'remark';
import remarkStringify from 'remark-stringify';
import codeCaption from 'remark-code-caption';

// our processing function
async function mdToRemark(buffer) {
  return await remark()
    .use(codeFigure)
    .use(remarkStringify)
    .process(readSync('./test.md'))
    .then(async (file) => {
      console.error(report(file));
      writeSync({path: './marked-test.md', value: String(await file)});
    })
}

mdToRemark();

This will write marked-test.md to the file system for us. That file will look like:
~markdown
Example markdown
<code class="language-js">
  console.log("Hello, world!");
</code>
My caption text
~

To HTML

You can also process markdown input into partial html output. Make sure to use {sanitize: false} in the remarkHtml options or the figure element and its children will be stripped from the document. After that, though, you'll want to use rehype-sanitize
to protect yourself from xss attacks.
async function mdHTML() {
  return await unified() // you can also use remark!
    .use(remarkParse) // but leave this line out if you do
    .use(codeFigure)
    .use(remarkHtml, {sanitize: false}) // important!
    .use(rehypeSanitize)
    .process(readSync('./test.md'))
    .then(async (file) => {
      console.error(report(file));
      writeSync({path: './html-test.html', value: String(await file)});
    });
}

mdHTML();

Which will give you:
<h1>Test Markdown file</h1>
<figure class="code-figure">
  <pre>
    <code class="language-js">
      console.log("Hello, world");
    </code>
  </pre>
  <figcaption class=code-caption>
    My caption text
  </figcaption>
</figure>

Syntax Highlighting

At the moment, this plugin will work with remark-shiki, but will not work with remark-prism.
This plugin will work with @mapbox/rehype-prism though, you'll just need to call things in the correct order, it's also recommended to use rehype-sanitize to help ensure your final output is safe from XSS attacks:
import prism from '@mapbox/rehype-prism';

async function mdPrism() {
  return await remark()
    .use(remarkRehype, {allowDangerousHtml: true})
    .use(prism)
    .use(codeFigure)
    .use(rehypeRaw)
    .use(rehypeSanitize)
    .use(rehypeDocument)
    .use(rehypeFormat) // make your html output look nice!
    .use(rehypeStringify)
    .process(readSync('./test.md'))
    .then(async (file) => {
      console.error(report(file));
      writeSync({path: './mdPrism.html', value: String(await file)});
    })
    
  }

mdPrism();

If you want to highlight with remark-shiki, you need to install shiki as a separate dependency.
You'll also need to call the shiki.getHighlighter function:
async function mdShiki() {
  const highlighter = await shiki.getHighlighter({theme: 'nord'});

  return remark()
    .use(remarkShiki, {highlighter})
    .use(codeFigure)
    .use(remarkRehype, {allowDangerousHtml: true})
    .use(rehypeRaw)
    .use(rehypeSanitize)
    .use(rehypeDocument)
    .use(rehypeFormat)
    .use(rehypeStringify)
    .process(readSync('./test.md'))
    .then(async(file) => {
      console.error(report(file));
      writeSync({path: './mdShiki.html', value: String(await file)});
    });
}

mdShiki();

Options

Options are passed to codeFigure as an object. By default, that object looks like this:
{
 className: 'code-figure', 
 captionOptions: {
   disable: false, 
   position: 'after', 
   className: 'code-caption'
 }
}

options.className

Specifies the class name of the figure element. Defaults to code-figure.
{
  type: string,
  default: 'code-figure'
}

options.captionOptions.disable

Specifies whether to disable the figcaption element.
{
  type: boolean,
  default: false
}

options.captionOptions.position

Specifies the position of the figcaption element. Can be either "before" or "after".
{
  type: string,
  default: 'after'
}

options.captionOptions.className

Specifies the class name for the figcaption element. Defaults to code-caption.
{
  type: string,
  default: 'code-caption'
}