#![allow(dead_code)]
use {
crate::{config::Config, specifier::Specifier},
log::debug,
serde::Deserialize,
std::{collections::HashMap, fs, rc::Rc, time::Instant},
};
pub type Catalog = HashMap<String, Rc<Specifier>>;
pub type CatalogsByName = HashMap<String, Catalog>;
pub fn from_config(config: &Config) -> Option<CatalogsByName> {
let start = Instant::now();
let catalogs = try_from_pnpm(config).or_else(|| try_from_bun(config));
debug!("Catalog discovery completed in {:?}", start.elapsed());
catalogs
}
fn try_from_pnpm(config: &Config) -> Option<CatalogsByName> {
let file_path = config.cli.cwd.join("pnpm-workspace.yaml");
if !file_path.exists() {
return None;
}
debug!("Reading catalogs from pnpm-workspace.yaml");
let contents = fs::read_to_string(&file_path).ok()?;
let workspace: PnpmWorkspace = serde_yaml::from_str(&contents).ok()?;
let mut catalogs_by_name = CatalogsByName::new();
if let Some(default_catalog) = workspace.catalog {
let mut catalog = Catalog::new();
for (name, version) in default_catalog {
catalog.insert(name, Specifier::new(&version));
}
if !catalog.is_empty() {
catalogs_by_name.insert("default".to_string(), catalog);
}
}
if let Some(named_catalogs) = workspace.catalogs {
for (catalog_name, dependencies) in named_catalogs {
let mut catalog = Catalog::new();
for (name, version) in dependencies {
catalog.insert(name, Specifier::new(&version));
}
if !catalog.is_empty() {
catalogs_by_name.insert(catalog_name, catalog);
}
}
}
if catalogs_by_name.is_empty() {
debug!("No catalogs found in pnpm-workspace.yaml");
None
} else {
debug!("Found {} catalog(s) in pnpm-workspace.yaml", catalogs_by_name.len());
Some(catalogs_by_name)
}
}
fn try_from_bun(config: &Config) -> Option<CatalogsByName> {
let file_path = config.cli.cwd.join("package.json");
if !file_path.exists() {
return None;
}
debug!("Reading catalogs from package.json");
let contents = fs::read_to_string(&file_path).ok()?;
let package_json: BunPackageJson = serde_json::from_str(&contents).ok()?;
let mut catalogs_by_name = CatalogsByName::new();
if let Some(workspaces) = package_json.workspaces {
if let Some(default_catalog) = workspaces.catalog {
let mut catalog = Catalog::new();
for (name, version) in default_catalog {
catalog.insert(name, Specifier::new(&version));
}
if !catalog.is_empty() {
catalogs_by_name.insert("default".to_string(), catalog);
}
}
if let Some(named_catalogs) = workspaces.catalogs {
for (catalog_name, dependencies) in named_catalogs {
let mut catalog = Catalog::new();
for (name, version) in dependencies {
catalog.insert(name, Specifier::new(&version));
}
if !catalog.is_empty() {
catalogs_by_name.insert(catalog_name, catalog);
}
}
}
}
if catalogs_by_name.is_empty() {
if let Some(default_catalog) = package_json.catalog {
let mut catalog = Catalog::new();
for (name, version) in default_catalog {
catalog.insert(name, Specifier::new(&version));
}
if !catalog.is_empty() {
catalogs_by_name.insert("default".to_string(), catalog);
}
}
if let Some(named_catalogs) = package_json.catalogs {
for (catalog_name, dependencies) in named_catalogs {
let mut catalog = Catalog::new();
for (name, version) in dependencies {
catalog.insert(name, Specifier::new(&version));
}
if !catalog.is_empty() {
catalogs_by_name.insert(catalog_name, catalog);
}
}
}
}
if catalogs_by_name.is_empty() {
debug!("No catalogs found in package.json");
None
} else {
debug!("Found {} catalog(s) in package.json", catalogs_by_name.len());
Some(catalogs_by_name)
}
}
#[derive(Debug, Deserialize)]
struct PnpmWorkspace {
catalog: Option<HashMap<String, String>>,
catalogs: Option<HashMap<String, HashMap<String, String>>>,
}
#[derive(Debug, Deserialize)]
struct BunPackageJson {
catalog: Option<HashMap<String, String>>,
catalogs: Option<HashMap<String, HashMap<String, String>>>,
workspaces: Option<BunWorkspaces>,
}
#[derive(Debug, Deserialize)]
struct BunWorkspaces {
catalog: Option<HashMap<String, String>>,
catalogs: Option<HashMap<String, HashMap<String, String>>>,
}