?私はクラスを抽出するために使用しないビジター方法
呼び出し - ?intl.formatMessage(本当にそれCallExpressionです)
はい、それはCallExpression
で、メソッド呼び出しのための特別なASTノードがに比べてありません関数呼び出し、変更する唯一のものは受信側(呼び出し先)です。あなたがASTの外観を疑問に思っているときはいつでも、あなたは素晴らしいAST Explorerを使用することができます。ボーナスとして、変形メニューでBabelを選択することでASTエクスプローラでBabelプラグインを作成することもできます。
formatMessageの呼び出しを検出するにはどうすればよいですか?私は別のAST表現を持っているあなたは、他のケースをカバーする必要があるだろう本当のプラグイン(例えばintl["formatMessage"](arg)
)のために、intl.formatMessage(arg)
への正確な呼び出しに焦点を当てる簡潔にするために
。
最初に、着信先がintl.formatMessage
であることを確認します。ご存知のように、これは単純なオブジェクトプロパティアクセスで、対応するASTノードはMemberExpression
と呼ばれます。ビジターは一致するASTノードCallExpression
をこの場合path.node
として受信します。つまり、path.node.callee
がMemberExpression
であることを確認する必要があります。ありがたいことに、それはbabel.types
がisX
の形式でメソッドを提供するので、それは非常に単純です。ここで、X
はASTノードタイプです。
if (t.isMemberExpression(path.node.callee)) {}
今、我々はそれがobject.property
に対応object
とproperty
を持ってMemberExpression
、だということを知っています。したがって、が識別子intl
とproperty
の識別子formatMessage
であるかどうかを確認できます。このためには、isIdentifier(node, opts)
を使用します。これには、指定された値を持つプロパティがあることを確認できる2番目の引数があります。すべてのisX
メソッドは、ショートカットを提供する形式です。詳細は、Check if a node is a certain typeを参照してください。また、ノードがnull
またはundefined
でないことを確認するので、技術的には不要ですが、別のタイプを別の方法で処理することもできます。私は、コールのパラメータの数を検出するにはどうすればよい
if (
t.isIdentifier(path.node.callee.object, { name: "intl" }) &&
t.isIdentifier(path.node.callee.property, { name: "formatMessage" })
) {}
?
CallExpression
引数のASTノードのアレイであるarguments
性質を有する(交換があっフォーマットされている場合に発生するように想定されていません)。繰り返しになりますが、簡潔にするために、私はちょうど1つの引数で呼び出しを検討しますが、実際にはintl.formatMessage(arg, undefined)
のようなものを変換することもできます。この場合、単に長さを確認するのはpath.node.arguments
です。また引数がオブジェクトであることが必要なので、ObjectExpression
をチェックします。
if (
path.node.arguments.length === 1 &&
t.isObjectExpression(path.node.arguments[0])
) {}
ObjectExpression
はObjectProperty
ノードの配列であるproperties
性質を有しています。技術的には、id
が唯一のプロパティであることを確認できますが、ここではスキップして、代わりにid
プロパティを探します。 にはkey
とvalue
があり、Array.prototype.find()
を使用して、キーが識別子id
であるプロパティを検索することができます。
const idProp = path.node.arguments[0].properties.find(prop =>
t.isIdentifier(prop.key, { name: "id" })
);
idProp
が存在する場合、それ以外の場合はundefined
なり、対応ObjectProperty
あろう。 undefined
でない場合は、ノードを置き換えます。
どのように交換しますか? (intl.formatMessage({ID: '何か'}?)intl.messagesには、[ '何か']
我々は全体CallExpression
を交換したいとバベルはpath.replaceWith(node)
を提供して残された唯一のものは、ASTを作成しています。その代わりに、intl.messages["section.someid"]
がASTでどのように表現されているかを理解する必要があります。intl.messages
はMemberExpression
とちょうど同様ですobj["property"]
はで表されるASTの計算されたプロパティオブジェクトアクセスです。 computed
プロパティがtrue
に設定されています。つまり、intl.messages["section.someid"]
がMemberExpression
で、MemberExpression
がオブジェクト。
は、これら二つの意味的に等価であることを忘れないでください:
intl.messages["section.someid"];
const msgs = intl.messages;
msgs["section.someid"];
我々はt.memberExpression(object, property, computed, optional)
を使用することができますMemberExpression
を構築します。 intl.messages
を作成するには、intl
をpath.node.callee.object
から再利用することができます。同じオブジェクトを使用しますが、プロパティを変更します。プロパティの場合は、という名前のmessages
を作成する必要があります。
t.memberExpression(path.node.callee.object, t.identifier("messages"))
だけ最初の二つの引数を必要とし、残りの我々は、デフォルト値(オプション用computed
ためfalse
とnull
)を使用しています。今度はMemberExpression
をオブジェクトとして使用することができ、先に計算したidProp
で利用可能なid
プロパティの値に対応する計算されたプロパティ(3番目の引数はtrue
に設定されています)をルックアップする必要があります。最後に、CallExpression
ノードを新しく作成したノードに置き換えます。
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で見つけることができます。
これまで数回述べたことがありますが、これは単純なバージョンであり、多くのケースがカバーされていないため、変換に適しています。より多くのケースをカバーすることは困難ではありませんが、それらを識別してASTエクスプローラに貼り付けると、必要なすべての情報が得られます。オブジェクトが{ "id": "section.someid" }
代わりの{ id: "section.someid" }
ある場合たとえば、それは、変換されたが、これをカバーすることもIdentifier
以外StringLiteral
をチェックするのと同じくらい簡単です、このようにされることはありません。
const idProp = path.node.arguments[0].properties.find(prop =>
t.isIdentifier(prop.key, { name: "id" }) ||
t.isStringLiteral(prop.key, { value: "id" })
);
私も導入していませんでした付加的な認知負荷を避けるために意図的に任意の抽象化を行うため、条件は非常に長く見える。
参考リソース:
マイケルは、非常に最良の答えありがとうございました。私は今日それを試してみます。 :) –
もう一度ありがとう。あなたが私の問題を解決するのを助けたり、プラグインをする方法を教えたりしなかったかもしれません。私はあなたの回答をここではリアクションintl github問題https://github.com/yahoo/react-intl/issues/1044で共有しました –