How to write markdown-it plugin (1)

Time:2022-11-24

foreword

exist“An article that takes you to build a blog with VuePress + Github Pages”In, we use VuePress to build a blog, the final effect view:TypeScript Chinese Documentation

In the process of building a blog, out of practical needs, we“Extended Markdown Grammar for VuePress Blog Optimization”How to write amarkdown-itplug-in, again“Markdown-it principle analysis”explained inmarkdown-itIn this article, we will explain the specific actual combat code to help you better write plug-ins.

renderer

markdown-itThe rendering process is divided into two parts,ParseandRender, if we want to change the effect of rendering, such as wrapping a layer in the outer layerdiv, or modify the attributes of HTML elements, addclassWait, you can start fromRenderThe process begins.

existOfficial documentation of markdown-itYou can find customRenderHow to render rules:

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-itbuilt in some defaultrules, you can directly modify them, which ones and rendering methods can be viewedThe source code of renderer.js, listed here directly:

  • code_inline
  • code_block
  • fence
  • image
  • hardbreak
  • softbreak
  • text
  • html_block
  • html_inline

Example one

If we look at the rendering results of the code blocks in VuePress, we will find that each code block is wrapped with a layer ofextra-classclass namediv:

How to write markdown-it plugin (1)

In fact, this is the result of VuePress modifying the rendering rules. ViewVuePress 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 processing of tokens is very cleverly avoided here, and the rendered results are directly used, and a layer of div is wrapped in the outer layer.

Example two

Similar to the way of VuePress, we can also use replace to replace some content after getting the default rendering content, such as in“Extended Markdown Grammar for VuePress Blog Optimization”In this article, we have customized a code block syntax, which is inrules.fenceThe rendered content is modified in:

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 three

But it is impossible to always be so tricky, sometimes it is necessary to deal with tokens, here we refer to markdown-itOfficial Design GuidelinesIn the example, when rendering an image, if the link matches/^https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/, we render it as aiframe, others keep 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.imageThe function parameters passed in can be viewedrenderer.jssource 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, so it can be passed in the codetokens[index]Get the target token.

Then we usedtokens.attrIndex, which methods are provided by tokens to viewOfficial API, or view directlyToken source code

Let’s explain some of the methods used in this example, starting with token, let’s give an example, see!([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 aattrattribute, which indicates the attributes of the img tag to be rendered,token.attrIndexIt is to get the attribute index by name, and then passtoken.attrs[aIndex][1]Get a specific attribute value.

Example four

Also from markdown-itOfficial Design GuidelinesIn the example, to all links addtarget="_blank"

// 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);
};

Maybe you will wonder why there isrules.link_open? This is not in the default rules, can it be used directly?

It is really possible, in fact, herelink_openand the previousimagefenceetc. are the types of the token, so as long as it is the type of the token, what are the types of the token? Is there any specific documentation?

about this issue,markdown-it There are also issues raised:
How to write markdown-it plugin (1)

What the author means is, no, if you want to write a plug-in, you should go to the source code…

Well, in fact, in our actual development, if you want to know a certain token type, you can actually print out the token and have a look, the officialLive DemoA debug mode is provided for viewing tokens:

How to write markdown-it plugin (1)

Of course, for the requirements in this example, the author also providesmarkdown-it-for-inlinePlugins are used 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';
              }
            });

aboutmarkdown-it-for-inlineIt will be introduced in a future article.

series of articles

The blog building series is the only practical series of tutorials I have written so far. It is expected to have about 20 articles, explaining how to use VuePress to build and optimize blogs, and deploy them to platforms such as GitHub, Gitee, and private servers. Full series of articles address:https://github.com/mqyqingfeng/Blog

WeChat: “mqyqingfeng”, add me to Sae Yu’s only reader group.

If there are mistakes or imprecise places, please be sure to give corrections, thank you very much. If you like or inspire something, welcome to star, which is also an encouragement to the author.

Recommended Today

vue-cli3.0 configure Iview custom theme

don’t panic, followIviewCome on, probably . . . no problem! 1. Create a new my-them.less @import ‘~view-design/src/styles/index.less’; /*Theme configuration and other configurations refer to https://github.com/view-design/ViewUI/blob/master/src/styles/custom.less*/ @primary-color: #6C86FF; @link-hover-color: #6C86FF; 2. Import the entry file main.js import ‘./assets/css/my-them.less’; illustrate The less file needs to be supported here, and the project should install less and less-loader // […]