本站基于 Next.js RSC 的代码高亮实现方法

本站是基于 Next.js 15 RSC 的,使用 next-mdx-remote 编译 Markdown 文件, rehype-pretty-code 插件实现代码高亮,先看一下实现的效果。

实现效果

JS 代码高亮

js

function myFunction() {
  const iterable = [10, 20, 30];
  for (const value of iterable) {
    console.log(value);
  }
}

行高亮,2~3 行和第 5 行,语法 js {2-3,5}

js

function myFunction() {
  const iterable = [10, 20, 30];
  for (const value of iterable) {
    console.log(value);
  }
}

关键字高亮,语法 js /Nshen/

js

const name = "Nshen";
 
function myFunction() {
  const iterable = [10, 20, 30];
  for (const value of iterable) {
    console.log(value);
  }
}

高亮组,语法 js /age/#1 /name/#1 /setAge/#2 /setName/#2 /40/#3 /"Nshen"/#3

js

const [age, setAge] = useState(40);
const [name, setName] = useState("Nshen");

高亮 Inline Code [1, 2, 3] {a:'aaa', str:'bbb', num: 123, b: true}

语法为

js

`[1, 2, 3] {a:'aaa', str:'bbb', num: 123, b: true}{:js}`

显示行号和代表标题 js showLineNumbers title="以下代码显示了行号"

以下代码显示了行号

js

function myFunction() {
  const iterable = [10, 20, 30];
  for (const value of iterable) {
    console.log(value);
  }
}
 
 
function myFunction() {
  const iterable = [10, 20, 30];
  for (const value of iterable) {
    console.log(value);
  }
}

实现方法

核心代码在 24 行,当编译 mdx 文件的时候,在 rehypePlugins 中传入 rehype-pretty-code 插件

typescript

import { compileMDX } from "next-mdx-remote/rsc";
import rehypePrettyCode from "rehype-pretty-code";
 
// rehypePrettyCode options
const options = {
  // Use one of Shiki's packaged themes
  // https://shiki.style/themes#themes
  theme: "one-dark-pro",
  // Keep the background or use a custom background color?
  keepBackground: true,
  defaultLang: "plaintext",
};
 
export async function compile(source: string): Promise<{
  content: JSX.Element;
  frontmatter: Record<string, unknown>;
}> {
  const result = await compileMDX({
    source,
    options: {
      parseFrontmatter: true,
      mdxOptions: {
        remarkPlugins: [remarkGfm],
        rehypePlugins: [[rehypePrettyCode, options]],
        format: "mdx",
      },
    },
    components: mdxComponents,
  });
 
  if (!result) {
    throw new Error("MDX result undefined");
  }
  return result;
}

传入插件后观察编译后的结构中多了很多类似 data-rehype-pretty-code-figure 的属性,这个叫做Data attributes

CSS 有专门的选择器可以针对生成的标签添加样式。由于本站是基于 Next.js 和 Tailwind 的,所以可以直接在 global.css 这个文件中继续添加样式。

css

/* 删除多余的 ` 符号 */
[data-rehype-pretty-code-figure] code::before{
  content: none;
}
 
[data-rehype-pretty-code-figure] code::after{
  content: none
}
 
figure[data-rehype-pretty-code-figure] pre {
  @apply py-4 overflow-x-auto rounded-lg leading-7
}
 
/* 代码块边距 */
pre [data-line] {
  @apply px-4
}
 
/* title */
[data-rehype-pretty-code-title]  {
  @apply ml-1 
}
 
/* Group 高亮 */
 
[data-highlighted-chars] {
  @apply border-b-2 rounded p-1 font-bold border-red-700 text-red-300 bg-red-950
}
 
[data-chars-id="1"] {
  @apply border-b-2 rounded p-1 font-bold border-red-600 text-red-300 bg-red-900
}
 
[data-chars-id="2"] {
  @apply border-b-2 rounded p-1 font-bold border-green-600 text-green-300 bg-green-900
}
 
[data-chars-id="3"] {
  @apply border-b-2 rounded p-1 font-bold border-cyan-600 text-cyan-300 bg-cyan-900
}
 
[data-chars-id="4"] {
  @apply border-b-2 rounded p-1 font-bold border-blue-600 text-blue-300 bg-blue-900
}
 
/* 行号实现 */
[data-line-numbers] {
  counter-reset: line;
}
 
[data-line-numbers] [data-line]::before {
  counter-increment: line;
  content: counter(line);
  display: inline-block;
  width: 1rem;
  margin-right: 1rem;
  text-align: right;
  @apply text-sm text-gray-600;
}
 
/* inline */
span[data-rehype-pretty-code-figure] code {
 @apply bg-gray-200 text-black text-sm font-normal px-2 py-1 mx-1 rounded !important
}
span[data-highlighted-line] {
  @apply bg-slate-300 bg-opacity-10;
}
 

我不是 CSS 专家,以上是我调试很久得到的效果,尽量做到了美观简洁,也欢迎分享你的样式。

全文完,如有帮助请支持我