How to cache Rails webpack packs

Viewed 1673

I have a Rails 6 app with a very large chunk of vendor CSS and JS. The CSS and JS will rarely ever change. I want Rails to cache it.

I have created 2 packs. 1 contains all the imports from vendor and the other contains imports of my app's JS. I use javascript_packs_with_chunks_tag in the head for both.

Whenever I modify my app's JS file, that pack's cache is invalidated along with the vendor's pack as well!

This behaviour seems to be normal as I was able to reproduce it with a new Rails app.

Edit: I can fix the problem in a new Rails app by turning off compiling (webpacker.yml compile: false) and running webpack in a separate terminal tab (./bin/webpack --watch --colors --progress), but in my app I still experience the problem. It seems like there is some global variable or keyword that Webpack is finding that connects the two packs. Unfortunately the Webpack logs and dependency graph doesn't reveal that "connection". It's 100% not an import.

3 Answers

One simple solution I found was to move the vendor CSS and JS to sprockets instead of webpack. I dislike this solution but it works.

To do this use the sprocket-type include statements

<%= javascript_include_tag 'vendor' %>
<%= stylesheet_link_tag 'vendor' %>

and place your vendor js and CSS in their respective app/assets folders

Another solution is to disable compiling webpacker.yml compile: false and run webpack separately $./bin/webpack --watch --colors --progress.

The is documented here https://github.com/rails/webpacker#development where it says "If [...] you have enough JavaScript that on-demand compilation is too slow...".

I dislike this solution as well because it doesn't explain why that is necessary. The problem is not that compilation is too slow but that caching isn't happening. This solution usually does solve the caching problem at the same time though...

Comments promoted here to an answer...

Whilst one would hope that compilation is skipped for the vendor code when that code doesn't change, this does not appear to be the case. However the filename of the compiled code should not* change if the content doesn't change.

Assuming that you have achieved a filename that doesn't change, then the normal http cache control mechanisms should give the desired caching, even though the vendor code is being unnecessarily compiled. One reference for how to control http cache control headers is here

*There is some ambiguity about this in the webpack docs, where it suggests that some versions may change the compiled filename (hash value), even with unchanged code, due to timestamps and other stuff added by webpack. The (webpack docs) do describe available mitigation measures if you find the filename hash to be changing.

Related