読者です 読者をやめる 読者になる 読者になる

UUUM攻殻機動隊

UUUMのエンジニアによる技術ブログです

BabelでES6で書いて、webpackでビルドして、mochaでテスト書いて、power-assertでassertの出力を見やすくして、karmaで複数ブラウザのテストを自動化して、カバレッジを出力するようにした

おはこんばんちは!! 尾藤 a.k.a. BTOです。

UUUMでは業務の効率改善に kintone を導入しようとしているのですが、kintone の機能を拡張するには Javascript でプログラムを書く必要があります。 そこで、Javascript の開発環境を構築したので、何をやったのかをまとめてみました。

具体的には、 BabelでES6で書いて、webpackでビルドして、mochaでテスト書いて、power-assertでassertの出力を見やすくして、karmaで複数ブラウザのテストを自動化して、カバレッジを出力するようにしました。 お前は何を言っているんだと思うかもしれませんが、僕も何を言っているのかわかりません。

Babel

言わずと知れた ES6 用のトランスパイルツールです。 ご存知の方も多いと思うので、詳細は割愛しますね。

Babel の導入は npm でインストールするだけ

% npm install babel babel-preset-es2015 --save-dev

babel-preset-es2015 は ES6(別名ES2015)用のプリセットです。 これでES6で記述できるようになります。

Babelは .babelrc というファイルがあると、その設定を読み込んでくれます。 ここに es2015 のプリセットを使用するように設定します。

{
  "preset": ["2015"]
}

Polyfill

Babel でトランスパイルすることで、古いブラウザでも動作する Javascript を出力できるようになったのですが、古いブラウザの標準オブジェクトの中には ES6 に対応していないものがあります。 そのために標準オブジェクトを拡張する Polyfill を導入します。

% npm install babel-polyfill --save-dev

わざわざ Polyfill が別ライブラリに分かれているのには、ちゃんと意味があります。 標準オブジェクトを拡張することは、既存の環境と互換性を失うことになります。 互換性を失うのは危険を伴うので、別ライブラリに分けているのです。 Polyfill を導入するときは、標準オブジェクトを拡張していることをちゃんと認識したうえで使う必要があります。

babel-runtime

上記のように Polyfill には標準オブジェクトを拡張するという問題があるので、それをしないようにする実装もあります。 それが babel-runtime です。 babel-runtime は標準オブジェクトの対応していない機能を、その場で別のコードにトランスパイルして解決します。

Polyfill を使うのか babel-runtime を使うのかは、決めの問題だと思うので、どちらかを導入すれば良いと思います。 Polyfill を導入しているケースが多いように思われますので、個人的には Polyfill でいいんじゃないかと思っています。

webpack

ブラウザで実行するには、複数に分けられたjsファイルを1つにして読み込まないといけません。 複数ファイルを読み込むことももちろんできますが、依存関係の解決とか大変ですよね。 そのためにビルドツールを使うわけですが、今回は webpack を採用しました。

webpack だと browserify と違って複数ファイルを生成するのが簡単にできるそうです。 (browserify使ったことないので、詳細はわかりません)

% npm install webpack babel-loader --save-dev

インストールすれば、すぐに webpack が使えます。 簡単ですね。 webpack は様々形式のデータを扱うのに、プラグイン形式で対応しており、babel を使う場合は babel-loader を使います。

これだけだと webpack がどのようにファイルをビルドするかがわからないので、設定ファイルを書きます。

webpack.config.js

webpack は設定ファイルを webpack.config.js で書くのですが、どうせなら設定ファイルも ES6 で書きたいですよね。 これをやるのはとても簡単で、bable-register をインストールすれば、 webpack.config.babel.js というファイルで設定を ES6 で書くことができます。

% npm install babel-register --save-dev

interpret

babel-register をインストールするだけで、なぜ ES6 で設定ファイルを書くことができるようになるのでしょうか。 そこには interpret というライブラリの存在があります。

interpret の仕組みはとても簡単で、設定された拡張子に対して動的な前処理を適用するだけです。 interpret の方で、 .babel.js という拡張子に対しては、 babel-register で変換して処理するという風に定義されていますので、 webpack.conf.babel.js でも問題なく処理できるというわけです。

つまり、interpret に対応しているツールに関しては、babel-register をインストールすれば、もれなく ES6 にも対応できるようになります。

JSのように逐次解釈して実行する言語をインタプリタ言語と言いますが、 interpret はインタプリタと同じ意味(品詞は違う)の単語で、とてもわかりやすい名前ですね。

webpack.config.babel.js

webpack.config.babel.js には次のような設定を書きました。

export default {
  entry: {
    main: './src/main.js',
    kintone: './src/kintone.js',
  },
  output: {
    filename: '[name].js',
    path: `${__dirname}/web`,
  },
  module: {
    loaders: [{
      test: /\.js$/,
      include: [
        `${__dirname}/src`,
        `${__dirname}/test`,
      ],
      loader: 'babel',
    }],
  },
};

entry には、どのファイルをビルドするのかを指定します。 ここでは、2つ(main, kintone)を指定しています。 このように、複数のファイルを指定できるのが、webpackの良いところですね。

output には、どのように出力するかを定義します。 ここでは web ディレクトリにビルド結果を出力するようにしています。

module.loader でローダの指定をします。 ここでは、 .js のファイルに対して、 babel-loader を使用して babel でトランスパイルするように指定しています。 include ではローダの対象となるファイルを指定しています。

相対パス地獄を解決する

相対パス地獄は ES6 だけでなく他の言語でも見られますが、みなさんも

require('../../../../foo.js');

のような相対パスでのモジュールの読み込みで苦い思いをした経験があるのではないでしょうか。 これは webpack の機能を使うことで解決できます。

webpack の設定の resolve.root でディレクトリを指定すれば、そのパスを起点としたモジュールが読み込めるようになります。 webpack.config.babel.js に記述を追加します。

  resolve: {
    root: [
      `${__dirname}/src`,
    ],
  },

この設定だと、例えば src/uuum/foo.js は、

import from 'uuum/foo';

のような記述で読み込みができるようになります。

ここで注意しないといけないのが、この機能は webpackが提供している機能 だというところです。 なので、 babel 単体ではうまく処理することができませんので、 webpack を通さない処理だと当然使用することができません。 例えば、 webpack.config.babel.js は、 webpack を通さずに直接 babel でトランスパイルするので、もし src/ 以下のファイルを読み込んでいた場合は、import に失敗します。(NODE_PATHを設定すれば大丈夫)

ソースマップ

Babel で ES6 で記述できるようになったのは良いのですが、ブラウザ上で実際に実行されるのは、トランスパイルされた Javascript なので、ブラウザ上でエラーが出た場合に元のソースコードのどこでエラーが発生したのかがわかりません。 それがわかるようにするためにソースマップを生成しておくと、オリジナルのソースコードのどこに対応するのかがわかるようになるので、デバッグが楽になります。

ソースマップを追加するのは devtoolinline-source-map を設定するだけです。

  devtool: 'inline-source-map',

名前の通り、この設定を追加すると生成されるファイルの中にソースマップが設定されます。 他にもソースマップの生成方法はいろいろありますが、詳しくはマニュアルをご参照ください。

ここでは1つ課題があって、本番環境ではソースマップは必要ないんですが、現状だと本番用でもソースマップが追加されてしまっています。 なので、本番環境と開発環境でわけないといけないのですが、 webpack.config.prod.babel.js の用に環境ごとに設定ファイルを分けるのが一般的なようですね。 これはおいおい解決していこうと思ってます。

Mocha

テストツールには Mocha を採用しました。 Mocha はシンプルな TDD/BDD 対応のテストツールです。 テストに必要な基本的な部分のみ実装していて、 assertion ですら外部のライブラリを使用します。 そのため時流に乗って適切なライブラリと組み合わせることができ、その点が気に入っています。

Mocha はインストールするだけで、すぐに使えるようになります。

% npm install mocha --save-dev

テストディレクトリは標準の test/ にします。

test/mocha.opts に Mocha のコマンドラインのオプションを書いておくと、自動的に適用されるようになります。 こうしておけば、 mocha コマンドを叩くだけでテストが実行できるようになります。

mocha.opts には次のような設定を書きました。

--compilers js:babel-register
--recursive
--require babel-polyfill
--require src/kintone

1行目の --compilers js:babel-register を指定しておくと、テストファイルを読み込む時に、 babel-register で前処理してくれるようになります。 つまり、テストコードも ES6 で記述できるようになります。

--recursive はその名の通り。

あとは Polyfill が必要なので読み込んでいるので、このプロジェクトは kintone ようなので、それ用のライブラリを読み込んでいます。

mocha コマンドは次のように実行します。

NODE_ENV=test NODE_PATH=$NODE_PATH:$PWD/src mocha

テストなので、 NODE_ENV に test をセットします。

NODE_PATH は先程の webpack のところでやった、相対パス地獄を回避するためにライブラリの読み込みパスを指定しないといけません。

power-assert

Mocha は assertion のライブラリを持っていないと書きましたが、 assertion には power-assert を採用しました。 power-assert を使うと、 assert で失敗した時の出力が超絶にわかりやすくなります。

どのような出力になるかは本家をご参照ください。(power-assert)

power-assert をインストールします。

% npm install power-assert babel-plugin-espower --save-dev

power-assert はそのままでは babel に対応していないので、 babel-plugin-espower も導入します。

.babelrc に次の設定を追加します。

  "env": {
    "test": {
      "plugins": [
        "espower",
      ]
    }
  }

power-assert はテスト環境だけで、動作すれば十分なので、テスト環境のみ espower を追加するようにしています。

Karma

ごめん、書くの疲れた。 また今度書きます。

感想

相変わらず JS 界はツール類が多くて大変というのが正直な感想です。 ブラウザの違い、バージョンの違い、サーバ・クライアントの違いと複雑な条件が絡み合っているので仕方がないとは思うのですが。。。

しかし、このあたりの仕組をちゃんと把握したうえで使いこなすのは、本当に大変だと思うし、今の環境も1年後には古くなっているのを考えると少し憂鬱な気持ちになってしまいます。

とはいえ、現環境ではまずまずの状態を作れたのではないかと思うので、しばらくはこの環境で開発を進めていこうと思っています。

まとめ

UUUMでは富士登山(任意参加!!)ができます。

f:id:masatobito:20160915160031j:plain

エンジニアで参加したのは僕だけでしたがw

クラウド電話を使ってモーニングコールで起床するリア充生活

spin13

エンジニアのspin13です.

今回はクラウド電話を使った電話端末への発信についてご紹介します.

続きを読む

wercker で Bitbucket のプライベートリポジトリをビルド!

新人エンジニアのハトネコエです。

UUUMでは毎週月曜日に社内勉強会をしていて、今週の担当は私なのですが、
昨日月曜日は台風のためお休みしましたので残念ながらおこなえず……

いえ、ごめんなさい!
実は土日かけて勉強会の準備してましたけどスライドはまだできてません!

土日は新幹線やホテルで、そして月曜は一日中、翌朝4時頃までがっつりやっていたのですが、
まだ発表できる段階まで至っていません。

これはテーマの決定をミスった気が……。

予定より軽めのテーマに変えることも考えつつ、
今日はその下準備の中で得たものを記事にしてみます。

個人開発はお金がない!

お金がないです。

でもソースは git で管理したい!

最近は GitHub の価格体系が変わり、
プライベートリポジトリをいくつ使用しても個人だと月800円程度、学生だと0円! と、とても安くなりました。
(以前はプライベートリポジトリの個数で月の料金が変わっていました)

とはいえ、Bitbucket のプライベートリポジトリは無料ですから、
個人開発やスタートアップ企業で使用しているところは多いことでしょう。

GitHub じゃないから Circle CI は使えないけど、カンタンなCIツールを使いたい!

そんなワガママを叶えてくれるのが、wercker です。

続きを読む

Railsの風が吹いたら確認したい、Rubyのモジュールシステム

こんにちは、入社して3ヶ月くらい経ちましたnakahashiです。

六本木のポケモンGOプレイヤーは気前がよく、職場周辺のポケストップはいつも花びらが舞っているので、キャッキャウフフな生活を過ごしています。

さて、UUUM攻殻機動隊では、最近Railsの風が吹き始めています。業務でRubyをどっぷり使い込んだことは個人的にはないので、言語の基本を見なおしているところです。

Rubyの基礎を勉強するにあたり非常に重要な、Rubyのモジュールシステムについてまとめてみました。

続きを読む

【小ネタ】Rails5でlogrageの出力にbacktraceを足す

nazoです。

Rails でログを出力する際に、書式とかをプラガブルに設定できる lograge という gem があるのですが、デフォルトで backtrace(スタックトレース) を含めてくれなくて困っていました。

従来はモンキーパッチで結構苦しい対応を迫られていたようですが、Rails5 から、payload に例外オブジェクトがそのまま入るようになった ので、

  config.lograge.custom_options = lambda do |event|
    { backtrace: event.payload[:exception_object].try(:backtrace) }
  end

で簡単に backtrace を挿入できるようになりました。便利ですね。

Angularfire2でつくるリアルタイムWEBアプリ

こんにちは、エンジニアのタナカです。

本日の社内勉強会ではFirebaseクライアントライブラリのAngularfire2を紹介させていただきました。
一昨年にGoogleに買収されて有名になったmBaasのFirebaseですが、社内でも意外と使ったことが無い人が多いようです。 無料でも利用できるので、個人での開発にもピッタリだと思います!

無料で利用できる範囲は以下のとおりですが、結構太っ腹な設定だと思います。

f:id:tnuuu:20160801112513p:plain

https://firebase.google.com/pricing/

リアルタイムWEBアプリについて

チャットアプリのSlackが有名なので皆さんご存知だと思いますが、以下の特徴があります。

続きを読む

徳丸本から学ぶクロスサイト・スクリプティング基礎

どうも、えんじにあのやまぐちです。

今回の内容ですが、前回に引き続き「安全なWebアプリケーションの作り方」からクロスサイト・スクリプティングの基礎を題材に社内勉強会で発表したので少しだけ書こうと思います。

http://www.amazon.co.jp/exec/obidos/ASIN/4797361190/bz2jp-22/ref=nosim/www.amazon.co.jp

クロスサイト・スクリプティング (XSS)

XSSは外部からの入力に対して表示が変化する箇所がある場合に発生します。
攻撃者が用意したスクリプトがサイト利用者のブラウザ上で実行
されることにより、クッキー値が盗まれ利用者がなりすましの被害に遭う、サイト利用者の権限でWebアプリケーションの機能を悪用される、といったことが起きてしまいます。
また、WEBサイト上に偽の入力フォームを表示され、フィッシングにより利用者の個人情報を盗まれるといったこともあります。

攻撃手法

  • クッキー値の盗み出し
  • JavaScriptによる攻撃
  • 画面の書き換え

クッキー値盗み出しの流れ
罠サイトのiframe内に脆弱なサイトを表示
脆弱なサイトはXSSによりクッキー値をクエリ文字列につけて遷移
情報収集ページは受け取ったクッキー値をメールで攻撃者に送信

続きを読む