Babel プラグインを TypeScript で開発する
Babel のプラグイン開発は、JavaScript や Flow/TypeScript のノードに対応した多くの API があり、API リファレンスを調べるだけでも面倒。どうせなら、VSCode の強力なサポートが得られる TypeScript で書きたい。
ということで、どんなふうに TypeScript で Babel プラグインを書いているかと、いくつか注意する点を紹介する。
パッケージ導入
特に複雑なことをしなければ、@babel/core
だけで十分
$ npm i @babel/core
開発用に TypeScript コンパイラと Babel CLI。あとは必要な型定義を導入
$ npm i typescript @babel/cli @types/babel__core
@types/babel-core
というパッケージも存在するので注意。 こちらの定義は古いので、@types/babel__core
を入れましょう。
コード
最低限、types と NodePath, PluginObj の型があればいい。
import { types as t } from '@babel/core';
import type { NodePath, PluginObj } from '@babel/core';
Visitor はこんな感じ。NodePath
に適切な型パラメータを与えれば、path.node
も期待通りの型になる。
const visitor = {
ClassDeclaration(path: NodePath<t.ClassDeclaration>) {
const { node } = path; // const node: t.ClassDeclaration
// ...
}
};
ノードの種別判定には、types.isXXX
() を使おう。TypeScript の type predicates のおかげで、types.isXXX()
を通過したあとのコードパスでは node が適切な型として認識される。
if (
t.isCallExpression(node.expression) && // node.expression: t.Expression
t.isSuper(node.expression.callee) && // node.expression: t.CallExpression
node.expression.arguments.length === 1 &&
t.isIdentifier(node.expression.arguments[0], { name: 'props' })
) {
// ...
}
最後に、このモジュールから返す関数の型定義は多少トリッキーだ。
function transform(_api: typeof t): PluginObj {
return {
name: 'babel-example-plugin',
visitor
};
}
export default transform;
_api
として渡されるのは @babel/core
の types
なのだが、これは、
import * as t from '@babel/types';
...
export { ParserOptions, GeneratorOptions, t as types, template, traverse, NodePath, Visitor };
このように、namespace imports されたシンボルがエクスポートされており、そのままでは型としてい使用できない。そのため、ここでは typeof
演算子を使っている(もっとも、この例では _api
を使っていないのだが...)
Discussion