babelプラグイン入門

4518 ワード

バベルについて
BabelはJavaScriptコンパイラです.
Babelは、ECMAScript 2015+バージョンのコードを後方互換性のあるJavaScript構文に変換するためのツールチェーンであり、現在および古いバージョンのブラウザまたは他の環境で動作することができる.
// Babel   : ES2015     
[1, 2, 3].map((n) => n + 1);

// Babel   : ES5          
[1, 2, 3].map(function(n) {
  return n + 1;
});
バベルは何をしましたか
babelの翻訳過程も三つの段階に分けられます.この三つのステップは具体的に:
解析パース
コード解析を抽象的な構文ツリー(すなわちAST)に生成します.
Trans formを変換
ASTに対して一連の操作を変換して、babelはASTを受け取って、babel-traverseを通じて(通って)それを遍歴して、この過程の中で添加、更新と除去などの操作を行います.
ジェネレーション
変換したASTをJSコードに変換し、使用したモジュールはbabel-generatorです.
私達が編纂したbabelプラグインは主に第二段階の転換過程の仕事に集中しています.コードの転化と開拓に専念しています.解析と生成の下側の相関操作には対応するモジュールサポートがあります.ここでは主に何をすればいいのかを理解しています.
準備工作
正式に始まる前に、二つの概念を紹介する必要があります.
Visitors訪問者
訪問者はAST遍歴のための言語横断のパターンです.簡単に言えば、それらはオブジェクトであり、ツリー構造において特定のノードを取得する方法を定義している.抽象的な話ですから、例を見てみましょう.
const MyVisitor = {
  Identifier(path) {
    console.log("Im Identifier");
  },
  FunctionDeclaration(path){
    console.log("Im FunctionDeclaration");
  }
};
これは簡単な訪問者であり、AST遍歴中に使用されると、Identifierにツリー内で出会うたびにIdentifier()方法を呼び出し、FunctionDeclarationに出会うとFunctionDeclaration()方法を呼び出す.
パスVisitorsは、各ノードを巡回するときに、ノードの情報とノードと所在の位置を含み、特定のノードを修正するために、pathパラメータを導入する.pathと呼ばれるのは、現在のノードオブジェクトではなく、2つのノード間で接続されているオブジェクトを表しているからです.
より具体的なAPIはBabelプラグインマニュアルを見ることができます.
プラグインの書式
プラグインの完全なフォーマットは以下の通りです.
export default function({ types: t }) {
  return {
    pre(state) {  //      
         
    },
    visitor: { //    
      VariableDeclaration(path) {
        // ... ...
      }
    },
    post(state) { //     
      
    },
  };
}
注意
ここには注目すべき問題があります.すべてのBabelプラグインは同じプロセスを共有します.
つまり、彼らのノードに対する処理は相互に影響しうる.
例えば、すべての方法にtry-catchを追加する必要があります.FuntionDeclaration訪問を定義して、すべての関数にアクセスする必要があります.
しかし、他のプラグインでは、例えばBabel-presetはいくつかの補助関数を生成します.これらの補助関数は私たちの訪問者にもアクセスされます.しかし、私たちはソースの処理が必要です.
これらの元のコードにないノードへのアクセスを避けるために、筆者は今も最善の方法を見つけていません.次のような試みがあります.
  • は、sourceMapを使用して、ノードがsourceMapの中で見つけられない場合、生成されたコードと判断する.しかし、sourceMapはwebpackを使って取得する必要があります.(もっといい方法があるかもしれませんが、まだ見つけられていません.ご指摘ください.)このようにプラグインの配置が複雑で、汎用性が足りません.
  • は、path.node.locを介して助けられる.この方法は正確ではなく、一部の生成ノードにもlocation属性が含まれている.
  • は、ノードが巡回を開始する前に、手動で追加のエルゴードを追加し、処理が完了したら他のプラグインによって処理される.筆者が今使っている方法です.今のところは比較的正確ですが、余分な遍歴が必要です.
  • 開始
    まずVisitorを定義して方法声明にアクセスします.
    const funcVisitor = {
      FunctionDeclaration(path) {
        const functionBody = path.node.body; //      body
        if (functionBody.type === 'BlockStatement') { //   block
          const body = functionBody.body; //      block body
          path.get('body').replaceWith(wrapFunction({
            BODY: body,
            HANDLER:t.identifier('console.log')
          }))
        } 
      }
    }
    
    babel-templateを介してASTノードを素早く生成します.
    const wrapFunction = template(`{
      try {
        BODY
      } catch(err) {
        HANDLER(err)
      }
    }`);
    
    次に組み立てます
    const t = require('@babel/types');
    const wrapFunction = template(`{
      try {
        BODY
      } catch(err) {
        HANDLER(err)
      }
    }`);
    const funcVisitor = {
      FunctionDeclaration(path) {
        const functionBody = path.node.body; //      body
        if (functionBody.type === 'BlockStatement') { //   block
          const body = functionBody.body; //      block body
          path.get('body').replaceWith(wrapFunction({
            BODY: body,
            HANDLER:t.identifier('console.log')
          }))
        } 
      }
    }
    module.exports = function () {
      return {
        pre(file){ //       
          file.path.traverse(funcVisitor); //        
        }
      };
    };
    
    使用webpack.config.js
    const myPlugin = require('xxx')
    module: {
        rules: [
          {
            test: /\.js$/,
            exclude:/node_modules/,//   node_module  
            loader:'babel-loader',
            options:{
              plugins:[myPlugin]
            },
          },
        ]
      },
    
    テストして、コードを入力します.
    function Foo(){
      console.log('Im foo')
    }
    
    出力
    function Foo(){
      try{
       console.log('Im foo') 
      }catch (err) {
        console.error(err)
      }
    }
    
    簡単な方法声明のためにtry-catchを追加するbabelプラグインが開発されました.ただ、試験中の簡単な状況に対処できるだけです.
    筆者は、比較的完全なプラグインを作成しました.Promise.catchを追加することもできます.また、方法に対して、キャプチャ文を注入することもできます.設定も比較的柔軟で、ディレクトリとファイルフィルタをサポートします.
    不完全なところは補充してください.
    ソーストランスポート