DEV: Introduce minification and source maps for Theme JS (#18646)

Theme javascript is now minified using Terser, just like our core/plugin JS bundles. This reduces the amount of data sent over the network.

This commit also introduces sourcemaps for theme JS. Browser developer tools will now be able show each source file separately when browsing, and also in backtraces.

For theme test JS, the sourcemap is inlined for simplicity. Network load is not a concern for tests.
This commit is contained in:
David Taylor
2022-10-18 18:20:10 +01:00
committed by GitHub
parent e23b247690
commit be3d6a56ce
14 changed files with 338 additions and 67 deletions
+44 -18
View File
@@ -9,10 +9,10 @@ class ThemeJavascriptsController < ApplicationController
:preload_json,
:redirect_to_login_if_required,
:verify_authenticity_token,
only: [:show, :show_tests]
only: [:show, :show_map, :show_tests]
)
before_action :is_asset_path, :no_cookies, :apply_cdn_headers, only: [:show, :show_tests]
before_action :is_asset_path, :no_cookies, :apply_cdn_headers, only: [:show, :show_map, :show_tests]
def show
raise Discourse::NotFound unless last_modified.present?
@@ -21,18 +21,29 @@ class ThemeJavascriptsController < ApplicationController
# Security: safe due to route constraint
cache_file = "#{DISK_CACHE_PATH}/#{params[:digest]}.js"
unless File.exist?(cache_file)
content = query.pluck_first(:content)
raise Discourse::NotFound if content.nil?
FileUtils.mkdir_p(DISK_CACHE_PATH)
File.write(cache_file, content)
write_if_not_cached(cache_file) do
content, has_source_map = query.pluck_first(:content, "source_map IS NOT NULL")
if has_source_map
content += "\n//# sourceMappingURL=#{params[:digest]}.map?__ws=#{Discourse.current_hostname}\n"
end
content
end
# this is only required for NGINX X-SendFile it seems
response.headers["Content-Length"] = File.size(cache_file).to_s
set_cache_control_headers
send_file(cache_file, disposition: :inline)
serve_file(cache_file)
end
def show_map
raise Discourse::NotFound unless last_modified.present?
return render body: nil, status: 304 if not_modified?
# Security: safe due to route constraint
cache_file = "#{DISK_CACHE_PATH}/#{params[:digest]}.map"
write_if_not_cached(cache_file) do
query.pluck_first(:source_map)
end
serve_file(cache_file)
end
def show_tests
@@ -48,14 +59,11 @@ class ThemeJavascriptsController < ApplicationController
@cache_file = "#{TESTS_DISK_CACHE_PATH}/#{digest}.js"
return render body: nil, status: 304 if not_modified?
if !File.exist?(@cache_file)
FileUtils.mkdir_p(TESTS_DISK_CACHE_PATH)
File.write(@cache_file, content)
write_if_not_cached(@cache_file) do
content
end
response.headers["Content-Length"] = File.size(@cache_file).to_s
set_cache_control_headers
send_file(@cache_file, disposition: :inline)
serve_file @cache_file
end
private
@@ -94,4 +102,22 @@ class ThemeJavascriptsController < ApplicationController
immutable_for(1.year)
end
end
def write_if_not_cached(cache_file)
unless File.exist?(cache_file)
content = yield
raise Discourse::NotFound if content.nil?
FileUtils.mkdir_p(File.dirname(cache_file))
File.write(cache_file, content)
end
end
def serve_file(cache_file)
# this is only required for NGINX X-SendFile it seems
response.headers["Content-Length"] = File.size(cache_file).to_s
set_cache_control_headers
type = cache_file.end_with?(".map") ? "application/json" : "text/javascript"
send_file(cache_file, type: type, disposition: :inline)
end
end