Skip to content

Commit 875b379

Browse files
committed
feat(shiki): allow custom highlighters
1 parent e753dd2 commit 875b379

File tree

6 files changed

+33
-26
lines changed

6 files changed

+33
-26
lines changed

apps/site/mdx/plugins.mjs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ import remarkTableTitles from '../util/table';
1515
// Reference: https://github.com/nodejs/nodejs.org/pull/7896#issuecomment-3009480615
1616
const OPEN_NEXT_CLOUDFLARE = 'Cloudflare' in global;
1717

18+
// Shiki is created out here to avoid an async rehype plugin
19+
const singletonShiki = await rehypeShikiji({
20+
// We use the faster WASM engine on the server instead of the web-optimized version.
21+
//
22+
// Currently we fall back to the JavaScript RegEx engine
23+
// on Cloudflare workers because `shiki/wasm` requires loading via
24+
// `WebAssembly.instantiate` with custom imports, which Cloudflare doesn't support
25+
// for security reasons.
26+
wasm: !OPEN_NEXT_CLOUDFLARE,
27+
28+
// TODO(@avivkeller): Find a way to enable Twoslash w/ a VFS on Cloudflare
29+
twoslash: !OPEN_NEXT_CLOUDFLARE,
30+
});
31+
1832
/**
1933
* Provides all our Rehype Plugins that are used within MDX
2034
*/
@@ -25,21 +39,7 @@ export const rehypePlugins = [
2539
[rehypeAutolinkHeadings, { behavior: 'wrap' }],
2640
// Transforms sequential code elements into code tabs and
2741
// adds our syntax highlighter (Shikiji) to Codeboxes
28-
[
29-
rehypeShikiji,
30-
{
31-
// We use the faster WASM engine on the server instead of the web-optimized version.
32-
//
33-
// Currently we fall back to the JavaScript RegEx engine
34-
// on Cloudflare workers because `shiki/wasm` requires loading via
35-
// `WebAssembly.instantiate` with custom imports, which Cloudflare doesn't support
36-
// for security reasons.
37-
wasm: !OPEN_NEXT_CLOUDFLARE,
38-
39-
// TODO(@avivkeller): Find a way to enable Twoslash w/ a VFS on Cloudflare
40-
twoslash: !OPEN_NEXT_CLOUDFLARE,
41-
},
42-
],
42+
() => singletonShiki,
4343
];
4444

4545
/**

packages/rehype-shiki/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@node-core/rehype-shiki",
3-
"version": "1.2.0",
3+
"version": "1.3.0",
44
"type": "module",
55
"exports": {
66
".": "./src/index.mjs",

packages/rehype-shiki/src/__tests__/plugin.test.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe('rehypeShikiji', async () => {
2525

2626
it('calls visit twice', async () => {
2727
mockVisit.mock.resetCalls();
28-
await rehypeShikiji()(mockTree);
28+
(await rehypeShikiji())(mockTree);
2929
assert.strictEqual(mockVisit.mock.calls.length, 2);
3030
});
3131

@@ -61,7 +61,7 @@ describe('rehypeShikiji', async () => {
6161
}
6262
});
6363

64-
await rehypeShikiji()(mockTree);
64+
(await rehypeShikiji())(mockTree);
6565
assert.ok(parent.children.some(child => child.tagName === 'CodeTabs'));
6666
});
6767
});

packages/rehype-shiki/src/highlighter.mjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,20 @@ export const getLanguageByName = (language, langs) => {
1818
);
1919
};
2020

21+
/**
22+
* @typedef {Object} SyntaxHighlighter
23+
* @property {import('@shikijs/core').HighlighterCoreSync} shiki - The underlying shiki core instance.
24+
* @property {(code: string, lang: string, meta?: Record<string, any>) => string} highlightToHtml - Highlights code and returns inner HTML of the <code> tag.
25+
* @property {(code: string, lang: string, meta?: Record<string, any>) => any} highlightToHast - Highlights code and returns a HAST tree.
26+
*/
27+
2128
/**
2229
* Factory function to create a syntax highlighter instance with utility methods.
2330
*
2431
* @param {Object} params - Parameters for highlighter creation.
2532
* @param {import('@shikijs/core').HighlighterCoreOptions} [params.coreOptions] - Core options for the highlighter.
2633
* @param {import('@shikijs/core').CodeToHastOptions} [params.highlighterOptions] - Additional options for highlighting.
34+
* @returns {SyntaxHighlighter}
2735
*/
2836
const createHighlighter = ({ coreOptions = {}, highlighterOptions = {} }) => {
2937
const options = {

packages/rehype-shiki/src/index.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import createHighlighter, { getLanguageByName } from './highlighter.mjs';
2121
* @property {boolean} [wasm=false] - Enable WebAssembly for the regex engine
2222
* @property {boolean} [twoslash=false] - Enable twoslash
2323
* @property {import('@shikijs/twoslash').TransformerTwoslashIndexOptions} [twoslashOptions] - Twoslash configuration options
24-
* @param {import('@shikijs/core').HighlighterCoreOptions} [coreOptions] - Core options for the highlighter.
25-
* @param {import('@shikijs/core').CodeToHastOptions} [highlighterOptions] - Additional options for highlighting.
24+
* @property {import('@shikijs/core').HighlighterCoreOptions} [coreOptions] - Core options for the highlighter.
25+
* @property {import('@shikijs/core').CodeToHastOptions} [highlighterOptions] - Additional options for highlighting.
2626
*/
2727

2828
/**

packages/rehype-shiki/src/plugin.mjs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,13 @@ function isCodeBlock(node) {
5454
}
5555

5656
/**
57-
* @param {import('./index.mjs').HighlighterOptions} options
57+
* @param {import('./index.mjs').HighlighterOptions & { highlighter: import('./highlighter.mjs').SyntaxHighlighter }} options
5858
*/
59-
export default function rehypeShikiji(options) {
60-
let highlighter;
61-
62-
return async function (tree) {
63-
highlighter ??= await createHighlighter(options);
59+
export default async function rehypeShikiji(options) {
60+
const highlighter =
61+
options?.highlighter ?? (await createHighlighter(options));
6462

63+
return function (tree) {
6564
visit(tree, 'element', (_, index, parent) => {
6665
const languages = [];
6766
const displayNames = [];

0 commit comments

Comments
 (0)