Add cache busting to local CSS and JS assets
Compute MD5 digests of static asset files at boot time and append
them as query parameters (e.g. /css/app.css?v=0f0be602c2). This
ensures browsers fetch fresh copies after deployments that change
these files.
https://claude.ai/code/session_01WV7M82TahkbYrL12MygZGE
diff --git a/lib/ketchup/views/layout.rb b/lib/ketchup/views/layout.rb
index 5ee369f..b9c521e 100644
--- a/lib/ketchup/views/layout.rb
+++ b/lib/ketchup/views/layout.rb
@@ -1,9 +1,17 @@
# frozen_string_literal: true
+require "digest"
require "phlex"
module Views
class Layout < Phlex::HTML
+ ASSET_VERSIONS = begin
+ root = File.expand_path("../../../public", __dir__)
+ %w[/css/reset.css /css/utopia.css /css/app.css /js/app.js].to_h do |path|
+ [path, Digest::MD5.file(File.join(root, path)).hexdigest[0, 10]]
+ end.freeze
+ end
+
def initialize(current_user:, title: "Ketchup", active_view: nil)
@current_user = current_user
@title = title
@@ -18,9 +26,9 @@ module Views
meta(name: "viewport", content: "width=device-width, initial-scale=1")
title { @title }
link(rel: "icon", href: "/favicon.svg", type: "image/svg+xml")
- link(rel: "stylesheet", href: "/css/reset.css")
- link(rel: "stylesheet", href: "/css/utopia.css")
- link(rel: "stylesheet", href: "/css/app.css")
+ link(rel: "stylesheet", href: asset_path("/css/reset.css"))
+ link(rel: "stylesheet", href: asset_path("/css/utopia.css"))
+ link(rel: "stylesheet", href: asset_path("/css/app.css"))
link(rel: "preconnect", href: "https://fonts.googleapis.com")
link(rel: "preconnect", href: "https://fonts.gstatic.com", crossorigin: true)
link(rel: "stylesheet",
@@ -28,7 +36,7 @@ module Views
script(src: "https://unpkg.com/overtype@2.1.1/dist/overtype.min.js",
integrity: "sha384-zp8RL0j4VLfaFKgqehca9l8rfcE4Jh0Nt1CFoVyUBn+qa4velUokXJXsW2h0J5xT",
crossorigin: "anonymous")
- script(src: "/js/app.js", defer: true)
+ script(src: asset_path("/js/app.js"), defer: true)
script(src: "https://cdn.jsdelivr.net/npm/@alpinejs/persist@3.15.0/dist/cdn.min.js",
integrity: "sha384-6WOLkykwLb3YWzXZ6lAq+GI0p3V+enUm9jY6yIXGpIriiAUOSF5dgNJLoSSNam4j",
crossorigin: "anonymous", defer: true)
@@ -55,6 +63,11 @@ module Views
private
+ def asset_path(path)
+ version = ASSET_VERSIONS[path]
+ version ? "#{path}?v=#{version}" : path
+ end
+
def render_footer
config = CONFIG
parts = []