Separate JavaScript Easily and Moderately from Rails with WebPack
Introduction
To dive into the world of front-end, I came up with an idea to separate JavaScript easily and moderately from a Rails project.
Some have already shared various ideas, but I found them too much or required more than couple of tools so they seemed like high hurdles. That's why I sought for easier ideas.
TL;DR
I've committed all settings that I mentioned here on initial commit.
Advantages
- To utilize good points of Sprockets
- To introduce only WebPack as additional tool, practically
- To set up a few items, which are easy to do
- Almost maintenance-free, I believe
Summary
As the following flow, you will program, build and publish your application with JavaScript:
- Program JavaScript under
frontend
directory - Build JavaScript you developed under
frontend
toapp/assets/javascripts
- Publish files under
app/assets/javascripts
without combining files
I think a good point of Sprockets is to publish files with fingerprint in the 3rd part of the flow. It costs a lot to cover this feature using other build tools for front-end.
In other words, if you leave it to Sprockets, you can separate JavaScript from Rails without large scale settings.
Views are still in Rails but the separation of this extent is good enough because if you want to clear all front-end from Rails, it might be okay to use Rails API in the first place.
Concrete Flow
To concretize summary a little, the flow would be as below:
- Do modern JS development in
frontend/src/javascripts/hoge.js
- Build
frontend/src/javascripts/hoge.js
with WebPack and outputassets/javascripts/hoge.js
- Publish
assets/javascripts/hoge.js
with fingerprint using Sprockets
Tools and Versions
Basically, only WebPack is the additional tool. To transpile them on a build with WebPack, Babel is introduced.
- Sprockets 3.4.1 (+ Rails 4.2.5)
- Publication (with MD5 fingerprint)
- WebPack 1.12.9 (+ Babel 6.1.18)
- Build
Structure of Directories
Add frontend
directory to the root of a Rails project.
$ ls Gemfile README.md app/ config/ db/ lib/ spec/ Gemfile.lock Rakefile bin/ config.ru frontend/ public/ vendor/
The structure of the frontend
directory is as below:
$ tree frontend -I node_modules . ├── config │ ├── development │ │ └── webpack.config.js │ └── production │ └── webpack.config.js ├── package.json ├── src │ └── javascripts │ └── application.js └── test └── javascripts
Roughly, it consists of for elements:
- package.json
- Config of WebPack
- Directory for source files
- Directory for test files
Procedure of settings
I'll explain the procedure of settings.
- Set Sprockets to publish JS files separately
- Add settings of JS build to WebPack
- Hook build by WebPack to Precompile
1. Set Sprockets to publish JS files separately
As the default settings, Rails binds all JavaScript files into application.js
to publish it, so we need to change this setting.
Clean up under app/assets/javascripts
Make the directory empty because app/assets/javascripts
will be the directory where built JS files are placed.
Add the following lines to .gitignore
to exclude this directory from git management.
!/app/assets/javascripts/.keep /app/assets/javascripts
Plus, as I wanted to publish CSS files separately, I removed *= require_tree .
from app/assets/stylesheets/application.scss
.
Add targets of precompile by Sprockets
As only application.js
and application.css
are the targets as the default, add settings to config/initializers/assets.rb
to change all JavaScript and CSS files to be compile targets.
Rails.application.config.assets.precompile += %w(*.js *.css)
Exclude files whose names start with underscore
Some gems related to views sometimes raise errors at precompile.
In this case, it's fine to use the following setting which excludes files starting from underscore from the targets instead of the above setting.
Rails.application.config.assets.precompile << /(^[^_\/]|\/[^_])[^\/]*(\.js|\.css)$/
Supplements: How to include JavaScripts and CSSs in Rails
Respectively, calling the following methods inside view templates enables you to use them:
javascript_include_tag
helperstylesheet_link_tag
helper
2. Add settings of JS build to WebPack
Install WebPack and Bable which is used as transpiler with WebPack.
If package.json
isn't right under frontend
, execute npm init
.
$ npm init $ npm install -D webpack babel babel-loader babel-core
Example: Use React.js, ES2015 and Stage2
As an example, do settings to use React.js (0.14.3), ES2015 and Stage2.
At first, install React.js:
$ npm install --save react react-dom
After that, install required presets of Babel:
$ npm install -D babel-preset-react babel-preset-es2015 babel-preset-stage-2
To enable WebPack to transpile JavaScript files, make setting files:
module.exports = { devtool: 'inline-source-map', entry: { application: './src/javascripts/application.js', }, output: { path: '../app/assets/javascripts', filename: '[name].js' }, module: { loaders: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel?presets[]=react,presets[]=es2015,presets[]=stage-2' } ] } }
If you exclude devlool
options, you can use it as production settings, I believe:
module.exports = { entry: { application: './src/javascripts/application.js', }, output: { path: '../app/assets/javascripts', filename: '[name].js' }, module: { loaders: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel?presets[]=react,presets[]=es2015,presets[]=stage-2' } ] } }
Now, if you execute webpack --config config/{enviroment}/webpack.config.js
, JavaScripts registered as entries will be built to app/assets/javascripts/*.js
.
Using the above settings, frontend/src/javascripts/application.js
will be built to app/assets/javascripts/application.js
.
You just need to add new entries and include them in views using helper if you want to add another JavaScript files.
Using npm run
Preparing commands such as npm run build
is handy, so register the following three commands:
- release:
webpack --config config/production/webpack.config.js"
- build:
webpack --config config/development/webpack.config.js"
- watch:
webpack --watch --config config/development/webpack.config.js"
{ "scripts": { "release": "webpack --config config/production/webpack.config.js", "build": "webpack --config config/development/webpack.config.js", "watch": "webpack --watch --config config/development/webpack.config.js" } }
Then, if you type npm run build
right under frontend
, build with WebPack will be exceuted.
3. Hook build by WebPack to Precompile
Finally, hook npm run release
just before executing assets:precompile
to do nothing at deployment:
task :build_frontend do cd "frontend" do sh "npm install" sh "npm run release" end end Rake::Task["assets:precompile"].enhance(%i(build_frontend))
Now, if you type rake assets:precompile
, npm run release
will be done just under frontend
before assets:precompile
.
Conclusion
To separate JavaScript easily and moderately from Rails, I adopt this approach:
- To build JavaScripts to
app/assets/javascripts
with WebPack - Publish files under
app/assets/javascripts
as they are with Sprockets
The pros of this approach are that there are only a few settings and additional tools, and easy to maintain. It makes settings of building JavaScript simple and not required to replace Sprockets forcefully.
After I read Why we should stop using Grunt & Gulp, I've been wondering if I can set it succinctly without Gulp. I've realized that thanks to accepting Sprockets easier than I expected.
The isolation level seems to be moderate, I think.
Postscript: Complete separation
I said that the benefit of Sprockets is to publish them with fingerprints but I found a plugin which can do it for WebPack.
You can separate JavaSscript from Rails completely because WebPack with this plugin will also publish them with fingerprints.
After building JavaScripts, JSON as below will be outputted for resolving paths.
{ "one": { "js": "/js/one_2bb80372ebe8047a68d4.bundle.js" }, "two": { "js": "/js/two_2bb80372ebe8047a68d4.bundle.js" } }
You can see an example with Rails at Using this with Rails in README and it looks really easy.
References
- Why we should stop using Grunt & Gulp
- Replacing the Rails asset pipeline with Gulp
- Gulp - a modern approach to asset pipeline for Rails developers
- Gulp on Rails: Replacing the Asset Pipeline
- フロントエンドエンジニアのための「俺の最近のRailsのJS開発環境」を考察する
- react + rails 構成パターンまとめ。
- RailsのAsset PipelineとPrecompileをNode.jsのみで処理できるgulp-sprocketsを作った
The original post was published on Nov. 29, 2015, by me in Japanese.