shiki-twoslash
Documentation / made lovely by counting words / maybe we would read!
Provides the API primitives to mix shiki with @typescript/twoslash to provide rich contextual code samples.
Things it handles:
- Shiki bootstrapping:
createShikiHighlighter
- Running Twoslash over code, with caching and DTS lookups:
runTwoSlash
- Rendering any code sample with Shiki:
renderCodeToHTML
Generic libraries for common tools which use this generator:
Plugins for common Static Site Generators:
Or you can use the API directly in a Node.js script:
import { renderCodeToHTML, runTwoSlash, createShikiHighlighter } from "shiki-twoslash"
import { writeFileSync } from "fs"
const go = async () => {
const highlighter = await createShikiHighlighter({ theme: "dark-plus" })
const code = `
// Hello world
const a = "123"
const b = "345"
`
const twoslash = runTwoSlash(code, "ts", {})
const html = renderCodeToHTML(twoslash.code, "ts", ["twoslash"], {}, highlighter, twoslash)
fs.writeFileSync("output.html", html, "utf8")
}
User Settings
The config which a user passes is an intersection of Shiki's HighlighterOptions
interface HighlighterOptions {
theme?: IThemeRegistration
langs?: (Lang | ILanguageRegistration)[]
themes?: IThemeRegistration[]
paths?: IHighlighterPaths
}
With twoslash's TwoSlashOptions
export interface TwoSlashOptions {
defaultOptions?: Partial<ExampleOptions>
defaultCompilerOptions?: CompilerOptions
customTransformers?: CustomTransformers
tsModule?: TS
lzstringModule?: LZ
fsMap?: Map<string, string>
vfsRoot?: string
}
And one extra for good luck:
export interface TwoslashShikiOptions {
addTryButton?: true
disableImplicitReactImport?: true
}
That said, most people will just want to set a theme
:
{
resolve: "gatsby-remark-shiki-twoslash",
options: {
theme: "github-light"
},
}
You can find all built-in themes here and all built-in languages here.
Common Use Case
Node Types in a Code Sample
To get the Node globals set up, import them via an inline triple-slash reference:
```ts twoslash
/// <reference types="node" />
import { execSync } from "child_process"
const files = execSync("git status --porcelain", { encoding: "utf8" })
files.length
```
This applies to other projects which use globals, like Jest etc. If you think that's ugly, that's OK, you can use // ---cut---
to trim the user-visible output.
API
The user-exposed parts of the API is a well documented single file, you might find it easier to just read that: src/index.ts
.
createShikiHighlighter
Sets up the highlighter for Shiki, accepts shiki options:
async function visitor(highlighterOpts) {
const highlighter = await createShikiHighlighter(userOpts)
visit(markdownAST, "code", visitor(highlighter, userOpts))
}
renderCodeToHTML
export declare const renderCodeToHTML: (
code: string,
lang: string,
info: string[],
shikiOptions?: import("shiki/dist/renderer").HtmlRendererOptions | undefined,
highlighter?: Highlighter | undefined,
twoslash?: TwoSlashReturn | undefined
) => string
For example:
const results = renderCodeToHTML(node.value, lang, node.meta || [], {}, highlighter, node.twoslash)
node.type = "html"
node.value = results
node.children = []
Uses:
renderers.plainTextRenderer
for language which shiki cannot handlerenderers.defaultRenderer
for shiki highlighted code samplesrenderers.twoslashRenderer
for twoslash powered TypeScript code samplesrenderers.tsconfigJSONRenderer
for extra annotations to JSON which is known to be a TSConfig file
These will be used automatically for you, depending on whether the language is available or what the info
param is set to.
To get access to the twoslash renderer, you'll need to pass in the results of a twoslash run to renderCodeToHTML
:
const highlighter = await createShikiHighlighter(highlighterOpts)
const twoslashResults = runTwoSlash(code, lang)
const results = renderCodeToHTML(
twoslashResults.code,
twoslashResults.lang,
node.meta || ["twoslash"],
{},
highlighter,
node.twoslash
)
runTwoSlash
Used to run Twoslash on a code sample. In this case it's looking at a code AST node and switching out the HTML with the twoslash results:
if (node.meta && node.meta.includes("twoslash")) {
const results = runTwoSlash(node.value, node.lang, settings)
node.value = results.code
node.lang = results.extension
node.twoslash = results
}