feat(core): fallback to file system for AssetResolver::get, closes #8411 (#10357)

* feat(core): fallback to file system for AssetResolver::get, closes #8411

Ports #10356 to v2

* fix test

---------

Co-authored-by: Chip Reed <chip@chip.sh>
This commit is contained in:
Lucas Fernandes Nogueira 2024-07-31 07:13:40 -03:00 committed by GitHub
parent d03332617c
commit 1e0793b682
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 80 additions and 0 deletions

View File

@ -0,0 +1,6 @@
---
"tauri": patch:enhance
"tauri-codegen": patch:enhance
---
Enhance `AssetResolver::get` in development mode by reading distDir directly as a fallback to the embedded assets.

View File

@ -483,6 +483,15 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
quote!(::std::option::Option::None)
};
let maybe_config_parent_setter = if dev {
let config_parent = config_parent.to_string_lossy();
quote!({
context.with_config_parent(#config_parent);
})
} else {
quote!()
};
Ok(quote!({
#[allow(unused_mut, clippy::let_and_return)]
let mut context = #root::Context::new(
@ -496,7 +505,10 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
#runtime_authority,
#plugin_global_api_script
);
#with_tray_icon_code
#maybe_config_parent_setter
context
}))
}

View File

@ -269,7 +269,45 @@ pub struct AssetResolver<R: Runtime> {
impl<R: Runtime> AssetResolver<R> {
/// Gets the app asset associated with the given path.
///
/// Resolves to the embedded asset that is part of the app
/// in dev when [`devPath`](https://tauri.app/v1/api/config/#buildconfig.devpath) points to a folder in your filesystem
/// or in production when [`distDir`](https://tauri.app/v1/api/config/#buildconfig.distdir)
/// points to your frontend assets.
///
/// Fallbacks to reading the asset from the [distDir] folder so the behavior is consistent in development.
/// Note that the dist directory must exist so you might need to build your frontend assets first.
pub fn get(&self, path: String) -> Option<Asset> {
#[cfg(dev)]
{
// on dev if the devPath is a path to a directory we have the embedded assets
// so we can use get_asset() directly
// we only fallback to reading from distDir directly if we're using an external URL (which is likely)
if let (Some(_), Some(crate::utils::config::FrontendDist::Directory(dist_path))) = (
&self.manager.config().build.dev_url,
&self.manager.config().build.frontend_dist,
) {
let asset_path = std::path::PathBuf::from(&path)
.components()
.filter(|c| !matches!(c, std::path::Component::RootDir))
.collect::<std::path::PathBuf>();
let asset_path = self
.manager
.config_parent()
.map(|p| p.join(dist_path).join(&asset_path))
.unwrap_or_else(|| dist_path.join(&asset_path));
return std::fs::read(asset_path).ok().map(|bytes| {
let mime_type = crate::utils::mime_type::MimeType::parse(&bytes, &path);
Asset {
bytes,
mime_type,
csp_header: None,
}
});
}
}
self.manager.get_asset(path).ok()
}

View File

@ -379,6 +379,8 @@ impl<R: Runtime> Assets<R> for EmbeddedAssets {
#[tauri_macros::default_runtime(Wry, wry)]
pub struct Context<R: Runtime> {
pub(crate) config: Config,
#[cfg(dev)]
pub(crate) config_parent: Option<std::path::PathBuf>,
/// Asset provider.
pub assets: Box<dyn Assets<R>>,
pub(crate) default_window_icon: Option<image::Image<'static>>,
@ -507,6 +509,8 @@ impl<R: Runtime> Context<R> {
) -> Self {
Self {
config,
#[cfg(dev)]
config_parent: None,
assets,
default_window_icon,
app_icon,
@ -519,6 +523,14 @@ impl<R: Runtime> Context<R> {
plugin_global_api_scripts,
}
}
#[cfg(dev)]
#[doc(hidden)]
pub fn with_config_parent(&mut self, config_parent: impl AsRef<std::path::Path>) {
self
.config_parent
.replace(config_parent.as_ref().to_owned());
}
}
// TODO: expand these docs

View File

@ -179,6 +179,8 @@ pub struct AppManager<R: Runtime> {
pub listeners: Listeners,
pub state: Arc<StateManager>,
pub config: Config,
#[cfg(dev)]
pub config_parent: Option<std::path::PathBuf>,
pub assets: Box<dyn Assets<R>>,
pub app_icon: Option<Vec<u8>>,
@ -278,6 +280,8 @@ impl<R: Runtime> AppManager<R> {
listeners: Listeners::default(),
state: Arc::new(state),
config: context.config,
#[cfg(dev)]
config_parent: context.config_parent,
assets: context.assets,
app_icon: context.app_icon,
package_info: context.package_info,
@ -458,6 +462,11 @@ impl<R: Runtime> AppManager<R> {
&self.config
}
#[cfg(dev)]
pub fn config_parent(&self) -> Option<&std::path::PathBuf> {
self.config_parent.as_ref()
}
pub fn package_info(&self) -> &PackageInfo {
&self.package_info
}

View File

@ -135,6 +135,9 @@ pub fn mock_context<R: Runtime, A: Assets<R>>(assets: A) -> crate::Context<R> {
pattern: Pattern::Brownfield,
runtime_authority: RuntimeAuthority::new(Default::default(), Resolved::default()),
plugin_global_api_scripts: None,
#[cfg(dev)]
config_parent: None,
}
}