Skip to content

Commit d478484

Browse files
Add Twoslash support via transformer
1 parent c27e462 commit d478484

File tree

4 files changed

+125
-9
lines changed

4 files changed

+125
-9
lines changed

.yarn/install-state.gz

13.8 KB
Binary file not shown.

packages/mdx/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
},
7070
"dependencies": {
7171
"@shikijs/transformers": "^3.11.0",
72+
"@shikijs/twoslash": "^3.12.2",
7273
"hast-util-to-string": "^3.0.1",
7374
"mdast-util-mdx-jsx": "^3.2.0",
7475
"next-mdx-remote-client": "^1.0.3",
@@ -77,6 +78,7 @@
7778
"remark-math": "^6.0.0",
7879
"remark-smartypants": "^3.0.2",
7980
"shiki": "^3.11.0",
81+
"twoslash-cdn": "^0.3.4",
8082
"unified": "^11.0.0",
8183
"unist-util-visit": "^5.0.0"
8284
}

packages/mdx/src/plugins/rehype/rehypeSyntaxHighlighting.ts

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1+
import {
2+
createTransformerFactory,
3+
rendererRich,
4+
transformerTwoslash,
5+
type TransformerTwoslashOptions,
6+
} from '@shikijs/twoslash';
17
import type { Element, Root } from 'hast';
28
import { toString } from 'hast-util-to-string';
39
import type { MdxJsxFlowElementHast, MdxJsxTextElementHast } from 'mdast-util-mdx-jsx';
410
import { createHighlighter, type Highlighter } from 'shiki';
11+
import { createTwoslashFromCDN } from 'twoslash-cdn';
12+
import ts from 'typescript';
513
import type { Plugin } from 'unified';
614
import { visit } from 'unist-util-visit';
715

@@ -21,6 +29,28 @@ import {
2129
} from './shiki-constants.js';
2230
import { getLanguage } from './utils.js';
2331

32+
const twoslashCompilerOptions = {
33+
target: ts.ScriptTarget.ESNext,
34+
lib: ['ESNext', 'DOM', 'esnext', 'dom', 'es2020'],
35+
};
36+
37+
const twoslashOptions: TransformerTwoslashOptions = {
38+
onTwoslashError(err, code, lang) {
39+
console.error(JSON.stringify({ err, code, lang }));
40+
},
41+
onShikiError(err, code, lang) {
42+
console.error(JSON.stringify({ err, code, lang }));
43+
},
44+
renderer: rendererRich(),
45+
langs: ['ts', 'typescript', 'js', 'javascript', 'tsx', 'jsx'],
46+
explicitTrigger: true,
47+
twoslashOptions: { compilerOptions: twoslashCompilerOptions },
48+
};
49+
50+
const cdnTwoslash = createTwoslashFromCDN({ compilerOptions: twoslashCompilerOptions });
51+
52+
const cdnTransformerTwoslash = createTransformerFactory(cdnTwoslash.runSync);
53+
2454
export type RehypeSyntaxHighlightingOptions = {
2555
theme?: ShikiTheme;
2656
themes?: Record<'light' | 'dark', ShikiTheme>;
@@ -90,15 +120,17 @@ export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?]
90120
getLanguage(child, DEFAULT_LANG_ALIASES) ??
91121
DEFAULT_LANG;
92122

93-
if (!DEFAULT_LANGS.includes(lang)) {
94-
asyncNodesToProcess.push(
95-
highlighter.loadLanguage(lang).then(() => {
123+
asyncNodesToProcess.push(
124+
(async () => {
125+
await cdnTwoslash.prepareTypes(toString(node));
126+
if (!DEFAULT_LANGS.includes(lang)) {
127+
await highlighter.loadLanguage(lang);
96128
traverseNode(node, index, parent, highlighter, lang, options);
97-
})
98-
);
99-
} else {
100-
traverseNode(node, index, parent, highlighter, lang, options);
101-
}
129+
} else {
130+
traverseNode(node, index, parent, highlighter, lang, options);
131+
}
132+
})()
133+
);
102134
});
103135
await Promise.all(asyncNodesToProcess);
104136
};
@@ -127,7 +159,11 @@ const traverseNode = (
127159
colorReplacements: shikiColorReplacements,
128160
tabindex: false,
129161
tokenizeMaxLineLength: 1000,
130-
transformers: SHIKI_TRANSFORMERS,
162+
transformers: [
163+
...SHIKI_TRANSFORMERS,
164+
transformerTwoslash(twoslashOptions),
165+
cdnTransformerTwoslash(twoslashOptions),
166+
],
131167
});
132168

133169
const codeElement = hast.children[0] as Element;

yarn.lock

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ __metadata:
434434
"@mintlify/prettier-config": "npm:^1.0.1"
435435
"@mintlify/ts-config": "npm:^2.0.2"
436436
"@shikijs/transformers": "npm:^3.11.0"
437+
"@shikijs/twoslash": "npm:^3.12.2"
437438
"@trivago/prettier-plugin-sort-imports": "npm:^4.3.0"
438439
"@tsconfig/recommended": "npm:1.x"
439440
"@types/hast": "npm:^3.0.4"
@@ -458,6 +459,7 @@ __metadata:
458459
remark-smartypants: "npm:^3.0.2"
459460
rimraf: "npm:^5.0.1"
460461
shiki: "npm:^3.11.0"
462+
twoslash-cdn: "npm:^0.3.4"
461463
typescript: "npm:^5.7.2"
462464
unified: "npm:^11.0.0"
463465
unist-util-visit: "npm:^5.0.0"
@@ -655,6 +657,18 @@ __metadata:
655657
languageName: node
656658
linkType: hard
657659

660+
"@shikijs/core@npm:3.12.2":
661+
version: 3.12.2
662+
resolution: "@shikijs/core@npm:3.12.2"
663+
dependencies:
664+
"@shikijs/types": "npm:3.12.2"
665+
"@shikijs/vscode-textmate": "npm:^10.0.2"
666+
"@types/hast": "npm:^3.0.4"
667+
hast-util-to-html: "npm:^9.0.5"
668+
checksum: 10c0/3a05bc0a316a8a0170996ffe5dfc76021d20a459ed2c1aa5e659468a1a65af1cf0f69415535ecbd54387f4c61caf5d4b4e88b8fde21caf6af649d4178da52092
669+
languageName: node
670+
linkType: hard
671+
658672
"@shikijs/engine-javascript@npm:3.11.0":
659673
version: 3.11.0
660674
resolution: "@shikijs/engine-javascript@npm:3.11.0"
@@ -704,6 +718,19 @@ __metadata:
704718
languageName: node
705719
linkType: hard
706720

721+
"@shikijs/twoslash@npm:^3.12.2":
722+
version: 3.12.2
723+
resolution: "@shikijs/twoslash@npm:3.12.2"
724+
dependencies:
725+
"@shikijs/core": "npm:3.12.2"
726+
"@shikijs/types": "npm:3.12.2"
727+
twoslash: "npm:^0.3.4"
728+
peerDependencies:
729+
typescript: ">=5.5.0"
730+
checksum: 10c0/810808514c8e562585afb8e835cf2a214f26e9bf609c17bfb7faf0bae7b059d545384da715a12fa623f906388ff14db39984c3aad6a53ee191475b158b0f7651
731+
languageName: node
732+
linkType: hard
733+
707734
"@shikijs/types@npm:3.11.0":
708735
version: 3.11.0
709736
resolution: "@shikijs/types@npm:3.11.0"
@@ -714,6 +741,16 @@ __metadata:
714741
languageName: node
715742
linkType: hard
716743

744+
"@shikijs/types@npm:3.12.2":
745+
version: 3.12.2
746+
resolution: "@shikijs/types@npm:3.12.2"
747+
dependencies:
748+
"@shikijs/vscode-textmate": "npm:^10.0.2"
749+
"@types/hast": "npm:^3.0.4"
750+
checksum: 10c0/74622ac69a84f0d7b66f6f9253bdaa0fee69b7bc97d5f85e12b2a70a9d77d2b04fdbccf65fcd9460449340a214594cb945fee8b3d2c091175e58e8cdf2cb2920
751+
languageName: node
752+
linkType: hard
753+
717754
"@shikijs/vscode-textmate@npm:^10.0.2":
718755
version: 10.0.2
719756
resolution: "@shikijs/vscode-textmate@npm:10.0.2"
@@ -1143,6 +1180,17 @@ __metadata:
11431180
languageName: node
11441181
linkType: hard
11451182

1183+
"@typescript/vfs@npm:^1.6.1":
1184+
version: 1.6.1
1185+
resolution: "@typescript/vfs@npm:1.6.1"
1186+
dependencies:
1187+
debug: "npm:^4.1.1"
1188+
peerDependencies:
1189+
typescript: "*"
1190+
checksum: 10c0/3878686aff4bf26813dad9242aa8e01c5c9734f4d37f31035f93e9c8b850f15ec6a4480f04cf3a3a1cbf78a4e796ae1be5d6c54f7f7c91556eafee913a8d0da4
1191+
languageName: node
1192+
linkType: hard
1193+
11461194
"@ungap/structured-clone@npm:^1.0.0":
11471195
version: 1.2.1
11481196
resolution: "@ungap/structured-clone@npm:1.2.1"
@@ -6745,6 +6793,36 @@ __metadata:
67456793
languageName: node
67466794
linkType: hard
67476795

6796+
"twoslash-cdn@npm:^0.3.4":
6797+
version: 0.3.4
6798+
resolution: "twoslash-cdn@npm:0.3.4"
6799+
dependencies:
6800+
twoslash: "npm:0.3.4"
6801+
peerDependencies:
6802+
typescript: ^5.5.0
6803+
checksum: 10c0/dc3bd3d54319f80a7c34057847522e1d5dffaec08c1d1195ea0f16aef73325576c9c3bf60eff10ce7233594c5ab980f68c3c82742d97d4c4b450ac160364a323
6804+
languageName: node
6805+
linkType: hard
6806+
6807+
"twoslash-protocol@npm:0.3.4":
6808+
version: 0.3.4
6809+
resolution: "twoslash-protocol@npm:0.3.4"
6810+
checksum: 10c0/4062a0dced5e8bca37db0b759cbb08f13b00d13572cd676686d139d436ec324d9b1ca959cf0929683b987b090c62ad52f02c8948919e89f8ab76e5502eb1e309
6811+
languageName: node
6812+
linkType: hard
6813+
6814+
"twoslash@npm:0.3.4, twoslash@npm:^0.3.4":
6815+
version: 0.3.4
6816+
resolution: "twoslash@npm:0.3.4"
6817+
dependencies:
6818+
"@typescript/vfs": "npm:^1.6.1"
6819+
twoslash-protocol: "npm:0.3.4"
6820+
peerDependencies:
6821+
typescript: ^5.5.0
6822+
checksum: 10c0/1220393b48dce0e0f20c7c9ce2801864ee99dc052b1f78d884a5eb737052164511d26c8ca7652170475a3bff28afdf96a136132d074f8e35dee5d79d6208162c
6823+
languageName: node
6824+
linkType: hard
6825+
67486826
"type-check@npm:^0.4.0, type-check@npm:~0.4.0":
67496827
version: 0.4.0
67506828
resolution: "type-check@npm:0.4.0"

0 commit comments

Comments
 (0)