posthtml-shiki v1.6.0
Introduction
This is a PostHTML plugin that uses Shiki to highlight code blocks.
Features:
- Configure
langs - Configure
themes langattributethemeattribute- Dual Themes
- Wrap in custom tag
- Default color theme
- Decorations
- Transformers
- Custom themes
- Custom languages
Input:
<shiki>
<h1 class="text-xl">Hello</h1>
</shiki>Output:
<pre class="shiki nord" style="background-color:#2e3440ff;color:#d8dee9ff" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color:#81A1C1"> <h1</span><span style="color:#8FBCBB"> class</span><span style="color:#ECEFF4">=</span><span style="color:#ECEFF4">"</span><span style="color:#A3BE8C">text-xl</span><span style="color:#ECEFF4">"</span><span style="color:#81A1C1">></span><span style="color:#D8DEE9FF">Hello</span><span style="color:#81A1C1"></h1></span></span>
<span class="line"></span></code></pre>Installation
npm i posthtml posthtml-shikiUsage
Use the <shiki> tag to highlight all code inside it:
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki()
])
.process('<shiki><h1 class="text-xl">Hello</h1></shiki>')
.then(result => result.html)Attributes
You may use certain attributes to configure which themes or language to use.
lang
Alias: language
Use the lang attribute to specify the language of the code block.
<shiki lang="javascript">
import { codeToHtml } from 'shiki'
</shiki>theme
Use the theme attribute to specify the theme to use.
<shiki theme="github-light">
<h1 class="text-xl">Hello</h1>
</shiki>theme-*
Shiki's Dual Themes is supported through theme-* attributes:
<shiki theme-light="github-light" theme-dark="github-dark">
<h1 class="text-xl">Hello</h1>
</shiki>!NOTE If a
themeattribute is present, it will override thetheme-*attributes.
This uses CSS variables to switch between themes, so you'll need to define the CSS variables in your stylesheet.
With media queries:
@media (prefers-color-scheme: dark) {
.shiki,
.shiki span {
color: var(--shiki-dark) !important;
background-color: var(--shiki-dark-bg) !important;
/* Optional, if you also want font styles */
font-style: var(--shiki-dark-font-style) !important;
font-weight: var(--shiki-dark-font-weight) !important;
text-decoration: var(--shiki-dark-text-decoration) !important;
}
}Class-based:
html.dark .shiki,
html.dark .shiki span {
color: var(--shiki-dark) !important;
background-color: var(--shiki-dark-bg) !important;
/* Optional, if you also want font styles */
font-style: var(--shiki-dark-font-style) !important;
font-weight: var(--shiki-dark-font-weight) !important;
text-decoration: var(--shiki-dark-text-decoration) !important;
}default-color
When using multiple themes, you may specify the default color theme for Shiki to use.
The value of the attribute must be the name of one of the theme-* attributes, so for example if you have theme-light and theme-dark attributes, the attribute value must be either light or dark.
<shiki
theme-light="github-light"
theme-dark="github-dark"
default-color="dark"
>
<h1 class="text-xl">Hello</h1>
</shiki>Shiki relies on CSS specificity and changes the order of the classes on the wrapping <pre> tag.
By default, the plugin does not set default-color.
wrap
By default, the <shiki> tag will be removed and the code block will be wrapped in a <pre> tag. Use the wrap attribute to define a custom tag to wrap the code block in.
<shiki lang="js" wrap="div">
import { codeToHtml } from 'shiki'
</shiki>Result:
<div><pre class="shiki nord" style="background-color:#2e3440ff;color:#d8dee9ff" tabindex="0"><code><span class="line"><span style="color:#81A1C1">import</span><span style="color:#ECEFF4"> {</span><span style="color:#8FBCBB"> codeToHtml</span><span style="color:#ECEFF4"> }</span><span style="color:#81A1C1"> from</span><span style="color:#ECEFF4"> '</span><span style="color:#A3BE8C">shiki</span><span style="color:#ECEFF4">'</span></span></code></pre></div>!IMPORTANT
The value of thewrapattribute must be a valid tag name, CSS selectors are not supported.
Options
The plugin accepts an options object as the first argument, which can be used to configure things like the tag name or the options to pass to Shiki.
tag
Type: string\
Default: shiki
Use the tag option to specify the tag name to use.
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
tag: 'highlight'
})
])
.process('<highlight>... your code</highlight>')
.then(result => result.html)langs
Type: string[]\
Default: ['html']
Use the langs option to specify the languages for Shiki to load.
It's recommended to load only the languages that you need.
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
langs: ['html', 'javascript']
})
])
.process(`
<shiki lang="html">... some html</shiki>
<shiki lang="javascript">... some js</shiki>
`)
.then(result => result.html)See the list of supported languages in Shiki.
Custom Languages
You may also load custom languages by passing a TextMate grammar object to the langs option.
const customDiffLang = JSON.parse(readFileSync('./custom-diff.json', 'utf8'))
posthtml([
shiki({
langs: [customDiffLang]
})
])
.process(`
<shiki lang="custom-diff">
- FOO
+ BAR
</shiki>
`)
.then(result => result.html)You must specify the lang attribute with the name of the language, and the value must match the name property of the TextMate grammar object.
See tm-grammars for examples.
themes
Type: Array<string> | Array<object>\
Default: ['nord']
Use the themes option to specify the themes for Shiki to load.
It's recommended to load only the themes that you need.
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
themes: ['github-light', 'github-dark']
})
])
.process(`
<shiki theme="github-light">[code]</shiki>
<shiki theme="github-dark">[code]</shiki>
`)
.then(result => result.html)See the list of available themes in Shiki.
!NOTE If you don't specify a
theme=""attribute, the first theme in thethemesoption will be used.
Custom Themes
You may also load custom themes by passing a TextMate theme object to the themes option:
// Define textmate theme
const myTheme = {
name: 'my-theme',
settings: [
{
scope: ['string'],
settings: {
foreground: '#888'
}
},
]
}
posthtml([
shiki({
themes: [myTheme],
})
])
.process(`<shiki theme="my-theme">[code]</shiki>`)
.then(result => result.html)If you're loading multiple themes, you will need to specify which theme to use with the theme="" attribute. For custom themes, the attribute value must match the name of the theme - in the example above, that would be my-theme.
wrapTag
Type: string|boolean\
Default: false
Use the wrapTag option to specify a custom tag to wrap the highlighted code block in.
By default, the plugin does not wrap the code block in any tag.
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
wrapTag: 'div'
})
])
.process('<shiki>... your code</shiki>')
.then(result => result.html)Result:
<div>
[highlighted code]
</div>defaultColor
Type: string\
Default: undefined
Use the defaultColor option to specify the default color theme for Shiki to use.
The value must be the key name of one of the themes in the themes option.
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
themes: {
light: 'github-light',
dark: 'github-dark',
},
defaultColor: 'dark'
})
])
.process(`
<shiki>
[code]
</shiki>
`)
.then(result => result.html)decorations
Type: array\
Default: []
Shiki's Decorations are supported through the decorations option.
You can use this to wrap custom classes and attributes around character ranges in your code.
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
decorations: [
{
// line and character are 0-indexed
start: { line: 0, character: 0 },
end: { line: 0, character: 5 },
properties: { class: 'highlighted-word' }
}
]
})
])
.process(`
<shiki>
const foo = 'bar'
</shiki>
`)
.then(result => result.html)The word const will be wrapped in a <span class="highlighted-word"> tag.
transformers
Type: array\
Default: []
Use this option to transform the highlighted code block with Shiki's Transformers.
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
import { transformerNotationHighlight } from '@shikijs/transformers'
posthtml([
shiki({
transformers: [
transformerNotationHighlight(),
{
code(node) {
this.addClassToHast(node, 'custom-class')
},
},
]
})
])
.process(`
<shiki>
const foo = 'bar'
let baz = 'biz' // [!code highlight]
</shiki>
`)
.then(result => result.html)See the docs for Shiki Transformers and a list of common Shiki Transformers.