preface
stay A blog with VuePress + Github Pages In, we used VuePress to build a blog. The final effect is as follows: TypeScript Chinese document.
In the process of building a blog, out of practical needs, we VuePress blog Optimization: extended Markdown syntax How to write a markdown it plug-in is explained in Analysis of markdown it principle The implementation principle of markdown it is explained in. In this article, we will explain the specific actual code to help you better write plug-ins.
renderer
The rendering process of markdown it is divided into two parts, Parse and Render. If we want to change the rendering effect, for example, wrap a div in the outer layer, or modify the attributes of HTML elements and add class es, we can start with the Render process.
stay Official document of markdown it You can find the way to customize Render rules in:
Instance of Renderer. Use it to modify output look. Or to add rendering rules for new token types, generated by plugins.
var md = require('markdown-it')(); function myToken(tokens, idx, options, env, self) { //... return result; }; md.renderer.rules['my_token'] = myToken
Markdown it has built-in some default rules. You can modify them directly. You can view the specific rules and rendering methods renderer.js source code , directly listed here:
- code_inline
- code_block
- fence
- image
- hardbreak
- softbreak
- text
- html_block
- html_inline
Example 1
If we look at the rendering results of code blocks in VuePress, we will find that the outer layer of each code block is wrapped with a div with extra class class class name:
In fact, this is the result of VuePress modifying the rendering rules. Check it out VuePress source code:
module.exports = md => { const fence = md.renderer.rules.fence md.renderer.rules.fence = (...args) => { const [tokens, idx] = args const token = tokens[idx] const rawCode = fence(...args) return `<!--beforebegin--><div class="language-${token.info.trim()} extra-class">` + `<!--afterbegin-->${rawCode}<!--beforeend--></div><!--afterend-->` } }
We can see that the token processing is skilfully avoided here, and the rendered results are directly used. A layer of div is wrapped in the outer layer.
Example 2
Similar to VuePress, we can also use replace to replace some content after obtaining the default rendering content, such as VuePress blog Optimization: extended Markdown syntax In this article, we customize a code block syntax, which is in rules The content of rendering has been modified in fence:
md.use(function(md) { const fence = md.renderer.rules.fence md.renderer.rules.fence = (...args) => { let rawCode = fence(...args); rawCode = rawCode.replace(/<span class="token comment">\/\/ try-link https:\/\/(.*)<\/span>\n/ig, '<a href="$1" class="try-button" target="_blank">Try</a>'); return `${rawCode}` } })
Example 3
However, it is not always possible to make such a trick. Sometimes token s need to be processed. Here we refer to markdown it Official design criteria In the example in, when rendering a picture, if the link matches / ^ https?:\/\/(www\.)?vimeo.com\/(\d +) ($| \ /) /, we will render it as an iframe, while others will maintain the default rendering method:
var md = require('markdown-it')(); var defaultRender = md.renderer.rules.image, vimeoRE = /^https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/; md.renderer.rules.image = function (tokens, idx, options, env, self) { var token = tokens[idx], aIndex = token.attrIndex('src'); if (vimeoRE.test(token.attrs[aIndex][1])) { var id = token.attrs[aIndex][1].match(vimeoRE)[2]; return '<div class="embed-responsive embed-responsive-16by9">\n' + ' <iframe class="embed-responsive-item" src="//player.vimeo.com/video/' + id + '"></iframe>\n' + '</div>\n'; } // pass token to default renderer. return defaultRender(tokens, idx, options, env, self); };
rules. The function parameters passed in by image can be viewed as renderer JS source code:
Renderer.prototype.render = function (tokens, options, env) { var i, len, type, result = '', rules = this.rules; for (i = 0, len = tokens.length; i < len; i++) { type = tokens[i].type; if (type === 'inline') { result += this.renderInline(tokens[i].children, options, env); } else if (typeof rules[type] !== 'undefined') { result += rules[tokens[i].type](tokens, i, options, env, this); } else { result += this.renderToken(tokens, i, options, env); } } return result; };
We can see the parameters passed in by rules, where tokens refers to the tokens list and idx refers to the token index to be rendered. Therefore, the target token can be obtained through tokens[index] in the code.
Then we used tokens What methods can attrindex and tokens provide to view Official API , or view it directly Token source code.
Let's explain some methods used in this example. Let's start with token. Let's take an example and have a look! [video link]([ https://www.vimeo.com/123 )]( https://www.vimeo.com/123 ))The token generated by this markdown syntax (simplified here):
{ "type": "image", "tag": "img", "attrs": [ [ "src", "https://www.vimeo.com/123" ], [ "alt", "" ] ], "children": [ { "type": "text", "tag": "", "attrs": null, "children": null, "content": "video link", } ], "content": "video link" }
You can see that the token has an attr attribute, indicating the attributes of the img tag to be rendered Attrindex is to get the attribute index by name, and then by token Attrs [aindex] [1] get the specific attribute value.
Example 4
Also from markdown it Official design criteria In the example in, add target="_blank" to all links:
// Remember old renderer, if overridden, or proxy to default renderer var defaultRender = md.renderer.rules.link_open || function(tokens, idx, options, env, self) { return self.renderToken(tokens, idx, options); }; md.renderer.rules.link_open = function (tokens, idx, options, env, self) { // If you are sure other plugins can't add `target` - drop check below var aIndex = tokens[idx].attrIndex('target'); if (aIndex < 0) { tokens[idx].attrPush(['target', '_blank']); // add new attribute } else { tokens[idx].attrs[aIndex][1] = '_blank'; // replace value of existing attr } // pass token to default renderer. return defaultRender(tokens, idx, options, env, self); };
You may wonder why there are rules link_ open? This is not in the default rule. Can I use it directly?
It's really OK. In fact, the link here_ Open, previous image and fence are all token types, so as long as they are token types, what are the types of tokens? Are there any specific instructions?
On this issue, markdown it also puts forward in issues:
The author means, no, if you want to write a plug-in, you go to see the source code
Well, in fact, in our actual development, if you want to know a token type, you can print the token and have a look. It's official Live Demo The debug mode is provided to view Tokens:
Of course, for the requirements in this example, the author also provides a markdown it for inline plug-in to simplify code writing:
var iterator = require('markdown-it-for-inline'); var md = require('markdown-it')() .use(iterator, 'url_new_win', 'link_open', function (tokens, idx) { var aIndex = tokens[idx].attrIndex('target'); if (aIndex < 0) { tokens[idx].attrPush(['target', '_blank']); } else { tokens[idx].attrs[aIndex][1] = '_blank'; } });
The markdown it for inline will be introduced in later articles.
Series articles
Blog building series is the only practical series of tutorials I have written so far. It is estimated that about 20 tutorials will explain how to use VuePress to build and optimize blogs and deploy them to GitHub, Gitee, private servers and other platforms. Full series address: https://github.com/mqyqingfeng/Blog
Wechat: "mqyqingfeng", add me to Yu Yu's only reader group.
If there is any mistake or lack of preciseness, please be sure to correct it. Thank you very much. If you like it or have some inspiration, welcome star, which is also an encouragement to the author.