DEV: Compile theme migrations javascript files when running theme qunit (#25219)

Why this change?

Currently, is it hard to iteratively write a theme settings migrations
because our theme migrations system does not rollback. Therefore, we
want to allow theme developers to be able to write QUnit tests for their
theme migrations files enabling them to iteratively write their theme
migrations.

What does this change do?

1. Update `Theme#baked_js_tests_with_digest` to include all `ThemeField`
records of `ThemeField#target` equal to `migrations`. Note that we do
not include the `settings` and `themePrefix` variables for migration files.

2. Don't minify JavaScript test files becasue it makes debugging in
   development hard.
This commit is contained in:
Alan Guo Xiang Tan 2024-01-16 09:50:44 +08:00 committed by GitHub
parent fd7c6f1ca8
commit 22614ca85b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 46 additions and 20 deletions

View File

@ -913,21 +913,27 @@ class Theme < ActiveRecord::Base
def baked_js_tests_with_digest def baked_js_tests_with_digest
tests_tree = tests_tree =
theme_fields theme_fields_to_tree(
.where(target_id: Theme.targets[:tests_js]) theme_fields.where(target_id: Theme.targets[:tests_js]).order(name: :asc),
.order(name: :asc) )
.pluck(:name, :value)
.to_h
return nil, nil if tests_tree.blank? return nil, nil if tests_tree.blank?
compiler = ThemeJavascriptCompiler.new(id, name) migrations_tree =
compiler.append_tree(tests_tree, for_tests: true) theme_fields_to_tree(
theme_fields.where(target_id: Theme.targets[:migrations]).order(name: :asc),
)
compiler = ThemeJavascriptCompiler.new(id, name, minify: false)
compiler.append_tree(migrations_tree, include_variables: false)
compiler.append_tree(tests_tree)
compiler.append_raw_script "test_setup.js", <<~JS compiler.append_raw_script "test_setup.js", <<~JS
(function() { (function() {
require("discourse/lib/theme-settings-store").registerSettings(#{self.id}, #{cached_default_settings.to_json}, { force: true }); require("discourse/lib/theme-settings-store").registerSettings(#{self.id}, #{cached_default_settings.to_json}, { force: true });
})(); })();
JS JS
content = compiler.content content = compiler.content
if compiler.source_map if compiler.source_map
@ -942,6 +948,13 @@ class Theme < ActiveRecord::Base
attr_accessor :theme_setting_requests_refresh attr_accessor :theme_setting_requests_refresh
def theme_fields_to_tree(theme_fields_scope)
theme_fields_scope.reduce({}) do |tree, theme_field|
tree[theme_field.file_path] = theme_field.value
tree
end
end
def to_scss_variable(name, value) def to_scss_variable(name, value)
escaped = SassC::Script::Value::String.quote(value.to_s, sass: true) escaped = SassC::Script::Value::String.quote(value.to_s, sass: true)
"$#{name}: unquote(#{escaped});" "$#{name}: unquote(#{escaped});"

View File

@ -18,24 +18,27 @@ class ThemeJavascriptCompiler
@@terser_disabled = false @@terser_disabled = false
end end
def initialize(theme_id, theme_name) def initialize(theme_id, theme_name, minify: true)
@theme_id = theme_id @theme_id = theme_id
@output_tree = [] @output_tree = []
@theme_name = theme_name @theme_name = theme_name
@minify = minify
end end
def compile! def compile!
if !@compiled if !@compiled
@compiled = true @compiled = true
@output_tree.freeze @output_tree.freeze
output = output =
if !has_content? if !has_content?
{ "code" => "" } { "code" => "" }
elsif @@terser_disabled elsif @@terser_disabled || !@minify
{ "code" => raw_content } { "code" => raw_content }
else else
DiscourseJsProcessor::Transpiler.new.terser(@output_tree.to_h, terser_config) DiscourseJsProcessor::Transpiler.new.terser(@output_tree.to_h, terser_config)
end end
@content = output["code"] @content = output["code"]
@source_map = output["map"] @source_map = output["map"]
end end
@ -92,7 +95,7 @@ class ThemeJavascriptCompiler
JS JS
end end
def append_tree(tree, for_tests: false) def append_tree(tree, include_variables: true)
# Replace legacy extensions # Replace legacy extensions
tree.transform_keys! do |filename| tree.transform_keys! do |filename|
if filename.ends_with? ".js.es6" if filename.ends_with? ".js.es6"
@ -168,9 +171,9 @@ class ThemeJavascriptCompiler
# Transpile and write to output # Transpile and write to output
tree.each_pair do |filename, content| tree.each_pair do |filename, content|
module_name, extension = filename.split(".", 2) module_name, extension = filename.split(".", 2)
module_name = "test/#{module_name}" if for_tests
if extension == "js" || extension == "gjs" if extension == "js" || extension == "gjs"
append_module(content, module_name, extension) append_module(content, module_name, extension, include_variables:)
elsif extension == "hbs" elsif extension == "hbs"
append_ember_template(module_name, content) append_ember_template(module_name, content)
elsif extension == "hbr" elsif extension == "hbr"
@ -238,6 +241,7 @@ class ThemeJavascriptCompiler
script = "#{theme_settings}#{script}" if include_variables script = "#{theme_settings}#{script}" if include_variables
transpiler = DiscourseJsProcessor::Transpiler.new transpiler = DiscourseJsProcessor::Transpiler.new
@output_tree << ["#{original_filename}.#{extension}", <<~JS] @output_tree << ["#{original_filename}.#{extension}", <<~JS]
if ('define' in window) { if ('define' in window) {
#{transpiler.perform(script, "", name, theme_id: @theme_id, extension: extension).strip} #{transpiler.perform(script, "", name, theme_id: @theme_id, extension: extension).strip}

View File

@ -916,12 +916,14 @@ HTML
name: "yaml", name: "yaml",
value: "some_number: 1", value: "some_number: 1",
) )
theme.set_field( theme.set_field(
target: :tests_js, target: :tests_js,
type: :js, type: :js,
name: "acceptance/some-test.js", name: "acceptance/some-test.js",
value: "assert.ok(true);", value: "assert.ok(true);",
) )
theme.save! theme.save!
end end
@ -930,6 +932,21 @@ HTML
expect(theme.baked_js_tests_with_digest).to eq([nil, nil]) expect(theme.baked_js_tests_with_digest).to eq([nil, nil])
end end
it "includes theme's migrations theme fields" do
theme.set_field(
target: :migrations,
type: :js,
name: "0001-some-migration",
value: "export default function migrate(settings) { return settings; }",
)
theme.save!
content, _digest = theme.baked_js_tests_with_digest
expect(content).to include("function migrate(settings)")
end
it "digest does not change when settings are changed" do it "digest does not change when settings are changed" do
content, digest = theme.baked_js_tests_with_digest content, digest = theme.baked_js_tests_with_digest
expect(content).to be_present expect(content).to be_present

View File

@ -229,13 +229,5 @@ RSpec.describe ThemeJavascriptsController do
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(response.body).to include("assert.ok(343434);") expect(response.body).to include("assert.ok(343434);")
end end
it "includes inline sourcemap" do
ThemeJavascriptCompiler.enable_terser!
content, digest = component.baked_js_tests_with_digest
get "/theme-javascripts/tests/#{component.id}-#{digest}.js"
expect(response.status).to eq(200)
expect(response.body).to include("//# sourceMappingURL=data:application/json;base64,")
end
end end
end end