イントロダクション

Laravel Elixir(エリクサー:万能薬)は、Laravelアプリケーションのための基本的なGulpタスクを定義しており、きれいでスラスラかけるAPIを提供しています。Elixirは人気のあるCSSとJavascriptプリプロセッサー、それにテストツールもサポートしています。メソッドのチェーンを使用し、Elixirでアセットパイプラインを流暢に定義できます。例を見てください。

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

Gulpやアセットのコンパイルを始めようとして、頭がこんがらがっているようならLaravel Elixirを気に入ってもらえるでしょう。しかしながらアプリケーションの開発時に必要だというわけではありません。どんなアセットパイプラインツールを使用してもかまいませんし、使わなくても結構です。

インストールと準備

Nodeのインストール

Elixirを始める前、最初にNode.jsを開発機にインストール済みであることを確認してください。

node -v

Laravel Homesteadならデフォルトのままでも必要なものが全部そろっています。しかしVagrantを使っていなくてもNodeのダウンロードページを読めば、簡単にインストールできます。

Gulp

次にGulpをグローバルNPMパッケージとしてインストールします。

npm install --global gulp

Laravel Elixir

残っているステップはElixirのインストールだけです。新しくLaravelをインストールすると、ルートディレクトリーにpackage.jsonがあることに気づくでしょう。PHPの代わりにNodeの依存パッケージが定義されている所が異なりますが、composer.jsonファイルと同じようなものだと考えてください。以下のコマンドで、依存パッケージをインストールしてください。

npm install

Windowsシステムで開発を行っている場合、もしくはWindowsをホストとして仮想マシンを実行しているときは、--no-bin-linksを有効にしてnpm installコマンドを実行してください。

npm install --no-bin-links

Elixirの実行

ElixirはGulp上に構築されていますので、端末でgulpコマンドを実行するだけでElixirタスクを走らせることができます。コマンドに--productionフラッグを付けることで、ElixirにCSSとJavaScriptファイルを圧縮するように指示できます。

// 全タスク実行…
gulp

// 全タスクを実行し、全CSSとJavaScriptを圧縮する…
gulp --production

アセットの変更監視

アセットを変更するごとに端末でgulpコマンドを実行するのは不便ですので、gulp watchコマンドを使用できます。このコマンドは端末で動き続けてアセットの変更を監視します。変化が起きると変更があるファイルは自動的にコンパイルされます。

gulp watch

スタイルシート操作

プロジェクトルートにあるgulpfile.jsファイルに全Elixirタスクを構成します。Elixirタスクはアセットをどのようにコンパイルするか実際に定義するためにチェーンでつなげます。

Less

LessをCSSへコンパイルするにはlessメソッドを使用します。lessメソッドはLessファイルがresources/assets/lessに保存されていることを想定しています。コンパイルされたCSSはデフォルトでpublic/css/app.cssへ保存されます。

elixir(function(mix) {
    mix.less('app.less');
});

さらに複数のLessファイルを一つのCSSファイルへ結合できます。この結果のCSSもpublic/css/app.cssに保存されます。

elixir(function(mix) {
    mix.less([
        'app.less',
        'controllers.less'
    ]);
});

コンパイル済みのCSSの出力先をカスタマイズしたい場合は、lessメソッドに第2引数で指定します。

elixir(function(mix) {
    mix.less('app.less', 'public/stylesheets');
});

// 出力ファイル名を指定する…
elixir(function(mix) {
    mix.less('app.less', 'public/stylesheets/style.css');
});

Sass

sassメソッドはSassをCSSへコンパイルします。Sassファイルはresources/assets/sassへ保存されていると想定します。このメソッドは次のように使用します。

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

lessメソッドと同様に複数のSassファイルを一つのCSSファイルへまとめることができ、結果のCSSを出力するディレクトリーもカスタマイズできます。

elixir(function(mix) {
    mix.sass([
        'app.scss',
        'controllers.scss'
    ], 'public/assets/css');
});

通常のCSS

通常のCSSスタイルシートを一つのファイルに結合したい場合は、stylesメソッドを使います。このメソッドに渡すパスはresources/assets/cssからの相対位置で、結果のCSSはpublic/css/all.cssに保存されます。

elixir(function(mix) {
    mix.styles([
        'normalize.css',
        'main.css'
    ]);
});

もちろんstylesメソッドの第2引数を指定すれば、結果の出力ファイルをカスタマイズできます。

elixir(function(mix) {
    mix.styles([
        'normalize.css',
        'main.css'
    ], 'public/assets/css');
});

ソースマップ

ソースマップ生成はデフォルトで有効になっています。各ファイルをコンパイルするごとに、*.css.mapファイルが同じディレクトリーに生成されます。このマッピングはデバッグの時にブラウザ上でコンパイル済みのスタイルシートセレクターからオリジナルのSassやLess定義を見つけるのに役立ちます

CSSのためにソースマップを生成したくなければ、設定オプションで無効にしてください。

elixir.config.sourcemaps = false;

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

スクリプト操作

ElixirはECMAScript 6のコンパイル、CoffeeScriptのコンパイル、Browserify、圧縮、JavaScriptファイルの結合などのJavaScript操作を手助けする多くの機能も提供しています。

CoffeeScript

coffeeメソッドはCoffeeScriptをJavaScriptへコンパイルするために使用します。coffee関数はresources/assets/coffeeディレクトリーからの相対パスの文字列か配列を引数に受け取り、public/jsディレクトリーにapp.jsファイルを生成します。

elixir(function(mix) {
    mix.coffee(['app.coffee', 'controllers.coffee']);
});

Browserify

Elixirはbrowserifyメソッドも用意しており、ブラウザーとECMAScript 6を使用するためにモジュールをrequireする全ての利点を享受できます。

このタスクはスクリプトがresources/assets/jsへ設置されていると考え、結果をpublic/js/main.jsファイルへ書き出します。

elixir(function(mix) {
    mix.browserify('main.js');
});

Browserifyは最初からPartialifyとBabelifyトランスフォーマーを含んでいますが、お望みならvueifyをインストールし追加することもできます。

npm install aliasify --save-dev
elixir.config.js.browserify.transformers.push({
    name: 'aliasify',
    options: {}
});

elixir(function(mix) {
    mix.browserify('main.js');
});

Babel

babelメソッドはECMAScript 6と7をJavaScriptへコンパイルするために使用します。この関数はresources/assets/jsディレクトリーからの相対ファイルパスの配列を引数に受け取り、public/jsディレクトリーのall.jsファイルを生成します。

elixir(function(mix) {
    mix.babel([
        'order.js',
        'product.js'
    ]);
});

別の場所へ出力したい場合は、希望するパスを第2引数に指定してください。このメソッドの使い方と機能はmix.scripts()と同じで、Babelのコンパイルを行う点のみ異なります。

JavaScript

複数のJavaScriptファイルを一つのファイルへまとめたい場合は、scriptsメソッドを使用します。

scriptsメソッドは全パスをresources/assets/jsからの相対位置であると想定し、結果をデフォルトでpublic/js/all.jsに置きます。

elixir(function(mix) {
    mix.scripts([
        'jquery.js',
        'app.js'
    ]);
});

複数のスクリプトを別々のファイルにまとめたい場合は、scriptsメソッドを複数回呼び出してください。メソッドの第2引数でそれぞれの結合ファイル名を指定します。

elixir(function(mix) {
    mix.scripts(['app.js', 'controllers.js'], 'public/js/app.js')
       .scripts(['forum.js', 'threads.js'], 'public/js/forum.js');
});

指定したディレクトリーの全スクリプトを結合するには、scriptsInメソッドを使います。結果のJavaScriptはpublic/js/all.jsに設置されます。

elixir(function(mix) {
    mix.scriptsIn('public/js/some/directory');
});

ファイルとディレクトリーのコピー

copyメソッドはファイルとディレクトリーを新しい場所へコピーするために使用できます。全操作はプロジェクトルートディレクトリーからの相対位置で指定します。

elixir(function(mix) {
    mix.copy('vendor/foo/bar.css', 'public/css/bar.css');
});

elixir(function(mix) {
    mix.copy('vendor/package/views', 'resources/views');
});

バージョン付け/キャッシュ破壊

多くの開発者がコンパイルしたアセットにタイムスタンプや一意なトークンをサフィックスとして付加し、保存されている古いコードの代わりに真新しいアセットを強制的にロードさせています。Elixirはこれをversionメソッドで処理します。

versionメソッドはpublicディレクトリーからの相対パスファイル名を受け取り、ファイル名に一意なハッシュを付けることでキャッシュを破壊します。たとえば生成されたファイル名はall-16d570a7.cssのような名前になります。

elixir(function(mix) {
    mix.version('css/all.css');
});

バージョンが付いたファイルを生成した後、ビューの中からLaravelのグローバルelixir PHPヘルパー関数を使い、ハッシュが付いたアセットをロードすることができます。elixir関数はハッシュをつけたファイル名を自動的に決定します。

<link rel="stylesheet" href="{{ elixir('css/all.css') }}">

複数ファイルのバージョン付け

versionメソッドへ配列で複数ファイルを渡すこともできます。

elixir(function(mix) {
    mix.version(['css/all.css', 'js/app.js']);
});

ファイルにバージョンが付けられたら、elixirヘルパー関数でハッシュが付いた実際のファイルへリンクを生成することができます。elixirヘルパー関数にはハッシュをつけていないファイル名を渡す必要があることを覚えておきましょう。

<link rel="stylesheet" href="{{ elixir('css/all.css') }}">

<script src="{{ elixir('js/app.js') }}"></script>

BrowserSync

BrowserSyncはフロントエンドのリソースに変更が起きたときに、Webブラウザを自動的に再読込します。gulp watchコマンド実行時にBrowserSyncサーバーを起動するようにElixirへ指示するため、browserSyncメソッドが利用できます。

elixir(function(mix) {
    mix.browserSync();
});

gulp watchを実行したら、ブラウザ同期を利用するにはhttp://homestead.app:3000でWebアプリケーションにアクセスしてください。ローカル開発にhomestead.app以外のドメインを使用しているなら、browserSyncメソッドの最初の引数としてオプションの配列を渡してください。

elixir(function(mix) {
    mix.browserSync({
        proxy: 'project.app'
    });
});

既存Gulpタスクの呼び出し

既存のGulpタスクをElixirから呼び出すにはtaskメソッドを使用します。たとえば呼びだされた時にただちょっとしたテキストを話すGulpタスクを考えてみてください。

gulp.task('speak', function() {
    var message = 'Tea...Earl Grey...Hot';

    gulp.src('').pipe(shell('say ' + message));
});

このタスクをElixirから呼び出すにはmix.taskメソッドを使い、メソッドの唯一の引数にタスク名を指定してください。

elixir(function(mix) {
    mix.task('speak');
});

カスタムタスク監視

カスタムタスクを実行するための監視対象ファイルを登録するには、taskメソッドの第2引数に正規表現で指定してください。

elixir(function(mix) {
    mix.task('speak', 'app/**/*.php');
});

Elixir拡張の定義

Elixirのtaskメソッドが提供しているより、さらに柔軟性が求められる場合は、カスタムElixir拡張を作成してください。Elixir拡張でカスタムタスクへ引数を引き渡せます。たとえば以下のような拡張を書いたとしましょう。

// File: elixir-extensions.js

var gulp = require('gulp');
var shell = require('gulp-shell');
var Elixir = require('laravel-elixir');

var Task = Elixir.Task;

Elixir.extend('speak', function(message) {

    new Task('speak', function() {
        return gulp.src('').pipe(shell('say ' + message));
    });

});

// mix.speak('Hello World');

これだけです! Gulp限定のロジックはTaskコンストラクターの第2引数として渡している関数に設置しなくてはならない点に注目です。これをGulpfileの先頭に設置するか、代わりにカスタムタスクファイルとして外部ファイルにします。たとえば拡張をelixir-extensions.jsファイルに設置し、次のようにメインのGulpfileから読み込みます。

// ファイル: Gulpfile.js

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

require('./elixir-extensions')

elixir(function(mix) {
    mix.speak('Tea, Earl Grey, Hot');
});

カスタム監視

gulp watchが実行されている間にカスタムタスクを再起動したい場合は、監視タスクを登録します。

new Task('speak', function() {
    return gulp.src('').pipe(shell('say ' + message));
})
.watch('./app/**');