jscodeshift是啥
jscodeshift
是一个重构代码的工具集,对recast(一个通过分析AST做代码修改的库)做了封装,通过jscodeshift
编写codemod
, 然后对指定文件运行就可以批量重构代码,大大减少了体力劳动,并可复用。常见的react
升级的codemod就是基于jscodeshift
的
为什么不是babel
babel
的目的是对代码向下兼容的,会进行代码转换,而且即使不做任何修改,输出的代码和原本的也有区别,比如空格,空行,注释位置会变化,所以不使用,而jscodeshift
会保留没发生修改的代码的原有格式。
用法
1. 创建codemod
module.exports = function(fileInfo, api) {
return api.jscodeshift(fileInfo.source)
.findVariableDeclarators('foo')
.renameTo('bar')
.toSource();
}
这里api.jscodeshift
将字符串源文件fileInfo.source
转换为一个可遍历/操作的Collection
, 使用ast的第一步就是要找到指定的ast节点,findVariableDeclarators('foo')
就是找到变量foo
的声明语句。找到节点下一步就是进行操作,Collection
的api是像jQuery
一样链式调用的,很方便,renameTo('bar')
是把变量foo
重命名为bar
, 这里renameTo
不光修改了foo
声明的地方,还向下遍历把作用域内出现的都替换了。最后一步是toSource
把处理后的ast转换为字符串输出。这就是codemod
的基本流程:找到节点->操作节点->输出
2. 使用codemod
terminal内使用:
$ npx jscodeshift -t mycodemod.js file
犯的一个错误:ts文件要指定
--parser=ts --extensions=js,ts
Collection
jscodeshift
对recast
的一个重要封装就是Collection
, 顾名思义Collection
是一个ast节点集合, 这里和jQuery
或数据库的操作真的很像,比如api.jscodeshift(fileInfo.source).findVariableDeclarators('foo')
是不是就是$('.foo')
或db.collection.find( { id: 'foo' } } )
Collection
通过api.jscodeshift(fileInfo.source).find获得
find(type: recast.NamedTypes, filter): Collection
type
: ast节点类型,通过ast神器astexplorer可以查看对应节点类型. 然后每个类型都是api.jscodeshift
上的一个属性,比如api.jscodeshift.VariableDeclaration
filter
: 是一个ast节点的部分样貌,find
就是去找符合的节点 比如找名字为foo
的标识符:
const { j: jscodeshift } = apil
j(fileInfo.source)
.find(j.Identtifier, { name: 'foo' })
而且前面的findVariableDeclarators
就是对find
的封装:
findVariableDeclarators: function(name) {
const filter = name ? {id: {name: name}} : null;
return this.find(VariableDeclarator, filter);
}
当然如果要找的节点很复杂,使用find
不好一次性找到,就需要使用Collection.filter
找
filter(path): Collection
比如要找一个声明时有两个元素的数组[1,2]
:
const { j: jscodeshift } = api;
j(fileInfo.source)
.find(j.ArrayExpression)
.filter((path) => {
return path.node.elements.length === 2;
});
Collection
的其他api可以到github.com/facebook/js…查看
Builder
两一个重要的概念是Builder
, 即ast构造器,有时我们不只要修改节点,还要创造节点,比如自动导入foo
模块,也即是要在代码里插入:
import foo from 'foo';
此时就要构造这个节点
j.importDeclaration(
[j.importDefaultDeclaration(j.identifier('foo'))],
j.literal('foo')
);
jscodeshift
所有ast相关操作都来自于ast-types, 具体构造器参数可以查看ast-types
痛点
- 文档太少了,需要结合源码看一些用法,也可以看看一些例子:github.com/cpojer/js-c…
- 使用
api.jscodeshift
不能获得vscode
的api提示, 比如buider
的参数是什么。可以导入jscodeshift
看一下参数是什么:
const j = require('jscodeshift');
j.importDeclaration() // 看下参数
不过此j
非彼j
, 这个不能拿来用,会有问题