From 0cf0ff796015412b2bdd6bd306519a70b898b036 Mon Sep 17 00:00:00 2001 From: Ave Aristov <100404669+avevotsira@users.noreply.github.com> Date: Tue, 30 Jul 2024 11:38:11 +0700 Subject: [PATCH] refactor: mdx highlight to server side , clean up side effect (#131) * 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 --- package-lock.json | 6 ++- package.json | 2 +- src/components/code-block.tsx | 72 ++++++++++++++--------------------- 3 files changed, 34 insertions(+), 46 deletions(-) diff --git a/package-lock.json b/package-lock.json index 11c9c525..f33be5f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -73,7 +73,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", @@ -104,6 +103,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", @@ -6883,6 +6883,7 @@ "version": "1.12.0", "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.12.0.tgz", "integrity": "sha512-mc1cLbm6UQ8RxLc0dZES7v5rkH+99LxQp/ZvTqV3NLyYsO/fD6JhEflP1H5b2SDq9gI0+0G36AVZWxvounfR9w==", + "dev": true, "dependencies": { "@types/hast": "^3.0.4" } @@ -6891,6 +6892,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, "dependencies": { "@types/unist": "*" } @@ -21799,6 +21801,7 @@ "version": "1.12.0", "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.12.0.tgz", "integrity": "sha512-BuAxWOm5JhRcbSOl7XCei8wGjgJJonnV0oipUupPY58iULxUGyHhW5CF+9FRMuM1pcJ5cGEJGll1LusX6FwpPA==", + "dev": true, "dependencies": { "@shikijs/core": "1.12.0", "@types/hast": "^3.0.4" @@ -21808,6 +21811,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, "dependencies": { "@types/unist": "*" } diff --git a/package.json b/package.json index 161adfcb..f66942ef 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", diff --git a/src/components/code-block.tsx b/src/components/code-block.tsx index 6917fdf8..f9f1c470 100644 --- a/src/components/code-block.tsx +++ b/src/components/code-block.tsx @@ -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[] = [ @@ -22,54 +19,41 @@ interface CodeBlockProps { className?: string; } -const CodeBlockInner: React.FC = ({ children, className }) => { - const [highlightedCode, setHighlightedCode] = useState(""); - 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: return
; -}; +} interface MDXCodeBlockProps { children?: React.ReactNode; className?: string; } -const CodeBlock: React.FC = (props) => { +async function CodeBlock(props: MDXCodeBlockProps) { if (typeof props.children === "string") { return ; } @@ -79,6 +63,6 @@ const CodeBlock: React.FC = (props) => { ); -}; +} export default CodeBlock;