tです。今回は、Babelを使ってJavaScriptコードをトランスパイルするときに便利な、babel-preset-envについて紹介します。
Babelは、ES2015、ES2016などの新しいJavaScriptの文法や機能を、対応していないJavaScript環境でも利用できるように、JavaScriptのコードを変換します。
最近では、ES2015、ES2016へのJavaScript環境の対応も進んでいますが、それと同時に、新しい文法や機能の策定も行われています。
babel-preset-envを使うことで、簡単に、既に対応している機能は変換を行わないようにすることができます。
環境準備
babel-preset-envを使ってどのように変換されるかを、「index.js」を使って見ていきます。以下の3つのファイルを、任意のディレクトリに準備してください。
1 2 3 4 |
/path/to/any/directory ├.babelrc ├index.js └package.json |
「package.json」では、以下のモジュールを導入する定義を記述しています。
- babel-cli: トランスパイルを行うコマンド
- babel-polyfill: Babelによる変換後のコードによっては必要となる関数などが定義されている
- babel-preset-env: 環境に合わせたトランスパイルを行うプリセット
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "name": "babel-env-example", "version": "1.0.0", "description": "", "scripts": { "build": "babel index.js --out-file compiled.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "babel-cli": "^6.18.0", "babel-polyfill": "^6.16.0", "babel-preset-env": "0.0.9" } } |
「index.js」では、いくつかの新しいJavaScriptの文法や機能を記述しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import 'babel-polyfill'; // アロー関数 const f = n => n > 1 ? f( n - 1 ) + f( n - 2 ) : n; // べき乗演算子 console.log( 3 ** 3 ); // async関数 const asyncFunc = async function () { await Promise.resolve( 1 ); return Promise.resolve( 2 ); } // クラス定義 class A { constructor() { this.a = 1; } } |
「.babelrc」では、babel-preset-envを使ってトランスパイルを行う定義をしています。
今回は、Node.jsでバージョンを変えた場合を想定して、どのようにトランスパイルされるかを見ていきます。
「”node”: “current”」とすることで、現在利用しているNode.jsのバージョンに合わせて、トランスパイルを行います。
1 2 3 4 5 6 7 8 9 |
{ "presets": [ [ "env", { "targets": { "node": "current" } } ] ] } |
ファイルの準備ができたら、「npm i」を実行して、モジュールをインストールします。
トランスパイル結果
違うバージョンのNode.jsでトランスパイルしたコードを見てみましょう。それぞれのバージョンのNode.jsにしてから、「npm run build」してください。そうすると、トランスパイル後のコードが「compiled.js」に生成されます。
(Node.jsのバージョン変更は、NVMなどを使うと手軽です)
今回は、Node.js の 4.6.2と7.2.0で切り替えて、トランスパイルしました。
以下が、それぞれのバージョンでのトランスパイル後のコードです。
4.6.2でトランスパイルしたcompiled.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
'use strict'; require('babel-polyfill'); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } // アロー関数 var f = function f(n) { return n > 1 ? f(n - 1) + f(n - 2) : n; }; // べき乗演算子 console.log(Math.pow(3, 3)); // async関数 var asyncFunc = function () { var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee() { return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.next = 2; return Promise.resolve(1); case 2: return _context.abrupt('return', Promise.resolve(2)); case 3: case 'end': return _context.stop(); } } }, _callee, this); })); return function asyncFunc() { return _ref.apply(this, arguments); }; }(); // クラス定義 var A = function A() { _classCallCheck(this, A); this.a = 1; }; |
7.2.0でトランスパイルしたcompiled.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
'use strict'; require('babel-polyfill'); function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } // アロー関数 const f = n => n > 1 ? f(n - 1) + f(n - 2) : n; // べき乗演算子 console.log(Math.pow(3, 3)); // async関数 const asyncFunc = (() => { var _ref = _asyncToGenerator(function* () { yield Promise.resolve(1); return Promise.resolve(2); }); return function asyncFunc() { return _ref.apply(this, arguments); }; })(); // クラス定義 class A { constructor() { this.a = 1; } } |
トランスパイル後コードの違いを見る
Node.js 4.6.2と7.2.0それぞれで、トランスパイルした後のコードについて、違いを見ていきます。アロー関数やクラス定義については、4.6.2では未対応ですが、7.2.0では対応されているので、4.6.2のみで変換されています。べき乗演算子についてはどちらのバージョンでも未対応なので変換されています。
async関数については、どちらも未対応なので変換は行われていますが、コードが少々異なっていることがわかります。7.2.0ではgenerator関数に対応しており、generator関数を使ったコードに変換されているのに対して、4.6.2ではgenerator関数にも未対応なので、switch-case文を使ったコードに変換されています。
async関数でのgenerator関数のように、できるだけ既にある機能を使うように変換されることがわかります。
最後に
今回は、Node.jsで使うコードで、バージョン間のコード変換をbabel-preset-envで行いました。JavaScriptもバージョンによって文法や機能が変わってきますが、変換してあげたら、手元にあるコードが未対応の環境でも、簡単に対応させることができます。覚えておいて損はないと思います!
( ちなみに、Node.jsのバージョンだけでなく、ブラウザを指定したトランスパイルもできます。babel-preset-envをご覧ください。 )
キー・ポイントでは、いろいろなライブラリを使いこなすエンジニアを募集しています。