Hashless assets (#4312)

* add support for external assets

* fix playwright tests

* add more documentation to with_hash_suffix

* restore external asset hashing tests

* Update gtk workflow dependencies

* fix doc test

* multiple versions of libgtk

* clear dx in playwright tests

* fix fullstack caching test

* try without apt cache

* cache everything except libxdo-dev

* revert ci changes

* fix wasm asset options

* fix merge conflict

* recommend the used attribute instead

* Move options to AssetOptionsBuilder

* Don't deprecate non-canonical new asset options functions

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
Evan AlmloffandJonathan Kelley authored and GitHub committed 2025-06-30 19:21:49 -07:00
1 parent 0bd376b873
commit e74c838441
22 files changed
+569 -316

No files matched your search

@@ -23,4 +23,25 @@ test("optimized scripts run", async ({ page }) => {
// Expect the urls to be different
expect(src).not.toEqual(src2);
// Expect the page to contain an image with the id "some_image_without_hash"
const image3 = page.locator("#some_image_without_hash");
await expect(image3).toBeVisible();
// Get the image src
const src3 = await image3.getAttribute("src");
// Expect the src to be without a hash
expect(src3).toEqual("/assets/toasts.avif");
});
test("unused external assets are bundled", async ({ page }) => {
await page.goto("http://localhost:8989");
// Assert http://localhost:8989/assets/toasts.png is found even though it is not used in the page
const response = await page.request.get(
"http://localhost:8989/assets/toasts.png"
);
// Expect the response to be ok
expect(response.status()).toBe(200);
// make sure the response is an image
expect(response.headers()["content-type"]).toBe("image/png");
});
@@ -3,9 +3,20 @@
use dioxus::prelude::*;
const MONACO_FOLDER: Asset = asset!("/monaco-editor/package/min/vs");
const SOME_IMAGE: Asset = asset!("/images/toasts.png", ImageAssetOptions::new().with_avif());
const SOME_IMAGE: Asset = asset!("/images/toasts.png", AssetOptions::image().with_avif());
const SOME_IMAGE_WITH_THE_SAME_URL: Asset =
asset!("/images/toasts.png", ImageAssetOptions::new().with_jpg());
asset!("/images/toasts.png", AssetOptions::image().with_jpg());
#[used]
static SOME_IMAGE_WITHOUT_HASH: Asset = asset!(
"/images/toasts.png",
AssetOptions::image().with_avif().with_hash_suffix(false)
);
// This asset is unused, but it should still be bundled because it is an external asset
#[used]
static _ASSET: Asset = asset!(
"/images/toasts.png",
AssetOptions::builder().with_hash_suffix(false)
);
fn main() {
dioxus::launch(App);
@@ -41,5 +52,9 @@ fn App() -> Element {
id: "some_image_with_the_same_url",
src: "{SOME_IMAGE_WITH_THE_SAME_URL}"
}
img {
id: "some_image_without_hash",
src: "{SOME_IMAGE_WITHOUT_HASH}"
}
}
}
+27 -20
View File
@@ -75,6 +75,13 @@ test("assets cache correctly", async ({ page }) => {
console.log("Response URL:", resp.url());
return resp.url().includes("/assets/image-") && resp.status() === 200;
});
const assetImageFuture = page.waitForResponse(
(resp) => resp.url().includes("/assets/image.png") && resp.status() === 200
);
const nestedAssetImageFuture = page.waitForResponse(
(resp) =>
resp.url().includes("/assets/nested/image.png") && resp.status() === 200
);
// Navigate to the page that includes the image.
await page.goto("http://localhost:3333");
@@ -86,27 +93,27 @@ test("assets cache correctly", async ({ page }) => {
console.log("Cache-Control header:", cacheControl);
expect(cacheControl).toContain("immutable");
// TODO: raw assets support was removed and needs to be restored
// https://github.com/DioxusLabs/dioxus/issues/4115
// // Wait for the asset image to be loaded
// const assetImageResponse = await page.waitForResponse(
// (resp) => resp.url().includes("/assets/image.png") && resp.status() === 200
// );
// // Make sure the asset image cache control header does not contain immutable
// const assetCacheControl = assetImageResponse.headers()["cache-control"];
// console.log("Cache-Control header:", assetCacheControl);
// expect(assetCacheControl).not.toContain("immutable");
// Wait for the asset image to be loaded
const assetImageResponse = await assetImageFuture;
console.log("Asset Image Response:", assetImageResponse);
// Make sure the asset image cache control header does not contain immutable
const assetCacheControl = assetImageResponse.headers()["cache-control"];
console.log("Cache-Control header:", assetCacheControl);
// Expect there to be no cache control header
expect(assetCacheControl).toBeFalsy();
// // Wait for the nested asset image to be loaded
// const nestedAssetImageResponse = await page.waitForResponse(
// (resp) =>
// resp.url().includes("/assets/nested/image.png") && resp.status() === 200
// );
// // Make sure the nested asset image cache control header does not contain immutable
// const nestedAssetCacheControl =
// nestedAssetImageResponse.headers()["cache-control"];
// console.log("Cache-Control header:", nestedAssetCacheControl);
// expect(nestedAssetCacheControl).not.toContain("immutable");
// Wait for the nested asset image to be loaded
const nestedAssetImageResponse = await nestedAssetImageFuture;
console.log(
"Nested Asset Image Response:",
nestedAssetImageResponse
);
// Make sure the nested asset image cache control header does not contain immutable
const nestedAssetCacheControl =
nestedAssetImageResponse.headers()["cache-control"];
console.log("Cache-Control header:", nestedAssetCacheControl);
// Expect there to be no cache control header
expect(nestedAssetCacheControl).toBeFalsy();
});
test("websockets", async ({ page }) => {
@@ -164,18 +164,20 @@ fn DocumentElements() -> Element {
/// Make sure assets in the assets folder are served correctly and hashed assets are cached forever
#[component]
fn Assets() -> Element {
#[used]
static _ASSET: Asset = asset!("/assets/image.png");
#[used]
static _OTHER_ASSET: Asset = asset!("/assets/nested");
rsx! {
img {
src: asset!("/assets/image.png"),
}
// TODO: raw assets support was removed and needs to be restored
// https://github.com/DioxusLabs/dioxus/issues/4115
// img {
// src: "/assets/image.png",
// }
// img {
// src: "/assets/nested/image.png",
// }
img {
src: "/assets/image.png",
}
img {
src: "/assets/nested/image.png",
}
}
}