Support public dir (#4783)

* Add optional static_dir support

- add optional application.static_dir field defaulting to none
- copy configured static directory into web output during build/serve
- document config knob and add unit tests
- force full rebuild when static_dir contents change
- sync removes stale outputs and logs copy activity
- track static manifest next to platform dir

Tests: cargo fmt

* default to public dir to match vite

* static_dir -> public_dir

* wip

* deser as none...

* wip... use .manifest.json

* implement dumb form of asset manifest generation

* implement manifest using asset system directly

* cleanup

* dont create extra asset dir for server

---------

Co-authored-by: Evan Almloff <evanalmloff@gmail.com>
Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
authored and GitHub committed 2025-10-20 17:03:34 -04:00
1 parent 2f6be083ce
commit b3966c3509
10 files changed
+245 -78

No files matched your search

+20
View File
@@ -6,6 +6,10 @@ pub(crate) struct ApplicationConfig {
#[serde(default)]
pub(crate) out_dir: Option<PathBuf>,
#[serde(default = "public_dir_default")]
#[serde(deserialize_with = "empty_string_is_none")]
pub(crate) public_dir: Option<PathBuf>,
#[serde(default)]
pub(crate) tailwind_input: Option<PathBuf>,
@@ -43,3 +47,19 @@ pub(crate) struct ApplicationConfig {
#[serde(default)]
pub(crate) android_min_sdk_version: Option<u32>,
}
fn public_dir_default() -> Option<PathBuf> {
Some("public".into())
}
fn empty_string_is_none<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
where
D: serde::Deserializer<'de>,
{
let opt: Option<String> = Option::deserialize(deserializer)?;
match opt {
Some(s) if s.is_empty() => Ok(None),
Some(s) => Ok(Some(PathBuf::from(s))),
None => Ok(None),
}
}
+40
View File
@@ -17,6 +17,7 @@ impl Default for DioxusConfig {
Self {
application: ApplicationConfig {
out_dir: None,
public_dir: Some("public".into()),
tailwind_input: None,
tailwind_output: None,
ios_info_plist: None,
@@ -55,3 +56,42 @@ impl Default for DioxusConfig {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn static_dir_defaults_to_public() {
let config = DioxusConfig::default();
assert_eq!(
config.application.public_dir,
Some(std::path::PathBuf::from("public"))
);
}
#[test]
fn static_dir_can_be_overridden() {
let source = r#"
[application]
public_dir = "public2"
"#;
let config: DioxusConfig = toml::from_str(source).expect("parse config");
assert_eq!(
config.application.public_dir.as_deref(),
Some(std::path::Path::new("public2"))
);
}
#[test]
fn static_dir_can_be_disabled() {
let source = r#"
[application]
public_dir = ""
"#;
let config: DioxusConfig = toml::from_str(source).expect("parse config");
assert_eq!(config.application.public_dir.as_deref(), None);
}
}