之前写了个vscode插件, 功能是选取一段html片段, 产生对应的css嵌套结构. (描述很有问题, 这2年里我也一直没想明白应该怎么说)
那次写完后写了个总结, 留了几个todo, 现在回头来补一部分.

要解决哪些问题

因为上一版的做法是: 在消费html片段时分析tag, 直接新建了个ret变量, 在分析的时候直接去拼接字符串了.
上次的博客记录了一些问题:

  1. 对非法或不完整的html片段没有校验, 所以输出可能也是不完整的.
  2. 同级别有重复的选择器, 输出的时候没有处理.
  3. 输出的内容没有格式化.

这些问题只要通过转成ast就都可以解决, 2年前的总结也提到了, 那么现在来简单走一遍ast流程.

因为是场景性的问题, 所以是正常编译流程的超级简化版, 因为输出和输入结构类似, 甚至少了转换ast的步骤.

下面开始, 所有代码都在文章开头的仓库链接中, 文章中不贴代码了.

分析html片段

分析html片段的过程是个有限状态自动机.
我们场景需要的状态很少: 标签开始, 标签结束, 剩下的状态都作为text处理就可以.

从html片段的第一个字符开始消费, 判断状态的类型, 并用正则取出对应的内容, 记录内容和类型, 并将html片段切割掉(消费动作).

构建ast

分析完html片段, 我们会得到一个tokenizer的数组, 元素内容是token类型(如标签开始/标签结束)和元素内容.
(虽然代码中我把这个步骤和构建ast合起来了, 并不重要, 想象已经获得一个tokenizer数组就可以了)

构建ast需要2步: 设计ast, 遍历tokenizer数组, 根据类型和内容生成ast.

ast的设计很简单, 属性有tag, attrs, children.

构建ast我们需要用到一个栈, 在标签开始的时候把当前元素推入栈, 后面的元素就可以挂在栈头元素的children上, 在关闭标签的时候让栈弹出一个就行.

这样, 如果遍历完毕, 栈不为空, 就可以知道html片段不完整.
另外, 在栈弹出的时候, 判断弹出元素的tag是否等于关闭标签的tag也可以判断非法html片段.

transform

构建完ast, 其实就可以利用我们熟悉的js对ast进行任何处理了, 而能解决多少问题或做多少优化, 就看个人算法能力了.

而在transform这步我们要先做的是设计一个函数调用结构, 来方便后续添加功能.

这里想参考babel, 但ast简单所以没这么多生命周期.
就创建一个context传给插件, 让插件可以获取一些状态, 并在遍历ast的合适的时间更新这些状态.

codegen

code generate其实是字符串拼接流程.
有了层级, 代码就可以方便的做缩进了. 这里也维护了个context来让代码调用更舒服, 当然我们的场景只有一种nodetype, 可能过度设计了, 具体代码看文章开头链接.