这篇文章主要介绍了如何使用JavaScript+Node.js写一款markdown解析器,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

1. 准备工作

首先编写getHtml函数,传入markdown文本字符串,这里使用fs读取markdown文件内容,返回值是转换过后的字符串。

constfs=require('fs');constsource=fs.readFileSync('./test.md','utf-8');constgetHtml=(source)=>{//处理标题returnsource;}constresult=getHtml(source);console.log(result);

主要设计正则表达式和String.prototype.replace方法,replace接收的第一个参数可以是正则,第二个参数如果是函数那么返回值就是所替换的内容。

2. 处理图片&超链接

图片和超链接的语法很像,![图片](url),[超链接](url),使用正则匹配同时需要排除`。props会获取正则中的$,$1,$2。也就是匹配的字符整体,第一个括号内容,第二个括号内容。比如这里props[0]就是匹配到的完整内容,第四个参数props[3]是[]中的alt,第五个参数props[4]是链接地址。

constimageora=(source)=>{returnsource.replace(/(`?)(!?)\[(.*)\]\((.+)\)/gi,(...props)=>{switch(props[0].trim()[0]){case'!':return`<ahref="${props[4]}"rel="externalnofollow"alt="${props[3]}">${props[3]}</a>`;case'[':return`<imgsrc="${props[4]}"alt="${props[3]}"/>`;default:returnprops[0];}});}constgetHtml=(source)=>{source=imageora(source);returnsource;}3. 处理blockquote

这里使用\x20匹配空格。如果匹配到内容,将文本props[3]放在blockquote标签返回就行了。

constblock=(source)=>{returnsource.replace(/(.*)(`?)\>\x20+(.+)/gi,(...props)=>{switch(props[0].trim()[0]){case'>':return`<blockquote>${props[3]}</blockquote>`;default:returnprops[0];}});}4. 处理标题

匹配必须以#开头,并且#的数量不能超过6,因为h7是最大的了,没有h7,最后props[2]是#后跟随的文本。

constformatTitle=(source)=>{returnsource.replace(/(.*#+)\x20?(.*)/g,(...props)=>{switch(props[0][0]){case'#':if(props[1].length<=6){return`<h${props[1].length}>${props[2].trim()}</h${props[1].length}>`;};default:returnprops[0];}})}5. 处理字体

写的开始复杂了

constformatFont=(source)=>{//处理~包裹的文本source=source.replace(/([`\\]*\~{2})(.*?)\~{2}/g,(...props)=>{switch(props[0].trim()[0]){case'~':return`<del>${props[2]}</del>`;;default:returnprops[0];}});//处理*-表示的换行source=source.replace(/([`\\]*)[*-]{3,}\n/g,(...props)=>{switch(props[0].trim()[0]){case'*':;case'-':return`<hr/>`;default:returnprops[0];}})//处理***表示的加粗或者倾斜。source=source.replace(/([`\\]*\*{1,3})(.*?)(\*{1,3})/g,(...props)=>{switch(props[0].trim()[0]){case'*':if(props[1]===props[3]){if(props[1].length===1){return`<em>${props[2]}</em>`;;}elseif(props[1].length===2){return`<strong>${props[2]}</strong>`;;}elseif(props[1].length===3){return`<strong><em>${props[2]}</em></strong>`;;}};default:returnprops[0];}});returnsource;}6. 处理代码块

使用正则匹配使用`包裹的代码块,props[1]是开头`的数量,props[5]是结尾`的数量,必须相等才生效。

constpre=(source)=>{source=source.replace(/([\\`]+)(\w+(\n))?([^!`]*?)(`+)/g,(...props)=>{switch(props[0].trim()[0]){case'`':if(props[1]===props[5]){return`<pre>${props[3]||''}${props[4]}</pre>`;};default:returnprops[0];}});returnsource;}7. 处理列表

这里只是处理了ul无序列表,写的同样很麻烦。主要我的思路是真复杂。而且bug肯定也不少。先匹配-+*加上空格,然后根据这一行前面的空格熟替换为ul。这样每一行都保证被ulli包裹。

第二步判断相邻ul之间相差的个数,如果相等则表示应该是同一个ul的li,替换掉</ul><ul>为空,如果后一个ul大于前一个ul,则表示后面有退格,新生成一个<ul>包裹退格后的li,如果是最后一个ul则补齐前面所有的</ul>。

constlist=(source)=>{source=source.replace(/.*?[\x20\t]*([\-\+\*]{1})\x20(.*)/g,(...props)=>{if(/^[\t\x20\-\+\*]/.test(props[0])){returnprops[0].replace(/([\t\x20]*)[\-\+\*]\x20(.*)/g,(...props)=>{constlen=props[1].length||'';return`<ul${len}><li>${props[2]}</li></ul${len}>`;})}else{returnprops[0];}});constset=newSet();source=source.replace(/<\/ul(\d*)>(\n<ul(\d*)>)?/g,(...props)=>{set.add(props[1]);if(props[1]==props[3]){return'';}elseif(props[1]<props[3]){return'<ul>';}else{constarr=[...set];constend=arr.indexOf(props[1]);letstart=arr.indexOf(props[3]);if(start>0){return'</ul>'.repeat(end-start);}else{return'</ul>'.repeat(end+1);}}});returnsource.replace(/<(\/?)ul(\d*)>/g,'<$1ul>');}8. 处理表格

consttable=(source)=>{source=source.replace(/\|.*\|\n\|\s*-+\s*\|.*\|\n/g,(...props)=>{letstr='<table><tr>';constdata=props[0].split(/\n/)[0].split('|');for(leti=1;i<data.length-1;i++){str+=`<th>${data[i].trim()}</th>`}str+='<tr></table>';returnstr;});returnformatTd(source);}constformatTd=(source)=>{source=source.replace(/<\/table>\|.*\|\n/g,(...props)=>{letstr='<tr>';constdata=props[0].split('|');for(leti=1;i<data.length-1;i++){str+=`<td>${data[i].trim()}</td>`}str+='<tr></table>';returnstr;});if(source.includes('</table>|')){returnformatTd(source);}returnsource;}9. 调用方法

constgetHtml=(source)=>{source=imageora(source);source=block(source);source=formatTitle(source);source=formatFont(source);source=pre(source);source=list(source);source=table(source);returnsource;}constresult=getHtml(source);console.log(result);

感谢你能够认真阅读完这篇文章,希望小编分享的“如何使用JavaScript+Node.js写一款markdown解析器”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!