mirror of
https://github.com/Dokploy/website
synced 2025-06-26 18:16:01 +00:00
feat: enhance code block rendering with dynamic formatting and highlighting
This commit is contained in:
@@ -1,55 +1,85 @@
|
|||||||
import { CopyButton } from "@/components/ui/copy-button";
|
"use client";
|
||||||
import prettier from "prettier";
|
|
||||||
import { codeToHtml } from "shiki";
|
|
||||||
import type { BundledLanguage } from "shiki/bundle/web";
|
|
||||||
|
|
||||||
interface LanguageProps {
|
import { CopyButton } from "@/components/ui/copy-button";
|
||||||
children: string;
|
import * as babel from "prettier/plugins/babel";
|
||||||
|
import * as estree from "prettier/plugins/estree";
|
||||||
|
import * as prettier from "prettier/standalone";
|
||||||
|
import { type JSX, useLayoutEffect, useState } from "react";
|
||||||
|
import type { BundledLanguage } from "shiki/bundle/web";
|
||||||
|
import { highlight } from "./shared";
|
||||||
|
|
||||||
|
interface CodeBlockProps {
|
||||||
|
code: string;
|
||||||
lang: BundledLanguage;
|
lang: BundledLanguage;
|
||||||
|
initial?: JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getParserForLanguage = (language: string): string => {
|
async function formatCode(code: string, lang: string) {
|
||||||
const languageMap: { [key: string]: string } = {
|
try {
|
||||||
js: "babel",
|
// Configuración básica para JavaScript/TypeScript
|
||||||
jsx: "babel",
|
const plugins = [babel, estree];
|
||||||
ts: "typescript",
|
console.log("Formatting with plugins:", plugins);
|
||||||
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";
|
const formatted = await prettier.format(code, {
|
||||||
};
|
parser: "babel-ts",
|
||||||
|
plugins,
|
||||||
|
semi: true,
|
||||||
|
singleQuote: true,
|
||||||
|
tabWidth: 2,
|
||||||
|
useTabs: false,
|
||||||
|
printWidth: 120,
|
||||||
|
});
|
||||||
|
|
||||||
export async function CodeBlock(props: LanguageProps) {
|
console.log("Formatted code:", formatted);
|
||||||
const format = await prettier.format(props.children, {
|
return formatted;
|
||||||
semi: true,
|
} catch (error) {
|
||||||
singleQuote: true,
|
console.error("Error formatting code:", error);
|
||||||
tabWidth: 2,
|
return code; // Retorna el código original si hay error
|
||||||
useTabs: false,
|
}
|
||||||
printWidth: 120,
|
}
|
||||||
parser: getParserForLanguage(props.lang),
|
|
||||||
});
|
export function CodeBlock({ code, lang, initial }: CodeBlockProps) {
|
||||||
const out = await codeToHtml(format, {
|
const [nodes, setNodes] = useState<JSX.Element | undefined>(initial);
|
||||||
lang: props.lang,
|
const [formattedCode, setFormattedCode] = useState(code);
|
||||||
theme: "houston",
|
|
||||||
});
|
useLayoutEffect(() => {
|
||||||
|
async function formatAndHighlight() {
|
||||||
|
try {
|
||||||
|
console.log("Original code:", code);
|
||||||
|
const formatted = await formatCode(code, lang);
|
||||||
|
setFormattedCode(formatted);
|
||||||
|
|
||||||
|
// Then highlight the formatted code
|
||||||
|
const highlighted = await highlight(formatted, lang);
|
||||||
|
setNodes(highlighted);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error in formatAndHighlight:", error);
|
||||||
|
// If formatting fails, try to highlight the original code
|
||||||
|
const highlighted = await highlight(code, lang);
|
||||||
|
setNodes(highlighted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void formatAndHighlight();
|
||||||
|
}, [code, lang]);
|
||||||
|
|
||||||
|
if (!nodes) {
|
||||||
|
return (
|
||||||
|
<div className="group relative">
|
||||||
|
<div className="text-sm p-4 rounded-lg bg-[#18191F] overflow-auto animate-pulse">
|
||||||
|
<div className="h-4 bg-gray-700 rounded w-3/4 mb-2" />
|
||||||
|
<div className="h-4 bg-gray-700 rounded w-1/2" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="group relative">
|
<div className="group relative">
|
||||||
<CopyButton text={format} />
|
<CopyButton text={formattedCode} />
|
||||||
<div
|
<div className="text-sm p-4 rounded-lg bg-[#18191F] overflow-auto">
|
||||||
dangerouslySetInnerHTML={{ __html: out }}
|
{nodes}
|
||||||
className="text-sm p-4 rounded-lg bg-[#18191F] overflow-auto"
|
</div>
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
19
apps/website/app/[locale]/blog/[slug]/components/shared.ts
Normal file
19
apps/website/app/[locale]/blog/[slug]/components/shared.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { toJsxRuntime } from "hast-util-to-jsx-runtime";
|
||||||
|
import type { JSX } from "react";
|
||||||
|
import { Fragment } from "react";
|
||||||
|
import { jsx, jsxs } from "react/jsx-runtime";
|
||||||
|
import type { BundledLanguage } from "shiki/bundle/web";
|
||||||
|
import { codeToHast } from "shiki/bundle/web";
|
||||||
|
|
||||||
|
export async function highlight(code: string, lang: BundledLanguage) {
|
||||||
|
const out = await codeToHast(code, {
|
||||||
|
lang,
|
||||||
|
theme: "houston",
|
||||||
|
});
|
||||||
|
|
||||||
|
return toJsxRuntime(out, {
|
||||||
|
Fragment,
|
||||||
|
jsx,
|
||||||
|
jsxs,
|
||||||
|
}) as JSX.Element;
|
||||||
|
}
|
||||||
@@ -177,9 +177,10 @@ export default async function BlogPostPage({ params }: Props) {
|
|||||||
code: ({ className, children }) => {
|
code: ({ className, children }) => {
|
||||||
const match = /language-(\w+)/.exec(className || "");
|
const match = /language-(\w+)/.exec(className || "");
|
||||||
return (
|
return (
|
||||||
<CodeBlock lang={match ? (match[1] as BundledLanguage) : "ts"}>
|
<CodeBlock
|
||||||
{children?.toString() || ""}
|
lang={match ? (match[1] as BundledLanguage) : "ts"}
|
||||||
</CodeBlock>
|
code={children?.toString() || ""}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,12 +6,13 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start -p 3001",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"browserslist": "defaults, not ie <= 11",
|
"browserslist": "defaults, not ie <= 11",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"hast-util-to-jsx-runtime": "2.3.5",
|
||||||
"@headlessui/react": "^2.2.0",
|
"@headlessui/react": "^2.2.0",
|
||||||
"@headlessui/tailwindcss": "^0.2.0",
|
"@headlessui/tailwindcss": "^0.2.0",
|
||||||
"@radix-ui/react-accordion": "^1.2.1",
|
"@radix-ui/react-accordion": "^1.2.1",
|
||||||
|
|||||||
30
pnpm-lock.yaml
generated
30
pnpm-lock.yaml
generated
@@ -151,6 +151,9 @@ importers:
|
|||||||
framer-motion:
|
framer-motion:
|
||||||
specifier: ^11.3.19
|
specifier: ^11.3.19
|
||||||
version: 11.3.19(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
version: 11.3.19(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||||
|
hast-util-to-jsx-runtime:
|
||||||
|
specifier: 2.3.5
|
||||||
|
version: 2.3.5
|
||||||
lucide-react:
|
lucide-react:
|
||||||
specifier: 0.364.0
|
specifier: 0.364.0
|
||||||
version: 0.364.0(react@18.2.0)
|
version: 0.364.0(react@18.2.0)
|
||||||
@@ -2458,6 +2461,9 @@ packages:
|
|||||||
hast-util-to-jsx-runtime@2.3.2:
|
hast-util-to-jsx-runtime@2.3.2:
|
||||||
resolution: {integrity: sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==}
|
resolution: {integrity: sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==}
|
||||||
|
|
||||||
|
hast-util-to-jsx-runtime@2.3.5:
|
||||||
|
resolution: {integrity: sha512-gHD+HoFxOMmmXLuq9f2dZDMQHVcplCVpMfBNRpJsF03yyLZvJGzsFORe8orVuYDX9k2w0VH0uF8oryFd1whqKQ==}
|
||||||
|
|
||||||
hast-util-to-parse5@8.0.0:
|
hast-util-to-parse5@8.0.0:
|
||||||
resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==}
|
resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==}
|
||||||
|
|
||||||
@@ -4454,7 +4460,7 @@ snapshots:
|
|||||||
estree-util-is-identifier-name: 3.0.0
|
estree-util-is-identifier-name: 3.0.0
|
||||||
estree-util-scope: 1.0.0
|
estree-util-scope: 1.0.0
|
||||||
estree-walker: 3.0.3
|
estree-walker: 3.0.3
|
||||||
hast-util-to-jsx-runtime: 2.3.2
|
hast-util-to-jsx-runtime: 2.3.5
|
||||||
markdown-extensions: 2.0.0
|
markdown-extensions: 2.0.0
|
||||||
recma-build-jsx: 1.0.0
|
recma-build-jsx: 1.0.0
|
||||||
recma-jsx: 1.0.0(acorn@8.12.1)
|
recma-jsx: 1.0.0(acorn@8.12.1)
|
||||||
@@ -6331,6 +6337,26 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
hast-util-to-jsx-runtime@2.3.5:
|
||||||
|
dependencies:
|
||||||
|
'@types/estree': 1.0.5
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
'@types/unist': 3.0.2
|
||||||
|
comma-separated-tokens: 2.0.3
|
||||||
|
devlop: 1.1.0
|
||||||
|
estree-util-is-identifier-name: 3.0.0
|
||||||
|
hast-util-whitespace: 3.0.0
|
||||||
|
mdast-util-mdx-expression: 2.0.0
|
||||||
|
mdast-util-mdx-jsx: 3.1.2
|
||||||
|
mdast-util-mdxjs-esm: 2.0.1
|
||||||
|
property-information: 7.0.0
|
||||||
|
space-separated-tokens: 2.0.2
|
||||||
|
style-to-object: 1.0.6
|
||||||
|
unist-util-position: 5.0.0
|
||||||
|
vfile-message: 4.0.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
hast-util-to-parse5@8.0.0:
|
hast-util-to-parse5@8.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
@@ -7319,7 +7345,7 @@ snapshots:
|
|||||||
'@types/mdast': 4.0.4
|
'@types/mdast': 4.0.4
|
||||||
'@types/react': 18.3.5
|
'@types/react': 18.3.5
|
||||||
devlop: 1.1.0
|
devlop: 1.1.0
|
||||||
hast-util-to-jsx-runtime: 2.3.2
|
hast-util-to-jsx-runtime: 2.3.5
|
||||||
html-url-attributes: 3.0.1
|
html-url-attributes: 3.0.1
|
||||||
mdast-util-to-hast: 13.2.0
|
mdast-util-to-hast: 13.2.0
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
|
|||||||
Reference in New Issue
Block a user