mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-22 04:03:04 +00:00
Deprecate relative asset paths, better warnings for asset!() (#3214)
* Deprecate relative asset paths * fix paths, better warnings in asset parser
This commit is contained in:
parent
8a2922c663
commit
ba4389567d
13 changed files with 95 additions and 36 deletions
|
@ -5,7 +5,7 @@ pub(crate) fn ChildrenOrLoading(children: Element) -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
document::Link {
|
document::Link {
|
||||||
rel: "stylesheet",
|
rel: "stylesheet",
|
||||||
href: asset!("./public/loading.css")
|
href: asset!("/public/loading.css")
|
||||||
}
|
}
|
||||||
SuspenseBoundary {
|
SuspenseBoundary {
|
||||||
fallback: |context: SuspenseContext| {
|
fallback: |context: SuspenseContext| {
|
||||||
|
|
|
@ -19,7 +19,7 @@ fn main() {
|
||||||
rsx! {
|
rsx! {
|
||||||
document::Link {
|
document::Link {
|
||||||
rel: "stylesheet",
|
rel: "stylesheet",
|
||||||
href: asset!("./public/tailwind.css")
|
href: asset!("/public/tailwind.css")
|
||||||
}
|
}
|
||||||
|
|
||||||
ChildrenOrLoading {
|
ChildrenOrLoading {
|
||||||
|
|
|
@ -23,7 +23,7 @@ fn app() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
document::Link {
|
document::Link {
|
||||||
rel: "stylesheet",
|
rel: "stylesheet",
|
||||||
href: asset!("./assets/fileexplorer.css")
|
href: asset!("/assets/fileexplorer.css")
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
document::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" }
|
document::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" }
|
||||||
|
|
|
@ -36,7 +36,7 @@ pub fn App() -> Element {
|
||||||
#[component]
|
#[component]
|
||||||
fn Homepage(story: ReadOnlySignal<PreviewState>) -> Element {
|
fn Homepage(story: ReadOnlySignal<PreviewState>) -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
document::Link { rel: "stylesheet", href: asset!("./assets/hackernews.css") }
|
document::Link { rel: "stylesheet", href: asset!("/assets/hackernews.css") }
|
||||||
div { display: "flex", flex_direction: "row", width: "100%",
|
div { display: "flex", flex_direction: "row", width: "100%",
|
||||||
div {
|
div {
|
||||||
width: "50%",
|
width: "50%",
|
||||||
|
|
|
@ -31,7 +31,7 @@ fn app() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
document::Link {
|
document::Link {
|
||||||
rel: "stylesheet",
|
rel: "stylesheet",
|
||||||
href: asset!("./examples/assets/calculator.css"),
|
href: asset!("/examples/assets/calculator.css"),
|
||||||
}
|
}
|
||||||
div { id: "wrapper",
|
div { id: "wrapper",
|
||||||
div { class: "app",
|
div { class: "app",
|
||||||
|
|
|
@ -32,7 +32,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
document::Link {
|
document::Link {
|
||||||
rel: "stylesheet",
|
rel: "stylesheet",
|
||||||
href: asset!("./examples/assets/crm.css"),
|
href: asset!("/examples/assets/crm.css"),
|
||||||
}
|
}
|
||||||
h1 { "Dioxus CRM Example" }
|
h1 { "Dioxus CRM Example" }
|
||||||
Router::<Route> {}
|
Router::<Route> {}
|
||||||
|
|
|
@ -30,7 +30,7 @@ fn app() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
document::Link {
|
document::Link {
|
||||||
rel: "stylesheet",
|
rel: "stylesheet",
|
||||||
href: asset!("./examples/assets/read_size.css"),
|
href: asset!("/examples/assets/read_size.css"),
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
width: "50%",
|
width: "50%",
|
||||||
|
|
|
@ -17,7 +17,7 @@ fn app() -> Element {
|
||||||
rsx!(
|
rsx!(
|
||||||
document::Link {
|
document::Link {
|
||||||
rel: "stylesheet",
|
rel: "stylesheet",
|
||||||
href: asset!("./examples/assets/read_size.css"),
|
href: asset!("/examples/assets/read_size.css"),
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
width: "50%",
|
width: "50%",
|
||||||
|
|
|
@ -86,7 +86,7 @@ impl LinkProps {
|
||||||
/// // You can use the meta component to render a meta tag into the head of the page
|
/// // You can use the meta component to render a meta tag into the head of the page
|
||||||
/// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds
|
/// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds
|
||||||
/// document::Link {
|
/// document::Link {
|
||||||
/// href: asset!("./assets/style.css"),
|
/// href: asset!("/assets/style.css"),
|
||||||
/// rel: "stylesheet",
|
/// rel: "stylesheet",
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
|
|
|
@ -74,7 +74,7 @@ impl ScriptProps {
|
||||||
/// rsx! {
|
/// rsx! {
|
||||||
/// // You can use the Script component to render a script tag into the head of the page
|
/// // You can use the Script component to render a script tag into the head of the page
|
||||||
/// document::Script {
|
/// document::Script {
|
||||||
/// src: asset!("./assets/script.js"),
|
/// src: asset!("/assets/script.js"),
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
|
|
|
@ -24,11 +24,8 @@ pub struct ResourceAsset {
|
||||||
pub bundled: String,
|
pub bundled: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AssetError {}
|
|
||||||
|
|
||||||
impl ResourceAsset {
|
impl ResourceAsset {
|
||||||
pub fn parse_any(raw: &str) -> Result<Self, AssetError> {
|
pub fn parse_any(raw: &str) -> Result<Self, AssetParseError> {
|
||||||
// get the location where the asset is absolute, relative to
|
// get the location where the asset is absolute, relative to
|
||||||
//
|
//
|
||||||
// IE
|
// IE
|
||||||
|
@ -43,13 +40,17 @@ impl ResourceAsset {
|
||||||
let input = PathBuf::from(raw);
|
let input = PathBuf::from(raw);
|
||||||
|
|
||||||
// 2. absolute path to the asset
|
// 2. absolute path to the asset
|
||||||
let absolute = manifest_dir
|
let absolute = manifest_dir.join(raw.trim_start_matches('/'));
|
||||||
.join(raw.trim_start_matches('/'))
|
let absolute =
|
||||||
.canonicalize()
|
absolute
|
||||||
.unwrap();
|
.canonicalize()
|
||||||
|
.map_err(|err| AssetParseError::AssetDoesntExist {
|
||||||
|
err,
|
||||||
|
path: absolute,
|
||||||
|
})?;
|
||||||
|
|
||||||
// 3. the bundled path is the unique name of the asset
|
// 3. the bundled path is the unique name of the asset
|
||||||
let bundled = Self::make_unique_name(absolute.clone());
|
let bundled = Self::make_unique_name(absolute.clone())?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
input,
|
input,
|
||||||
|
@ -58,14 +59,16 @@ impl ResourceAsset {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_unique_name(file_path: PathBuf) -> String {
|
fn make_unique_name(file_path: PathBuf) -> Result<String, AssetParseError> {
|
||||||
// Create a hasher
|
// Create a hasher
|
||||||
let mut hash = std::collections::hash_map::DefaultHasher::new();
|
let mut hash = std::collections::hash_map::DefaultHasher::new();
|
||||||
|
|
||||||
// Open the file to get its options
|
// Open the file to get its options
|
||||||
let file = std::fs::File::open(&file_path).unwrap();
|
let file = std::fs::File::open(&file_path).map_err(AssetParseError::FailedToReadAsset)?;
|
||||||
let metadata = file.metadata().unwrap();
|
let modified = file
|
||||||
let modified = metadata.modified().unwrap_or(SystemTime::UNIX_EPOCH);
|
.metadata()
|
||||||
|
.and_then(|metadata| metadata.modified())
|
||||||
|
.unwrap_or(SystemTime::UNIX_EPOCH);
|
||||||
|
|
||||||
// Hash a bunch of metadata
|
// Hash a bunch of metadata
|
||||||
// name, options, modified time, and maybe the version of our crate
|
// name, options, modified time, and maybe the version of our crate
|
||||||
|
@ -74,12 +77,42 @@ impl ResourceAsset {
|
||||||
file_path.hash(&mut hash);
|
file_path.hash(&mut hash);
|
||||||
|
|
||||||
let uuid = hash.finish();
|
let uuid = hash.finish();
|
||||||
let file_name = file_path.file_stem().unwrap().to_string_lossy();
|
let file_name = file_path
|
||||||
|
.file_stem()
|
||||||
|
.expect("file_path should have a file_stem")
|
||||||
|
.to_string_lossy();
|
||||||
|
|
||||||
let extension = file_path
|
let extension = file_path
|
||||||
.extension()
|
.extension()
|
||||||
.map(|f| f.to_string_lossy())
|
.map(|f| f.to_string_lossy())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
format!("{file_name}-{uuid:x}.{extension}")
|
Ok(format!("{file_name}-{uuid:x}.{extension}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AssetParseError {
|
||||||
|
ParseError(String),
|
||||||
|
AssetDoesntExist {
|
||||||
|
err: std::io::Error,
|
||||||
|
path: std::path::PathBuf,
|
||||||
|
},
|
||||||
|
FailedToReadAsset(std::io::Error),
|
||||||
|
FailedToReadMetadata(std::io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for AssetParseError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
AssetParseError::ParseError(err) => write!(f, "Failed to parse asset: {}", err),
|
||||||
|
AssetParseError::AssetDoesntExist { err, path } => {
|
||||||
|
write!(f, "Asset at {} doesn't exist: {}", path.display(), err)
|
||||||
|
}
|
||||||
|
AssetParseError::FailedToReadAsset(err) => write!(f, "Failed to read asset: {}", err),
|
||||||
|
AssetParseError::FailedToReadMetadata(err) => {
|
||||||
|
write!(f, "Failed to read asset metadata: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use manganis_core::ResourceAsset;
|
use manganis_core::{AssetParseError, ResourceAsset};
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
use syn::{
|
use syn::{
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
LitStr,
|
LitStr,
|
||||||
|
@ -8,7 +8,7 @@ use syn::{
|
||||||
|
|
||||||
pub struct AssetParser {
|
pub struct AssetParser {
|
||||||
/// The asset itself
|
/// The asset itself
|
||||||
asset: ResourceAsset,
|
asset: Result<ResourceAsset, AssetParseError>,
|
||||||
|
|
||||||
/// The source of the trailing options
|
/// The source of the trailing options
|
||||||
options: TokenStream2,
|
options: TokenStream2,
|
||||||
|
@ -19,13 +19,13 @@ impl Parse for AssetParser {
|
||||||
//
|
//
|
||||||
// This gives you the Asset type - it's generic and basically unrefined
|
// This gives you the Asset type - it's generic and basically unrefined
|
||||||
// ```
|
// ```
|
||||||
// asset!("myfile.png")
|
// asset!("/assets/myfile.png")
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// To narrow the type, use a method call to get the refined type
|
// To narrow the type, use a method call to get the refined type
|
||||||
// ```
|
// ```
|
||||||
// asset!(
|
// asset!(
|
||||||
// "myfile.png",
|
// "/assets/myfile.png",
|
||||||
// asset::image()
|
// asset::image()
|
||||||
// .format(ImageType::Jpg)
|
// .format(ImageType::Jpg)
|
||||||
// .size(512, 512)
|
// .size(512, 512)
|
||||||
|
@ -36,7 +36,7 @@ impl Parse for AssetParser {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
// And then parse the options
|
// And then parse the options
|
||||||
let src = input.parse::<LitStr>()?;
|
let src = input.parse::<LitStr>()?;
|
||||||
let asset = ResourceAsset::parse_any(&src.value()).unwrap();
|
let asset = ResourceAsset::parse_any(&src.value());
|
||||||
let options = input.parse()?;
|
let options = input.parse()?;
|
||||||
|
|
||||||
Ok(Self { asset, options })
|
Ok(Self { asset, options })
|
||||||
|
@ -60,23 +60,39 @@ impl ToTokens for AssetParser {
|
||||||
// a limitation from rust itself. We technically could support them but not without some hoops
|
// a limitation from rust itself. We technically could support them but not without some hoops
|
||||||
// to jump through
|
// to jump through
|
||||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||||
|
let asset = match self.asset.as_ref() {
|
||||||
|
Ok(asset) => asset,
|
||||||
|
Err(err) => {
|
||||||
|
let err = err.to_string();
|
||||||
|
tokens.append_all(quote! { compile_error!(#err) });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 1. the link section itself
|
// 1. the link section itself
|
||||||
let link_section = crate::generate_link_section(&self.asset);
|
let link_section = crate::generate_link_section(&asset);
|
||||||
|
|
||||||
// 2. original
|
// 2. original
|
||||||
let input = self.asset.input.display().to_string();
|
let input = asset.input.display().to_string();
|
||||||
|
|
||||||
// 3. resolved on the user's system
|
// 3. resolved on the user's system
|
||||||
let local = self.asset.absolute.display().to_string();
|
let local = asset.absolute.display().to_string();
|
||||||
|
|
||||||
// 4. bundled
|
// 4. bundled
|
||||||
let bundled = self.asset.bundled.to_string();
|
let bundled = asset.bundled.to_string();
|
||||||
|
|
||||||
// 5. source tokens
|
// 5. source tokens
|
||||||
let option_source = &self.options;
|
let option_source = &self.options;
|
||||||
|
|
||||||
|
// generate the asset::new method to deprecate the `./assets/blah.css` syntax
|
||||||
|
let method = if asset.input.is_relative() {
|
||||||
|
quote::quote! { new_relative }
|
||||||
|
} else {
|
||||||
|
quote::quote! { new }
|
||||||
|
};
|
||||||
|
|
||||||
tokens.extend(quote! {
|
tokens.extend(quote! {
|
||||||
Asset::new(
|
Asset::#method(
|
||||||
{
|
{
|
||||||
#link_section
|
#link_section
|
||||||
manganis::Asset {
|
manganis::Asset {
|
||||||
|
|
|
@ -22,6 +22,16 @@ impl Asset {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new asset but with a relative path
|
||||||
|
///
|
||||||
|
/// This method is deprecated and will be removed in a future release.
|
||||||
|
#[deprecated(
|
||||||
|
note = "Relative asset!() paths are not supported. Use a path like `/assets/myfile.png` instead of `./assets/myfile.png`"
|
||||||
|
)]
|
||||||
|
pub const fn new_relative(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the path to the asset
|
/// Get the path to the asset
|
||||||
pub fn path(&self) -> PathBuf {
|
pub fn path(&self) -> PathBuf {
|
||||||
PathBuf::from(self.input.to_string())
|
PathBuf::from(self.input.to_string())
|
||||||
|
|
Loading…
Reference in a new issue