我使用哪个访问者方法提取类调用-intl.formatMessage(它真的是CallExpression吗)?
是的,它是一个
CallExpression
AST Explorer
. 作为奖励,您甚至可以通过在Transform菜单中选择Babel,在AST浏览器中编写Babel插件。
为了简单起见,我将只关注
intl.formatMessage(arg)
,对于一个真正的插件,您还需要涵盖其他情况(例如。
intl["formatMessage"](arg)
)具有不同的AST表示。
第一件事是确定被叫方是
intl.formatMessage
MemberExpression
调用表达式
在这种情况下,作为
path.node
. 这意味着我们需要验证
path.node.callee
成员表达式
. 谢天谢地,这很简单,因为
babel.types
isX
哪里
X
是AST节点类型。
if (t.isMemberExpression(path.node.callee)) {}
成员表达式
object
property
对应于
object.property
. 所以我们可以检查一下
对象
是标识符
intl
和
所有物
标识符
formatMessage
isIdentifier(node, opts)
,它接受第二个参数,该参数允许您检查它是否具有具有给定值的属性。全部的
isX公司
Check if a node is a certain type
null
或
undefined
,所以
isMemberExpression
从技术上讲,这是不必要的,但您可能希望以不同的方式处理另一种类型。
if (
t.isIdentifier(path.node.callee.object, { name: "intl" }) &&
t.isIdentifier(path.node.callee.property, { name: "formatMessage" })
) {}
如何检测通话中的参数数量?(如果存在格式,则不应进行替换)
这个
有一个
arguments
属性,它是参数的AST节点的数组。再说一次,为了简洁起见,我只考虑只有一个参数的调用,但实际上,您也可以转换如下内容
intl.formatMessage(arg, undefined)
. 在这种情况下,它只是检查
path.node.arguments
. 我们还希望参数是一个对象,所以我们检查
ObjectExpression
.
if (
path.node.arguments.length === 1 &&
t.isObjectExpression(path.node.arguments[0])
) {}
一
ObjectExpression
properties
属性,它是
ObjectProperty
id
身份证件
所有物这个
key
value
,我们可以使用
Array.prototype.find()
搜索键为标识符的属性
.
const idProp = path.node.arguments[0].properties.find(prop =>
t.isIdentifier(prop.key, { name: "id" })
);
idProp
将是相应的
ObjectProperty
如果它存在,否则它将是
. 当它不是
未定义
我如何更换?(intl.formatMessage({id:'something})到intl.messages['something']?
我们想替换整个
调用表达式
path.replaceWith(node)
. 剩下的唯一一件事是创建应该替换的AST节点。为此,我们首先需要了解
intl.messages["section.someid"]
在AST中表示。
intl.messages
是一个
就像
是
obj["property"]
是计算属性对象访问,也表示为
在AST中,但具有
computed
属性设置为
true
. 这意味着
intl.messages[“section.someid”]
是一个
成员表达式
用一个
请记住,这两者在语义上是等价的:
intl.messages["section.someid"];
const msgs = intl.messages;
msgs["section.someid"];
构建
成员表达式
我们可以使用
t.memberExpression(object, property, computed, optional)
. 用于创建
国际消息
我们可以重复使用
从…起
path.node.callee.object
因为我们想使用同一个对象,但改变了属性。对于属性,我们需要创建一个
Identifier
messages
.
t.memberExpression(path.node.callee.object, t.identifier("messages"))
false
计算的
和
可选)。现在我们可以用它了
成员表达式
真的
身份证件
属性,该属性可在
我们早些时候计算过。最后,我们替换
具有新创建的节点。
if (idProp) {
path.replaceWith(
t.memberExpression(
t.memberExpression(
path.node.callee.object,
t.identifier("messages")
),
idProp.value,
// Is a computed property
true
)
);
}
完整代码:
export default function({ types: t }) {
return {
visitor: {
CallExpression(path) {
// Make sure it's a method call (obj.method)
if (t.isMemberExpression(path.node.callee)) {
// The object should be an identifier with the name intl and the
// method name should be an identifier with the name formatMessage
if (
t.isIdentifier(path.node.callee.object, { name: "intl" }) &&
t.isIdentifier(path.node.callee.property, { name: "formatMessage" })
) {
// Exactly 1 argument which is an object
if (
path.node.arguments.length === 1 &&
t.isObjectExpression(path.node.arguments[0])
) {
// Find the property id on the object
const idProp = path.node.arguments[0].properties.find(prop =>
t.isIdentifier(prop.key, { name: "id" })
);
if (idProp) {
// When all of the above was true, the node can be replaced
// with an array access. An array access is a member
// expression with a computed value.
path.replaceWith(
t.memberExpression(
t.memberExpression(
path.node.callee.object,
t.identifier("messages")
),
idProp.value,
// Is a computed property
true
)
);
}
}
}
}
}
}
};
}
完整的代码和一些测试用例可以在中找到
this AST Explorer Gist
{ "id": "section.someid" }
而不是
{ id: "section.someid" }
StringLiteral
除了一个
标识符
const idProp = path.node.arguments[0].properties.find(prop =>
t.isIdentifier(prop.key, { name: "id" }) ||
t.isStringLiteral(prop.key, { value: "id" })
);
我也没有故意引入任何抽象,以避免额外的认知负荷,因此条件看起来很长。
有用的资源: