“This is the first day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

preface

In “A blog with VuePress + Github Pages”, we build a blog with VuePress and see what the final TypeScript Chinese document looks like.

If we look at the TypeScript official documentation, we’ll see that many blocks of code have a Try button hovering over them:

Click to jump to the corresponding Playground. For example, the button in the picture jumps to this link, where we can modify and verify the code effect.

If we were to implement such a feature, how would we implement it?

thinking

It’s tempting to write a VuePress plug-in to do this. The effect looks a bit like a code copy plug-in, but if you think about it, it’s not.

Code duplication plug-in implementation way, reference from zero to achieve a VuePress plug-ins, can be done in the page rendering, after traversal every block of code and then insert a copy button, click to write code into the clipboard copy, but the code block jump on the other hand, the code jump needs us to write a link address first, And then render the button, and the question is where do I put the address of this link? Remember, all we can write is a normal Markdown file…

So we thought, can we extend the syntax of Markdown? For example, the normal code block would be:

```typescript const message = "Hello World!" ; ` ` `Copy the code

To achieve this effect, can we write:

```typescript // try-link: https://www.baidu.com const message = 'Hello World! '; ` ` `Copy the code

But instead of rendering the try-link line, it will render something like this:

When you click Try, jump to the corresponding link.

Or better yet, display the Try button while the mouse is hovering over the block, something like this:

markdown-it

VuePress uses markdown-it to render a markdown. What is markdown-it? Check out Markdown-it’s Github repository and you’ll see this introduction:

Markdown parser done right. Fast and easy to extend.

In simple terms, Markdown-it is a Markdown renderer that can render markdown into HTML, etc. Markdown-it also supports writing plug-ins to extend functions. In fact, The reason why markdown files in VuePress project can support writing Vue components is because VuePress wrote plug-ins to support Vue syntax. Can we extend markdown syntax as well?

Fortunately, the VuePress documentation provides a configuration for customizing markdown-it plugins:

VuePress uses Markdown-it to render markdown, and most of the above extensions are implemented through custom plug-ins. To take this one step further, you can customize the current markdown-it instance using the markdown option of.vuepress/config.js:

module.exports = {
  markdown: {
    // Markdown-it-anchor option
    anchor: { permalink: false },
    // Markdown-it-TOC option
    toc: { includeLevel: [1.2]},extendMarkdown: md= > {
      // Use more markdown-it plugins!
      md.use(require('markdown-it-xxx'))}}}Copy the code

I know how to introduce it, but how to write the markdown-it plug-in?

Markdown – it plug-in

Looking at the Github repository code and documentation for MarkDown-it, we can get an idea of how Markdown-it works. It is transformed in a similar way to Babel, into an abstract syntax tree and then generating the corresponding code. A simple summary is divided into two processes: Parse and Render.

We can also see this in the source code:

MarkdownIt.prototype.render = function (src, env) {
  env = env || {};
  return this.renderer.render(this.parse(src, env), this.options, env);
};
Copy the code

The first is to Parse and the second is to Render. For the sake of simplicity, I decided to handle the Render process directly by looking at the Render source code. We can see that default Rules are already written in Render based on some fixed types, such as code blocks:

default_rules.fence = function (tokens, idx, options, env, slf) {
  var token = tokens[idx],
      info = token.info ? unescapeAll(token.info).trim() : ' ',
      langName = ' ',
      langAttrs = ' ',
      highlighted, i, arr, tmpAttrs, tmpToken;

  if (info) {
    // ...
  }

  if (options.highlight) {
    highlighted = options.highlight(token.content, langName, langAttrs) || escapeHtml(token.content);
  } else {
    highlighted = escapeHtml(token.content);
  }

  if (highlighted.indexOf('<pre') = = =0) {
    return highlighted + '\n';
  }

  if (info) {
    / /...
  }


  return  '<pre><code' + slf.renderAttrs(token) + '>'
        + highlighted
        + '</code></pre>\n';
};
Copy the code

We can override this rule by following the plugin writing guidelines provided by Markdown-it:

Md.renderer.rules.fence =function (tokens, idx, options, env, self) {
	// ...
};
Copy the code

//try-link: XXX //try-link: XXX //try-link: XXX //try-link: XXX //try-link: XXX

Modify the config.js file:

module.exports = {
    markdown: {
      extendMarkdown: md= > {
        md.use(function(md) {
          const fence = md.renderer.rules.fence
          md.renderer.rules.fence = (. args) = > {
            letrawCode = 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}`}})}}}Copy the code

For brevity, INSTEAD of writing the style of the link directly inline, I added a class. Where do I write the style of this class?

VuePress provides the docs /. VuePress/styles/index styl file, as the global style file will be automatically applied, will be generated at the end of the final CSS file, with a higher priority than the default styles.

So we’ll write styles in the index.styl file:

// Default style.try-button {
    position: absolute;
    bottom: 1em;
    right: 1em;
    font-weight: 100;
    border: 1px solid #719af4;
    border-radius: 4px;
    color: #719af4;
    padding: 2px 8px;
    text-decoration: none;
    transition-timing-function: ease;
    transition: opacity .3s;
    opacity: 0; } // hover style.content__default:not(.custom) a.try-button:hover {
    background-color: #719af4;
    color: #fff;
    text-decoration: none;
}
Copy the code

In some cases, automatic compilation may not work, so we can rerun YARN Run docs:dev.

The button should now display normally (default style opacity is 0, here opacity is forced to 1 for screenshots) :

When VuePress mounts, retrieve all elements of the code block, and add events to the config. Js file:

module.exports = {
    plugins: [
      (options, ctx) = > {
        return {
          name: 'vuepress-plugin-code-try'.clientRootMixin: path.resolve(__dirname, 'vuepress-plugin-code-try/index.js')}}],markdown: {
      extendMarkdown: md= > {
        md.use(function(md) {
          const fence = md.renderer.rules.fence
          md.renderer.rules.fence = (. args) = > {
            letrawCode = 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}`}})}}}Copy the code

Create a vuepress-plugin-code-try directory under config.js and create an index.js file: vuepress-plugin-code-try

export default {
  mounted () {
    setTimeout(() = > {
        document.querySelectorAll('div[class*="language-"] pre').forEach(el= > {
            if (el.querySelector('.try-button')) {
                el.addEventListener('mouseover'.() = > {
                    el.querySelector('.try-button').style.opacity = '1';
                })
                el.addEventListener('mouseout'.() = > {
                    el.querySelector('.try-button').style.opacity = '0'; }}}})),100)}}Copy the code

At this point, we run the project again, and we have achieved what we originally wanted:

series

Blog Building is the only practical tutorial series I’ve written so far, explaining how to use VuePress to build a blog and deploy it on GitHub, Gitee, personal servers, etc.

  1. Build a blog with VuePress + GitHub Pages
  2. This article will teach you how to synchronize GitHub and Gitee code
  3. Can’t use GitHub Actions yet? Look at this article
  4. How does Gitee automatically deploy Pages? GitHub Actions again!
  5. A Linux command with an adequate front end
  6. A simple enough Nginx Location configuration explained
  7. A detailed tutorial from buying a server to deploying blog code
  8. A domain name from the purchase to record to resolve the detailed tutorial
  9. VuePress’s last updated date is set
  10. VuePress blog optimized to add statistics features
  11. VuePress blog optimized for HTTPS enabled
  12. VuePress blog optimization to enable Gzip compression
  13. Implement a VuePress plug-in from scratch

Wechat: “MQyqingfeng”, add me Into Hu Yu’s only readership group.

If there is any mistake or not precise place, please be sure to give correction, thank you very much. If you like or are inspired by it, welcome star and encourage the author.