Skip to content

@shikijs/twoslash

NPM versionNPM downloadsGitHub

适用于 Twoslash 的 shiki 转换器,用于在代码块内提供行内的类型悬停显示。

TwoSlash 注释参考

安装

bash
npm i -D @shikijs/twoslash

这个包是 shiki 的一个转换器插件,这意味着对于每个支持传递 shiki 转换器的集成,你都可以使用此包。

ts
import {
  
transformerTwoslash
,
} from '@shikijs/twoslash' import {
codeToHtml
,
} from 'shiki' const
html
= await
codeToHtml
(`console.log()`, {
lang
: 'ts',
theme
: 'vitesse-dark',
transformers
: [
transformerTwoslash
(), // <-- 这里
// ... ], })

默认的输出是没有样式的,你需要添加额外的 CSS 来装饰它们。

如果你想在浏览器或者 Workers 上运行 Twoslash,参考 使用 CDN 部分。

渲染器

由于 hast 的灵活性,本转换器允许你使用 AST 来自定义每个片段在输出 HTML 中的呈现方式。

我们提供了两个内建的渲染器,你也可以创建你自己的渲染器。


rendererRich

源代码

提示

自 v1.0 开始,本渲染器是默认渲染器。

此渲染器提供了一个更明确的类名,前缀为 twoslash-,以便更好地进行作用域的限定。 此外,它还在悬停信息上进行语法的高亮显示。

ts
import { 
rendererRich
,
transformerTwoslash
} from '@shikijs/twoslash'
transformerTwoslash
({
renderer
:
rendererRich
() // <--
})

这里有一些使用内置 style-rich.css 的例子:

ts
interface Todo {
  
title
: string
} const
todo
:
Readonly
<Todo> = {
title
: 'Delete inactive users'.
toUpperCase
(),
}
todo
.title = 'Hello'
Cannot assign to 'title' because it is a read-only property.
Number
.p
arseInt
('123', 10)
// //
ts
import { 
createHighlighterCore
} from 'shiki/core'
const
highlighter
= await
createHighlighterCore
({})
const
a
= 1
自定义日志信息
const
b
= 1
自定义错误信息
const
c
= 1
自定义警告信息
自定义注释信息

rendererClassic

源代码

此渲染器与旧版的 shiki-twoslash 的输出一致。

ts
import { 
rendererClassic
,
transformerTwoslash
} from '@shikijs/twoslash'
transformerTwoslash
({
renderer
:
rendererClassic
() // <--
})

你可能需要引用 shiki-twoslash 的 CSS 来装饰它。在这里 我们也提供了来自 shiki-twoslash 的 CSS 拷贝,不过它可能需要进一步的优化。

rendererFloatingVue

源代码

这个渲染器使用 Floating Vue 作为浮动组件(在容器外渲染),并生成 Vue 模版语法。这个渲染器不可以直接使用,而是作为 VitePress 集成 的内部渲染器。在这里列出它,对你可能创建的自己的渲染器提供一些参考。

选项

显式使用

当与 markdown-it-shikirehype-shiki 集成时,我们可能不希望 Twoslash 在每个代码块上运行。在这种情况下,我们可以将 explicitTrigger 设置为 true,仅在指定呈现 Twoslash 的代码块上显示。

ts
import { 
transformerTwoslash
} from '@shikijs/twoslash'
transformerTwoslash
({
explicitTrigger
: true // <--
})
md
在 Markdown 中,你可以使用如下的语法来触发 Twoslash

```ts
// 这是一个普通的代码块
```

```ts twoslash
// 这是一个运行 Twoslash 的代码块
```

集成

你可以根据上面的说明自行将 Twoslash 与 Shiki 集成,也可以在这里找到与框架和工具的高级集成:

  • VitePress - 在 VitePress 中启用 Twoslash 支持。
  • Nuxt - 在 Nuxt Content 中启用 Twoslash 支持。
  • Vocs - 在 Vocs 中启用 Twoslash 支持。
  • Slidev - Slidev 具有内建的 Twoslash 支持。

用例指南

使用 CDN

默认情况下,twoslash 在 Node.js 上运行,并依赖于你的本地系统来解析 TypeScript 和导入的类型。在非 Node.js 环境中直接导入它将无法工作。

幸运的是,Twoslash 实现了一个虚拟文件系统,允许你提供自己的文件以供 TypeScript 在内存中解析。然而,在浏览器中加载这些文件仍然是一个挑战。在 TypeScript 网站 中,TypeScript 团队提供了一些用来从 CDN 上按需获取类型的工具,他们称之为 自动类型获取(ATA,Automatic Type Acquisition)

我们简易地封装了它们,并在 twoslash-cdn 中提供了易于使用的 API。 例如:

js
// TODO: 在生产环境中使用显式的版本替换
import { createTransformerFactory, rendererRich } from 'https://esm.sh/@shikijs/twoslash@latest/core'
import { codeToHtml } from 'https://esm.sh/shiki@latest'
import { createTwoslashFromCDN } from 'https://esm.sh/twoslash-cdn@latest'
import { createStorage } from 'https://esm.sh/unstorage@latest'
import indexedDbDriver from 'https://esm.sh/unstorage@latest/drivers/indexedb'

// ============= 初始化 =============

// 使用 IndexedDB 和 unstorage 来缓存虚拟文件系统的示例
const storage = createStorage({
  driver: indexedDbDriver({ base: 'twoslash-cdn' }),
})

const twoslash = createTwoslashFromCDN({
  storage,
  compilerOptions: {
    lib: ['esnext', 'dom'],
  },
})

const transformerTwoslash = createTransformerFactory(twoslash.runSync)({
  renderer: rendererRich(),
})

// ============= 执行 =============

const app = document.getElementById('app')

const source = `
import { ref } from 'vue'

console.log("Hi! Shiki + Twoslash on CDN :)")

const count = ref(0)
//     ^?
`.trim()

// 在渲染之前,我们需要准备好类型,以便能够同步地进行渲染
await twoslash.prepareTypes(source)

// 接着我们可以渲染代码
app.innerHTML = await codeToHtml(source, {
  lang: 'ts',
  theme: 'vitesse-dark',
  transformers: [transformerTwoslash],
})

以 MIT 许可证发布