fix: Allow links with duplicate href if rel is different (#5071)

Fixes #5070
This commit is contained in:
Paul Hansen authored and GitHub committed 2025-12-09 13:53:42 +00:00
1 parent c01770fcfb
commit 8a38e7ca76
3 files changed
+52 -4

No files matched your search

+10 -4
View File
@@ -20,7 +20,7 @@ pub struct LinkProps {
pub disabled: Option<bool>, pub disabled: Option<bool>,
pub r#as: Option<String>, pub r#as: Option<String>,
pub sizes: Option<String>, pub sizes: Option<String>,
/// Links are deduplicated by their href attribute /// Links are deduplicated by their href and rel attributes
pub href: Option<String>, pub href: Option<String>,
pub crossorigin: Option<String>, pub crossorigin: Option<String>,
pub referrerpolicy: Option<String>, pub referrerpolicy: Option<String>,
@@ -122,7 +122,7 @@ pub fn Link(props: LinkProps) -> Element {
let document = document(); let document = document();
let mut insert_link = document.create_head_component(); let mut insert_link = document.create_head_component();
if let Some(href) = &props.href { if let Some(href) = &props.href {
if !should_insert_link(href) { if !should_insert_link(href, props.rel.as_deref()) {
insert_link = false; insert_link = false;
} }
} }
@@ -140,8 +140,14 @@ pub fn Link(props: LinkProps) -> Element {
#[derive(Default, Clone)] #[derive(Default, Clone)]
struct LinkContext(DeduplicationContext); struct LinkContext(DeduplicationContext);
fn should_insert_link(href: &str) -> bool { fn should_insert_link(href: &str, rel: Option<&str>) -> bool {
// Include rel in the deduplication key so that the same href can be used
// with different rel values (e.g., rel="preload" and rel="stylesheet")
let key = match rel {
Some(rel) => format!("{href}|{rel}"),
None => href.to_string(),
};
get_or_insert_root_context::<LinkContext>() get_or_insert_root_context::<LinkContext>()
.0 .0
.should_insert(href) .should_insert(&key)
} }
+16
View File
@@ -156,6 +156,22 @@ test("document elements", async ({ page }) => {
await expect(main).toHaveCSS("font-family", "Roboto"); await expect(main).toHaveCSS("font-family", "Roboto");
}); });
test("link preload and stylesheet with same href are not deduplicated", async ({ page }) => {
await page.goto("http://localhost:9990");
// Both links should exist (different rel = not deduplicated)
await expect(page.locator("link#dedup-preload[rel='preload']")).toHaveCount(1);
await expect(page.locator("link#dedup-stylesheet[rel='stylesheet']")).toHaveCount(1);
});
test("links with same href and rel are deduplicated", async ({ page }) => {
await page.goto("http://localhost:9990");
// Only first link should exist (same rel = deduplicated)
await expect(page.locator("link#dedup-first")).toHaveCount(1);
await expect(page.locator("link#dedup-second")).toHaveCount(0);
});
test("merge styles", async ({ page }) => { test("merge styles", async ({ page }) => {
await page.goto("http://localhost:9990"); await page.goto("http://localhost:9990");
// wait until the div is mounted // wait until the div is mounted
+26
View File
@@ -154,6 +154,32 @@ fn DocumentElements() -> Element {
document::Stylesheet { id: "stylesheet-head", href: "https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic" } document::Stylesheet { id: "stylesheet-head", href: "https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic" }
document::Script { id: "script-head", async: true, "console.log('hello world');" } document::Script { id: "script-head", async: true, "console.log('hello world');" }
document::Style { id: "style-head", "body {{ font-family: 'Roboto'; }}" } document::Style { id: "style-head", "body {{ font-family: 'Roboto'; }}" }
// Test that links with same href but different rel are NOT deduplicated
// See https://github.com/DioxusLabs/dioxus/issues/5070
document::Link {
id: "dedup-preload",
rel: "preload",
href: "dedup-test.css",
r#as: "style",
}
document::Link {
id: "dedup-stylesheet",
rel: "stylesheet",
href: "dedup-test.css",
}
// Test that links with same href AND same rel ARE deduplicated
document::Link {
id: "dedup-first",
rel: "stylesheet",
href: "dedup-same.css",
}
document::Link {
id: "dedup-second",
rel: "stylesheet",
href: "dedup-same.css",
}
} }
} }