最近はgulpやgruntでビルドプロセスを自動化していかないとやってられない、というような流れになってきている気がしますが、gulpもgruntもいろんなライブラリが出まくっていて、一つ覚えたと思ったらもう新しくてさらに効率のいいライブラリが出てきたりしてますよね。。。と、愚痴を言っていても仕方ないのですが、今回はlinkタグとかscriptタグを自動で注入(inject)してくれるgulpのライブラリ、wiredepとgulp-injectの基本的な使い方について検証したのでまとめます。
指摘事項や、「こうしたらほうがいいよ?」などありましたら遠慮無くコメント欄やメッセージなどで気軽に教えていただけると喜びますm(__)m
はじめに
まず、下のようなページがあったとします。
<html>
<head>
<link rel="stylesheet" href="../../bower_components/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="../../bower_components/font-awesome/css/font-awesome.css" />
<link rel="stylesheet" href="styles/app.css">
</head>
<body>
<div id="main-region"></div>
</body>
<script src="../../bower_components/react/react.js"></script>
<script src="app/app.js"></script>
</html>
すごいシンプルなので、ここからは問題はあまり見えてこないんですけど、bower使って便利ライブラリをいろいろとインストールしていって、cssやらscriptやらが増えてくると、いちいち自分でindex.htmlに書いてられないですよね?おまけにこれに各アプリケーション特有のcssとscriptも増えていくのがほとんどのケースだと思います。これを全部管理するっていうのは人間のやることじゃない。って考える人は世の中にいっぱいいて、そうなるとやっぱり便利ツールは出現するんです。そこで登場するのがwiredepとgulp-inject。
ライブラリについて
wiredep
Wire Bower dependencies to your source code.
と、githubで書かれているように、bower install --saveしたライブラリをhtmlに自動で注入してくれるのがwiredepです。dependenciesを見ながらhtmlに注入してくれるので、(やや難がありますが)注入順についても意識しなくても良いという特徴があります。
gulp-inject
A javascript, stylesheet and webcomponent injection plugin for Gulp, i.e. inject file references into your index.html
と、githubに書かれているように、bowerに特化せず、cssやらscriptやらを注入できるのがgulp-inject(多分)。 依存関係とかを勝手にいい感じにやってくれる って機能はないはずなので、今回はgulp-injectをアプリ特有のcssやscriptに使おうと思います。
下準備
あらかじめ入れてある前提で話を進めるnpm packageはgulp(もちろん)とgulp-load-pluginsです。
今回のように小規模なサンプルの場合gulp-load-pluginsはどう考えても必要ないけど、たいていの場合私はgulp-*系なライブラリを多用するのでgulp-load-pluginsを使います。これについては詳しく説明しません。ってなわけでインストールしておきます。
npm install --save-dev gulp gulp-load-plugins
wiredepとgulp-injectもインストールしちゃいましょう。
npm install --save-dev wiredep gulp-inject
wiredep関連の作業
html編集
bower経由でインストールしているライブラリのタグをcssならbower:cssで、scriptならbower:jsで下のように囲んで、元のタグは消しちゃいます。ここにwiredepが自動注入してくれるようになるからです。
<html>
<head>
<!-- bower:css -->
<!-- endbower -->
<link rel="stylesheet" href="./styles/app.css">
</head>
<body>
<div id="main-region"></div>
</body>
<!-- bower:js -->
<!-- endbower -->
<script src="./app/app.js" charset="utf-8"></script>
</html>
gulpfile.js編集
それではgulpfile.jsの編集をします。書きっぷりとしては下のように書いておけば(たいていの場合)完了です。
gulp.task('wiredep', function() {
var wiredep = require('wiredep').stream;
return gulp
.src('./src/client/index.html')
.pipe(wiredep()) // wiredep bower dependencies
.pipe(gulp.dest('./src/client')) // output the index.html
})
ただし、今回の例の場合うまくいきません。仮にgulp wiredepと実行してもタスクは完了して何も注入されていないことに気づきます。
何故かと言うと、wiredepの仕組み上、注入するための情報が不足しているからです。
wiredepが自動注入する際に参考にしている情報は各ライブラリの.bower.jsonのmainの内容とdependenciesの内容です。
今回使っているbootstrapの.bower.jsonを見ると、
// 割愛
"main": [
"less/bootstrap.less",
"dist/js/bootstrap.js"
],
// 割愛
"dependencies": {
"jquery": ">= 1.9.1"
},
となっており、注入してほしいdist/css/bootstrap.cssが記述されていません。こんな時は自分のbower.jsonに明示的に情報を付加してあげる必要があります。書きっぷりは下のような感じになります。
"overrides": {
"bootstrap": {
"main": "dist/css/bootstrap.css",
"dependencies": {}
},
"font-awesome": {
"main": "css/font-awesome.css"
}
}
overridesの下にライブラリ名: {プロパティ名: 与えたい値}という具合で情報を増やしてあげます。今回はbootstrapのdependenciesであるjqueryも使いたくなかったので明示的にdependenciesを空オブジェクトにしています。ここのそのままにしているとbower:jsで囲まれている箇所にjqueryが意図せず注入されてしまいます。
これでwiredep関連の自動化は完了。gulp wiredepでbower関連のライブラリが注入されるはずです。
gulp-inject関連の作業
それでは、各アプリ特有のcssとscriptも注入できるようにします(多くの場合app.jsだったりapp.cssだったりするファイルです)。要領はwiredepのときと同様ですが、囲むタグをcssの場合はinject:cssで、scriptの場合はinject:jsとします。
下のようになるはずです。これで手動で書くタグはなくなりましたね!
htmlの編集
<html>
<head>
<!-- bower:css -->
<!-- endbower -->
<!-- inject:css -->
<!-- endinject -->
</head>
<body>
<div id="main-region"></div>
</body>
<!-- bower:js -->
<!-- endbower -->
<!-- inject:js -->
<!-- endinject -->
</html>
gulpfile.jsの編集
先ほどのwiredepタスクにちょっとコードを足します。
gulp.task('wiredep', function() {
var wiredep = require('wiredep').stream;
return gulp
.src('./src/client/index.html')
.pipe(wiredep()) // wiredep bower dependencies
.pipe($.inject(gulp.src(['./src/**/*.js', './src/**/*.css']), {
relative: true // no need for the './src/client' part
}))
.pipe(gulp.dest('./src/client')) // output the index.html
})
gulp-load-pluginsを使っていて、かつそれを$としてrequireしているのでgulp-injectは$.injectでアクセス可能になってます。対象ファイルはとりあえずsrc配下のcssとjsとしてます。そして、オプションなしでこのまま実行するとlinkやscriptタグが./src/clientからパスを初めてしまうので、index.htmlからみた相対パスを注入してもらうようにrelative: trueというオプションを渡しています。
以上で完了です。これでgulp wiredepを実行したらindex.htmlに自動でlinkやscriptタグが注入されるようになります。
それぞれのソースの最終形
<html>
<head>
<!-- bower:css -->
<!-- endbower -->
<!-- inject:css -->
<!-- endinject -->
</head>
<body>
<div id="main-region"></div>
</body>
<!-- bower:js -->
<!-- endbower -->
<!-- inject:js -->
<!-- endinject -->
</html>
var gulp = require('gulp');
var $ = require('gulp-load-plugins')({
lazy: true
});
gulp.task('wiredep', function() {
var wiredep = require('wiredep').stream;
return gulp
.src('./src/client/index.html')
.pipe(wiredep()) // wiredep bower dependencies
.pipe($.inject(gulp.src(['./src/**/*.js', './src/**/*.css']), {
relative: true // no need for the './src/client' part
}))
.pipe(gulp.dest('./src/client')) // output the index.html
})
{
"name": "wiredep-inject-starter",
"version": "0.0.0",
"homepage": "https://github.com/kenfdev/wiredep-inject-starter",
"authors": [
"Ken Fukuyama <kenfdev@gmail.com>"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"bootstrap": "~3.3.5",
"font-awesome": "~4.4.0",
"react": "~0.13.3"
},
"overrides": {
"bootstrap": {
"main": "dist/css/bootstrap.css",
"dependencies": {}
},
"font-awesome": {
"main": "css/font-awesome.css"
}
}
}
課題
課題というか、このサンプルだけでは足りていないことは、
- アプリ特有の
scriptの依存関係を考えた順番の注入ができていない - 実際は
cssやjsのビルドプロセスが存在するため、このタスク単体では実務では使えない。(依存タスクをもっと作る必要あり)
などなど、まだまだたくさんあります。
が、とりあえずはwiredepとgulp-injectの雰囲気がつかめたのでよしとしようかと。
おまけ
wiredep-inject-starterとして今回のソースを上げています。
