2019-05-02 18:17:27 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2019-01-17 06:46:11 -05:00
|
|
|
class ThemeJavascriptCompiler
|
|
|
|
|
2022-09-01 06:50:46 -04:00
|
|
|
COLOCATED_CONNECTOR_REGEX = /\A(?<prefix>.*)\/connectors\/(?<outlet>[^\/]+)\/(?<name>[^\/\.]+)\z/
|
2019-01-17 06:46:11 -05:00
|
|
|
|
|
|
|
class CompileError < StandardError
|
|
|
|
end
|
|
|
|
|
|
|
|
attr_accessor :content
|
|
|
|
|
2019-05-24 10:25:55 -04:00
|
|
|
def initialize(theme_id, theme_name)
|
2019-01-17 06:46:11 -05:00
|
|
|
@theme_id = theme_id
|
2019-05-02 18:17:27 -04:00
|
|
|
@content = +""
|
2019-05-24 10:25:55 -04:00
|
|
|
@theme_name = theme_name
|
2019-01-17 06:46:11 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def prepend_settings(settings_hash)
|
|
|
|
@content.prepend <<~JS
|
|
|
|
(function() {
|
2021-04-12 08:02:58 -04:00
|
|
|
if ('require' in window) {
|
|
|
|
require("discourse/lib/theme-settings-store").registerSettings(#{@theme_id}, #{settings_hash.to_json});
|
2019-01-17 06:46:11 -05:00
|
|
|
}
|
|
|
|
})();
|
|
|
|
JS
|
|
|
|
end
|
|
|
|
|
|
|
|
def append_ember_template(name, hbs_template)
|
2022-09-01 06:50:46 -04:00
|
|
|
name = "/#{name}" if !name.start_with?("/")
|
|
|
|
module_name = "discourse/theme-#{@theme_id}#{name}"
|
|
|
|
|
|
|
|
# Some themes are colocating connector JS under `/connectors`. Move template to /templates to avoid module name clash
|
|
|
|
if (match = COLOCATED_CONNECTOR_REGEX.match(module_name)) && !match[:prefix].end_with?("/templates")
|
|
|
|
module_name = "#{match[:prefix]}/templates/connectors/#{match[:outlet]}/#{match[:name]}"
|
2021-04-12 08:02:58 -04:00
|
|
|
end
|
2022-09-01 06:50:46 -04:00
|
|
|
|
|
|
|
# Mimics the ember-cli implementation
|
|
|
|
# https://github.com/ember-cli/ember-cli-htmlbars/blob/d5aa14b3/lib/template-compiler-plugin.js#L18-L26
|
|
|
|
script = <<~JS
|
|
|
|
import { hbs } from 'ember-cli-htmlbars';
|
|
|
|
export default hbs(#{hbs_template.to_json}, { moduleName: #{module_name.to_json} });
|
|
|
|
JS
|
|
|
|
|
|
|
|
template_module = DiscourseJsProcessor.transpile(script, "", module_name, theme_id: @theme_id)
|
2019-01-17 06:46:11 -05:00
|
|
|
content << <<~JS
|
2022-09-01 06:50:46 -04:00
|
|
|
if ('define' in window) {
|
|
|
|
#{template_module}
|
|
|
|
}
|
2019-01-17 06:46:11 -05:00
|
|
|
JS
|
2022-09-01 06:50:46 -04:00
|
|
|
rescue MiniRacer::RuntimeError, DiscourseJsProcessor::TranspileError => ex
|
|
|
|
raise CompileError.new ex.message
|
2019-01-17 06:46:11 -05:00
|
|
|
end
|
|
|
|
|
2020-03-06 11:35:52 -05:00
|
|
|
def raw_template_name(name)
|
|
|
|
name = name.sub(/\.(raw|hbr)$/, '')
|
|
|
|
name.inspect
|
|
|
|
end
|
|
|
|
|
2019-01-17 06:46:11 -05:00
|
|
|
def append_raw_template(name, hbs_template)
|
2022-09-01 06:50:46 -04:00
|
|
|
compiled = DiscourseJsProcessor::Transpiler.new.compile_raw_template(hbs_template, theme_id: @theme_id)
|
2019-01-17 06:46:11 -05:00
|
|
|
@content << <<~JS
|
|
|
|
(function() {
|
2020-05-05 12:15:03 -04:00
|
|
|
const addRawTemplate = requirejs('discourse-common/lib/raw-templates').addRawTemplate;
|
|
|
|
const template = requirejs('discourse-common/lib/raw-handlebars').template(#{compiled});
|
|
|
|
addRawTemplate(#{raw_template_name(name)}, template);
|
2019-01-17 06:46:11 -05:00
|
|
|
})();
|
|
|
|
JS
|
2022-09-01 06:50:46 -04:00
|
|
|
rescue MiniRacer::RuntimeError, DiscourseJsProcessor::TranspileError => ex
|
|
|
|
raise CompileError.new ex.message
|
2019-01-17 06:46:11 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def append_raw_script(script)
|
|
|
|
@content << script + "\n"
|
|
|
|
end
|
|
|
|
|
2019-11-05 06:54:12 -05:00
|
|
|
def append_module(script, name, include_variables: true)
|
2021-04-12 08:02:58 -04:00
|
|
|
name = "discourse/theme-#{@theme_id}/#{name.gsub(/^discourse\//, '')}"
|
2022-09-01 06:50:46 -04:00
|
|
|
|
|
|
|
# Some themes are colocating connector JS under `/templates/connectors`. Move out of templates to avoid module name clash
|
|
|
|
if (match = COLOCATED_CONNECTOR_REGEX.match(name)) && match[:prefix].end_with?("/templates")
|
|
|
|
name = "#{match[:prefix].delete_suffix("/templates")}/connectors/#{match[:outlet]}/#{match[:name]}"
|
|
|
|
end
|
|
|
|
|
2021-04-12 08:02:58 -04:00
|
|
|
script = "#{theme_settings}#{script}" if include_variables
|
2020-03-11 09:43:55 -04:00
|
|
|
transpiler = DiscourseJsProcessor::Transpiler.new
|
2021-04-12 08:02:58 -04:00
|
|
|
@content << <<~JS
|
|
|
|
if ('define' in window) {
|
|
|
|
#{transpiler.perform(script, "", name).strip}
|
|
|
|
}
|
|
|
|
JS
|
2022-09-01 06:50:46 -04:00
|
|
|
rescue MiniRacer::RuntimeError, DiscourseJsProcessor::TranspileError => ex
|
2019-06-03 05:41:00 -04:00
|
|
|
raise CompileError.new ex.message
|
|
|
|
end
|
|
|
|
|
2019-01-17 06:46:11 -05:00
|
|
|
def append_js_error(message)
|
|
|
|
@content << "console.error('Theme Transpilation Error:', #{message.inspect});"
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2021-04-12 08:02:58 -04:00
|
|
|
def theme_settings
|
2019-06-03 05:41:00 -04:00
|
|
|
<<~JS
|
2021-04-12 08:02:58 -04:00
|
|
|
const settings = require("discourse/lib/theme-settings-store")
|
2019-06-03 05:41:00 -04:00
|
|
|
.getObjectForTheme(#{@theme_id});
|
|
|
|
const themePrefix = (key) => `theme_translations.#{@theme_id}.${key}`;
|
|
|
|
JS
|
|
|
|
end
|
2019-01-17 06:46:11 -05:00
|
|
|
end
|