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).
- Node.js > 4
- NPM > 3
- Add
webpacked
to your Gemfile - Run
bundle install
to install the gem - Run
bin/rails generate webpacked:install
to 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-server
andrails server
at the same time
Webpack configs are divided into on three files:
frontend/base.config.js
contains common setup for all environmentsfrontend/development.config.js
contains setup specific for developmentfrontend/production.config.js
includes 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:dev
runs webpack build for development environmentnpm run build:production
same for production environment
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.
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.
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
;kind
could be:js
or:css
webpacked_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.
Gem exposes a few configuration options:
webpacked.enabled
default totrue
; you probably want to disable webpacked in test environment, so helpers will not fail if manifest does not existwebpacked.manifest_path
default towebpack-assets.json
webpacked.load_manifest_on_initialize
default tofalse
; iftrue
then parse manifest on application bootstrapwebpacked.common_entry_name
default tocommon
(if you've changed this, you need to update it in the webpack config as well)webpacked.bin
default tonode_modules/.bin/webpack
webpacked.config
default tofrontend/main.config.js
webpacked.dev_server
enabled only in development mode- webpack-dev-server starts on port 3500 on localhost via HTTP (use
WEBPACK_DEV_HOST
andWEBPACK_DEV_PORT
env variables to change it) - assets compiled to
public/assets/webpack
; you can change it in webpack config (anddeploy.rb
if capistrano webpacked task used)
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_roles
so 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_force
unconditionally invokes production build on local machinedeploy:webpacked:sync
synchronize assets under:webpacked_release_output_path
with remote server
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_path
set :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
)
- deploy with remote webpack build performing
Pull requests, issues and discussion are welcomed
- 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