Remove default features when fullstack is enabled and one platform is enabled in the default features (#3925)
* Don't enable default features when only either the server or client is enabled
This commit is contained in:
1 parent
23286ade2b
commit
8c866b783d
14 files changed
+135
-26
No files matched your search
Generated
+7
@@ -4104,6 +4104,13 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dioxus-playwright-default-features-disabled-test"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"dioxus",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dioxus-playwright-fullstack-mounted-test"
|
name = "dioxus-playwright-fullstack-mounted-test"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ members = [
|
|||||||
"packages/playwright-tests/nested-suspense",
|
"packages/playwright-tests/nested-suspense",
|
||||||
"packages/playwright-tests/cli-optimization",
|
"packages/playwright-tests/cli-optimization",
|
||||||
"packages/playwright-tests/wasm-split-harness",
|
"packages/playwright-tests/wasm-split-harness",
|
||||||
|
"packages/playwright-tests/default-features-disabled"
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ impl BuildRequest {
|
|||||||
pub(crate) async fn build_server(&self) -> Result<Option<BuildArtifacts>> {
|
pub(crate) async fn build_server(&self) -> Result<Option<BuildArtifacts>> {
|
||||||
tracing::debug!("Building server...");
|
tracing::debug!("Building server...");
|
||||||
|
|
||||||
if !self.build.fullstack {
|
if !self.build.fullstack() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ pub(crate) struct BuildArgs {
|
|||||||
/// Build the fullstack variant of this app, using that as the fileserver and backend
|
/// Build the fullstack variant of this app, using that as the fileserver and backend
|
||||||
///
|
///
|
||||||
/// This defaults to `false` but will be overridden to true if the `fullstack` feature is enabled.
|
/// This defaults to `false` but will be overridden to true if the `fullstack` feature is enabled.
|
||||||
#[clap(long)]
|
#[arg(long, default_missing_value="true", num_args=0..=1)]
|
||||||
pub(crate) fullstack: bool,
|
pub(crate) fullstack: Option<bool>,
|
||||||
|
|
||||||
/// Run the ssg config of the app and generate the files
|
/// Run the ssg config of the app and generate the files
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
@@ -91,12 +91,25 @@ impl BuildArgs {
|
|||||||
/// IE if they've specified "fullstack" as a feature on `dioxus`, then we want to build the
|
/// IE if they've specified "fullstack" as a feature on `dioxus`, then we want to build the
|
||||||
/// fullstack variant even if they omitted the `--fullstack` flag.
|
/// fullstack variant even if they omitted the `--fullstack` flag.
|
||||||
pub(crate) async fn resolve(&mut self, krate: &DioxusCrate) -> Result<()> {
|
pub(crate) async fn resolve(&mut self, krate: &DioxusCrate) -> Result<()> {
|
||||||
let default_platform = krate.default_platform();
|
let default_platforms = krate.default_platforms();
|
||||||
|
let default_platform = default_platforms.iter().find(|p| **p != Platform::Server);
|
||||||
|
let default_server = default_platforms.iter().any(|p| *p == Platform::Server);
|
||||||
let auto_platform = krate.autodetect_platform();
|
let auto_platform = krate.autodetect_platform();
|
||||||
|
|
||||||
// The user passed --platform XYZ but already has `default = ["ABC"]` in their Cargo.toml
|
// Make sure we set the fullstack platform so we actually build the fullstack variant
|
||||||
// We want to strip out the default platform and use the one they passed, setting no-default-features
|
// Users need to enable "fullstack" in their default feature set or explicitly pass the flag
|
||||||
if self.platform.is_some() && default_platform.is_some() {
|
self.fullstack = Some(
|
||||||
|
self.fullstack()
|
||||||
|
|| self.fullstack.is_none()
|
||||||
|
&& (default_server || krate.has_dioxus_feature("fullstack")),
|
||||||
|
);
|
||||||
|
|
||||||
|
// If the current build is a fullstack build which includes either the client or the server in the default features,
|
||||||
|
// remove that default feature and just add it back into the client or server args. If they passed in an explicit platform
|
||||||
|
// but they also have a default feature platform, strip out the default features and add back in the platform they passed in.
|
||||||
|
if self.fullstack() && (default_server || default_platform.is_some())
|
||||||
|
|| self.platform.is_some() && default_platform.is_some()
|
||||||
|
{
|
||||||
self.target_args.no_default_features = true;
|
self.target_args.no_default_features = true;
|
||||||
self.target_args
|
self.target_args
|
||||||
.features
|
.features
|
||||||
@@ -126,16 +139,11 @@ impl BuildArgs {
|
|||||||
.server_features
|
.server_features
|
||||||
.push(krate.feature_for_platform(Platform::Server));
|
.push(krate.feature_for_platform(Platform::Server));
|
||||||
|
|
||||||
// Make sure we set the fullstack platform so we actually build the fullstack variant
|
|
||||||
// Users need to enable "fullstack" in their default feature set.
|
|
||||||
// todo(jon): fullstack *could* be a feature of the app, but right now we're assuming it's always enabled
|
|
||||||
self.fullstack = self.fullstack || krate.has_dioxus_feature("fullstack");
|
|
||||||
|
|
||||||
// Make sure we have a server feature if we're building a fullstack app
|
// Make sure we have a server feature if we're building a fullstack app
|
||||||
//
|
//
|
||||||
// todo(jon): eventually we want to let users pass a `--server <crate>` flag to specify a package to use as the server
|
// todo(jon): eventually we want to let users pass a `--server <crate>` flag to specify a package to use as the server
|
||||||
// however, it'll take some time to support that and we don't have a great RPC binding layer between the two yet
|
// however, it'll take some time to support that and we don't have a great RPC binding layer between the two yet
|
||||||
if self.fullstack && self.target_args.server_features.is_empty() {
|
if self.fullstack() && self.target_args.server_features.is_empty() {
|
||||||
return Err(anyhow::anyhow!("Fullstack builds require a server feature on the target crate. Add a `server` feature to the crate and try again.").into());
|
return Err(anyhow::anyhow!("Fullstack builds require a server feature on the target crate. Add a `server` feature to the crate and try again.").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,4 +199,9 @@ impl BuildArgs {
|
|||||||
pub(crate) fn platform(&self) -> Platform {
|
pub(crate) fn platform(&self) -> Platform {
|
||||||
self.platform.expect("Platform was not set")
|
self.platform.expect("Platform was not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if this is a fullstack build
|
||||||
|
pub(crate) fn fullstack(&self) -> bool {
|
||||||
|
self.fullstack.unwrap_or(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ impl Bundle {
|
|||||||
let mut bundles = vec![];
|
let mut bundles = vec![];
|
||||||
|
|
||||||
// Copy the server over if it exists
|
// Copy the server over if it exists
|
||||||
if bundle.build.build.fullstack {
|
if bundle.build.build.fullstack() {
|
||||||
bundles.push(bundle.server_exe().unwrap());
|
bundles.push(bundle.server_exe().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ impl RunArgs {
|
|||||||
let fullstack_ip = "127.0.0.1:8080".parse().unwrap();
|
let fullstack_ip = "127.0.0.1:8080".parse().unwrap();
|
||||||
let mut open_address = None;
|
let mut open_address = None;
|
||||||
|
|
||||||
if self.build_args.platform() == Platform::Web || self.build_args.fullstack {
|
if self.build_args.platform() == Platform::Web || self.build_args.fullstack() {
|
||||||
open_address = Some(fullstack_ip);
|
open_address = Some(fullstack_ip);
|
||||||
tracing::info!("Serving at: {}", fullstack_ip);
|
tracing::info!("Serving at: {}", fullstack_ip);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ impl ServeArgs {
|
|||||||
match self.build_arguments.platform() {
|
match self.build_arguments.platform() {
|
||||||
Platform::Server => true,
|
Platform::Server => true,
|
||||||
// During SSG, just serve the static files instead of running the server
|
// During SSG, just serve the static files instead of running the server
|
||||||
_ => self.build_arguments.fullstack && !self.build_arguments.ssg,
|
_ => self.build_arguments.fullstack() && !self.build_arguments.ssg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -252,7 +252,9 @@ impl DioxusCrate {
|
|||||||
.iter()
|
.iter()
|
||||||
.flat_map(|feature| {
|
.flat_map(|feature| {
|
||||||
tracing::trace!("Autodetecting platform from feature {feature}");
|
tracing::trace!("Autodetecting platform from feature {feature}");
|
||||||
Platform::autodetect_from_cargo_feature(feature).map(|f| (f, feature.to_string()))
|
Platform::autodetect_from_cargo_feature(feature)
|
||||||
|
.filter(|platform| *platform != Platform::Server)
|
||||||
|
.map(|f| (f, feature.to_string()))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
@@ -459,8 +461,11 @@ impl DioxusCrate {
|
|||||||
.map(|krate| krate.krate.version.to_string())
|
.map(|krate| krate.krate.version.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn default_platform(&self) -> Option<Platform> {
|
pub(crate) fn default_platforms(&self) -> Vec<Platform> {
|
||||||
let default = self.package().features.get("default")?;
|
let Some(default) = self.package().features.get("default") else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
let mut platforms = vec![];
|
||||||
|
|
||||||
// we only trace features 1 level deep..
|
// we only trace features 1 level deep..
|
||||||
for feature in default.iter() {
|
for feature in default.iter() {
|
||||||
@@ -468,8 +473,8 @@ impl DioxusCrate {
|
|||||||
if feature.starts_with("dioxus/") {
|
if feature.starts_with("dioxus/") {
|
||||||
let dx_feature = feature.trim_start_matches("dioxus/");
|
let dx_feature = feature.trim_start_matches("dioxus/");
|
||||||
let auto = Platform::autodetect_from_cargo_feature(dx_feature);
|
let auto = Platform::autodetect_from_cargo_feature(dx_feature);
|
||||||
if auto.is_some() {
|
if let Some(auto) = auto {
|
||||||
return auto;
|
platforms.push(auto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,15 +485,17 @@ impl DioxusCrate {
|
|||||||
if feature.starts_with("dioxus/") {
|
if feature.starts_with("dioxus/") {
|
||||||
let dx_feature = feature.trim_start_matches("dioxus/");
|
let dx_feature = feature.trim_start_matches("dioxus/");
|
||||||
let auto = Platform::autodetect_from_cargo_feature(dx_feature);
|
let auto = Platform::autodetect_from_cargo_feature(dx_feature);
|
||||||
if auto.is_some() {
|
if let Some(auto) = auto {
|
||||||
return auto;
|
platforms.push(auto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
platforms.sort();
|
||||||
|
platforms.dedup();
|
||||||
|
platforms
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gather the features that are enabled for the package
|
/// Gather the features that are enabled for the package
|
||||||
|
|||||||
@@ -497,7 +497,7 @@ impl Output {
|
|||||||
state.build_engine.compile_duration(),
|
state.build_engine.compile_duration(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if state.build_engine.request.build.fullstack {
|
if state.build_engine.request.build.fullstack() {
|
||||||
self.render_single_gauge(
|
self.render_single_gauge(
|
||||||
frame,
|
frame,
|
||||||
second_progress,
|
second_progress,
|
||||||
@@ -651,7 +651,7 @@ impl Output {
|
|||||||
Paragraph::new(Line::from(vec![
|
Paragraph::new(Line::from(vec![
|
||||||
"Platform: ".gray(),
|
"Platform: ".gray(),
|
||||||
self.platform.expected_name().yellow(),
|
self.platform.expected_name().yellow(),
|
||||||
if state.opts.build_arguments.fullstack {
|
if state.opts.build_arguments.fullstack() {
|
||||||
" + fullstack".yellow()
|
" + fullstack".yellow()
|
||||||
} else {
|
} else {
|
||||||
" ".dark_gray()
|
" ".dark_gray()
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// @ts-check
|
||||||
|
const { test, expect } = require("@playwright/test");
|
||||||
|
|
||||||
|
test("loads with correct features", async ({ page }) => {
|
||||||
|
await page.goto("http://localhost:8002");
|
||||||
|
|
||||||
|
// Expect the page to contain the pending text.
|
||||||
|
const main = page.locator("#main");
|
||||||
|
await expect(main).toContainText('server features: ["server"]');
|
||||||
|
await expect(main).toContainText('client features: ["web"]');
|
||||||
|
});
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.dioxus
|
||||||
|
dist
|
||||||
|
target
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "dioxus-playwright-default-features-disabled-test"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dioxus = { workspace = true, features = ["fullstack"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# Web is a default feature, but it shouldn't actually be enabled in server builds
|
||||||
|
default = ["web"]
|
||||||
|
server = ["dioxus/server"]
|
||||||
|
web = ["dioxus/web"]
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// This test asserts that the client feature is disable on the server build by the cli
|
||||||
|
// even if it is set as a default feature
|
||||||
|
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
launch(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app() -> Element {
|
||||||
|
let server_features = use_server_future(get_server_features)?.unwrap().unwrap();
|
||||||
|
let mut client_features = use_signal(Vec::new);
|
||||||
|
use_effect(move || {
|
||||||
|
client_features.set(current_platform_features());
|
||||||
|
});
|
||||||
|
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
"server features: {server_features:?}"
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
"client features: {client_features:?}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_platform_features() -> Vec<String> {
|
||||||
|
let mut features = Vec::new();
|
||||||
|
if cfg!(feature = "web") {
|
||||||
|
features.push("web".to_string());
|
||||||
|
}
|
||||||
|
if cfg!(feature = "server") {
|
||||||
|
features.push("server".to_string());
|
||||||
|
}
|
||||||
|
features
|
||||||
|
}
|
||||||
|
|
||||||
|
#[server]
|
||||||
|
async fn get_server_features() -> Result<Vec<String>, ServerFnError> {
|
||||||
|
Ok(current_platform_features())
|
||||||
|
}
|
||||||
@@ -166,5 +166,14 @@ module.exports = defineConfig({
|
|||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: !process.env.CI,
|
||||||
stdout: "pipe",
|
stdout: "pipe",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
cwd: path.join(process.cwd(), "default-features-disabled"),
|
||||||
|
command:
|
||||||
|
'cargo run --package dioxus-cli --release -- serve --force-sequential --addr "127.0.0.1" --port 8002',
|
||||||
|
port: 8002,
|
||||||
|
timeout: 50 * 60 * 1000,
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
stdout: "pipe",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user