Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .yarn/install-state.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion examples/app-router/next.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
serverExternalPackages: ['@shikijs/twoslash'],
serverExternalPackages: ['@shikijs/twoslash', '@typescript/vfs', 'typescript'],
};

module.exports = nextConfig;
4 changes: 3 additions & 1 deletion examples/pages-router/next.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
const nextConfig = {
serverExternalPackages: ['@shikijs/twoslash', '@typescript/vfs', 'typescript'],
};

module.exports = nextConfig;
6 changes: 3 additions & 3 deletions examples/pages-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
},
"dependencies": {
"@mintlify/mdx": "workspace:^",
"next": "14.0.4",
"react": "^18",
"react-dom": "^18"
"next": "15.4.2",
"react": "18.3.1",
"react-dom": "18.3.1"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.10",
Expand Down
19 changes: 8 additions & 11 deletions packages/mdx/src/plugins/rehype/rehypeSyntaxHighlighting.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { transformerTwoslash } from '@shikijs/twoslash';
import type { Element, Root } from 'hast';
import { toString } from 'hast-util-to-string';
import type { MdxJsxFlowElementHast, MdxJsxTextElementHast } from 'mdast-util-mdx-jsx';
Expand All @@ -21,10 +20,10 @@ import {
SHIKI_TRANSFORMERS,
} from './shiki-constants.js';
import {
cdnTransformerTwoslash,
cdnTwoslash,
getTwoslashOptions,
parseLineComment,
getCdnTwoslashTransformer,
cdnTwoslash,
} from './twoslash/config.js';
import { getLanguage } from './utils.js';

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

nodesToProcess.push(
(async () => {
await cdnTwoslash.prepareTypes(toString(node));
if (!DEFAULT_LANGS.includes(lang)) await highlighter.loadLanguage(lang);
traverseNode({ node, index, parent, highlighter, lang, options });
await traverseNode({ node, index, parent, highlighter, lang, options });
})()
);
});
await Promise.all(nodesToProcess);
};
};

function traverseNode({
async function traverseNode({
node,
index,
parent,
Expand Down Expand Up @@ -150,7 +148,10 @@ function traverseNode({

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

await cdnTwoslash.init();
await cdnTwoslash.prepareTypes(code);
const twoslashOptions = getTwoslashOptions({ linkMap });
const cdnTwoslashTransformer = getCdnTwoslashTransformer(twoslashOptions);

const hast = highlighter.codeToHast(code, {
lang: lang ?? DEFAULT_LANG,
Expand All @@ -165,11 +166,7 @@ function traverseNode({
colorReplacements: shikiColorReplacements,
tabindex: false,
tokenizeMaxLineLength: 1000,
transformers: [
...SHIKI_TRANSFORMERS,
transformerTwoslash(twoslashOptions),
cdnTransformerTwoslash(twoslashOptions),
],
transformers: [...SHIKI_TRANSFORMERS, cdnTwoslashTransformer],
});

const codeElement = hast.children[0] as Element;
Expand Down
65 changes: 60 additions & 5 deletions packages/mdx/src/plugins/rehype/twoslash/config.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,81 @@
import {
createTransformerFactory,
rendererRich,
createTransformerFactory,
type TransformerTwoslashOptions,
} from '@shikijs/twoslash';
import type { ElementContent } from 'hast';
import type { ShikiTransformer } from 'shiki/types';
import { createTwoslashFromCDN, type TwoslashCdnReturn } from 'twoslash-cdn';
import ts from 'typescript';
import type { TwoslashInstance } from 'twoslash';
import { createTwoslashFromCDN } from 'twoslash-cdn';
import * as ts from 'typescript';

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

const twoslashCompilerOptions: ts.CompilerOptions = {
target: ts.ScriptTarget.ESNext,
lib: ['ESNext', 'DOM', 'esnext', 'dom', 'es2020'],
allowJs: true,
allowSyntheticDefaultImports: true,
allowUnreachableCode: true,
alwaysStrict: false,
};

export const cdnTwoslash: TwoslashCdnReturn = createTwoslashFromCDN({
const fsMap: Map<string, string> = new Map();
const twoslashStorageMap = new Map();

export const cdnTwoslash = createTwoslashFromCDN({
compilerOptions: twoslashCompilerOptions,
fsMap,
fetcher(input, init) {
console.log(`[GLOBAL__FETCHER] Fetching ${input}`);
return fetch(input, init);
},
storage: {
getItemRaw(key) {
console.log(`[GLOBAL__STORAGE] Getting ${key}`);
return twoslashStorageMap.get(key);
},
setItemRaw(key, value) {
console.log(`[GLOBAL__STORAGE] Setting ${key}`);
twoslashStorageMap.set(key, value);
},
},
});
export const cdnTransformerTwoslash: TransformerFactory = createTransformerFactory(

export const cdnTwoslashTransformer: TransformerFactory = createTransformerFactory(
cdnTwoslash.runSync
);

export function getCdnTwoslashTransformer(options: TransformerTwoslashOptions): ShikiTransformer {
function getInstance() {
return createTwoslashFromCDN({
compilerOptions: twoslashCompilerOptions,
fetcher(input, init) {
console.log(`[FETCHER] Fetching ${input}`);
return fetch(input, init);
},
fsMap,
storage: {
getItemRaw(key) {
console.log(`[STORAGE] Getting ${key}`);
return twoslashStorageMap.get(key);
},
setItemRaw(key, value) {
console.log(`[STORAGE] Setting ${key}`);
twoslashStorageMap.set(key, value);
},
},
});
}

return createTransformerFactory(
// lazy load Twoslash instance so it works on serverless platforms
((...args) => getInstance().runSync(...args)) as TwoslashInstance
)({
...options,
});
}

function onTwoslashError(err: unknown, code: string, lang: string) {
console.error(JSON.stringify({ err, code, lang }));
}
Expand Down Expand Up @@ -69,6 +123,7 @@ export function getTwoslashOptions(
langs: ['ts', 'typescript', 'js', 'javascript', 'tsx', 'jsx'],
explicitTrigger: /mint-twoslash/,
twoslashOptions: {
tsModule: ts,
compilerOptions: twoslashCompilerOptions,
},
};
Expand Down
Loading