mirror of
https://github.com/element-hq/matrix-authentication-service.git
synced 2026-09-01 20:08:36 +00:00
Emit a modulepreload for dynamically imported chunks in include_asset
Now that the translations are lazily imported JS chunks rather than plain JSON
assets, `include_asset('locales/<lang>.json')` resolves to a manifest entry
flagged `isDynamicEntry`. Those must not be evaluated eagerly with a `<script>`
tag, so emit the same `modulepreload` hint we already use for imported chunks,
integrity included, and drop the now-unreachable top-level JSON preload.
This commit is contained in:
+10
-1
@@ -1,3 +1,4 @@
|
||||
// Copyright 2025, 2026 Element Creations Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -30,7 +31,6 @@ pub struct ManifestEntry {
|
||||
#[expect(dead_code)]
|
||||
is_entry: Option<bool>,
|
||||
|
||||
#[expect(dead_code)]
|
||||
is_dynamic_entry: Option<bool>,
|
||||
|
||||
imports: Option<Vec<Utf8PathBuf>>,
|
||||
@@ -90,6 +90,7 @@ pub struct Asset<'a> {
|
||||
file_type: FileType,
|
||||
name: &'a Utf8Path,
|
||||
integrity: Option<&'a str>,
|
||||
is_dynamic_entry: bool,
|
||||
}
|
||||
|
||||
impl<'a> Asset<'a> {
|
||||
@@ -101,6 +102,7 @@ impl<'a> Asset<'a> {
|
||||
file_type,
|
||||
name,
|
||||
integrity,
|
||||
is_dynamic_entry: entry.is_dynamic_entry.unwrap_or(false),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -116,6 +118,13 @@ impl<'a> Asset<'a> {
|
||||
self.file_type
|
||||
}
|
||||
|
||||
/// Whether this asset is a chunk which is only reachable through a dynamic
|
||||
/// `import()`, and therefore must not be evaluated eagerly
|
||||
#[must_use]
|
||||
pub fn is_dynamic_entry(&self) -> bool {
|
||||
self.is_dynamic_entry
|
||||
}
|
||||
|
||||
/// Get the integrity HTML tag attribute, with a leading space, if any
|
||||
#[must_use]
|
||||
pub fn integrity_attr(&self) -> String {
|
||||
|
||||
@@ -485,6 +485,20 @@ impl Object for IncludeAsset {
|
||||
// We'll accumulate the output in this string
|
||||
let mut output = String::new();
|
||||
match main.file_type() {
|
||||
mas_spa::FileType::Script if main.is_dynamic_entry() => {
|
||||
// Chunks which are only reachable through a dynamic `import()`
|
||||
// (like the translation files) must not be evaluated eagerly,
|
||||
// we only hint the browser to fetch them
|
||||
let integrity = main.integrity_attr();
|
||||
let src = main.src(assets_base);
|
||||
if tracker.mark_preloaded(&src) {
|
||||
writeln!(
|
||||
output,
|
||||
r#"<link rel="modulepreload" href="{src}" crossorigin="anonymous"{integrity} />"#
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
mas_spa::FileType::Script => {
|
||||
let integrity = main.integrity_attr();
|
||||
let src = main.src(assets_base);
|
||||
@@ -508,14 +522,6 @@ impl Object for IncludeAsset {
|
||||
}
|
||||
}
|
||||
|
||||
mas_spa::FileType::Json => {
|
||||
// When a JSON is included at the top level (a translation), we preload it
|
||||
let src = main.src(assets_base);
|
||||
if tracker.mark_preloaded(&src) {
|
||||
writeln!(output, r#"<link rel="preload" href="{src}" as="fetch" />"#).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
file_type => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidOperation,
|
||||
@@ -560,10 +566,8 @@ impl Object for IncludeAsset {
|
||||
}
|
||||
}
|
||||
mas_spa::FileType::Woff | mas_spa::FileType::Woff2 | mas_spa::FileType::Json => {
|
||||
// Skip pre-loading fonts and JSON (translations) as it will
|
||||
// lead to many wasted preloads. For translations, we only
|
||||
// include them as preload if they are included on the
|
||||
// top-level
|
||||
// Skip pre-loading fonts and raw JSON assets, as it will
|
||||
// lead to many wasted preloads
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user