DEV: Introduce support for template colocation in themes

This commit is contained in:
David Taylor 2022-10-17 14:47:16 +01:00
parent 65a5c84a92
commit cb87067c77
5 changed files with 83 additions and 1 deletions

View File

@ -4,6 +4,7 @@
const makeEmberTemplateCompilerPlugin =
require("babel-plugin-ember-template-compilation").default;
const colocatedBabelPlugin = require("colocated-babel-plugin").default;
const precompile = require("ember-template-compiler").precompile;
const Handlebars = require("handlebars").default;
@ -50,6 +51,7 @@ function buildTemplateCompilerBabelPlugins({ themeId }) {
}
return [
colocatedBabelPlugin,
require("widget-hbs-compiler").WidgetHbsCompiler,
[
makeEmberTemplateCompilerPlugin(() => compileFunction),

View File

@ -6,7 +6,7 @@ require 'json_schemer'
class Theme < ActiveRecord::Base
include GlobalPath
BASE_COMPILER_VERSION = 63
BASE_COMPILER_VERSION = 64
attr_accessor :child_components

View File

@ -157,6 +157,7 @@ class DiscourseJsProcessor
load_file_in_context(ctx, "node_modules/babel-plugin-ember-template-compilation/src/plugin.js", wrap_in_module: "babel-plugin-ember-template-compilation/index")
load_file_in_context(ctx, "node_modules/babel-plugin-ember-template-compilation/src/expression-parser.js", wrap_in_module: "babel-plugin-ember-template-compilation/expression-parser")
load_file_in_context(ctx, "node_modules/babel-import-util/src/index.js", wrap_in_module: "babel-import-util")
load_file_in_context(ctx, "node_modules/ember-cli-htmlbars/lib/colocated-babel-plugin.js", wrap_in_module: "colocated-babel-plugin")
# Widget HBS compiler
widget_hbs_compiler_source = File.read("#{Rails.root}/lib/javascripts/widget-hbs-compiler.js")

View File

@ -39,6 +39,46 @@ class ThemeJavascriptCompiler
end
end
# Handle colocated components
tree.dup.each_pair do |filename, content|
is_component_template = filename.end_with?(".hbs") && filename.start_with?("#{root_name}/components/")
next if !is_component_template
template_contents = content
hbs_invocation_options = {
moduleName: filename,
parseOptions: {
srcName: filename
}
}
hbs_invocation = "hbs(#{template_contents.to_json}, #{hbs_invocation_options.to_json})"
prefix = <<~JS
import { hbs } from 'ember-cli-htmlbars';
const __COLOCATED_TEMPLATE__ = #{hbs_invocation};
JS
js_filename = filename.sub(/\.hbs\z/, ".js")
js_contents = tree[js_filename] # May be nil for template-only component
if js_contents && !js_contents.include?("export default")
message = "#{filename} does not contain a `default export`. Did you forget to export the component class?"
js_contents += "throw new Error(#{message.to_json});"
end
if js_contents.nil?
# No backing class, use template-only
js_contents = <<~JS
import templateOnly from '@ember/component/template-only';
export default templateOnly();
JS
end
js_contents = prefix + js_contents
tree[js_filename] = js_contents
tree.delete(filename)
end
# Transpile and write to output
tree.each_pair do |filename, content|
module_name, extension = filename.split(".", 2)

View File

@ -86,5 +86,44 @@ RSpec.describe ThemeJavascriptCompiler do
expect(compiler.content).to include('define("discourse/theme-1/components/mycomponent"')
expect(compiler.content).to include('define("discourse/theme-1/discourse/templates/components/mycomponent"')
end
it "handles colocated components" do
compiler.append_tree(
{
"discourse/components/mycomponent.js" => <<~JS,
import Component from "@glimmer/component";
export default class MyComponent extends Component {}
JS
"discourse/components/mycomponent.hbs" => "{{my-component-template}}"
}
)
expect(compiler.content).to include("__COLOCATED_TEMPLATE__ =")
expect(compiler.content).to include("setComponentTemplate")
end
it "prints error when default export missing" do
compiler.append_tree(
{
"discourse/components/mycomponent.js" => <<~JS,
import Component from "@glimmer/component";
class MyComponent extends Component {}
JS
"discourse/components/mycomponent.hbs" => "{{my-component-template}}"
}
)
expect(compiler.content).to include("__COLOCATED_TEMPLATE__ =")
expect(compiler.content).to include("throw new Error")
end
it "handles template-only components" do
compiler.append_tree(
{
"discourse/components/mycomponent.hbs" => "{{my-component-template}}"
}
)
expect(compiler.content).to include("__COLOCATED_TEMPLATE__ =")
expect(compiler.content).to include("setComponentTemplate")
expect(compiler.content).to include("@ember/component/template-only")
end
end
end