LoginSignup
27
22

More than 5 years have passed since last update.

babelを使ってES2015でJScriptを書こう

Last updated at Posted at 2016-06-08

Windowsで使えるJScriptはES3ぐらいまでしか使えず、非常につらいです。
まあそもそもJscriptを書くこと自体あまりないと思いますが。

今回はbabelとwebpackを使ってES2015+でJScriptを書けるようにしてみました。

準備

パッケージインストール

$ npm i -D json3 babel-core babel-loader babel-polyfill babel-preset-latest \
babel-plugin-transform-es3-member-expression-literals babel-plugin-transform-es3-property-literals \
babel-plugin-transform-jscript \
imports-loader webpack@1.13.2

webpackは1.13.2を使用します。
2系はie8のサポートを切るらしいので、jscriptが動かないと思います。
また、1.13.3以降は依存しているUglifyJS 2が2.7系で、圧縮したコードがie8でうまく動きません。
UglifyJS 2のバグが直れば1.14でも大丈夫だと思います。

JScriptではJSONオブジェクトが存在しないため、json3をインストールして使えるようにします。
他はwebpackとかbabel関連のパッケージです。

webpack.config.js

webpack.config.js
module.exports = {
  entry: __dirname + '/script.js',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },

  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel',
        query:{
          presets: ['latest']
        }
      },
      {
        test: /\.js$/,
        loader: 'babel',
        query: {
          plugins: [
            'transform-es3-member-expression-literals',
            'transform-es3-property-literals',
            'transform-jscript',
          ]
        }
      }
    ]
  },
  resolve: {
    extensions: ['', '.js']
  }
};

自分で書くコードにはbabel-preset-latestを適用します。
読み込まれるすべてのjsにはtransform-es3-member-expression-literalstransform-es3-property-literalstransform-jscriptを適用します。

JScriptでは以下のような、予約語のプロパティを使ったコードはエラーになってしまいます。

var hoge = {default: 'hoge'}; // エラー
WScript.Echo(hoge.default); // エラー

そのため、transform-es3-member-expression-literalstransform-es3-property-literalsを適用し、
以下のようなコードに自動的に変換されるようにします。

var hoge = {'default': 'hoge'}; // OK
WScript.Echo(hoge['default']); // OK

transform-jscriptはJScriptのバグを回避してくれるものらしいです。

timers.js

JScriptにはsetTimeoutなどがありません。
しかし、htmlfileオブジェクトのsetTimeoutが使えるようですので、無理やり使えるようにしてあげます。
webpackでビルドされてもグローバル空間に展開されるようにvarなどの修飾子は付けていません。

timers.js
/*global ActiveXObject*/
const htmlfile = new ActiveXObject('htmlfile');
const window = htmlfile.parentWindow;

setTimeout = function wrapSetTimeout(f, t) {
  return window.setTimeout(f, t);
};
clearTimeout = function wrapClearTimeout(id) {
  return window.clearTimeout(id);
};
setInterval = function wrapSetInterval(f, t) {
  return window.setInterval(f, t);
};
clearInterval = function wrapClearInterval(id) {
  return window.clearInterval(id);
};

script.js

script.js
require('./timers.js');
JSON = require('imports?this=>window,this=>global!json3');
require('babel-polyfill');

(() => {
  const text = JSON.stringify([1, 2, 3].map(x => x * 10)).repeat(2);
  WScript.Echo(text); // [10,20,30][10,20,30]
})();

先ほどのtimers.jsを読み込んでいます。
また、polyfillを有効にするためにbabel-polyfillを読み込んでいます。
JSONオブジェクトは変数に入れて使います。
requireするときにimports-loaderの機能を使っています。
thisをwindowやglobalに設定して、ビルドインオブジェクトにJSON関連のメソッドを追加できるようにします。
また、グローバル空間に配置することで、他のライブラリからJSONが使えるようにしてあげます。

babel-polyfillではすべてのpolyfillを読み込むため、ファイルサイズが大きくなります。
もし使わないpolyfillは省きたい場合、core-jsのパッケージを読み込むようにするといいと思います。

$ npm i -D core-js
JSON = require('imports?this=>global!json3');
require('core-js/es5');

require('core-js/es6/object');
require('core-js/es6/function');
require('core-js/es6/array');
require('core-js/es6/string');
require('core-js/es6/promise');

1つのファイルにまとめる

$ $(npm bin)/webpack

dist/bundle.jsに出力されます。
npm run scriptに書いておくと便利だと思います。

package.json
{
  "scripts": {
    "build": "webpack",
    "watch": "webpack --watch"
  }
}
$ npm run build

JScriptではできなさそうなこと

getterとsetter

以下のコードのようなgetterやsetterは使えなさそうです。

var foo = {
  get bar() {
    return "bar";
  }
};

transform-es5-property-mutatorsを使えば変換はしてくれますが、
変換後に使用されるObject.defineProperty()のpolyfillがgetterやsetterに対応してません。

圧縮の設定

圧縮の設定で重要なのは、UglifyJsPluginに渡すオプションです。

webpack.config.js
plugins: [
  new webpack.optimize.OccurenceOrderPlugin(),
  new webpack.optimize.DedupePlugin(),
  new webpack.optimize.UglifyJsPlugin({
    compress: {
      screw_ie8: false,
    },
    mangle: {
      screw_ie8: false,
    },
    output: {
      screw_ie8: false,
      ascii_only: true
    }
  }),
  new webpack.optimize.AggressiveMergingPlugin(),
],

UglifyJS 2が2.7以降の場合(webpack 1.13.3以降を使用する場合)は、screw_ie8compressmangleoutputfalseに設定して、IE8のサポートを有効にします。
UglifyJS 2が2.6系の場合(webpack 1.13.2までを使用する場合)は、IE8のサポートがデフォルトで有効なので設定しなくても問題ないです。
IE8のサポートが有効な場合、予約語のプロパティは変換しないようにしてくれます。

また、outputではascii_onlytrueにしています。
Jscriptはユニコードをうまく扱うことができないため、asciiで出力するようにします。

おわりに

ES2015までの多くの機能が使えるようになりました。
もともとがES3なので、polyfillではカバーできない部分もあると思うので、エラーが発生するメソッドや構文などもあるかと思います。
その点に注意しながらコードを書くといいと思います。

参考

27
22
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
22