blog

Middleman 4 の新機能を試す シリーズMiddleman 4 の External Pipeline を活用した Sass のプリコンパイルと Browserify のバンドル

    • Ryuichi Nonaka

    はじめに

    前回の記事で External Pipeline を使って $ middleman server/build コマンドに合わせて Gulp タスクを呼べるようになりました。今回は Gulp 経由でプリコンパイルした CSS や Browserify で バンドルした JavaScript を適切なパスでビルドできるようにしたいと思います。

    External Pipeline で Sass や Browserify する際のポイント

    External Pipeline で Sass をプリコンパイルしたり、Browserify でバンドルする場合、Middleman の Renderer(Sass や CoffeeScript 等) に注意が必要です。Renderer というのは特に設定をせずとも .scss.css ファイルを .css に自動でプリコンパイルしてくれるといった便利なものです。

    そんな便利な Renderer ですが、External Pipeline を使う場合には邪魔な存在です。CSS や JavaScript は Rails のお作法に則るのであれば assets/javascripts/bundle.jsassets/stylesheets/style.css などのパスにしようと考えると思います。ですが、これらの階層は Renderer の対象に設定されており、server/build の際にMiddleman 側のプリコンパイルが走ってしまうため、External Pipeline と処理が被ってしまいます。これを解消するには Renderer の影響しないところにファイルを置くか、Renderer の対象外となるように対象ファイルを ignore に設定しなければなりません(残念ながら Renderer 自体を無効にすることはできませんでした)。

    今回は主に後者の方法を紹介します。

    Sass, JavaScript を Renderer の対象外に設定する

    まずは Sass, JavaScript ( bundle.js 以外) ファイルを Renderer の対象から外します。これにより 従来のパスのまま External Pipeline を活用できます。

    config.rb に ignore を追加する

    configure :build で ignore を設定します。対象となるファイルは stylesheets フォルダ内の .scssjavascripts フォルダ内の bundle.js を除く .js です。

    # Build-specific configuration
    configure :build do
      # ignore
      ignore /assets/stylesheets\/.*\.scss/
      ignore /assets/javascripts\/(?!bundle).*\.js/
    end
    

    これで assets フォルダ内の Sass や JavaScript が Renderer によってビルドされること防げます。

    おまけ:Renderer の対象ディレクトリを変更する

    JavaScript や CSS の 対象ディレクトリを変更するには以下の設定を行います。

    config[:js_dir] = 'js'
    config[:css_dir] = 'css'
    

    Gulp から出力されたファイルがビルドされるまでの流れ

    External Pipeline を活用しようと思うと、ファイルがどのように処理されるのかが複雑でわかりにくくなるので、そこを補足しておきます。まず、Gulp から出力された CSS や JavaScript ファイルはテンポラリファイルとして一時保存用フォルダ .tmp/dist に保存されます。

    function jsBundle() {
      return b.bundle()
        .pipe(source(jsConf.destFileName))
        .pipe(gulp.dest(jsConf.destPath));
    }
    

    .tmp/dest に保存された一時ファイルは Middleman の External Pipeline によって Middleman の Sitemap に追加されます。Middleman に一時ファイルがどこにあるのか認識させるための設定は activate :external_pipelinesource です。

    activate :external_pipeline,
      name:    :gulp,
      command: build? ? 'gulp build' : 'gulp server',
      source:  ".tmp/dest",
      latency: 0.25
    

    .tmp/dest 内の各ファイルは Sitemap に追加されることにより、Middleman から参照できるようになります。参照できるということは、アセットヘルパーから参照でき、livereload や asset_hash にも対応できます。

    <%= stylesheet_link_tag 'assets/stylesheets/style' %>
    <%= javascript_include_tag 'assets/javascripts/bundle' %>
    

    ビルド時には source で設定したパスの階層を起点にフォルダごとビルドフォルダにコピーされます。例えば .tmp/dest/assets/stylesheets/style.css のような CSS は build/assets/stylesheets/style.css となります。

    .tmp
    └── dest
        └── assets
            ├── javascripts
            │   └── bundle.js
            └── stylesheets
                └── style.css
    

    ビルドするとこのようになります。

    build/
    ├── 2012
    │   ├── 01
    │   │   ├── 01
    │   │   │   └── example-article.html
    │   │   └── 01.html
    │   └── 01.html
    ├── 2012.html
    ├── 2016
    │   ├── 09
    │   │   ├── 20
    │   │   │   └── hoge.html
    │   │   └── 20.html
    │   └── 09.html
    ├── 2016.html
    ├── assets
    │   ├── javascripts
    │   │   └── bundle-57d165d5.js
    │   └── stylesheets
    │       └── style-a792db47.css
    ├── feed.xml
    ├── index.html
    └── tags
        └── example.html
    

    Gulp タスクを作る

    Sass のプリコンパイルと Browserify のバンドルを行う Gulp タスクを作成します。Sass(gulp-sass)にはライブラリの Bourbon(node-bourbon) と グリッドレイアウトを制御するための neat(node-neat) も追加します。

    gulp-sass とライブラリのインストール

    gulp-sass と合わせてライブラリもインストールします。

    $ npm install gulp-sass node-bourbon node-neat --save-dev
    

    Gulp タスクのサンプルコード

    特に難しいことはやっていませんが、 Bourbon や neat はライブラリのパスを Sass 側に伝えてやる必要があります。このパスを取得するメソッドが neat.includePaths です。neat は Bourbon に依存していることもあり、neat.includePaths には Bourbon のパスも内包されています。以下が取得できる配列です。

    [ [ '/Users/rin/Projects/mm4/node_modules/bourbon/app/assets/stylesheets' ],
      '/Users/rin/Projects/mm4/node_modules/bourbon-neat/app/assets/stylesheets' ]
    

    Gulp タスクは以下のようになります。

    var gulp       = require('gulp');
    var browserify = require('browserify');
    var source     = require('vinyl-source-stream');
    var watchify   = require('watchify');
    var sass       = require('gulp-sass');
    var bourbon    = require('node-bourbon');
    var neat       = require('node-neat');
    
    // settings ---
    var jsConf = {
      srcPath:      'source/assets/javascripts/main.js',
      destPath:     '.tmp/dest/assets/javascripts',
      destFileName: 'bundle.js'
    }
    var cssConf = {
      srcPath:      'source/assets/stylesheets/**/*.scss',
      destPath:     '.tmp/dest/assets/stylesheets'
    }
    
    var b = browserify({
      entries: jsConf.srcPath,
      cache: {},
      packageCache: {}
    });
    
    // gulp tasks ---
    gulp.task('default', ['build']);
    gulp.task('build', ['sass', 'bundle']);
    gulp.task('server', ['sass', 'bundle'], watch);
    gulp.task('bundle', jsBundle);
    gulp.task('sass', sassPreCompile);
    
    // functions ---
    function watch() {
      // watchify
      b.plugin(watchify);
      b.on('update', jsBundle);
      jsBundle();
    
      // sass watch
      gulp.watch(cssConf.srcPath, ['sass']);
    }
    
    function jsBundle() {
      return b.bundle()
        .pipe(source(jsConf.destFileName))
        .pipe(gulp.dest(jsConf.destPath));
    }
    
    function sassPreCompile(){
      gulp.src(cssConf.srcPath)
        .pipe(sass({
          includePaths: neat.includePaths
        }))
        .pipe(gulp.dest(cssConf.destPath));
    }
    

    Bowerなどの外部リソースを取り込む方法

    Bowerなど Gulp を介さないリソースを Middleman の Sitemap に取り込むには import を使います。

    import_path File.expand_path('bower_components', app.root)
    

    終わりに

    これで External Pipeline を活用した Sass のプリコンパイルと Browserify のバンドルが実用的なレベルで実装できました。実用的な使い方の情報が少ないのですんなり使えるもではないですがここまで理解できれば Gulp タスク次第でできることの幅が大きく広がりますね。 使わない Renderer を無効にできたりすれば余計な ignore を設定しなくても済むのですが、今後に期待しましょう。

    git の ignore 設定

    .tmp フォルダ配下はバージョン管理不要なので ignore に追記しておきます。

    参考サイト

    シリーズ

    1. Middleman 4 をインストールしてみる
    2. Middleman 4 で middleman-blog をインストールしてみる
    3. Browserify を試してみる
    4. Gulp から Browserify を実行する
    5. Watchify を使った Browserify の 変更監視と自動バンドル
    6. Gulp から Watchify と Browserify を使う
    7. Middleman 4 の External Pipeline(外部パイプライン)を試す
    8. Middleman 4 の External Pipeline を活用した Sass のプリコンパイルと Browserify のバンドル
    9. Middleman 4 で middleman-syntax を使った Syntax Highlight(小ネタ)
    10. gulp-sass で Font Awesome を導入する方法(Bourbon, Neat 対応)
    11. Middleman 4 の External Pipeline に対応した Skeleten(テンプレート)「middleman-tail」 を作った
    12. Wercker の step-s3sync で起こった 「Parameter problem: Invalid source/destination」エラーの直し方(小ネタ)

    コメント・フィードバック