Twoslash: Improve experience with Shiki Twoslash

This commit is contained in:
Fuma Nama
2026-01-05 23:18:47 +08:00
parent edd37c8187
commit dc2ffacc4a
3 changed files with 43 additions and 21 deletions
@@ -36,6 +36,10 @@ export default defineConfig({
dark: 'github-dark',
},
transformers: [...(rehypeCodeDefaultOptions.transformers ?? []), transformerTwoslash()],
// important: Shiki doesn't support lazy loading languages for codeblocks in Twoslash popups
// make sure to define them first (e.g. the common ones)
langs: ['js', 'jsx', 'ts', 'tsx']
},
},
});
@@ -57,6 +61,7 @@ import type { MDXComponents } from 'mdx/types';
export function getMDXComponents(components?: MDXComponents): MDXComponents {
return {
...defaultComponents,
// [!code ++]
...Twoslash,
...components,
};
@@ -71,6 +76,20 @@ console.log('Hello World');
```
````
### Cache
Optionally, you can enable filesystem cache with `typesCache` option:
```ts twoslash title="source.config.ts"
import { transformerTwoslash } from 'fumadocs-twoslash';
import { createFileSystemTypesCache } from 'fumadocs-twoslash/cache-fs';
transformerTwoslash({
typesCache: createFileSystemTypesCache(),
});
```
### Example
Learn more about [Twoslash notations](https://twoslash.netlify.app/refs/notations).
@@ -106,16 +125,3 @@ const a = '123';
a = 132;
```
## Cache
You can enable filesystem cache with `typesCache` option:
```ts twoslash title="source.config.ts"
import { transformerTwoslash } from 'fumadocs-twoslash';
import { createFileSystemTypesCache } from 'fumadocs-twoslash/cache-fs';
transformerTwoslash({
typesCache: createFileSystemTypesCache(),
});
```
+2 -1
View File
@@ -132,7 +132,8 @@ export function rehypeCode(
'themes' in options
? (Object.values(options.themes).filter(Boolean) as BuiltinTheme[])
: [options.theme],
langs: options.langs ?? (options.lazy ? ['ts', 'tsx'] : Object.keys(bundledLanguages)),
langs:
options.langs ?? (options.lazy ? ['js', 'jsx', 'ts', 'tsx'] : Object.keys(bundledLanguages)),
langAlias: options.langAlias,
});
+22 -7
View File
@@ -3,10 +3,11 @@ import type { Code } from 'mdast';
import { fromMarkdown } from 'mdast-util-from-markdown';
import { gfmFromMarkdown } from 'mdast-util-gfm';
import { defaultHandlers, toHast } from 'mdast-util-to-hast';
import type {
ShikiTransformer,
ShikiTransformerContext,
ShikiTransformerContextCommon,
import {
ShikiError,
type ShikiTransformer,
type ShikiTransformerContext,
type ShikiTransformerContextCommon,
} from 'shiki';
import {
createTransformerFactory,
@@ -127,7 +128,9 @@ function renderMarkdown(this: ShikiTransformerContextCommon, md: string): Elemen
toHast(mdast, {
handlers: {
code: (state, node: Code) => {
if (node.lang) {
if (!node.lang) return defaultHandlers.code(state, node);
try {
return this.codeToHast(node.value, {
...this.options,
transformers: [],
@@ -136,9 +139,21 @@ function renderMarkdown(this: ShikiTransformerContextCommon, md: string): Elemen
},
lang: node.lang,
}).children[0] as Element;
}
} catch (e) {
if (e instanceof Error) {
console.error(
`[fumadocs-twoslash] encountered an error when highlighting codeblock in a Twoslash popup: ${e.message}`,
);
}
return defaultHandlers.code(state, node);
if (e instanceof ShikiError) {
console.error(
`[fumadocs-twoslash] if language "${node.lang}" is not found, you may have enabled lazy loading which is not compatible with Twoslash, please consider to define "${node.lang}" in the "langs" option first.`,
);
}
return defaultHandlers.code(state, node);
}
},
},
}) as Element