webpacked
webpacked helps you to integrate Webpack in to a Ruby on Rails application.
It can be used alongside sprockets but normaly you no need sprockets at all now: webpack replaces sprockets completely.
Using webpacked and webpack itself means that Javascript is a first-class citizen: you should use npm to manage packages (no more gemified Javascript libraries!).
In development mode assets are served via webpack-dev-server that brings a cool hot module reloading feature to your workflow. Webpack's manifest file is generated using assets-webpack-plugin.
Also webpacked offers deploy automation within capistrano task (see detailed instructions bellow).
Requirements
- Node.js > 4
- NPM > 3
Installation
- Add
webpackedto your Gemfile - Run
bundle installto install the gem - Run
bin/rails generate webpacked:installto get required files in your application, install NPM packages and foreman gem if you wish so - Run
foreman start(if you've decided to install it) to startwebpack-dev-serverandrails serverat the same time
Usage
Webpack configuration
Webpack configs are divided into on three files:
frontend/base.config.jscontains common setup for all environmentsfrontend/development.config.jscontains setup specific for developmentfrontend/production.config.jsincludes production optimizations
Follow the comments in these files and webpack official docs to conclude what meets your requirements.
Environment is defined via NODE_ENV variable and initialized infrontend/main.config.js. Normally you don't need to manual set up NODE_ENV unless you want to add another environment for your frontend application.
In development mode webpack-dev-server starts using npm run dev:server command or simply foreman start. Internally it uses frontend/dev-server.js.
Just in case there are a bunch other preconfigured NPM commands shipped (you can find them in the package.json):
npm run build:devruns webpack build for development environmentnpm run build:productionsame for production environment
View helpers
To add webpacked assets in to your application, use following helpers:
<% # layout.html.erb %>
<!DOCTYPE html>
<html>
<head>
<%== webpacked_css_tags 'application' %>
</head>
<body>
<%== webpacked_js_tags 'application' %>
</body>
</html>
Where application is a one of the your entry points in webpack config.
NOTE: if you using common chunks optimization (it is so indeed in production most likely), these helpers may produce additional CSS/Javascript include tag for that common bundle.
You should not concern about including any extra scripts to get hot module reloading works: it integrates transparently through a same webpack manifest file.
Controller helper
webpacked offers an optional approach to link entry points with Rails application. The idea is in mapping controllers to entry points. Consider the following code:
class ApplicationController < ActionController::Base
include Webpacked::ControllerHelper
webpacked_entry "application"
end
class FooController < ApplicationController
webpacked_entry "foo"
end
class BarController < ApplicationController
webpacked_entry %w(vendor bar) # accepts multiple entry points as well
end
<%== webpacked_css_tags webpacked_entry_name %>
<%== webpacked_js_tags webpacked_entry_name %>
In the example above the decision which entry point to use comes in controllers instead views. Therefore in layout we can use a common webpacked_entry_name helper method. Notice that webpacked_entry in ApplicationController will be used if concrete controller does not define its own entry.
Alternative ways
If you don't like none of the mentioned above, you can use more generic helpers to access webpack manifest:
asset_tag(entry, kind)return include tags for entry pointentry;kindcould be:jsor:csswebpacked_asset_path(entry, kind = nil)return only assets path for given entry (or entries if array given)
Be aware that common entry point is not included by these methods. So if you use common chunks optimization do not forget to include common (or whatever name you pick) entry point manually.
Rails configuration and other assumptions
Gem exposes a few configuration options:
webpacked.enableddefault totrue; you probably want to disable webpacked in test environment, so helpers will not fail if manifest does not existwebpacked.manifest_pathdefault towebpack-assets.jsonwebpacked.load_manifest_on_initializedefault tofalse; iftruethen parse manifest on application bootstrapwebpacked.common_entry_namedefault tocommon(if you've changed this, you need to update it in the webpack config as well)webpacked.bindefault tonode_modules/.bin/webpackwebpacked.configdefault tofrontend/main.config.jswebpacked.dev_serverenabled only in development mode- webpack-dev-server starts on port 3500 on localhost via HTTP (use
WEBPACK_DEV_HOSTandWEBPACK_DEV_PORTenv variables to change it) - assets compiled to
public/assets/webpack; you can change it in webpack config (anddeploy.rbif capistrano webpacked task used)
Capistrano deployment
Installation and usage
To deploy generated assets add require "capistrano/webpacked" to your Capfile.
Also you need to add some code in to deploy.rb:
- set up the
:assets_rolesso webpacked could run its tasks - define a hook when the task should be run:
after 'deploy:updated', 'deploy:webpacked:build'or (if you use sprockets)before 'deploy:compile_assets', 'deploy:webpacked:build'are good variants
What under the hood? The task makes diff of files and folders (specified in :webpacked_dependencies option) in current release against previous release and decides whether to run production webpack build. If there is no diff then simply a manifest copied from previous release path. If some of dependencies were changed then production build starts locally and assets synchronized via rsync over SSH.
NOTE: scince webpack build runs locally, you should pay an extra attention to your working copy condition: current branch, not published commits, not commited changes, etc.
Additionally there are some extra tasks exposed (they are used by deploy:webpacked:build internally):
deploy:webpacked:build_forceunconditionally invokes production build on local machinedeploy:webpacked:syncsynchronize assets under:webpacked_release_output_pathwith remote server
Configuration
There are following options available to set up (example values are defaults in fact):
set :webpacked_dependencies, %w(frontend npm-shrinkwrap.json)webpacked build will perform if one of these will be changedset :webpacked_manifest_path, "webpack-assets.json"same asRails.configuration.webpacked.manifest_pathset :webpacked_deploy_manifest_path, "webpack-assets-deploy.json"used for production manifest only; choose another path to not clash with local dev manifestset :webpacked_local_output_path, "public/#{fetch(:assets_prefix)}/webpack"where webpack generates its assetsset :webpacked_release_output_path, "public/#{fetch(:assets_prefix)}/webpack"where webpack assets are stored on the deploy server (started fromshared_path)
TODO
- deploy with remote webpack build performing
Contributing
Pull requests, issues and discussion are welcomed
Thanks
- Marketplacer team for their webpack-rails gem which guides this implementation in some way ;)
- Contributors of capistrano-faster-assets gem for conditionally assets compile idea