This is the 9th day of my participation in the August Wen Challenge.More challenges in August

The introduction

Markdown’s syntax is simpler and more straightforward than others.

There are many markdown to HTML tools out there. However, each conversion tool only has a fixed transformation style, and the conversion tool that supports custom style, such as Markdown-it, although supports the configuration of plug-ins to achieve style customization, but the threshold is a little higher, need to understand their plug-in API.

Therefore, I hope to implement a markdown to HTML tool named @MD2html that supports the following functions.

  • Low difficulty in getting started
  • Flexible configuration
  • Real-time preview

The principle of

The overall execution process is exactly the same as the AST transformation steps in javascript, except that the language used is changed from javascript to Markdown.

  • First of all tomarkdownBroken down intoThe syntax tree(JSON data structure used to describe markdown syntax)
  • Second traversalThe syntax treeIs executed at the corresponding syntax nodeconversion(The transformed HTML needs to have style attributes)
  • And then assembledconversionAfter theHTML tags 与 Custom CSS stylesInto a completeThe HTML fileAnd the output
  • Finally, build a Web MarkDown conversion site and listenchangeEvent to repeat the above steps for the online transformation

Technology stack

  • remarkIs used tomarkdownText conversion toThe syntax tree
  • @md2html/traversereceiveremarkOutput of theThe syntax treeAnd provide similar@babel/traverseThe API is used to traverse all nodes
  • @md2html/parseThis is achieved by adding a series of markdown node traversal operationsconversionThe core steps of

The code analysis

How to getmarkdownSyntax tree?

import remark from 'remark'


const md = Title '#'
const ast = remark().parse(md)
Copy the code

How do I traverse the AST?

Since there is no library to traverse ast like @babel/traverse, consider writing your own mardown-traverse.

  • Methods information

    • The function name: traverse
    • The arguments:
    • @param1: AST[JSON structure syntax tree for Markdown]
    • @param2: visitors[Traverses the object, the key corresponds to the node type in the syntax tree, the value type is the function, passes the node information]
  • AST structure and Visitors structure

// ast
{
  "type": "root".// Node type (root node)
  "children": [ / / child nodes
    {
      "type": "heading".// Node type (H1 / H2 / H3 / H4 / H5 / H6)
      "depth": 1.// Node information
      "children": []}]}// visitors
{
  /* node: indicates the node information. Utils: tool method provided by 'mardown-traverse' */
  heading(node, utils) {
    const { depth } = node
  }
}
      
Copy the code
  • The core code
function traverse(ast, visitors, parentNode = null) {
  // Tools provided to visitors
  const util = {
    parentNode: parentNode, // Record the parent of the current node
    // ...
  }


  if (visitors[ast.type]) {
    // Enter the node
    if (visitors[ast.type].enter) {
      visitors[ast.type].enter(ast, util)
    }
  }


  if (ast.children && ast.children.length) {
    ast.children.map(child= > traverse(child, visitors, ast))
  }


  if (visitors[ast.type]) {
    // Leave the node
    if (visitors[ast.type].leave) {
      visitors[ast.type].leave(ast, util)
    }
  }
}
Copy the code

How to replace nodes

In the case of the # header, the node corresponding to this markdown is heading, and you just need to configure the replacement logic for the heading node in the above incoming visitors.

const result = []

traverse(ast, {
  heading: {
    Util encapsulates the tag name conversion and style conversion for different tags. Here is only an example to illustrate the process
    enter(node, util) {
      const tagName = `h${node.depth}`
      const style = `class="md2html-${tagName}"`


      result.push(` <${tagName} ${style}> `) // <h1>
    },
    leave(node, util) {
      result.push(`</h${node.depth}> `) // </h1>}}})Copy the code

At this point, the transformation logic of the heading node is basically complete.

How to make the style flexible and configurable

As you can see from the previous step, the final tag of the # title has this attribute: class=” md2HTmL-h1 “, so all you need to do is configure a piece of CSS with the following code.

.md2html-h1{ 
    /* Specific style */ 
}
Copy the code