和大神一样玩转AST—— Find & Patch

235 阅读1分钟

想要像大神一样用jscodeshift改代码吗?别再用那些烦人的命令式API了。试试ast-grep的Find & Patch,一个用声明式语言改代码的新玩法。它既强大又简单,美美哒。😍

ast-grep重写代码简简单单就两个概念:一、 用规则来找到代码,二、生成修复代码替换ast节点。你可以用它来搞各种代码转换,比如重构,修bug,代码迁移,或者代码生成。想怎么玩就怎么玩!!🚀

下面就是一个简单的把console.log改写成logger的规则。

rule:
  pattern: console.log($MSG) # 找到console.log
fix: logger.log($MSG)  # 改写成logger.log

但是有个坑。ast-grep里简单的“Find & Patch”流程只能一次换一个节点。这意味着我们不能搞涉及多个节点或节点列表的转换。比如,把barrel import改成single import。

import {a, b, c} from './barrel';
// 改写成
import a from './barrel/a';
import b from './barrel/b';
import c from './barrel/c';

别怕,我们有招。ast-grep通过加rewriter子规则来升级Find & Patch,可以在匹配过程中应用到特定节点的单独规则。这让我们可以用多个不同的规则来搞多个节点。聪明吧?

# 找import语句
rule:
  pattern: import {$$$IDENTS} from './barrel'
# 注册子规则
rewriters:
- id: rewrite-identifer
  rule:
    pattern: $IDENT # 继续找identifier语句
    kind: identifier
  fix: import $IDENT from './barrel/$IDENT' # 改写成import
# 使用子规则
transform:
  IMPORTS:
    rewrite:
      source: $$$IDENTS  # 找到AST子树
      rewriters: [rewrite-identifer]  # 运用子规则
      joinBy: "\n"

rewriter子规则就像旧的“Find and Patch”流程,但是递归的。就像把旧流程反复应用到匹配的节点上。我们可以用rewriter子规则来把barrel import里的每个标识符改成单个import语句。在线演示

编程在树上

Find & Patch是ast-grep专门为AST操作而设计的新玩法,但是它可以做到和函数式编程语言类似的功能,帮助你用声明式和优雅的方式转换你的代码。我们可以把Find & Patch看作是AST上的“函数式编程”。

如果你觉得Find & Patch有用和有趣,可以看看博客文章或者在线演示来试试ast-grep。

请转发这个摘要,阅读完整文章,或者回复你的想法。我很想听听你的意见!