From 8ba62e0800f8cab782837aaf4decc106d2765e5e Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 2 Mar 2025 20:06:30 -0600
Subject: [PATCH] refactor: extract CodeBlock component for blog post code
syntax highlighting
---
.../blog/[slug]/components/CodeBlock.tsx | 55 +++++++++++++++
.../website/app/[locale]/blog/[slug]/page.tsx | 68 +------------------
2 files changed, 58 insertions(+), 65 deletions(-)
create mode 100644 apps/website/app/[locale]/blog/[slug]/components/CodeBlock.tsx
diff --git a/apps/website/app/[locale]/blog/[slug]/components/CodeBlock.tsx b/apps/website/app/[locale]/blog/[slug]/components/CodeBlock.tsx
new file mode 100644
index 0000000..960dad8
--- /dev/null
+++ b/apps/website/app/[locale]/blog/[slug]/components/CodeBlock.tsx
@@ -0,0 +1,55 @@
+import { CopyButton } from "@/components/ui/copy-button";
+import prettier from "prettier";
+import { codeToHtml } from "shiki";
+import type { BundledLanguage } from "shiki/bundle/web";
+
+interface LanguageProps {
+ children: string;
+ lang: BundledLanguage;
+}
+
+const getParserForLanguage = (language: string): string => {
+ const languageMap: { [key: string]: string } = {
+ js: "babel",
+ jsx: "babel",
+ ts: "typescript",
+ tsx: "typescript",
+ json: "json",
+ css: "css",
+ scss: "scss",
+ less: "less",
+ html: "html",
+ xml: "xml",
+ markdown: "markdown",
+ md: "markdown",
+ yaml: "yaml",
+ yml: "yaml",
+ };
+
+ return languageMap[language.toLowerCase()] || "babel";
+};
+
+export async function CodeBlock(props: LanguageProps) {
+ const format = await prettier.format(props.children, {
+ semi: true,
+ singleQuote: true,
+ tabWidth: 2,
+ useTabs: false,
+ printWidth: 120,
+ parser: getParserForLanguage(props.lang),
+ });
+ const out = await codeToHtml(format, {
+ lang: props.lang,
+ theme: "houston",
+ });
+
+ return (
+
+ );
+}
diff --git a/apps/website/app/[locale]/blog/[slug]/page.tsx b/apps/website/app/[locale]/blog/[slug]/page.tsx
index 3fea130..962a302 100644
--- a/apps/website/app/[locale]/blog/[slug]/page.tsx
+++ b/apps/website/app/[locale]/blog/[slug]/page.tsx
@@ -1,28 +1,23 @@
-import { CopyButton } from "@/components/ui/copy-button";
import { getPost, getPosts } from "@/lib/ghost";
import type { Metadata, ResolvingMetadata } from "next";
-import { getTranslations, setRequestLocale } from "next-intl/server";
+import { getTranslations } from "next-intl/server";
import Image from "next/image";
import Link from "next/link";
import { notFound } from "next/navigation";
-import prettier from "prettier";
-import type { DetailedHTMLProps, HTMLAttributes } from "react";
import type React from "react";
import ReactMarkdown from "react-markdown";
import type { Components } from "react-markdown";
import rehypeRaw from "rehype-raw";
import remarkGfm from "remark-gfm";
import remarkToc from "remark-toc";
-import { codeToHtml } from "shiki";
import type { BundledLanguage } from "shiki/bundle/web";
-import slugify from "slugify";
import TurndownService from "turndown";
// @ts-ignore
import * as turndownPluginGfm from "turndown-plugin-gfm";
+import { CodeBlock } from "./components/CodeBlock";
import { H1, H2, H3 } from "./components/Headings";
import { TableOfContents } from "./components/TableOfContents";
import { ZoomableImage } from "./components/ZoomableImage";
-
type Props = {
params: { locale: string; slug: string };
};
@@ -84,62 +79,6 @@ export async function generateStaticParams() {
);
}
-interface CodeProps
- extends DetailedHTMLProps, HTMLElement> {
- inline?: boolean;
- className?: string;
- children?: React.ReactNode;
-}
-interface LanguageProps {
- children: string;
- lang: BundledLanguage;
-}
-
-const getParserForLanguage = (language: string): string => {
- const languageMap: { [key: string]: string } = {
- js: "babel",
- jsx: "babel",
- ts: "typescript",
- tsx: "typescript",
- json: "json",
- css: "css",
- scss: "scss",
- less: "less",
- html: "html",
- xml: "xml",
- markdown: "markdown",
- md: "markdown",
- yaml: "yaml",
- yml: "yaml",
- };
-
- return languageMap[language.toLowerCase()] || "babel";
-};
-
-async function CodeBlock(props: LanguageProps) {
- const format = await prettier.format(props.children, {
- semi: true,
- singleQuote: true,
- tabWidth: 2,
- useTabs: false,
- printWidth: 120,
- parser: getParserForLanguage(props.lang),
- });
- const out = await codeToHtml(format, {
- lang: props.lang,
- theme: "houston",
- });
-
- return (
-
- );
-}
export default async function BlogPostPage({ params }: Props) {
const { locale, slug } = await params;
// setRequestLocale(locale);
@@ -235,9 +174,8 @@ export default async function BlogPostPage({ params }: Props) {
className="object-cover max-w-lg mx-auto rounded-lg border max-lg:w-64 border-border overflow-hidden"
/>
),
- code: ({ inline, className, children, ...props }: CodeProps) => {
+ code: ({ className, children }) => {
const match = /language-(\w+)/.exec(className || "");
-
return (
{children?.toString() || ""}