Skip to content

Commit

Permalink
chore: synchronous shiki
Browse files Browse the repository at this point in the history
  • Loading branch information
ovflowd committed Dec 22, 2024
1 parent 052e8f6 commit 07c920f
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 27 deletions.
8 changes: 3 additions & 5 deletions apps/site/components/JSX/CodeBox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,23 @@ import type { FC, PropsWithChildren } from 'react';

import MDXCodeBox from '@/components/MDX/CodeBox';
import { reactRuntime } from '@/next.mdx.compiler.mjs';
import { highlightToHast, shikiPromise } from '@/util/getHighlighter';
import { highlightToHast } from '@/util/getHighlighter';

type CodeBoxProps = {
language: string;
showCopyButton?: boolean;
className?: string;
};

const codeToHast = highlightToHast(await shikiPromise);

const CodeBox: FC<PropsWithChildren<CodeBoxProps>> = ({
children,
language,
showCopyButton,
className,
}) => {
const out = codeToHast(dedent(children as string), language);
const highlighted = highlightToHast(dedent(String(children)), language);

return toJsxRuntime(out, {
return toJsxRuntime(highlighted, {
...reactRuntime,
components: {
pre: ({ children }) => (
Expand Down
11 changes: 3 additions & 8 deletions apps/site/next.mdx.shiki.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import classNames from 'classnames';
import { toString } from 'hast-util-to-string';
import { SKIP, visit } from 'unist-util-visit';

import { shikiPromise, highlightToHast } from './util/getHighlighter';
import { highlightToHast } from './util/getHighlighter';

// This is what Remark will use as prefix within a <pre> className
// to attribute the current language of the <pre> element
Expand Down Expand Up @@ -54,12 +54,7 @@ function isCodeBlock(node) {
}

export default function rehypeShikiji() {
return async function (tree) {
// We do a top-level await, since the Unist-tree visitor
// is synchronous, and it makes more sense to do a top-level
// await, rather than an await inside the visitor function
const memoizedShiki = highlightToHast(await shikiPromise);

return function (tree) {
visit(tree, 'element', (_, index, parent) => {
const languages = [];
const displayNames = [];
Expand Down Expand Up @@ -174,7 +169,7 @@ export default function rehypeShikiji() {
const languageId = codeLanguage.slice(languagePrefix.length);

// Parses the <pre> contents and returns a HAST tree with the highlighted code
const { children } = memoizedShiki(preElementContents, languageId);
const { children } = highlightToHast(preElementContents, languageId);

// Adds the original language back to the <pre> element
children[0].properties.class = classNames(
Expand Down
25 changes: 11 additions & 14 deletions apps/site/util/getHighlighter.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
import { getSingletonHighlighterCore } from '@shikijs/core';
import type { HighlighterCore } from '@shikijs/core';
import { createHighlighterCoreSync } from '@shikijs/core';
import { createJavaScriptRegexEngine } from '@shikijs/engine-javascript';

import { LANGUAGES, DEFAULT_THEME } from '@/shiki.config.mjs';

// This creates a memoized minimal Shikiji Syntax Highlighter
export const shikiPromise = getSingletonHighlighterCore({
export const shiki = createHighlighterCoreSync({
themes: [DEFAULT_THEME],
langs: LANGUAGES,
// Let's use Shiki's new Experimental JavaScript-based regex engine!
engine: createJavaScriptRegexEngine(),
});

export const highlightToHtml =
(shiki: HighlighterCore) => (code: string, language: string) =>
shiki
.codeToHtml(code, { lang: language, theme: DEFAULT_THEME })
// Shiki will always return the Highlighted code encapsulated in a <pre> and <code> tag
// since our own CodeBox component handles the <code> tag, we just want to extract
// the inner highlighted code to the CodeBox
.match(/<code>(.+?)<\/code>/s)![1];
export const highlightToHtml = (code: string, language: string) =>
shiki
.codeToHtml(code, { lang: language, theme: DEFAULT_THEME })
// Shiki will always return the Highlighted code encapsulated in a <pre> and <code> tag
// since our own CodeBox component handles the <code> tag, we just want to extract
// the inner highlighted code to the CodeBox
.match(/<code>(.+?)<\/code>/s)![1];

export const highlightToHast =
(shiki: HighlighterCore) => (code: string, language: string) =>
shiki.codeToHast(code, { lang: language, theme: DEFAULT_THEME });
export const highlightToHast = (code: string, language: string) =>
shiki.codeToHast(code, { lang: language, theme: DEFAULT_THEME });

0 comments on commit 07c920f

Please sign in to comment.