It's nice that the Rails Webpacker gem and NPM package abstracts your webpack config... that is until you need to make changes.

In my previous post, I talked about how to customize the webpack config... but how can you be sure you're making the right change? The webpack config is JavaScript, so you can't simply jump into the Rails console to poke around. But you do have some other tools at your disposal though.

In this post, I'll share some tips for debugging the webpack config in your Rails app.

The one-liner

For the following examples, I'm using Node v12.13.1.

Here's a quick one-liner for printing the entire Rails webpack config in development:

$ RAILS_ENV=development node -e 'console.dir(require("./config/webpack/development"), { depth: null })'

I like console.dir as a nice alternative to console.log for inspecting JavaScript objects.

For inspecting the test or production configs, just update the RAILS_ENV and the target file:

$ RAILS_ENV=development node -e 'console.dir(require("./config/webpack/development"), { depth: null })'
# OR
$ RAILS_ENV=test node -e 'console.dir(require("./config/webpack/test"), { depth: null })'
# OR
$ RAILS_ENV=production node -e 'console.dir(require("./config/webpack/production"), { depth: null })'

We ensure the RAILS_ENV is set so Webpacker's NPM package will load the correct settings from your config/webpacker.yml file.

To make it even easier, I'll put this into a script file in bin/inspect_webpack with my Rails projects.

#!/usr/bin/env sh

env=${RAILS_ENV:-development}
RAILS_ENV=${env} node -e "console.dir(require(\"./config/webpack/${env}\"), { depth: null })"

Then to run:

$ chmod a+x ./bin/inspect_webpack
$ ./bin/inspect_webpack
# OR
$ RAILS_ENV=test ./bin/inspect_webpack
# OR
$ RAILS_ENV=production ./bin/inspect_webpack

On the console

For an interactive experience, you can run node to pull up the Node.js REPL. This is especially helpful for isolating pieces of the webpack config "tree":

$ RAILS_ENV=development node
> const config = require('./config/webpack/development')
undefined
> console.dir(config, { depth: null })
{
  mode: 'development',
  output: {
// displays the entire webpack config
// ...
> console.dir(config.plugins, { depth: null })
// displays the plugins ...
// ...

As with the script I showed earlier, change the RAILS_ENV to inspect the configs for the other environments.

From the node console, you can also access and play around with the Webpack environment object directly:

> const { environment } = require('@rails/webpacker')
undefined
> environment.plugins.get('Manifest')
// displays configured WebpackAssetsManifest plugin

Debugging with DevTools

While the above examples help inspect the webpack config in a REPL, it may help to debug the config within the build process itself. It's possible to use the debugger provided by Chrome DevTools on a Node.js process (as opposed to a browser's JavaScript process).

For the following examples, I'm using Chrome Version 80.0.3987.163

We could, for example, drop a debugger; statement into our Webpacker webpack config:

const { environment } = require('@rails/webpacker')

debugger

// changes I want to debug ...

module.exports = environment

We can then run the webpack build with the --debug flag:

$ ./bin/webpack --debug

which is in the local development environment is equivalent to:

$ RAILS_ENV=development node --inspect-brk yarn webpack --config ./config/webpack/development.js

Running the webpack process in debug mode will open up a websocket to communicate with Chrome DevTools:

$ ./bin/webpack --debug
Debugger listening on ws://127.0.0.1:9229/861b81ed-6f2f....
For help, see: https://nodejs.org/en/docs/inspector

Visit chrome://inspect in your Chrome browser and we can find a link for our running Node process in the menu: Screenshot of the chrome://inspect page

This will start a instance of the DevTools for your Node process where we can click "Play" to resume execution: Screenshot of Chrome DevTools debugger start

The process halts when it hits our debugger statement and we can modify values on the console: Screenshot of Chrome DevTools console

For larger (or misconfigured) projects, you may experience memory usage issues with the webpack build. The DevTools debugger also provides a Memory tab for taking heap snapshots and tracking down the memory hogs in your build process.

Screenshot of DevTools Memory tab Screenshot of DevTools heap snapshot

There's more on using DevTools with webpack on the webpack blog.

Speed measure plugin

To help isolate slow parts of your build, I highly recommend the Speed Measure Plugin for webpack. This is a plugin you would install and configure in your project temporarily to get feedback about individual parts of the build process.

First, install the plugin:

yarn add speed-measure-webpack-plugin

Then temporarily configure your production build (you could also do something similar for development or test):

process.env.NODE_ENV = process.env.NODE_ENV || 'production'

const environment = require('./environment')

const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()

module.exports = smp.wrap(environment.toWebpackConfig())

And then run the production build:

$ RAILS_ENV=production NODE_ENV=production ./bin/webpack

The Speed Measure plugin will print information to $stdout which may help identify the slow parts:

 SMP  ⏱
General output time took 3.094 secs

 SMP  ⏱  Plugins
CaseSensitivePathsPlugin took 0.391 secs
TerserPlugin took 0.306 secs
WebpackAssetsManifest took 0.066 secs
CompressionPlugin took 0.019 secs
MiniCssExtractPlugin took 0.001 secs
OptimizeCssAssetsWebpackPlugin took 0.001 secs
EnvironmentPlugin took 0 secs

 SMP  ⏱  Loaders
modules with no loaders took 1.27 secs
  module count = 365
babel-loader took 0.824 secs
  module count = 4

See How to boost the speed of your webpack build and the official webpack build performance docs for a number of useful tips for improving build/compilation performance.

Wrapping up

Even though Webpacker hides away much of the complexity of webpack configuration, sometimes it's necessary to peel back the abstraction layer. Like anything else that's new, wrapping your head around webpack build can be intimidating, especially if you don't know where to start. If things go wrong, all is not lost. Hopefully this post helped illustrate some ways you can get insight into what's happening in your Rails webpack config.

Discuss it on Twitter · Published on May 4, 2020

Need help with Webpack on Rails?

My name is Ross Kaffenberger.

Webpacker now ships by default in Rails 6 but there's no need to stress. I'm putting together a course to help you master Webpack on Rails.

I teach full stack web developers about frontend tools and performance, especially at the intersection of JavaScript and Ruby on Rails.

Subscribe to stay in the loop.

    More posts

    The webpack plugin I can't live without

    In this post, we'll take a look at installing and using the webpack-bundle-analyzer, perhaps the most invaluable webpack plugin, to analyze and debug the output of the webpack build in a Rails project configured to use Webpacker.

    Why does Rails 6 include both Webpacker and Sprockets?

    A new Rails 6 application will install both Webpacker and Sprockets by default. Don't they solve the same problem? This article dives into why Sprockets lives on even though webpack has surpassed most of its features and why you might want to choose one over the other.

    How to customize webpack in Rails apps

    Configuring webpack is precisely the main job of Webpacker's NPM package, @rails/webpacker. This post provides an overview of how to customize its default settings for your Rails application.

    Photo by Alan Emery on Unsplash