当前位置: 首页 > 后端技术 > Node.js

玩转Markdown(二)——抽象语法树的抽取与操作

时间:2023-04-03 16:44:44 Node.js

玩转Markdown(二)——抽象语法树的抽取与操作距离渲染图发布已经半年多了。最近在操作mdast生成md文件时,突发奇想,给它加了FunMarkdown(2)。本篇我们来聊一聊markdown的AbstractSyntaxTree:以下简称mdastAST,不是灵丹妙药。必须首先说明这一点。如果你的需求只是写一个md文档,那么你只需要一个简单易用的md编辑器(个人推荐vscodewithmarkdownplugin)。如果你的需求稍微复杂一点,比如数据映射自动生成md文件,那么编程语言+字符串操作+文件数据流适合你。但是,如果你的需求更进一步,你想把md文件转成html、vue、react组件,或者你想扩展md的语法,用md嵌入silde做ppt。或者你想把jsx嵌入到md中,成为一种全新的文件格式(mdx)。那么此时分析和操作mdast是比较合适的。mdast虽然也可以解决以上两个问题,但是把简单的问题复杂化了,不符合我们写代码的初衷。你不这么认为吗?对于常见的AST,我们经常会反汇编各种语言,比如用acornbabel解析js、ts,用postcss转换css、scss、less等。同样的,html也可以解析成hast,markdown也可以解析成mdast,更不用说/.+\.(?:(?:(?:wx|ax|jx|ks|tt|q)ml)|swan)$/此类的变体。如果你写过webpack、rollup、postcss插件,你一定对这个不陌生。它们通过各种形式运行在我们的应用程序中,但它们往往被封装起来,我们感受不到它们的存在。当MarkdownAST阅读这一段时,希望你对markdown语法有一个清晰的认识。提到mdast就不得不提到unifiedjs和它的rehype、retext、remark。是语法树的统一解决方案,本文不介绍其用法。我们看下syntax-tree中mdast的定义。大致将Nodes(节点)分为这几种:Parent:父节点基类继承,用于声明子数组Literal:字面基类继承,用于声明值Root:继承自Parent,根节点Paragraph:最常用的段落携带内容对应html中的pHeading:(h1,h2,h3,h4,h5,h6)ThematicBreak:---***语法,对应html中的hrBlockquote:>语法,对应html中的blockquoteList:对应ul和html中的ol,用于声明列表ListItem:对应html中的li,字面意思与ItemHTML相同htmlstringCode:`语法,用的很多,最常见的就是给代码块上色,比如`js,`html,`css等,通常需要配合代码高亮lib给颜色定义:md变量定义,如:[Alpha]:https://example.comText:最常用的节点类型,只是text强调:*alpha*_bravo_对应html中的Strong:**alpha**__bravo__对应在html中InlineCode:\`InlineCode\`=>InlineCodeBreak:(2个空格)或者··,一个换行符,它不像\n,这个换行符还在Link:html上一段,但我很熟悉[alpha](https://example.com"bravo")的Image:html太熟悉了![alpha](https://example.com/favicon.ico"bravo")LinkReference:指向引用变量的节点的链接,与Definition结合使用,例如[alpha][Bravo]ImageReference:同上,只是图像引用变量的节点。这些节点是经典降价语法的抽象。与js相比,markdown语法和ast实在是太简单了。构建mdast有上面的列表接下来我们自己构建一个mdastyarn,Emphasis,Strong,InlineCode,Break,Link,Image,LinkReference,ImageReference}from'mdast'//存储你的内容constchildren:Content[]=[]constastTree:Root={type:'root',children}所以mdast已构建成功。Convertmarkdownstringimport{toMarkdown}from'mdast-util-to-markdown'//生成markdwonstringtoMarkdown(astTree)这样就把mdast转成markdown了,真的很简单。parsemarkdownstringimport{fromMarkdown}from'mdast-util-from-markdown'constdoc=fs.readFileSync('example.md')constastTree=fromMarkdown(doc)添加目录TOCimport{toc}from'mdast-util-toc'consttocResult=toc(tree,{tight:true})if(tocResult.map){//添加目录到第一个节点后tree.children.splice(1,0,tocResult.map)}More有很多用于操作mdast-util-*的工具包。与此同时,remark、unified、mdx、vfile、Syntaxtree生态也在蓬勃发展。我们可以自由组合它们以达到我们想要的目标。实际自动生成markdown案例写一个包,自动拉取Github项目,生成一个README.md文件,有目录和语言分类。可以同时发布在GithubAction和npm上,代码应该进行tree-shaking,避免@actions/core,@actions/github到npm,@octokit/*到GithubAction。Githubmarketplace:地址npm:地址源码见:github-repository-distributor