Laravel ElixirをWebpackと組み合わせて使う

Laravel Elixirを使ってみます。Laravel Elixirとはフロントまわり(SCSS、JavaScript)のプリコンパイル等の処理を行ってくれるツーリングです(テスト実行などもやってくれるみたい)。フロントのまわりのプリコンパイルは古くはgrunt、最近ではgulp、Webpackを利用しますが、Laravel Elixirではgulpをラップする形でより簡潔に設定できるようになっているようです。

なお、通常Laravel ElixirではJavaScriptのモジュール管理にBrowserifyを使うようですが、ぼくは最近はBrowserifyではなくWebpackを利用するようにしているのでWebpackが使えないかも試してみました。

環境の準備

こちらの記事を参考にしました。

他のフレームワークでも使える Laravel-Elixir

やることは簡単でLaravelを使っている場合はそのままnpm installするだけです。そして実際にビルドしたい時にgulpを実行します。ただ、Vagrantでgulpを実行するとエラーになってしまいました。gulp-notify: [Error in notifier] Error in plugin 'gulp-notify' not found: notify-sendというエラーが出ます。ターミナル通知でエラーが出るみたいです。Laracastsでも同じイシューが出ています。

gulp-notify: [Error in notifier] Error in plugin ‘gulp-notify’ not found: notify-send

ターミナル通知しようとしてそれがVagrantなのでコケてるようなのですが、下記の記事によるとvagrant plugin install vagrant-notifyしたりすると良いよと書いてあります。

Laravel Elixirを使ってみた(Advent Calendar 7日目)

やってみたんですが、ぼくの環境の場合なぜかVagrantが動かなくなってしまいました。手が掛かりそうだったので、一旦Vagrantで実行することは諦めてホストOSのOSXからLaravel Elixirを実行する事にしました。OSX側ではterminal-notifierを入れています。

$ brew install terminal-notifier

これで下記のようにうまく動きました。

$ gulp
[04:18:59] Using gulpfile ~/workspaces/petgo/vagrants/vagrantfiles/sitter/laravelchat/gulpfile.js
[04:18:59] Starting 'default'...
[04:18:59] Starting 'sass'...

Fetching Sass Source Files...
   - resources/assets/sass/app.scss


Saving To...
   - public/css/app.css

[04:19:00] Finished 'default' after 928 ms
[04:19:00] gulp-notify: [Laravel Elixir] Sass Compiled!
[04:19:00] Finished 'sass' after 1.04 s

プリコンパイルが完了するとSlackみたいな感じで通知が来るのでカッコよいです。

Webpackを試してみる

BrowserifyのかわりにWebpackが使えないか調べてみたら下記のパッケージを見つけました。他にもパッケージがありますが、ぼくの場合は最終的に下記のパッケージでうまく動きました。

laravel-elixir-webpack

$ npm install --save laravel-elixir-webpack underscore

シンプルな構成の場合は下記のような形で設定できます。

var elixir = require('laravel-elixir');
require('laravel-elixir-webpack');

elixir(function(mix) {
    mix.sass('app.scss')
      .webpack('app.js');
});

試したコミットは下記のような感じです。public/jsにあったソースファイルをresources/assets/js配下に移動して、それから今までjQueryはHTML側でロードされたものを使っていましたが、npmパッケージからrequireして読みこむように変更しました。

larval elixir and webpack

複数ファイルに分割して出力する

次に、プリコンパイル後のスクリプトを自分で開発した部分とjQueryに分けて出力します。Webpackが必要になりました。

nom install --save webpack

webpack.config.jsを作成します。gulpfile.jsのほうに直接書き入れてもOKです。

var webpack = require('webpack');

module.exports = {
  entry: {
    app: './resources/assets/js/app.js',
    vendor: ['jquery']
  },
  output: {
    filename: '[name].js'
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      names: ['vendor']
    })
  ]
}

gulpfile.jsでwebpack.config.jsを読み込んでオプションとして渡します。

elixir(function(mix) {
    mix.sass('app.scss')
      .webpack('app.js', require('./webpack.config.js'));
});

対応するコミットは下記です。

split to two compiled scripts using webpack

これでgulpを実行するとapp.jsとvendor.jsという2つのファイルに分割されて出力されました。

ES2015に対応する

Babelも問題なく使えました。Babelで必要になるパッケージをインストールします。

$  npm install --save babel-core babel-loader babel-preset-es2015 babel-plugin-transform-runtime

webpack.config.jsを書き換えます。

var webpack = require('webpack');

module.exports = {
  entry: {
    app: './resources/assets/js/app.js',
    vendor: ['jquery']
  },
  output: {
    filename: '[name].js'
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      names: ['vendor']
    })
  ],
  module: {
    loaders: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        loader: 'babel',
        query: {
          cacheDirectory: true,
          plugins: ['transform-runtime'],
          presets: ['es2015']
        }
      }
    ]
  }
}

JavaScriptも下記のようにES2015に書き換えました。

import $ from 'jquery';

const $btnSend = $('#btnSend');
$btnSend.click(() => {

  const $selectTo = $('#selectTo');
  const $textMessage = $('#textMessage');
  const data = {
    'message': $textMessage.val()
  };

  console.log('data', data);

  $.ajaxSetup({
    headers: {
      'X-CSRF-TOKEN': $('#csrfToken').val()
    }
  });
  $.ajax({
    type: 'POST',
    url: '/messages/to/' + $selectTo.val(),
    data: data,
    success: (message) => {
      console.log('success', message);
      $textMessage.val('');
    }
  })

});

const $selectTo = $('#selectTo');
$selectTo.change(() => {
  const userId = $selectTo.val();
  $.ajax({
    type: 'GET',
    url: '/messages/to/' + userId,
    success: (messages) => {
      messages = JSON.parse(messages);
      console.log('success', messages);
      var $ulMessages = $('#ulMessages');
      $ulMessages.empty();
      var elmMessages = messages.map((message) => {
        return `<li>${message.message} by ${message.from_user.name} at ${message.created_at}</li>`;
      });
      $ulMessages.append(elmMessages);
    }
  })
});

const $ulMessages = $('#ulMessages');
const socket = io.connect('//192.168.33.40:3000');
socket.on('chat', (message, fn) => {
  console.log('on chat', message);

  if (+$selectTo.val() === +message.to_user.id || +$selectTo.val() === +message.from_user.id) {
    $ulMessages.prepend(`<li>${message.message} by ${message.from_user.name} at ${message.created_at}</li>`);
  }
});

gulpを実行するBabelによってES5に変換されて問題なくビルドされました。

対応するコミットは下記です。

webpack with babel

ちなみにgulp --productionと実行するとミニファイされてビルドされます。これはWebpack側では何もしてませんが、Laravel Elixirがやってくれるようです。便利ですね!