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 to
markdown
Broken down intoThe syntax tree
(JSON data structure used to describe markdown syntax) - Second traversal
The syntax tree
Is executed at the corresponding syntax nodeconversion
(The transformed HTML needs to have style attributes) - And then assembled
conversion
After theHTML tags
与Custom CSS styles
Into a completeThe HTML file
And the output - Finally, build a Web MarkDown conversion site and listen
change
Event to repeat the above steps for the online transformation
Technology stack
remark
Is used tomarkdown
Text conversion toThe syntax tree
@md2html/traverse
receiveremark
Output of theThe syntax tree
And provide similar@babel/traverse
The API is used to traverse all nodes@md2html/parse
This is achieved by adding a series of markdown node traversal operationsconversion
The core steps of
The code analysis
How to getmarkdown
Syntax 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