Skip to content

Commit

Permalink
feat(mfe): enable proxy for no turbo json (#9560)
Browse files Browse the repository at this point in the history
### Description

This PR adds the MFE support for repositories that do not contain a
`turbo.json`. All this boils down to is:
- Slight refactor for constructing MFE configs to allow for us to test
this without hitting disk
- Going down the "no turbo.json" codepath even if not explicitly enabled
as long as MFE configs are present
 - Update generated `turbo.json` with `proxy` tasks injected

### Testing Instructions

Added unit test for sans-`turbo.json`, but containing a MFE config.

Manual test on a MFE test repo w/o a `turbo.json`
  • Loading branch information
chris-olszewski authored Dec 4, 2024
1 parent d985382 commit 917f30b
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 13 deletions.
22 changes: 15 additions & 7 deletions crates/turborepo-lib/src/microfrontends.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,30 @@ pub struct MicrofrontendsConfigs {
}

impl MicrofrontendsConfigs {
pub fn new(
/// Constructs a collection of configurations from disk
pub fn from_disk(
repo_root: &AbsoluteSystemPath,
package_graph: &PackageGraph,
) -> Result<Option<Self>, Error> {
Self::from_configs(package_graph.packages().map(|(name, info)| {
(
name.as_str(),
MFEConfig::load_from_dir(&repo_root.resolve(info.package_path())),
)
}))
}

/// Constructs a collection of configurations from a list of configurations
pub fn from_configs<'a>(
configs: impl Iterator<Item = (&'a str, Result<Option<MFEConfig>, Error>)>,
) -> Result<Option<Self>, Error> {
let PackageGraphResult {
configs,
config_filenames,
missing_default_apps,
unsupported_version,
mfe_package,
} = PackageGraphResult::new(package_graph.packages().map(|(name, info)| {
(
name.as_str(),
MFEConfig::load_from_dir(&repo_root.resolve(info.package_path())),
)
}))?;
} = PackageGraphResult::new(configs)?;

for (package, err) in unsupported_version {
warn!("Ignoring {package}: {err}");
Expand Down
9 changes: 7 additions & 2 deletions crates/turborepo-lib/src/run/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,8 @@ impl RunBuilder {
repo_telemetry.track_package_manager(pkg_dep_graph.package_manager().name().to_string());
repo_telemetry.track_size(pkg_dep_graph.len());
run_telemetry.track_run_type(self.opts.run_opts.dry_run.is_some());
let micro_frontend_configs = MicrofrontendsConfigs::new(&self.repo_root, &pkg_dep_graph)?;
let micro_frontend_configs =
MicrofrontendsConfigs::from_disk(&self.repo_root, &pkg_dep_graph)?;

let scm = scm.await.expect("detecting scm panicked");
let async_cache = AsyncCache::new(
Expand All @@ -398,10 +399,14 @@ impl RunBuilder {
self.root_turbo_json_path.clone(),
root_package_json.clone(),
)
} else if self.allow_no_turbo_json && !self.root_turbo_json_path.exists() {
} else if !self.root_turbo_json_path.exists() &&
// Infer a turbo.json if allowing no turbo.json is explicitly allowed or if MFE configs are discovered
(self.allow_no_turbo_json || micro_frontend_configs.is_some())
{
TurboJsonLoader::workspace_no_turbo_json(
self.repo_root.clone(),
pkg_dep_graph.packages(),
micro_frontend_configs.clone(),
)
} else if let Some(micro_frontends) = &micro_frontend_configs {
TurboJsonLoader::workspace_with_microfrontends(
Expand Down
113 changes: 109 additions & 4 deletions crates/turborepo-lib/src/turbo_json/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ enum Strategy {
WorkspaceNoTurboJson {
// Map of package names to their scripts
packages: HashMap<PackageName, Vec<String>>,
microfrontends_configs: Option<MicrofrontendsConfigs>,
},
TaskAccess {
root_turbo_json: AbsoluteSystemPathBuf,
Expand Down Expand Up @@ -89,12 +90,16 @@ impl TurboJsonLoader {
pub fn workspace_no_turbo_json<'a>(
repo_root: AbsoluteSystemPathBuf,
packages: impl Iterator<Item = (&'a PackageName, &'a PackageInfo)>,
microfrontends_configs: Option<MicrofrontendsConfigs>,
) -> Self {
let packages = workspace_package_scripts(packages);
Self {
repo_root,
cache: HashMap::new(),
strategy: Strategy::WorkspaceNoTurboJson { packages },
strategy: Strategy::WorkspaceNoTurboJson {
packages,
microfrontends_configs,
},
}
}

Expand Down Expand Up @@ -182,12 +187,20 @@ impl TurboJsonLoader {
turbo_json
}
}
Strategy::WorkspaceNoTurboJson { packages } => {
Strategy::WorkspaceNoTurboJson {
packages,
microfrontends_configs,
} => {
let script_names = packages.get(package).ok_or(Error::NoTurboJSON)?;
if matches!(package, PackageName::Root) {
root_turbo_json_from_scripts(script_names)
} else {
workspace_turbo_json_from_scripts(script_names)
let turbo_json = workspace_turbo_json_from_scripts(script_names);
if let Some(mfe_configs) = microfrontends_configs {
mfe_configs.update_turbo_json(package, turbo_json)
} else {
turbo_json
}
}
}
Strategy::TaskAccess {
Expand Down Expand Up @@ -382,6 +395,7 @@ mod test {
use anyhow::Result;
use tempfile::tempdir;
use test_case::test_case;
use turborepo_unescape::UnescapedString;

use super::*;
use crate::{task_graph::TaskDefinition, turbo_json::CONFIG_FILE};
Expand Down Expand Up @@ -680,7 +694,10 @@ mod test {
let mut loader = TurboJsonLoader {
repo_root: repo_root.to_owned(),
cache: HashMap::new(),
strategy: Strategy::WorkspaceNoTurboJson { packages },
strategy: Strategy::WorkspaceNoTurboJson {
packages,
microfrontends_configs: None,
},
};

{
Expand Down Expand Up @@ -714,4 +731,92 @@ mod test {
let goose_err = loader.load(&PackageName::from("goose")).unwrap_err();
assert!(matches!(goose_err, Error::NoTurboJSON));
}

#[test]
fn test_no_turbo_json_with_mfe() {
let root_dir = tempdir().unwrap();
let repo_root = AbsoluteSystemPath::from_std_path(root_dir.path()).unwrap();
let packages = vec![
(PackageName::Root, vec![]),
(
PackageName::from("web"),
vec!["dev".to_owned(), "build".to_owned()],
),
(
PackageName::from("docs"),
vec!["dev".to_owned(), "build".to_owned()],
),
]
.into_iter()
.collect();

let microfrontends_configs = MicrofrontendsConfigs::from_configs(
vec![
(
"web",
turborepo_microfrontends::Config::from_str(
r#"{"version": "2", "applications": {"web": {}, "docs": {}}}"#,
"mfe.json",
)
.map(Some),
),
(
"docs",
Err(turborepo_microfrontends::Error::ChildConfig {
reference: "web".into(),
}),
),
]
.into_iter(),
)
.unwrap();

let mut loader = TurboJsonLoader {
repo_root: repo_root.to_owned(),
cache: HashMap::new(),
strategy: Strategy::WorkspaceNoTurboJson {
packages,
microfrontends_configs,
},
};

{
let web_json = loader.load(&PackageName::from("web")).unwrap();
for task_name in ["dev", "build", "proxy"] {
if let Some(def) = web_json.tasks.get(&TaskName::from(task_name)) {
assert_eq!(
def.cache.as_ref().map(|cache| *cache.as_inner()),
Some(false)
);
// Make sure proxy is in there
if task_name == "dev" {
assert_eq!(
def.siblings.as_ref().unwrap().first().unwrap().as_inner(),
&UnescapedString::from("web#proxy")
)
}
} else {
panic!("didn't find {task_name}");
}
}
}

{
let docs_json = loader.load(&PackageName::from("docs")).unwrap();
for task_name in ["dev"] {
if let Some(def) = docs_json.tasks.get(&TaskName::from(task_name)) {
assert_eq!(
def.cache.as_ref().map(|cache| *cache.as_inner()),
Some(false)
);
assert_eq!(
def.siblings.as_ref().unwrap().first().unwrap().as_inner(),
&UnescapedString::from("web#proxy")
)
} else {
panic!("didn't find {task_name}");
}
}
}
}
}

0 comments on commit 917f30b

Please sign in to comment.