Skip to content

Commit dea2f31

Browse files
committed
feat: 🚀 mdH1 插件针对 MD 文件特殊格式的兼容
1 parent 29275ab commit dea2f31

File tree

4 files changed

+59
-23
lines changed

4 files changed

+59
-23
lines changed

README.md

-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ pnpm demo:localesRoot dev
4242
## TODO
4343

4444
- 归档页添加 commit 图标风格,如:`http://niubin.site/archive.html`
45-
- 文章页基础信息支持配置到 h1 下
4645
- vitepress-plugin-permalink 启动后给每个 md 自动生成永久链接
4746
- 样式打包
4847
- 主题使用文档编写

plugins/vitepress-plugin-md-h1/src/index.ts

+56-9
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,71 @@ import { readFileSync } from "node:fs";
33
import { basename } from "node:path";
44
import matter from "gray-matter";
55

6+
const specialPrefix = `return (_openBlock(), _createElementBlock("div", null, [`;
7+
const createStaticVNodeTag = `_createStaticVNode("`;
8+
69
export default function VitePluginVitePressMdH1(): Plugin & { name: string } {
710
return {
811
name: "vite-plugin-vitepress-md-h1",
912
transform: (code: string, id: string) => {
1013
if (!id.endsWith(".md")) return code;
1114

1215
const content = readFileSync(id, "utf-8");
13-
1416
const { data = {}, content: mdContent } = matter(content, {});
17+
// 如果已经存在一级标题,则不需要往下处理
18+
if (mdContent.trimStart().split(/\r?\n/)[0].startsWith("# ")) return code;
19+
20+
// 获取文章标题,如果为目录,则默认为文件夹名。如果为 md 文件,则尝试获取 frontmatter 中的 title,否则为文件名为标题
1521
const title = data.title || getMdFileTitle(basename(id)) || "";
22+
const titleId = formatSpecialStr(title);
23+
24+
// 将 " 替换为 \",因为 " 会导致页面解析失败
25+
const finalTitle = title.replace(/"+/g, '\\"');
26+
// 提前截取 code,防止 code 太长导致 replace 性能边差
27+
const newCode = code.split(createStaticVNodeTag);
28+
29+
if (newCode.length === 2 && newCode[0].includes(specialPrefix)) {
30+
// 第一个 replace 先将 _cache 的下标加 1,第二个 replace 把 h1 标题加到 _cache[0] 里
31+
return newCode[0]
32+
.replace(/_cache\[(\d+)\]/g, (_, p1) => {
33+
const newIndex = parseInt(p1, 10) + 1;
34+
return `_cache[${newIndex}]`;
35+
})
36+
.replace(
37+
specialPrefix,
38+
`${specialPrefix}
39+
_cache[0] || (_cache[0] = _createElementVNode("h1", {
40+
id: "${titleId}",
41+
tabindex: "-1"
42+
}, [
43+
_createTextVNode("${finalTitle} "),
44+
_createElementVNode("a", {
45+
class: "header-anchor",
46+
href: "#${titleId}",
47+
"aria-label": "Permalink to \\"${finalTitle}\\""
48+
}, "​")
49+
], -1 /* HOISTED */)),
50+
`
51+
)
52+
.concat(createStaticVNodeTag + newCode[1]); // 最后拼接上截取的代码
53+
}
1654

17-
return mdContent.trimStart().split(/\r?\n/)[0].startsWith("# ")
18-
? code
19-
: code.replace(
20-
`_createStaticVNode("`,
21-
`_createStaticVNode("<h1 id=\\"${title}\\" tabindex=\\"-1\\">${title} <a class=\\"header-anchor\\" href=\\"#${title}\\" aria-label=\\"Permalink to &quot;${title}&quot;\\">​</a></h1>`
22-
);
55+
return code.replace(
56+
createStaticVNodeTag,
57+
`${createStaticVNodeTag}<h1 id=\\"${titleId}\\" tabindex=\\"-1\\">${finalTitle} <a class=\\"header-anchor\\" href=\\"#${titleId}\\" aria-label=\\"Permalink to &quot;${finalTitle}&quot;\\">​</a></h1>`
58+
);
2359
},
2460
};
2561
}
2662

2763
/**
28-
* 解析文件名
64+
* 获取实际的文件名
2965
*
3066
* @param filename 文件名
3167
*/
3268
export const getMdFileTitle = (filename: string) => {
33-
// 文章标题,如果为目录,则默认为文件夹名。如果为 md 文件,则尝试获取 frontmatter 中的 title,否则为文件名为标题
3469
let title = "";
70+
// 如果文件名带序号,如【1.xx.md】,则取 xx
3571
const fileNameArr = filename.split(".");
3672

3773
if (fileNameArr.length === 2) title = fileNameArr[0];
@@ -44,3 +80,14 @@ export const getMdFileTitle = (filename: string) => {
4480

4581
return title;
4682
};
83+
84+
/**
85+
* 格式化字符串
86+
* @param str 字符串
87+
*/
88+
export const formatSpecialStr = (str: string): string => {
89+
return str
90+
.toLowerCase()
91+
.replace(/[\s+]/g, "") // 清除空格
92+
.replace(/['"`*]+/g, ""); // 去除反引号、星号等 Markdown 语法字符
93+
};

pnpm-lock.yaml

+2-12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vitepress-theme-teeker/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"vitepress-plugin-catalogue": "latest",
4545
"vitepress-plugin-doc-analysis": "latest",
4646
"vitepress-plugin-file-content-loader": "latest",
47-
"vitepress-plugin-md-h1": "latest",
47+
"vitepress-plugin-md-h1": "workspace:*",
4848
"vitepress-plugin-permalink": "latest",
4949
"vitepress-plugin-sidebar-resolve": "latest"
5050
},

0 commit comments

Comments
 (0)