-
-
Notifications
You must be signed in to change notification settings - Fork 317
Description
I recently did some maintenance work on an application that uses Alchemy CMS and replaced good old Webpacker with ViteRuby to improve build times. Everything was looking great and builds are now insanely fast. A few days later, I noticed that some pages wouldn’t load the referenced application.css, as the browser received a 404. The underlying reason is that ETags change when page content is updated, but not when frontend assets change. As a result, after rebuilding assets with Vite, browsers may still receive 304 Not Modified and keep using outdated HTML that references old assets.
This issue was not noticeable with Webpacker because it integrated more tightly with Rails asset digest system. The stylesheet_pack_tag helper produced fingerprinted asset URLs that were tracked as template dependencies, so cache digests and ETags automatically changed whenever assets were rebuilt.
I fixed the issue by hashing the Vite entrypoints (in an initializer) and using the resulting digest as an additional component in the ETag generation in the PagesController.
# Alchemy::PagesController
def page_etag
[@page, Alchemy::Vite.assets_digest, current_alchemy_user]
end
I think it would be great if ViteRuby also hooked into ActionView for a seamless integration. But this might not be within their scope, idk. At the same time, it would be very useful if Alchemy provided a way to hook into its ETag generation, so the main application could supply additional components such as asset digests.
I haven't really looked deeper in to the new Configuration class, but from what I've seen maybe the cache key for the etag can move into Configurations::PageCache and then be used like this, what do you think?
# Alchemy::PagesController
def render_fresh_page?
must_not_cache? || stale?(
etag: Alchemy.config.page_cache.cache_key_for(@page, current_alchemy_user),
last_modified: @page.last_modified_at,
...
)
end
# main app
# config/initializers/alchemy_page_cache.rb
Alchemy::Configurations::PageCache.extra_cache_key_parts << lambda do
entrypoints = ...
Digest::SHA1.hexdigest(
entrypoints.map { |e| ViteRuby.instance.manifest.path_for(e) }.join
)
end