createMarkdown 解析
markdown-it
markdown-it 辅助 markdown 的库,和 es 类似,将 md 文件的语言转化成 html 的语言,比如
# hello-world or
md.render('# markdown-it rulezz!');
# result
# <h1>hello world</h1>
这个逻辑和 es/tslint 一样,都有各自的规则,只不过 es/tslint 是将 es/ts 语言转化成 js,根据规则可以生产对应的 html 文件
下面主要分析一下 vuepress 内置的一些 markdown 插件和一些个人理解,但实现代码我粗略看了下,过于复杂,就不细分析了,只是说下做了哪些功能和一些实现精髓,具体功能可以用测试代码来反推实现。
vuepress 中 markdown 的主源码引入的 markdown-it 插件库
代码块
md.render(input)
# 输出
exports[`highlight should highlight code 1`] = `
<pre v-pre class="language-js">
<code>
<span class="token keyword">new</span>
<span class="token class-name">Vue</span>
<span class="token punctuation">(</span>
<span class="token punctuation">)</span>
</code>
</pre>
`
代码高亮 这里用的是 prismjs
, markdown-it 官方推荐的是 highlight.js
测试代码反推实现
用测试反推实现是另一种理解 api 的方法,而且非常好用,你不用去了解代码,你会直接知道是怎么实现的,具体我们可以参考sorrycc 的分享 通过写测试用例学习前端知识
我们这里可以直接参考相关用例结合测试一起看代码高亮的实现
识别 vue 标签插件
重写官方 html_block 方法
这个插件是重写了官方的 html_block 插件,不同的是以下 2 行代码
const HTML_SEQUENCES = [
[/^<(script|pre|style)(?=(\s|>|$))/i, /<\/(script|pre|style)>/i, true],
[/^<!--/, /-->/, true],
[/^<\?/, /\?>/, true],
[/^<![A-Z]/, />/, true],
[/^<!\[CDATA\[/, /\]\]>/, true],
// PascalCase Components
[/^<[A-Z]/, />/, true],
// custom elements with hyphens
[/^<\w+\-/, />/, true],
[
new RegExp("^</?(" + blockNames.join("|") + ")(?=(\\s|/?>|$))", "i"),
/^$/,
true
],
[new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + "\\s*$"), /^$/, false]
];
加入了PascalCase Components 帕斯卡写法,和 vue 标准规范中带连接符的控件;
代码行高亮
对以后的 highlight 中对指定行进行高亮,官方章节
js {1-2,4-5}
const app = new Vue({
render,
router
})
app.$mount('#app')
# 输出
exports[`highlightLines highlight multiple lines 1`] = `
<div class="highlight-lines">
<div class="highlighted"> </div>
<div class="highlighted"> </div><br>
<div class="highlighted"> </div>
<div class="highlighted"> </div><br><br>
</div>const app = new Vue({
render,
router
})
app.$mount('#app')
`;
这边有 3 个 br,4 个 div,div 就是高亮的代码 br 就是空出来的代码;其实会多套一层 html 元素
预加载容器
这块官方并没有文档,对于不同语言的文件外层会包一层 <div class="language-${type} extra-class"></div>
可以用来区分样式的不同
导入代码片段功能
从文件中读取代码片段,支持本地文件上传,官方使用说明
具体实现其实就是导入文件中的内容在进行操作,因为 markdown-it 不是支持异步的,但 fs.readFileSync 居然读文件是有同步的方法。
链接解析
链接解析官方文档
外联的链接都带上 <OutboundLink />
内联的文件关联单元测试表
const internalLinkAsserts = {
// START abosolute path usage
"/": "/",
"/foo/": "/foo/",
"/foo/#hash": "/foo/#hash",
"/foo/two.md": "/foo/two.html",
"/foo/two.html": "/foo/two.html",
// END abosolute path usage
// START relative path usage
"README.md": "./",
"./README.md": "./",
"index.md": "./",
"./index.md": "./",
"one.md": "./one.html",
"./one.md": "./one.html",
"foo/README.md": "./foo/",
"./foo/README.md": "./foo/",
"foo/README.md#hash": "./foo/#hash",
"./foo/README.md#hash": "./foo/#hash",
"../README.md": "./../",
"../README.md#hash": "./../#hash",
"../foo.md": "./../foo.html",
"../foo.md#hash": "./../foo.html#hash",
"../foo/one.md": "./../foo/one.html",
"../foo/one.md#hash": "./../foo/one.html#hash"
// END relative path usage
};
对于以前的用法内联 md 的 hash 特殊处理一开始还是不知道的
hoist 解析文档中的语言标签
这个内部变量是解析 script 和 style 并从文档中分离的。看下测试代码
# hoist.md
# H1
<script src="vue.js"></script>
<style>
.vue {
font-size: 16px;
}
</style>
## H2
test("Should miss script and style when using hoist", () => {
const input = getFragment(__dirname, "hoist.md");
const { html, data } = mdH.render(input);
expect(html).toMatchSnapshot();
expect(data).toMatchSnapshot();
});
# 输出
exports[`hoist Should miss script and style when using hoist 1`] = `
<h1>H1</h1>
<h2>H2</h2>
`;
exports[`hoist Should miss script and style when using hoist 2`] = `
Object {
"__data_block": Object {},
"hoistedTags": Array [
<script src="vue.js"></script>,
<style>
.vue {
font-size: 16px;
}
</style>,
],
}
`;
md 的 html_block 功能只能返回最终展示的 html,但 md.$data 支持将 script 和 style 代码数据储存到实例中,供后续使用。
markdown-it-emoji
emoji 图标生成markdown-it-emoji,emoji 配置相关 json 文件
markdown-it-anchor
标签生成 header-anchor 链接,用的是 markdown 的插件 markdown-it-anchor
markdown-it-table-of-contents
toc 目录 功能参考的是一个 markdown-it 插件,markdown-it-table-of-contents
这个对于单 markdown 的长文件来说以前比如用马克飞象的时候是挺好用的,但在 vuepress 可以用多 markdown 文件个人感觉还是比较累赘的。因为原生的路由已经实现的减少又长又臭的 markdown 文件。
总结
作者在这里提炼了markdown-it-chain
类似webpack-chain
功能的插件,并提炼出来供项目使用,相关markdown-it-xxx
的插件也可以根据相关配置串联并组合成一个chain
,不得不说chain
配置真的是个好东西;
说明,createMarkdown 最终返回的是
markdown-it
实例
- TODO:
- 实现一个 markdown-it 插件