Skip to content

Commit 3e8d2b2

Browse files
wip: custom type acquisition plugin
1 parent 6c7b6d2 commit 3e8d2b2

File tree

3 files changed

+58
-16
lines changed

3 files changed

+58
-16
lines changed

.yarn/install-state.gz

138 Bytes
Binary file not shown.

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

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { transformerTwoslash } from '@shikijs/twoslash';
21
import type { Element, Root } from 'hast';
32
import { toString } from 'hast-util-to-string';
43
import type { MdxJsxFlowElementHast, MdxJsxTextElementHast } from 'mdast-util-mdx-jsx';
@@ -21,10 +20,10 @@ import {
2120
SHIKI_TRANSFORMERS,
2221
} from './shiki-constants.js';
2322
import {
24-
cdnTransformerTwoslash,
25-
cdnTwoslash,
2623
getTwoslashOptions,
2724
parseLineComment,
25+
getCdnTwoslashTransformer,
26+
cdnTwoslash,
2827
} from './twoslash/config.js';
2928
import { getLanguage } from './utils.js';
3029

@@ -101,17 +100,16 @@ export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?]
101100

102101
nodesToProcess.push(
103102
(async () => {
104-
await cdnTwoslash.prepareTypes(toString(node));
105103
if (!DEFAULT_LANGS.includes(lang)) await highlighter.loadLanguage(lang);
106-
traverseNode({ node, index, parent, highlighter, lang, options });
104+
await traverseNode({ node, index, parent, highlighter, lang, options });
107105
})()
108106
);
109107
});
110108
await Promise.all(nodesToProcess);
111109
};
112110
};
113111

114-
function traverseNode({
112+
async function traverseNode({
115113
node,
116114
index,
117115
parent,
@@ -150,6 +148,8 @@ function traverseNode({
150148

151149
code = splitCode.join('\n');
152150

151+
await cdnTwoslash.init();
152+
await cdnTwoslash.prepareTypes(code);
153153
const twoslashOptions = getTwoslashOptions({ linkMap });
154154

155155
const hast = highlighter.codeToHast(code, {
@@ -165,11 +165,7 @@ function traverseNode({
165165
colorReplacements: shikiColorReplacements,
166166
tabindex: false,
167167
tokenizeMaxLineLength: 1000,
168-
transformers: [
169-
...SHIKI_TRANSFORMERS,
170-
transformerTwoslash(twoslashOptions),
171-
cdnTransformerTwoslash(twoslashOptions),
172-
],
168+
transformers: [...SHIKI_TRANSFORMERS, getCdnTwoslashTransformer(twoslashOptions)],
173169
});
174170

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

packages/mdx/src/plugins/rehype/twoslash/config.ts

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,72 @@
11
import {
2-
createTransformerFactory,
32
rendererRich,
3+
createTransformerFactory,
44
type TransformerTwoslashOptions,
55
} from '@shikijs/twoslash';
66
import type { ElementContent } from 'hast';
77
import type { ShikiTransformer } from 'shiki/types';
8-
import { createTwoslashFromCDN, type TwoslashCdnReturn } from 'twoslash-cdn';
9-
import ts from 'typescript';
8+
import { createTwoslasher, type TwoslashInstance } from 'twoslash';
9+
import { createTwoslashFromCDN, TwoslashCdnReturn } from 'twoslash-cdn';
10+
import * as ts from 'typescript';
1011

1112
type TransformerFactory = (options?: TransformerTwoslashOptions) => ShikiTransformer;
1213

1314
const twoslashCompilerOptions: ts.CompilerOptions = {
1415
target: ts.ScriptTarget.ESNext,
1516
lib: ['ESNext', 'DOM', 'esnext', 'dom', 'es2020'],
17+
allowJs: true,
18+
allowSyntheticDefaultImports: true,
19+
allowUnreachableCode: true,
20+
alwaysStrict: false,
1621
};
1722

18-
export const cdnTwoslash: TwoslashCdnReturn = createTwoslashFromCDN({
23+
const fsMap: Map<string, string> = new Map();
24+
const twoslashStorageMap = new Map();
25+
26+
export const cdnTwoslash = createTwoslashFromCDN({
1927
compilerOptions: twoslashCompilerOptions,
28+
fsMap,
29+
storage: {
30+
getItemRaw(key) {
31+
return twoslashStorageMap.get(key);
32+
},
33+
setItemRaw(key, value) {
34+
twoslashStorageMap.set(key, value);
35+
},
36+
},
2037
});
21-
export const cdnTransformerTwoslash: TransformerFactory = createTransformerFactory(
38+
39+
let cachedInstance: TwoslashCdnReturn | undefined;
40+
41+
export const cdnTwoslashTransformer: TransformerFactory = createTransformerFactory(
2242
cdnTwoslash.runSync
2343
);
2444

45+
export function getCdnTwoslashTransformer(options: TransformerTwoslashOptions): ShikiTransformer {
46+
function getInstance() {
47+
cachedInstance ??= createTwoslashFromCDN({
48+
compilerOptions: twoslashCompilerOptions,
49+
fsMap,
50+
storage: {
51+
getItemRaw(key) {
52+
return twoslashStorageMap.get(key);
53+
},
54+
setItemRaw(key, value) {
55+
twoslashStorageMap.set(key, value);
56+
},
57+
},
58+
});
59+
return cachedInstance;
60+
}
61+
62+
return createTransformerFactory(
63+
// lazy load Twoslash instance so it works on serverless platforms
64+
((...args) => getInstance().runSync(...args)) as TwoslashInstance
65+
)({
66+
...options,
67+
});
68+
}
69+
2570
function onTwoslashError(err: unknown, code: string, lang: string) {
2671
console.error(JSON.stringify({ err, code, lang }));
2772
}
@@ -69,6 +114,7 @@ export function getTwoslashOptions(
69114
langs: ['ts', 'typescript', 'js', 'javascript', 'tsx', 'jsx'],
70115
explicitTrigger: /mint-twoslash/,
71116
twoslashOptions: {
117+
tsModule: ts,
72118
compilerOptions: twoslashCompilerOptions,
73119
},
74120
};

0 commit comments

Comments
 (0)