0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-06 02:51:10 +08:00
discourse/lib/theme_javascript_compiler.rb
David Taylor e3054ef590
DEV: Improve cross-plugin/theme import handling (#40939)
1. Remove 'federated exports' system, which was named entrypoint exports
for every module inside the plugin. Replace it with a single import of
the target plugin's 'compatModules', and then update call sites to do a
'just in time' lookup of the module and export. This is implemented in a
new `babel-resolve-plugin-imports` plugin

2. Update theme & plugin build systems to produce a list of external
plugins which are imported. For plugins, it's stored in the manifest.
For themes, it's stored in a new column of the javascript_caches table.

3. Refactor theme extra_js loading to use a more structured data model,
and move the HTML generation to the erb template

4. Update core importmap to identify any missing plugin dependencies and
add a fake placeholder module for them. This allows optional imports to
exist without causing a boot error. The imported values will resolve to
'null'.

5. Update core plugins to remove use of `optionalRequire`, and replace
it with regular imports, and a `with { discourseImport: "optional" }`
suffix. This is functionally equivalent to optionalRequire, but without
leaning on the legacy `loader.js` system of core

6. Add support for `with { discourseImport: "optional" }` for
plugins/themes importing core modules. This is useful when a
theme/plugin needs to target multiple versions of Discourse core.

Co-authored-by: Jarek Radosz <jarek@cvx.dev>
2026-06-30 16:11:38 +01:00

83 lines
1.8 KiB
Ruby
Vendored

# frozen_string_literal: true
class ThemeJavascriptCompiler
class CompileError < StandardError
end
@@terser_disabled = false
def self.disable_terser!
raise "Tests only" if !Rails.env.test?
@@terser_disabled = true
end
def self.enable_terser!
raise "Tests only" if !Rails.env.test?
@@terser_disabled = false
end
def initialize(theme_id, theme_name, minify: true)
@theme_id = theme_id
@input_tree = {}
@theme_name = theme_name
@minify = minify
end
def compile!
if !@compiled
@compiled = true
@input_tree =
@input_tree.to_h do |k, v|
if k.end_with?(".js.es6")
[k.sub(/\.js\.es6$/, ".js"), AssetProcessor.append_es6_deprecation(v, k)]
else
[k, v]
end
end
@input_tree.freeze
output =
AssetProcessor.new.rollup(
@input_tree,
{
themeId: @theme_id,
minify: @minify && !@@terser_disabled,
entrypoints: {
main: {
modules: @input_tree.keys,
},
},
},
)
main = output.values.find { |chunk| chunk["name"] == "main" }
@content = main["code"]
@source_map = main["map"]
@external_plugin_imports = main["externalPluginImports"] || []
end
[@content, @source_map]
rescue AssetProcessor::TranspileError => e
message = "[THEME #{@theme_id} '#{@theme_name}'] Compile error: #{e.message}"
@content = "throw new Error(#{message.to_json});\n"
@external_plugin_imports = []
[@content, @source_map]
end
def content
compile!
@content
end
def source_map
compile!
@source_map
end
def external_plugin_imports
compile!
@external_plugin_imports
end
def append_tree(tree)
@input_tree.merge!(tree)
end
end