@nrsk/shikigami

Opinionated syntax highlighting with shiki for markdown-it.

Usage no npm install needed!

<script type="module">
  import nrskShikigami from 'https://cdn.skypack.dev/@nrsk/shikigami';
</script>

README

式神 shikigami

Build/Test Coverage NPM Semantic Release

Opinionated syntax highlighting with shiki for markdown-it.

Installation

Just use your favorite package manager.

npm i @nrsk/shikigami

This package comes both in CommonJS and ESM flavors. No additional steps required, just import or require it.

Usage with other plugins

Yet to be confirmed, but most likely this plugin is incompatible with other plugins that process fenced code blocks (e.g. markdown-it-attrs, which can be used to assign classes to elements and has similar syntax).

Usage

This package exposes a function named shikigami, which is a Promise, that resolves to a markdown-it plugin function. Hence, you must await it (or use .then syntax), and only then pass the resulting function to markdown-it.

Why? shiki uses a lot of asynchrounous stuff under the hood, and its public API is mostly asynchronous as well, while markdown-it is synchronous, so yeah, I had to wrap the plugin in a Promise.

Below is an example of usage.

import { shikigami, loadTheme } from '@nrsk/shikigami'
import md from 'markdown-it'

async function processMarkdown(input) {
  const parser = md('commonmark').use(
    await shikigami({
      withLanguage: true,
      withLineNumbers: true,
      highlightInvert: true,
      highlighter: {
        // All other shiki's highlighter options are available as well,
        // so you can load additional languages and so on.
        theme: await loadTheme('./theme/custom.json')
      }
    })
  )

  return parser.render(input)
}

Markdown syntax

This plugin introduces a simple syntax extension for fenced code blocks, that allows you to change plugin options in-place. Options defined this way have precedence over plugin options.

# Example

```typescript {{ <option>: <value>, <option>: <value>, ... }}
interface User {
  id: number
  firstName: string
  lastName: string
  role: string
}
```

Options

List of options that can be passed to shikigami function. Options allowed to be used directly in Markdown through syntax extension are marked accordingly.

openToken?: string

  • Default: {{
  • Allowed in Markdown: no

Use this option to change the opening token for extended Markdown syntax.

closeToken?: string

  • Default: {{
  • Allowed in Markdown: no

Use this option to change the closing token for extended Markdown syntax.

withLanguage?: boolean

  • Default: false
  • Allowed in Markdown: yes

Set to true to allow rendering a sibling <div> with a language-<languageId> class. This is useful when you want to show the language specified in your fenced code block.

withLineNumbers?: boolean

  • Default: false
  • Allowed in Markdown: yes

Set to true to render line numbers.

highlight?: Array<number | Array<number>>

  • Default: []
  • Allowed in Markdown: yes

Use this option to specify which lines or ranges of lines should be marked with specified highlighting class (or classes). These classes can be customized via highlightClasses and highlightRangeClasses options.

Examples

  • Highlight lines 5 and 7:

    {{ highlight: [5, 7] }}
    
  • Highlight lines 5, 7, and lines in range from 10 to 15:

    {{ highlight: [5, 7, [10, 15]] }}
    

highlightClasses?: Array<string>

  • Default: ['highlight']
  • Allowed in Markdown: no

Use this option to customize classes for highlighted lines.

highlightRangeClasses?: HighlightRangeClasses

  • Default: ['highlight-start', 'highlight-inner', 'highlight-end']
  • Allowed in Markdown: no

Use this option to customize classes for highlighted ranges. Must be a tuple of exactly three elements.

Explanation

  • The first class is assigned to the first line of a range.
  • The second class is assigned to all lines between the first and the last line of a range. This only applied if the range covers 3 and more lines.
  • The third class is assigned to the last line of a range.

Is this overkill? Probably...

highlightInvert?: boolean

  • Default: false
  • Allowed in Markdown: yes

Set to true to allow marking lines that are not highlighted with specified classes. These classes can be customised via highlightInvertClasses.

Note: this will work only if highlight option is set to a non-empty array of lines.

highlightInvertClasses?: Array<string>

  • Default: ['highlight-invert']
  • Allowed in Markdown: no

Use this option to customize classes for unhighlighted lines.

highlighter?: HighlighterOptions

  • Default: {}
  • Allowed in Markdown: no

Highlighter options which will be passed right into shiki's getHighlighter. See shiki documentation for available options.

API

Besides exposing the shikigami function, this plugin re-exports shiki's loadTheme just for convenience.

shikigami(userOptions?: ShikigamiOptions): Promise<PluginSimple>

Plugin fabric.

import { shikigami } from '@nrsk/shikigami'
import anchor from 'markdown-it-anchor'
import md from 'markdown-it'

async function processMarkdown(input) {
  return md('commonmark')
    .use(
      await shikigami({
        /** Plugin options. */
      })
    )
    .use(anchor)
    .render(input)
}

loadTheme(themePath: string): Promise<IShikiTheme>

Shiki's theme loader.

Todo

  • Tests.
  • Diffs support.
  • Plugin for remark.
  • Plugin for rehype.

License

MIT.