Skip to content

@shikijs/rehype

NPM versionNPM downloadsGitHub

rehype 的 Shiki 插件。

安装

sh
npm i -D @shikijs/rehype
sh
yarn add -D @shikijs/rehype
sh
pnpm add -D @shikijs/rehype
sh
bun add -D @shikijs/rehype
sh
deno add npm:@shikijs/rehype

使用方法

ts
import 
rehypeShiki
from '@shikijs/rehype'
import
rehypeStringify
from 'rehype-stringify'
import
remarkParse
from 'remark-parse'
import
remarkRehype
from 'remark-rehype'
import {
unified
} from 'unified'
const
file
= await
unified
()
.
use
(
remarkParse
)
.
use
(
remarkRehype
)
.
use
(
rehypeShiki
, {
// 或者使用单个主题的 `theme`
themes
: {
light
: 'vitesse-light',
dark
: 'vitesse-dark',
} }) .
use
(
rehypeStringify
)
.
process
(await fs.readFile('./input.md'))

@shikijs/rehype 的默认导出使用来自 getSingletonHighlighter 的共享 shiki 实例,该实例将在进程间保持。如果你希望完全控制高亮器的生命周期,请改用 精细粒度打包 @shikijs/rehype/core

精细粒度打包

默认情况下,会导入 shiki 的完整包。如果你使用的是精细粒度打包,你可以从 @shikijs/rehype/core 导入 rehypeShikiFromHighlighter,并传入你自己的高亮器:

ts
import 
rehypeShikiFromHighlighter
from '@shikijs/rehype/core'
import
rehypeStringify
from 'rehype-stringify'
import
remarkParse
from 'remark-parse'
import
remarkRehype
from 'remark-rehype'
import {
createHighlighterCore
} from 'shiki/core'
import {
createOnigurumaEngine
} from 'shiki/engine/oniguruma'
import {
unified
} from 'unified'
const
highlighter
= await
createHighlighterCore
({
themes
: [
import('@shikijs/themes/vitesse-light') ],
langs
: [
import('@shikijs/langs/javascript'), ],
engine
:
createOnigurumaEngine
(() => import('shiki/wasm'))
}) const
raw
= await fs.readFile('./input.md')
const
file
= await
unified
()
.
use
(
remarkParse
)
.
use
(
remarkRehype
)
.
use
(
rehypeShikiFromHighlighter
,
highlighter
, {
// 或者使用单个主题的 `theme`
themes
: {
light
: 'vitesse-light',
dark
: 'vitesse-dark',
} }) .
use
(
rehypeStringify
)
.
processSync
(
raw
) // 也可以同步处理

特性

行内代码

你也可以通过 inline 选项高亮行内代码。

选项示例说明
false-禁用行内代码高亮(默认)
'tailing-curly-colon'let a = 1{:js}使用 {:language} 标记在代码块内进行高亮

在 Rehype 插件中启用 inline

ts
import 
rehypeShiki
from '@shikijs/rehype'
import
rehypeStringify
from 'rehype-stringify'
import
remarkParse
from 'remark-parse'
import
remarkRehype
from 'remark-rehype'
import {
unified
} from 'unified'
const
file
= await
unified
()
.
use
(
remarkParse
)
.
use
(
remarkRehype
)
.
use
(
rehypeShiki
, {
inline
: 'tailing-curly-colon', // 或其他选项
// ... }) .
use
(
rehypeStringify
)
.
process
(await fs.readFile('./input.md'))

然后你可以在 markdown 中使用行内代码:

md
This code `console.log("Hello World"){:js}` will be highlighted.

后处理转换器

INFO

postprocess 转换器钩子仅在生成 HTML 字符串(即使用 codeToHtml)时调用。由于 @shikijs/rehype 是基于 HAST(超文本抽象语法树)而非 HTML 字符串进行操作,所以不会执行 postprocess 转换器。

这是有意为之:在 rehype 中运行 postprocess 钩子需要进行 HAST → HTML → 运行 postprocess → 解析回 HAST 的转换流程,这样会改变语义,可能让期望仅操作 HAST 的用户感到意外。

针对基于 HTML 的后处理的解决方案

如果你需要在 rehype 中执行基于 HTML 的后处理,可以使用一个 root 转换器,它:

  1. 使用 hast-util-to-html 将 HAST 片段转换成 HTML
  2. 运行你的 HTML 转换
  3. 使用 hast-util-from-html 转换回 HAST

示例:

ts
import { fromHtml } from 'hast-util-from-html'
import { toHtml } from 'hast-util-to-html'

const file = await unified()
  .use(remarkParse)
  .use(remarkRehype)
  .use(rehypeShiki, {
    themes: {
      light: 'vitesse-light',
      dark: 'vitesse-dark',
    },
    transformers: [
      {
        name: 'custom-html-postprocessor',
        root(node) {
          // 转换 HAST 到 HTML
          const html = toHtml(node)

          // 执行自定义 HTML 转换
          const processedHtml = myCustomPostprocess(html)

          // 解析回 HAST
          const newNode = fromHtml(processedHtml, { fragment: true })

          // 替换节点
          return newNode
        }
      }
    ]
  })
  .use(rehypeStringify)
  .process(await fs.readFile('./input.md'))