Middleman 4 の新機能を試す シリーズMiddleman 4 の External Pipeline を活用した Sass のプリコンパイルと Browserify のバンドル
はじめに
前回の記事で 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.js
や assets/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
フォルダ内の .scss
、javascripts
フォルダ内の 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_pipeline
の source
です。
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 に追記しておきます。
参考サイト
シリーズ
- Middleman 4 をインストールしてみる
- Middleman 4 で middleman-blog をインストールしてみる
- Browserify を試してみる
- Gulp から Browserify を実行する
- Watchify を使った Browserify の 変更監視と自動バンドル
- Gulp から Watchify と Browserify を使う
- Middleman 4 の External Pipeline(外部パイプライン)を試す
- Middleman 4 の External Pipeline を活用した Sass のプリコンパイルと Browserify のバンドル
- Middleman 4 で middleman-syntax を使った Syntax Highlight(小ネタ)
- gulp-sass で Font Awesome を導入する方法(Bourbon, Neat 対応)
- Middleman 4 の External Pipeline に対応した Skeleten(テンプレート)「middleman-tail」 を作った
- Wercker の step-s3sync で起こった 「Parameter problem: Invalid source/destination」エラーの直し方(小ネタ)