From 7d66cd7f21c2a8bfc47e845e502ccfe5455c349e Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Fri, 20 Oct 2023 12:33:41 +0200 Subject: [PATCH] Add contenthash to js/css build files Should prevent caching issues, see https://github.com/Earlopain/reverser/issues/99#issuecomment-1741835201 --- app/logical/esbuild_manifest.rb | 40 +++++++++++++++++++ app/views/layouts/application.html.erb | 4 +- .../{yml_autload.rb => listen.rb} | 4 ++ docker-compose.yml | 8 +++- test/logical/esbuild_manifest_test.rb | 26 ++++++++++++ 5 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 app/logical/esbuild_manifest.rb rename config/initializers/{yml_autload.rb => listen.rb} (66%) create mode 100644 test/logical/esbuild_manifest_test.rb diff --git a/app/logical/esbuild_manifest.rb b/app/logical/esbuild_manifest.rb new file mode 100644 index 00000000..481dfb83 --- /dev/null +++ b/app/logical/esbuild_manifest.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module EsbuildManifest + FILE_LOCATION = Rails.public_path.join("build/manifest.json") + + module_function + + def [](entry) + raise StandardError, "Entrypoint '#{entry}' not found" if entrypoints[entry].blank? && !Rails.env.test? + + "/#{entrypoints[entry]}" + end + + def entrypoints + @entrypoints ||= parse + end + + def parse + data = JSON.parse(FILE_LOCATION.read) + available_entrypoints = data["outputs"].select { |_k, v| v["entryPoint"].present? } + result = {} + available_entrypoints.each do |entrypoint_path, entrypoint_data| + relative_path = relative_to_public(entrypoint_path) + entrypoint_name = Pathname.new(entrypoint_data["entryPoint"]).basename + result[entrypoint_name.to_s] = relative_path.to_s + result[entrypoint_name.sub_ext(".css").to_s] = relative_to_public(entrypoint_data["cssBundle"]).to_s if entrypoint_data["cssBundle"] + end + result + rescue Errno::ENOENT, JSON::ParserError + {} + end + + def reset_cache + @entrypoints = nil + end + + def relative_to_public(input) + Rails.root.join(input).relative_path_from(Rails.public_path) + end +end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index c0bbd4ab..14ba6ef9 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -7,8 +7,8 @@ <%= csp_meta_tag %> - <%= stylesheet_link_tag "/build/application", media: "all" %> - <%= javascript_include_tag "/build/application" %> + <%= stylesheet_link_tag EsbuildManifest["application.css"], media: "all" %> + <%= javascript_include_tag EsbuildManifest["application.ts"] %> diff --git a/config/initializers/yml_autload.rb b/config/initializers/listen.rb similarity index 66% rename from config/initializers/yml_autload.rb rename to config/initializers/listen.rb index f382144c..05f0c2d3 100644 --- a/config/initializers/yml_autload.rb +++ b/config/initializers/listen.rb @@ -8,4 +8,8 @@ Listen.to(Rails.root.join("app/logical/sites/definitions"), only: /.*\.yml/) do Sites.reset_cache end.start + + Listen.to(EsbuildManifest::FILE_LOCATION.dirname, only: /#{EsbuildManifest::FILE_LOCATION.basename}$/) do + EsbuildManifest.reset_cache + end.start end diff --git a/docker-compose.yml b/docker-compose.yml index 9cd6f2c4..38ea602c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,13 @@ services: environment: <<: *common-env RAILS_SERVER_EXEC: bin/rails server -p 9000 -b 0.0.0.0 - ESBUILD_EXEC: esbuild app/typescript/application.ts --target=chrome111,firefox111,safari16 --bundle --sourcemap --outdir=public/build --loader:.png=file --watch=forever --color=true + ESBUILD_EXEC: >- + esbuild app/typescript/application.ts + --target=chrome111,firefox111,safari16 + --bundle --sourcemap + --outdir=public/build --loader:.png=file + --entry-names=[name]-[hash] --metafile=public/build/manifest.json + --watch=forever --color=true GOOD_JOB_EXEC: bundle exec good_job --queues="scraping:1;e6_iqdb:1;variant_generation:5;default:1;submission_download:5" volumes: - .:/app diff --git a/test/logical/esbuild_manifest_test.rb b/test/logical/esbuild_manifest_test.rb new file mode 100644 index 00000000..2075b4e2 --- /dev/null +++ b/test/logical/esbuild_manifest_test.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require "test_helper" + +class EsbuildManifestTest < ActiveSupport::TestCase + test "it correctly parses the entrypoints" do + manifest = <<~JSON + { + "outputs": { + "public/build/application-QTMXSKWO.js": { + "entryPoint": "app/typescript/application.ts", + "cssBundle": "public/build/application-D737BFOQ.css" + }, + "public/build/application-D737BFOQ.css": {} + } + } + JSON + expected = { + "application.ts" => "build/application-QTMXSKWO.js", + "application.css" => "build/application-D737BFOQ.css", + } + stub_const(EsbuildManifest, :FILE_LOCATION, StringIO.new(manifest)) do + assert_equal(expected, EsbuildManifest.parse) + end + end +end