Edited at

bowerと仲良くなる

More than 3 years have passed since last update.

bowerを使っているのに素で

<script src="/bower_components/angular/angular.js"></script>

いちいちこういうコード書いてる人、いませんよね。bowerを使えるなら自動で挿入(インジェクション)すべきです。


main-bower-filesを活用すべし

しばらくgruntでgrunt-wiredepを使っていたんですが、useminに脳を焼かれたためGrunt自体やめました。Gulpでもwiredepのストリームを使うこともできますが、concatが辛めです。main-bower-filesを使いましょう。


実践

実際に動かしている構成に近づけようとしたら余計なものが結構入ってきてしまった感があるが、気にしない。

ちなみに本サンプルは

https://github.com/endaaman/main-bower-files-is-nice

に公開してあります。


サンプル構成

.

├── .bowerrc
├── bower.json
├── gulpfile.coffee
├── karma.conf.coffee
├── node_modules
│   └── [略]
├── package.json
├── public
│   └── bower_components
└── src
├── app.coffee
├── index.jade
└── test
└── app.coffee


Gulpで使う

index.jadeをindex.htmlにコンパイルして、開発時はbower_componentsから直接、プロダクションではconcatして、インジェクションしてみる。

サンプルでは、


bower.json

{

"name": "example",
"dependencies": {
"angular": "~1.2.26",
"angular-resource": "~1.2.26"
},
"devDependencies": {
"angular-mocks": "~1.2.26"
}
}



package.json

{

"scripts": {
"test": "./node_modules/karma/bin/karma start"
},
"devDependencies": {
"coffee-script": "^1.8.0",
"del": "^0.1.3",
"gulp": "^3.8.8",
"gulp-coffee": "^2.2.0",
"gulp-concat": "^2.4.1",
"gulp-inject": "^1.0.2",
"gulp-jade": "^0.8.0",
"karma": "^0.12.23",
"karma-coffee-preprocessor": "^0.2.1",
"karma-jasmine": "^0.1.5",
"karma-phantomjs-launcher": "^0.1.4",
"main-bower-files": "^2.0.0"
}
}


index.jade

doctype html

html(ng-app="exampleApp")
meta(charset="utf-8")
body
<!-- bower:js -->
<!-- endinject -->
<!-- inject:js -->
<!-- endinject -->

という感じだとする。


gulpfile.coffee

g = require 'gulp'

jade = require 'gulp-jade'
inject = require 'gulp-inject'
concat = require 'gulp-concat'
coffee = require 'gulp-coffee'
del = require 'del'
bowerFiles = require 'main-bower-files'

conf =
src: 'src'
dest: 'public'
prod: false
bowerDir: 'public/bower_components'

g.task 'clean', ->
del [
"#{conf.dest}/**/*"
"!#{conf.bowerDir}"
"!#{conf.bowerDir}/**/*"
]

g.task 'coffee', ['clean'], ->
g.src "#{conf.src}/*.coffee"
.pipe coffee()
.pipe g.dest "#{conf.dest}/"

g.task 'build:bower', ->
if conf.prod
g.src bowerFiles()
.pipe concat('_vendor.js')
.pipe g.dest("#{conf.dest}/")

g.task 'index', ['build:bower', 'coffee'], ->
ignores = ['public/', 'client/']

t = g.src "#{conf.src}/index.jade"
.pipe jade pretty: not conf.prod
if not conf.prod
t.pipe inject(
g.src(bowerFiles(),
base: conf.bowerDir
read: false),
ignorePath: ignores
name: 'bower'
)
t.pipe inject(
g.src("#{conf.dest}/*.js", read: false),
ignorePath: ignores
)

t.pipe g.dest "#{conf.dest}/"

g.task 'build', ['index']

g.task 'prod', ->
conf.prod = true

g.task 'default', ['build']


venoder.jsだとapp.jsより後にインジェクションされてしまうため、本来なら順番をちゃんと指定するところだけど面倒だから、結合したファイルを_vendor.jsという名前にしている。

タスクは



  • gulp clean public/以下bower_components以外を掃除


  • gulp build(またはgulp) で開発用ビルド


  • gulp prod build でプロダクションビルド

になる。

gulp-injectの作者が「bowerに対してはmain-bower-filesを使うといいよ!」と言っているだけあって、楽ちんです。結合も楽勝。

Gulpでwiredepを使うとgulp-injectとは別でインジェクションされてしまい、少し気持ち悪いです。結合もやりにくい。


Karmaでも使える

というか、これが言いたくて書き始めた。


karma.conf.cofee

module.exports = (config) ->

bowerFiles = require 'main-bower-files'

files = [
'src/**/*.coffee'
]
files = bowerFiles(includeDev: true).concat files

config.set
basePath: ''
frameworks: ['jasmine']
files: files
exclude: []
preprocessors:
'src/**/*.coffee': ['coffee']

reporters: ['progress']
port: 9876
colors: true
logLevel: config.LOG_INFO
autoWatch: false
browsers: ['PhantomJS']
singleRun: true


includeDevを指定することでdevDependenciesも含めることができます。なので、KarmaのコンフィグにつかってもOKです。

これは使うべきです。というか、使わない手はありません。


src/app.coffee

'use strict'

angular.module 'exampleApp', [
'ngResource'
]
.factory 'Add16', ->
(i)->
i + 16

.run (Add16)->
console.log 'Add(3): ' + Add16(3)
console.log 'Add(12): ' + Add16(12)



src/test/app.coffee

'use strict'

describe 'Add16', ->
Add16 = null
beforeEach ->
module 'exampleApp'
inject ($injector)->
Add16 = $injector.get 'Add16'

it ->
expect(Add16(14)).toBe 30


angular-mocksがちゃんと含まれているので、テストも通ります。


CSSとかfontとかは守備範囲外

それでも個人的には問題になりません。というのも、CSSは生のCSSで使うことがほぼなく、大体LESSやSASS/SCSSで使うからです。この場合はインクルードパスをbowerにも通して、インポートやらで宣言を手元のCSSに引き込めばOKです。

fontは、minifyする必要がないのでbower_componentsをそのまま公開するか、公開しないならコピーしてもいいでしょう。そもそもfontsを扱うパッケージなどごく一部なので、苦にならないと思います。