Rails5.1で導入が予定されているWebpackerを使ってみたくて、いろいろ調べたことをまとめます。
以下9つの手順を行った環境をgithubで公開しました。お役に立てるかわかりませんが、ご参考まで。
環境
Rails 5.0.1
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
Step.1 rails new
する
rails new rails-webpacker-riot
cd rails-webpacker-riot
Step.2 gemファイルにwebpacker
と書いてbundle install
rails serverとwebpackのバンドル開始を1つのコマンドでできるようになるforemanも入れてみる
gem 'webpacker'
gem 'foreman'
bundle install
Step.3 rails webpacker:install
でwebpacker関連のフォルダやファイルが作成される
rails webpacker:install
Step.4 必要なJSパッケージをbin/yarn
で追加する
パッケージはyarnで管理される。step.3 でbin/yarn
が実行され、次のパッケージがインストールされる
{
"devDependencies": {
"babel-core": "^6.22.1",
"babel-loader": "^6.2.10",
"babel-preset-latest": "^6.22.0",
"coffee-loader": "^0.7.2",
"coffee-script": "^1.12.3",
"path-complete-extname": "^0.1.0",
"rails-erb-loader": "^3.2.0",
"webpack": "beta",
"webpack-dev-server": "beta",
"webpack-merge": "^2.6.1"
}
}
自分で追加したいパッケージがあれば、bin/yarn add -D
で追加していく。
bin/yarn add font-awesome font-awesome-webpack2 riot riotjs-loader node-sass sass-loader file-loader url-loader promise-polyfill less babel-preset-es2015-riot bootstrap-sass -D
{
"devDependencies": {
"babel-core": "^6.22.1",
"babel-loader": "^6.2.10",
"babel-preset-es2015-riot": "^1.1.0",
"babel-preset-latest": "^6.22.0",
"bootstrap-sass": "^3.3.7",
"coffee-loader": "^0.7.2",
"coffee-script": "^1.12.3",
"file-loader": "^0.10.0",
"font-awesome": "^4.7.0",
"font-awesome-webpack2": "^0.0.6",
"less": "^2.7.2",
"node-sass": "^4.5.0",
"path-complete-extname": "^0.1.0",
"promise-polyfill": "^6.0.2",
"rails-erb-loader": "^3.2.0",
"riot": "^3.2.1",
"riotjs-loader": "^4.0.0",
"sass-loader": "^5.0.1",
"url-loader": "^0.5.7",
"webpack": "beta",
"webpack-dev-server": "beta",
"webpack-merge": "^2.6.1"
}
}
なお、globalにyarnを入れておく必要があるのか、Step.3で合わせてインストールされるのか、よくわからなかった。
Step.5 foremanのProcfileをroot配下に作る
foreman start
でProcfileにある2つのコマンドを実行してくれる。
webpack: bin/webpack-dev-server
rails: rails s
Step.6 webpack.config関連の設定
webpack.config.js 相当の設定は、config/webpackに格納される。shared, production, developmentとあるので、それぞれ以下の通り設定した。
- shared.jsのruleに、各パッケージのloaderを設定
- production.js, development.jsに、webpack.ProvidePluginを設定
// Note: You must restart bin/webpack-watcher for changes to take effect
var path = require('path')
var glob = require('glob')
var extname = require('path-complete-extname')
module.exports = {
entry: glob.sync(path.join('..', 'app', 'javascript', 'packs', '*.js*')).reduce(
function(map, entry) {
var basename = path.basename(entry, extname(entry))
map[basename] = entry
return map
}, {}
),
output: { filename: '[name].js', path: path.resolve('..', 'public', 'packs') },
module: {
rules: [
{ test: /\.coffee(.erb)?$/, loader: "coffee-loader" },
{
test: /\.js(.erb)?$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[ 'latest', { 'es2015-riot': { 'modules': false } } ]
]
}
},
{
test: /\.erb$/,
enforce: 'pre',
loader: 'rails-erb-loader',
options: {
runner: '../bin/rails runner'
}
},
// config for riot
{
test: /\.tag$/,
loader: "riotjs-loader"
},
// config for font-awesome-webpack2
{
test: /\.woff(2)?(\?v=[a-z0-9]\.[a-z0-9]\.[a-z0-9])?$/,
loader: 'url-loader?limit=100000'
},
{
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: "file-loader"
},
// config for using SCSS.
{
test: /\.scss$/,
enforce: 'pre',
loader: ['style-loader', 'css-loader', 'sass-loader']
}
]
},
plugins: [ ],
resolve: {
extensions: [ '.js', '.coffee' ],
modules: [
path.resolve('../app/javascript'),
path.resolve('../vendor/node_modules')
]
},
resolveLoader: {
modules: [ path.resolve('../vendor/node_modules') ]
}
}
// Note: You must restart bin/webpack-watcher for changes to take effect
var path = require('path')
var webpack = require('webpack')
var merge = require('webpack-merge')
var config = require('./shared.js')
module.exports = merge(config, {
devtool: 'sourcemap',
stats: {
errorDetails: true
},
output: {
pathinfo: true
},
plugins: [
new webpack.LoaderOptionsPlugin({
debug: true
}),
// config for riot.
new webpack.ProvidePlugin({ riot: 'riot' }),
]
})
// Note: You must restart bin/webpack-watcher for changes to take effect
var path = require('path')
var webpack = require('webpack')
var merge = require('webpack-merge')
var config = require('./shared.js')
module.exports = merge(config, {
output: { filename: "[name]-[hash].js" },
plugins: [
new webpack.LoaderOptionsPlugin({
minimize: true
}),
new webpack.ProvidePlugin({ riot: 'riot' })
]
})
Step.7 app/javascript配下のこと
tagファイルの置き場所
app/javascript配下にフォルダを作り、index.jsを作る。また、このフォルダの中にtagファイルを格納する。今回はmainというフォルダ名にした。
require('./style.scss')
// for use bootstrap-fonts
$icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/";
@import "~bootstrap-sass/assets/stylesheets/_bootstrap.scss";
packs配下のこと
application.jsで使うパッケージをrequireする。
また、上記で作ったmainフォルダもrequireする。requireされるフォルダにindex.jsがないとうまくコンパイルされない。
require('font-awesome-webpack2')
require('bootstrap-sass')
require('main')
Step.8 config/environment/development.rb
rails webpack:install
のときに一番上に書き込まれているこれをコメントアウトする
Rails.application.configure do
# Make javascript_pack_tag load assets from webpack-dev-server.
config.x.webpacker[:dev_server_host] = "http://localhost:8080"
...
Step.9 herokuへのpushに失敗する問題への対応
対応せずにherokuへpushすると、bin/yarn
が実行されずにwebpacker:compile
が実行されてしまい、webpackがないため、エラーとなってしまう。
ここで議論されていて、多分rails5.1では問題なくなっているのだろうけど、忘れずに書いておく。
対応1:buildpacksでnodejsを最初に行うよう設定
$ heroku buildpacks:set heroku/nodejs
対応2:rootに偽物のpackage.jsonとyarn.lockを作る
対応1をうまくこなすため、2つのファイルを作る。
package.jsonでenginesにnodeを指定。enginesでyarnを入れてもいいようですが、私はdependenciesでも大丈夫でした。
{
"name": "rails-riot-webpacker",
"description": "\"dummy package.json for heroku/nodejs buildpack support.\"",
"engines":{
"node": "6.5.0"
},
"dependencies": {
"yarn": "0.19.1"
}
}
# Dummy yarn.lock to force heroku/nodejs to install yarn
対応3:Rakeタスクの追加
ここのをそのまま引用した。
Rake::Task['assets:precompile']
.clear_prerequisites
.enhance(['assets:compile_environment'])
namespace :assets do
task compile_environment: [:yarn, :webpack] do
Rake::Task['assets:environment'].invoke
end
desc 'Install node deps via yarn'
task :yarn do
sh 'bundle exec ./bin/yarn'
end
desc 'Compile assets with webpack'
task :webpack do
sh 'bundle exec rails webpacker:compile'
end
end
最後に
役に立つかわかりませんが、step9まで実施した内容をここに公開しました。