Skip to content

Commit 3043826

Browse files
jelllidimaMachinaDimitri POSTOLOV
authoredFeb 2, 2024
Add shikiji twoslash (#2612)
* chore: upgrade deps 作者: Jellli <dt@live.it> * feat: add twoslash transformer * feat: update plugins to fit rehype-pretty-code's updates * fix: prevent the crash from not receiving the style prop * feat: handle when classname is a array * feat: fix darkmode support * feat: update snapshot * docs: add twoslash support document * update * prettier * prettier * more * more * more * more * aa * Update .changeset/rich-apricots-bake.md --------- Co-authored-by: Dimitri POSTOLOV <dmytropostolov@gmail.com> Co-authored-by: Dimitri POSTOLOV <en3m@ya.ru>
1 parent 9f55bd1 commit 3043826

File tree

7 files changed

+379
-40
lines changed

7 files changed

+379
-40
lines changed
 

‎.changeset/rich-apricots-bake.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'nextra-theme-blog': minor
3+
'nextra-theme-docs': minor
4+
'nextra': minor
5+
---
6+
7+
add shikiji twoslash
8+
9+
Demo feature: https://nextra-v2-na3obnhub-shuding1.vercel.app/docs/guide/twoslash-support
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Twoslash Support
2+
3+
Twoslash provides an inline type hove inside the code block.
4+
5+
## Basic usage
6+
7+
You can enable twoslash to your code blocks by adding a `twoslash` metadata:
8+
9+
````md copy=false filename="Markdown"
10+
```ts twoslash
11+
// @errors: 2540
12+
interface Todo {
13+
title: string
14+
}
15+
16+
const todo: Readonly<Todo> = {
17+
title: 'Delete inactive users'.toUpperCase()
18+
// ^?
19+
}
20+
21+
todo.title = 'Hello'
22+
23+
Number.parseInt('123', 10)
24+
// ^|
25+
```
26+
````
27+
28+
Renders:
29+
30+
```ts twoslash
31+
// @errors: 2540
32+
interface Todo {
33+
title: string
34+
}
35+
36+
const todo: Readonly<Todo> = {
37+
title: 'Delete inactive users'.toUpperCase()
38+
// ^?
39+
}
40+
41+
todo.title = 'Hello'
42+
43+
Number.parseInt('123', 10)
44+
// ^|
45+
```
46+
47+
## Custom log message
48+
49+
You can add log message to your code by adding:
50+
51+
- `@log: <message>` Custom log message
52+
- `@error: <message>` Custom error message
53+
- `@warn: <message>` Custom warn message
54+
- `@annotate: <message>` Custom annotate message
55+
56+
```ts twoslash
57+
// @log: Custom log message
58+
const a = 1
59+
// @error: Custom error message
60+
const b = 1
61+
// @warn: Custom warning message
62+
const c = 1
63+
// @annotate: Custom annotation message
64+
```

‎packages/nextra/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@
142142
"remark-reading-time": "^2.0.1",
143143
"remark-smartypants": "^2.1.0",
144144
"shikiji": "^0.10.2",
145+
"shikiji-twoslash": "^0.10.2",
145146
"slash": "^5.1.0",
146147
"title": "^3.5.3",
147148
"unist-util-remove": "^4.0.0",

‎packages/nextra/src/server/compile.ts

+11
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import remarkGfm from 'remark-gfm'
1313
import remarkMath from 'remark-math'
1414
import remarkReadingTime from 'remark-reading-time'
1515
import remarkSmartypants from 'remark-smartypants'
16+
import { rendererRich, transformerTwoslash } from 'shikiji-twoslash'
1617
import type { Pluggable, Plugin } from 'unified'
1718
import type {
1819
FrontMatter,
@@ -276,6 +277,16 @@ export async function compileMdx(
276277
rehypePrettyCode,
277278
{
278279
...DEFAULT_REHYPE_PRETTY_CODE_OPTIONS,
280+
// TODO: For some reason I get Error: Cannot find module 'path' in remote content,
281+
// disable twoslash temporarily
282+
transformers: isRemoteContent
283+
? []
284+
: [
285+
transformerTwoslash({
286+
renderer: rendererRich(),
287+
explicitTrigger: true
288+
})
289+
],
279290
...rehypePrettyCodeOptions
280291
}
281292
] as any,

‎packages/nextra/src/server/rehype-plugins/rehype.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Element } from 'hast'
22
import type { Options as RehypePrettyCodeOptions } from 'rehype-pretty-code'
3+
import { bundledLanguages, getHighlighter } from 'shikiji'
34
import type { Plugin } from 'unified'
45
import { visit } from 'unist-util-visit'
56

@@ -22,11 +23,18 @@ export const DEFAULT_REHYPE_PRETTY_CODE_OPTIONS: RehypePrettyCodeOptions = {
2223
}
2324
delete node.properties['data-line']
2425
},
25-
filterMetaString: meta => meta.replace(CODE_BLOCK_FILENAME_REGEX, ''),
2626
theme: {
2727
light: 'github-light',
2828
dark: 'github-dark'
29-
}
29+
},
30+
getHighlighter(opts) {
31+
return getHighlighter({
32+
...opts,
33+
// Without `getHighlighter` option ```mdx lang is not highlighted...
34+
langs: Object.keys(bundledLanguages)
35+
})
36+
},
37+
filterMetaString: meta => meta.replace(CODE_BLOCK_FILENAME_REGEX, '')
3038
}
3139

3240
export const rehypeParseCodeMeta: Plugin<
@@ -63,7 +71,8 @@ export const rehypeAttachCodeMeta: Plugin<[], any> = () => ast => {
6371
const isRehypePrettyCode =
6472
'data-rehype-pretty-code-figure' in node.properties
6573
if (!isRehypePrettyCode) return
66-
// remove <div data-rehype-pretty-code-fragment /> element that wraps <pre /> element
74+
75+
// remove <figure data-rehype-pretty-code-figure /> element that wraps <pre /> element
6776
// because we'll wrap with our own <div />
6877
const preEl: PreElement = Object.assign(node, node.children[0])
6978
delete preEl.properties['data-theme']
@@ -93,7 +102,7 @@ export const rehypeAttachCodeMeta: Plugin<[], any> = () => ast => {
93102
...Object.entries(node.properties).map(([name, value]) => ({
94103
type: 'mdxJsxAttribute',
95104
name,
96-
value
105+
value: Array.isArray(value) ? value.join(' ') : value
97106
}))
98107
)
99108
}

0 commit comments

Comments
 (0)
Please sign in to comment.