diff --git a/app/assets/javascripts/discourse/app/index.html b/app/assets/javascripts/discourse/app/index.html
index 13db87b245e..3b465595f3b 100644
--- a/app/assets/javascripts/discourse/app/index.html
+++ b/app/assets/javascripts/discourse/app/index.html
@@ -24,14 +24,9 @@
{{content-for "head"}}
-
-
-
+
-
-
-
-
+
diff --git a/app/assets/javascripts/discourse/ember-cli-build.js b/app/assets/javascripts/discourse/ember-cli-build.js
index d22036ad697..4ed050884d7 100644
--- a/app/assets/javascripts/discourse/ember-cli-build.js
+++ b/app/assets/javascripts/discourse/ember-cli-build.js
@@ -13,6 +13,7 @@ const DeprecationSilencer = require("deprecation-silencer");
const generateWorkboxTree = require("./lib/workbox-tree-builder");
const { compatBuild } = require("@embroider/compat");
const { Webpack } = require("@embroider/webpack");
+const { StatsWriterPlugin } = require("webpack-stats-plugin");
process.env.BROCCOLI_ENABLED_MEMOIZE = true;
@@ -135,6 +136,13 @@ module.exports = function (defaults) {
output: {
publicPath: "auto",
},
+ entry: {
+ "assets/discourse.js/features/markdown-it.js": {
+ import: "./static/markdown-it",
+ dependOn: "assets/discourse.js",
+ runtime: false,
+ },
+ },
externals: [
function ({ request }, callback) {
if (
@@ -175,6 +183,39 @@ module.exports = function (defaults) {
},
],
},
+ plugins: [
+ // The server use this output to map each asset to its chunks
+ new StatsWriterPlugin({
+ filename: "assets.json",
+ stats: {
+ all: false,
+ entrypoints: true,
+ },
+ transform({ entrypoints }) {
+ let names = Object.keys(entrypoints);
+ let output = {};
+
+ for (let name of names.sort()) {
+ let assets = entrypoints[name].assets.map(
+ (asset) => asset.name
+ );
+
+ let parent = names.find((parentName) =>
+ name.startsWith(parentName + "/")
+ );
+
+ if (parent) {
+ name = name.slice(parent.length + 1);
+ output[parent][name] = { assets };
+ } else {
+ output[name] = { assets };
+ }
+ }
+
+ return JSON.stringify(output, null, 2);
+ },
+ }),
+ ],
},
},
});
diff --git a/app/assets/javascripts/discourse/package.json b/app/assets/javascripts/discourse/package.json
index f2a4a5f4adc..743be342876 100644
--- a/app/assets/javascripts/discourse/package.json
+++ b/app/assets/javascripts/discourse/package.json
@@ -130,6 +130,7 @@
"util": "^0.12.5",
"virtual-dom": "^2.1.1",
"webpack": "^5.89.0",
+ "webpack-stats-plugin": "^1.1.3",
"wizard": "1.0.0",
"workbox-cacheable-response": "^7.0.0",
"workbox-core": "^7.0.0",
diff --git a/app/assets/javascripts/discourse/tests/index.html b/app/assets/javascripts/discourse/tests/index.html
index 625754161b3..d12a6ae8004 100644
--- a/app/assets/javascripts/discourse/tests/index.html
+++ b/app/assets/javascripts/discourse/tests/index.html
@@ -45,19 +45,12 @@
{{content-for "body"}} {{content-for "test-body"}}
-
-
-
-
-
-
-
+
-
-
-
-
+
+
+
diff --git a/app/assets/javascripts/yarn.lock b/app/assets/javascripts/yarn.lock
index 789436df5a0..b90242c503c 100644
--- a/app/assets/javascripts/yarn.lock
+++ b/app/assets/javascripts/yarn.lock
@@ -10789,6 +10789,11 @@ webpack-sources@^3.2.3:
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
+webpack-stats-plugin@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/webpack-stats-plugin/-/webpack-stats-plugin-1.1.3.tgz#ebcc36c8b468074ad737882e2043c1ce4b55d928"
+ integrity sha512-yUKYyy+e0iF/w31QdfioRKY+h3jDBRpthexBOWGKda99iu2l/wxYsI/XqdlP5IU58/0KB9CsJZgWNAl+/MPkRw==
+
webpack@^5.89.0:
version "5.89.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc"
diff --git a/app/views/qunit/theme.html.erb b/app/views/qunit/theme.html.erb
index eff72c156ce..7a1d27396d4 100644
--- a/app/views/qunit/theme.html.erb
+++ b/app/views/qunit/theme.html.erb
@@ -8,7 +8,8 @@
<%- if @has_test_bundle && !@suggested_themes %>
<%= preload_script "vendor" %>
<%= preload_script "test-support" %>
- <%= preload_script "discourse-for-tests" %>
+ <%= preload_script "discourse" %>
+ <%= preload_script "test" %>
<%= preload_script "locales/#{I18n.locale}" %>
<%= preload_script "admin" %>
<%- Discourse.find_plugin_js_assets(include_disabled: true).each do |file| %>
diff --git a/lib/ember_cli.rb b/lib/ember_cli.rb
index b2e7607c9ce..d866cd2bf38 100644
--- a/lib/ember_cli.rb
+++ b/lib/ember_cli.rb
@@ -1,6 +1,9 @@
# frozen_string_literal: true
-module EmberCli
+class EmberCli < ActiveSupport::CurrentAttributes
+ # Cache which persists for the duration of a request
+ attribute :request_cached_script_chunks
+
def self.dist_dir
"#{Rails.root}/app/assets/javascripts/discourse/dist"
end
@@ -10,30 +13,23 @@ module EmberCli
end
def self.script_chunks
- return @chunk_infos if @chunk_infos
+ return @production_chunk_infos if @production_chunk_infos
+ return self.request_cached_script_chunks if self.request_cached_script_chunks
- chunk_infos = {}
+ chunk_infos = JSON.parse(File.read("#{dist_dir}/assets.json"))
- begin
- test_html = File.read("#{dist_dir}/tests/index.html")
- chunk_infos.merge! parse_chunks_from_html(test_html)
- rescue Errno::ENOENT
- # production build
+ chunk_infos.transform_keys! { |key| key.delete_prefix("assets/").delete_suffix(".js") }
+
+ chunk_infos.transform_values! do |value|
+ value["assets"].map { |chunk| chunk.delete_prefix("assets/").delete_suffix(".js") }
end
- index_html = File.read("#{dist_dir}/index.html")
- chunk_infos.merge! parse_chunks_from_html(index_html)
-
- @chunk_infos = chunk_infos if Rails.env.production?
- chunk_infos
+ @production_chunk_infos = chunk_infos if Rails.env.production?
+ self.request_cached_script_chunks = chunk_infos
rescue Errno::ENOENT
{}
end
- def self.parse_source_map_path(file)
- File.read("#{dist_dir}/assets/#{file}")[%r{//# sourceMappingURL=(.*)$}, 1]
- end
-
def self.is_ember_cli_asset?(name)
assets.include?(name) || script_chunks.values.flatten.include?(name.delete_suffix(".js"))
end
@@ -56,31 +52,13 @@ module EmberCli
end
end
- def self.parse_chunks_from_html(html)
- doc = Nokogiri::HTML5.parse(html)
-
- chunk_infos = {}
-
- doc
- .css("discourse-chunked-script")
- .each do |discourse_script|
- entrypoint = discourse_script.attr("entrypoint")
- chunk_infos[entrypoint] = discourse_script
- .css("script[src]")
- .map do |script|
- script.attr("src").delete_prefix("#{Discourse.base_path}/assets/").delete_suffix(".js")
- end
- end
-
- chunk_infos
- end
-
def self.has_tests?
File.exist?("#{dist_dir}/tests/index.html")
end
def self.clear_cache!
- @chunk_infos = nil
+ @prod_chunk_infos = nil
@assets = nil
+ self.request_cached_script_chunks = nil
end
end
diff --git a/spec/lib/ember_cli_spec.rb b/spec/lib/ember_cli_spec.rb
index 59de34d6709..2079f9f483e 100644
--- a/spec/lib/ember_cli_spec.rb
+++ b/spec/lib/ember_cli_spec.rb
@@ -6,39 +6,4 @@ describe EmberCli do
expect(EmberCli.ember_version).to match(/\A\d+\.\d+/)
end
end
-
- describe ".parse_chunks_from_html" do
- def generate_html
- <<~HTML
-
-
-
-
-
-
-
-
- Hello world
-
-
- HTML
- end
-
- it "can parse chunks for a normal site" do
- chunks = EmberCli.parse_chunks_from_html generate_html
- expect(chunks["discourse"]).to eq(%w[firstchunk secondchunk])
- end
-
- it "can parse chunks for a subfolder site" do
- set_subfolder "/discuss"
-
- html = generate_html
-
- # sanity check that our fixture is working
- expect(html).to include("/discuss/assets/firstchunk.js")
-
- chunks = EmberCli.parse_chunks_from_html html
- expect(chunks["discourse"]).to eq(%w[firstchunk secondchunk])
- end
- end
end