Skip to content

Commit e25eafb

Browse files
authored
Merge pull request #41 from godwei123/develop
整理内容,优化结构,新增了自定义的markdown插件
2 parents d589656 + 9c6eb72 commit e25eafb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

130 files changed

+4723
-3141
lines changed

docs/.vitepress/config.ts

+47-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import nav from "./nav";
22
import sidebar from "./sidebar";
33
import { defineConfig } from "vitepress";
44
import { resolve } from "node:url";
5-
import markdownItCodeDemo from "./plugins/markdown-it-code-demo";
5+
import { applyPlugins } from "@ruabick/md-demo-plugins";
6+
import markdownItFootnote from "markdown-it-footnote";
7+
import mdContainer, { ContainerOpts } from "markdown-it-container";
8+
import { containerPreview, componentPreview } from "./plugins";
9+
import JSON5 from "json5";
610

711
export default defineConfig({
812
title: "JavaScriptGuide",
@@ -52,7 +56,36 @@ export default defineConfig({
5256
lazyLoading: true,
5357
},
5458
config: (md) => {
55-
md.use(markdownItCodeDemo);
59+
applyPlugins(md);
60+
md.use(markdownItFootnote);
61+
md.use(containerPreview);
62+
md.use(componentPreview);
63+
const defaultRender = md.renderer.rules.fence!;
64+
md.renderer.rules.fence = (tokens, idx, options, env, self) => {
65+
const isInProTableContainer =
66+
tokens[idx - 1].info.trim() === "pro-table" &&
67+
tokens[idx + 1].type === "container_pro-table_close";
68+
69+
if (tokens[idx].type === "fence" && isInProTableContainer) {
70+
return "";
71+
}
72+
return defaultRender(tokens, idx, options, env, self);
73+
};
74+
md.use(mdContainer, "pro-table", {
75+
validate(params) {
76+
return !!params.trim().match(/^pro-table\s*(.*)$/);
77+
},
78+
79+
render: function (tokens, idx) {
80+
const m = tokens[idx].type.trim().match(/pro-table\s*(.*)$/);
81+
if (tokens[idx].nesting === 1 /* means the tag is opening */) {
82+
const content = tokens[idx + 1].type === "fence" ? tokens[idx + 1].content : "";
83+
return "<n-data-table v-bind=" + "'" + JSON.stringify(JSON5.parse(content)) + "'" + ">";
84+
} else {
85+
return "</n-data-table>";
86+
}
87+
},
88+
} as ContainerOpts);
5689
},
5790
},
5891
vite: {
@@ -61,5 +94,17 @@ export default defineConfig({
6194
"~": resolve(__dirname, "../packages"),
6295
},
6396
},
97+
build: {
98+
rollupOptions: {
99+
output: {
100+
assetFileNames: (assetInfo) => {
101+
if (assetInfo?.name === "webWorker.js") {
102+
return assetInfo.name;
103+
}
104+
return `assets/[name]_[hash].[ext]`;
105+
},
106+
},
107+
},
108+
},
64109
},
65110
});

docs/.vitepress/nav/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const nav: DefaultTheme.NavItem[] = [
99
text: "CSS",
1010
link: "/css/introduction",
1111
},
12-
// link: "/basic/html/introduction",
1312
{
1413
text: "network",
1514
link: "/basic/browser/introduction",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/* eslint-disable no-param-reassign */
2+
import MarkdownIt from "markdown-it";
3+
import Token from "markdown-it/lib/token";
4+
import { resolve, dirname } from "path";
5+
import { readFileSync } from "fs";
6+
import {
7+
composeComponentName,
8+
injectComponentImportScript,
9+
isCheckingRelativePath,
10+
transformHighlightCode,
11+
} from "./utils";
12+
13+
const titleRegex = /title=['"](.*?)['"]/;
14+
const pathRegex = /path=['"](.*?)['"]/;
15+
const descriptionRegex = /description=['"](.*?)['"]/;
16+
const onlyRenderRegex = /onlyRender=['"](.*?)['"]/;
17+
18+
export interface DefaultProps {
19+
path: string;
20+
title: string;
21+
description: string;
22+
onlyRender?: boolean;
23+
}
24+
25+
/**
26+
* 编译预览组件
27+
* @param md
28+
* @param token
29+
* @param env
30+
* @returns
31+
*/
32+
export const transformPreview = (md: MarkdownIt, token: Token, env: any) => {
33+
const componentProps: DefaultProps = {
34+
path: "",
35+
title: "默认标题",
36+
description: "描述内容",
37+
onlyRender: false,
38+
};
39+
40+
// 获取Props相关参数
41+
const titleValue = token.content.match(titleRegex);
42+
const pathRegexValue = token.content.match(pathRegex);
43+
const descriptionRegexValue = token.content.match(descriptionRegex);
44+
const onlyRenderRegexValue = token.content.match(onlyRenderRegex);
45+
46+
if (!pathRegexValue)
47+
throw new Error("@vitepress-demo-preview/plugin: path is a required parameter");
48+
// eslint-disable-next-line prefer-destructuring
49+
componentProps.path = isCheckingRelativePath(pathRegexValue[1]);
50+
componentProps.title = titleValue ? titleValue[1] : "";
51+
componentProps.description = descriptionRegexValue ? descriptionRegexValue[1] : "";
52+
componentProps.onlyRender = onlyRenderRegexValue ? onlyRenderRegexValue[1] === "true" : false;
53+
54+
// 组件绝对路径
55+
const componentPath = resolve(dirname(env.path), componentProps.path || ".");
56+
57+
// 组件名
58+
const componentName = composeComponentName(componentProps.path);
59+
// 后缀名
60+
const suffixName = componentPath.substring(componentPath.lastIndexOf(".") + 1);
61+
62+
// 注入组件导入语句
63+
injectComponentImportScript(env, componentProps.path, componentName);
64+
65+
// 组件源码
66+
const componentSourceCode = readFileSync(componentPath, {
67+
encoding: "utf-8",
68+
});
69+
// 源码代码块(经过处理)
70+
const compileHighlightCode = transformHighlightCode(md, componentSourceCode, suffixName);
71+
72+
const code = encodeURI(componentSourceCode);
73+
const showCode = encodeURIComponent(compileHighlightCode);
74+
75+
const sourceCode = `<demo-preview :onlyRender="${componentProps.onlyRender}" title="${componentProps.title}" description="${componentProps.description}" code="${code}" showCode="${showCode}" suffixName="${suffixName}" absolutePath="${componentPath}" relativePath="${componentProps.path}">
76+
<${componentName}></${componentName}>
77+
</demo-preview>`;
78+
79+
return sourceCode;
80+
};
+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/* eslint-disable no-param-reassign */
2+
/* eslint-disable prefer-destructuring */
3+
import MarkdownIt from "markdown-it";
4+
import Token from "markdown-it/lib/token";
5+
import Renderer from "markdown-it/lib/renderer";
6+
import { resolve, dirname } from "path";
7+
import { readFileSync } from "fs";
8+
import markdownItContainer from "markdown-it-container";
9+
import {
10+
composeComponentName,
11+
injectComponentImportScript,
12+
isCheckContainerPreview,
13+
isCheckingRelativePath,
14+
transformHighlightCode,
15+
} from "./utils";
16+
import { it } from "node:test";
17+
18+
const validateContainerRE = /^preview.*$/;
19+
const parseContainerParamRE = /^preview\s?(.*?)(?:\s\|\|\s(.*?))(?:\s\|\|\s(.*?))?$/;
20+
21+
/**
22+
* 自定义容器的注册
23+
* @param md
24+
*/
25+
export const containerDirectiveMount = (md: MarkdownIt) => {
26+
md.use(markdownItContainer, "preview", {
27+
marker: ":",
28+
validate: (params: any) => {
29+
const validateContainer = params.trim().match(validateContainerRE);
30+
if (validateContainer && validateContainer.length !== 0) return true;
31+
return false;
32+
},
33+
});
34+
};
35+
36+
/**
37+
* 解析自定义日期的Tag
38+
* @param md
39+
*/
40+
export const parseContainerTag = (md: MarkdownIt) => {
41+
// 开始标签 :::preview
42+
const defaultContainerPreviewOpenRender = md.renderer.rules.container_preview_open!;
43+
md.renderer.rules.container_preview_open = (
44+
tokens: Token[],
45+
idx: number,
46+
options: MarkdownIt.Options,
47+
env: any,
48+
self: Renderer
49+
) => {
50+
const token = tokens[idx];
51+
52+
const res = tokens[idx + 2].children
53+
.filter((item) => {
54+
return item.type === "text";
55+
})
56+
.reduce((pre, cur) => {
57+
const [k, v] = cur.content.split("=") || [];
58+
pre[k] = v;
59+
return pre;
60+
}, {});
61+
// 组件的相对路径
62+
tokens[idx + 2].children.forEach((item) => {
63+
item.attrSet("display", "none");
64+
});
65+
66+
const componentRelativePath = isCheckingRelativePath(res["demo-preview"]);
67+
68+
// 组件绝对路径
69+
const componentPath = resolve(dirname(env.path), componentRelativePath || ".");
70+
71+
// 后缀名
72+
const suffixName = componentPath.substring(componentPath.lastIndexOf(".") + 1);
73+
// 组件源码
74+
const componentSourceCode = readFileSync(componentPath, {
75+
encoding: "utf-8",
76+
});
77+
// 源码代码块(经过处理)
78+
const compileHighlightCode = transformHighlightCode(md, componentSourceCode, suffixName);
79+
80+
const code = encodeURI(componentSourceCode);
81+
const showCode = encodeURIComponent(compileHighlightCode);
82+
83+
const onlyRender = res["onlyRender"] || false;
84+
const title = res["title"] || "";
85+
const description = res["description"] || "";
86+
87+
if (token.nesting === 1)
88+
return `<demo-preview :onlyRender="${onlyRender}" title="${title}" description="${description}" code="${code}" showCode="${showCode}" suffixName="${suffixName}" absolutePath="${componentPath}" relativePath="${componentRelativePath}">\n`;
89+
return defaultContainerPreviewOpenRender(tokens, idx, options, env, self);
90+
};
91+
// 闭合标签 :::
92+
const defaultContainerPreviewCloseRender = md.renderer.rules.container_preview_close!;
93+
md.renderer.rules.container_preview_close = (
94+
tokens: Token[],
95+
idx: number,
96+
options: MarkdownIt.Options,
97+
env: any,
98+
self: Renderer
99+
) => {
100+
const token = tokens[idx];
101+
102+
if (token.nesting === -1) return `</demo-preview>\n`;
103+
return defaultContainerPreviewCloseRender(tokens, idx, options, env, self);
104+
};
105+
};
106+
107+
/**
108+
* 解析自定义容器
109+
* @param md
110+
*/
111+
export const parseContainer = (md: MarkdownIt) => {
112+
const defaultHtmlTextRender = md.renderer.rules.text!;
113+
md.renderer.rules.text = (
114+
tokens: Token[],
115+
idx: number,
116+
options: MarkdownIt.Options,
117+
env: any,
118+
self: Renderer
119+
) => {
120+
const token = tokens[idx];
121+
if (token.type === "text" && token.content.match(isCheckContainerPreview)) {
122+
const componentRelativePath = isCheckingRelativePath(
123+
token.content.match(isCheckContainerPreview)![1]
124+
);
125+
const componentName = composeComponentName(componentRelativePath);
126+
injectComponentImportScript(env, componentRelativePath, componentName);
127+
return `<${componentName}></${componentName}>`;
128+
} else if (
129+
token.type === "text" &&
130+
token.attrGet("display") === "none" &&
131+
token.content.match(/^(title|description|onlyRender)=(.+)$/)
132+
) {
133+
return "";
134+
}
135+
return defaultHtmlTextRender(tokens, idx, options, env, self);
136+
};
137+
};

docs/.vitepress/plugins/index.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import MarkdownIt from "markdown-it";
2+
import Renderer from "markdown-it/lib/renderer";
3+
import Token from "markdown-it/lib/token";
4+
import { isCheckPreviewCom1, isCheckPreviewCom2 } from "./utils";
5+
import { transformPreview } from "./componentPreview";
6+
import { containerDirectiveMount, parseContainer, parseContainerTag } from "./containerPreview";
7+
8+
export const componentPreview = (md: MarkdownIt) => {
9+
const defaultHtmlInlineRender = md.renderer.rules.html_inline!;
10+
md.renderer.rules.html_inline = (
11+
tokens: Token[],
12+
idx: number,
13+
options: MarkdownIt.Options,
14+
env: any,
15+
self: Renderer
16+
) => {
17+
const token = tokens[idx];
18+
if (isCheckPreviewCom1.test(token.content) || isCheckPreviewCom2.test(token.content)) {
19+
return transformPreview(md, token, env);
20+
}
21+
return defaultHtmlInlineRender(tokens, idx, options, env, self);
22+
};
23+
};
24+
25+
export const containerPreview = (md: MarkdownIt) => {
26+
containerDirectiveMount(md);
27+
parseContainerTag(md);
28+
parseContainer(md);
29+
};

docs/.vitepress/plugins/markdown-it-code-demo.ts

-34
This file was deleted.

0 commit comments

Comments
 (0)