Skip to content

Commit

Permalink
refactor: mdx highlight to server side , clean up side effect (#131)
Browse files Browse the repository at this point in the history
* feat: add syntax highlight to mdx code block

* fix: add more styles

* refactor: move mdx code block serverside , shiki to dev dependency

* refactor: clean up mdx codeblock side effect

---------

Co-authored-by: Visal .In <[email protected]>
  • Loading branch information
avevotsira and invisal authored Jul 30, 2024
1 parent 68a2648 commit 0cf0ff7
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 46 deletions.
6 changes: 5 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@
"react": "^18.2.01",
"react-dom": "^18.2.0",
"react-resizable-panels": "^1.0.9",
"shiki": "^1.12.0",
"sonner": "^1.4.41",
"sql-formatter": "^15.3.2",
"sql-query-identifier": "^2.6.0",
Expand Down Expand Up @@ -123,6 +122,7 @@
"jest-environment-jsdom": "^29.7.0",
"postcss": "^8.4.38",
"prettier": "^3.2.5",
"shiki": "^1.12.0",
"showdown": "^2.1.0",
"tailwindcss": "^3.4.3",
"ts-node": "^10.9.2",
Expand Down
72 changes: 28 additions & 44 deletions src/components/code-block.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
"use client";

import { useState, useEffect } from "react";
import { createHighlighter, type Highlighter } from "shiki";
import { createHighlighter } from "shiki";
import type { BundledLanguage } from "shiki/bundle/full";

const ALLOWED_LANGS: BundledLanguage[] = [
Expand All @@ -22,54 +19,41 @@ interface CodeBlockProps {
className?: string;
}

const CodeBlockInner: React.FC<CodeBlockProps> = ({ children, className }) => {
const [highlightedCode, setHighlightedCode] = useState<string>("");
const language = className ? className.replace(/language-/, "") : "text";

useEffect(() => {
let highlighter: Highlighter;

const highlight = async () => {
highlighter = await createHighlighter({
themes: ["dracula", "snazzy-light"],
langs: ALLOWED_LANGS,
});

// Validate the language

const validLang = ALLOWED_LANGS.includes(language as BundledLanguage)
? (language as BundledLanguage)
: "text";

const highlighted = highlighter.codeToHtml(children, {
lang: validLang,
themes: {
dark: "dracula",
light: "snazzy-light",
},
});
setHighlightedCode(highlighted);
};

highlight();
async function getHighlighter() {
return await createHighlighter({
themes: ["dracula", "snazzy-light"],
langs: ALLOWED_LANGS,
});
}

return () => {
if (highlighter) {
highlighter.dispose();
}
};
}, [children, language]);
function getValidLang(className?: string): BundledLanguage | "text" {
const language = className ? className.replace(/language-/, "") : "text";
return ALLOWED_LANGS.includes(language as BundledLanguage)
? (language as BundledLanguage)
: "text";
}

// biome-ignore lint/security/noDangerouslySetInnerHtml: We are using controlled html
async function CodeBlockInner({ children, className }: CodeBlockProps) {
const validLang = getValidLang(className);
const highlighter = await getHighlighter();
const highlightedCode = highlighter.codeToHtml(children, {
lang: validLang,
themes: {
dark: "dracula",
light: "snazzy-light",
},
});

// biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation>
return <div dangerouslySetInnerHTML={{ __html: highlightedCode }} />;
};
}

interface MDXCodeBlockProps {
children?: React.ReactNode;
className?: string;
}

const CodeBlock: React.FC<MDXCodeBlockProps> = (props) => {
async function CodeBlock(props: MDXCodeBlockProps) {
if (typeof props.children === "string") {
return <CodeBlockInner {...(props as CodeBlockProps)} />;
}
Expand All @@ -79,6 +63,6 @@ const CodeBlock: React.FC<MDXCodeBlockProps> = (props) => {
<code {...props} />
</pre>
);
};
}

export default CodeBlock;

0 comments on commit 0cf0ff7

Please sign in to comment.