pub const JS_PROGRAM_EXT: &str = if cfg!(windows) { ".cmd" } else { "" };
use axoasset::SourceFile;
use camino::{Utf8Path, Utf8PathBuf};
use oro_common::{Manifest, Repository};
use oro_package_spec::GitInfo;
use crate::{
errors::AxoprojectError, PackageInfo, Result, Version, WorkspaceInfo, WorkspaceKind,
WorkspaceSearch, WorkspaceStructure,
};
pub fn get_workspace(start_dir: &Utf8Path, clamp_to_dir: Option<&Utf8Path>) -> WorkspaceSearch {
let manifest_path = match workspace_manifest(start_dir, clamp_to_dir) {
Ok(path) => path,
Err(e) => {
return WorkspaceSearch::Missing(e);
}
};
match read_workspace(&manifest_path) {
Ok(workspace) => WorkspaceSearch::Found(workspace),
Err(e) => WorkspaceSearch::Broken {
manifest_path,
cause: e,
},
}
}
fn read_workspace(manifest_path: &Utf8Path) -> Result<WorkspaceStructure> {
let root = manifest_path.parent().unwrap().to_owned();
let manifest = load_manifest(manifest_path)?;
let target_dir = root.join("node_modules");
let root_auto_includes = crate::find_auto_includes(&root)?;
let Some(true_package_name) = manifest.name else {
return Err(crate::errors::AxoprojectError::NamelessNpmPackage {
manifest: manifest_path.to_owned(),
});
};
let (package_scope, package_name) =
if let Some((scope, name)) = true_package_name.split_once('/') {
(Some(scope.to_owned()), name.to_owned())
} else {
(None, true_package_name.clone())
};
let version = manifest.version.map(Version::Npm);
let authors = manifest
.author
.and_then(|a| match a {
oro_common::PersonField::Str(s) => Some(vec![s]),
oro_common::PersonField::Obj(_) => None,
})
.unwrap_or_default();
let repository_url = manifest.repository.and_then(|url| match url {
Repository::Str(magic) => {
let obj: Option<GitInfo> = magic.parse().ok();
obj.and_then(|obj| obj.https())
.as_ref()
.map(ToString::to_string)
}
Repository::Obj { url, .. } => url,
});
let build_manifest =
oro_common::BuildManifest::from_path(manifest_path).map_err(|details| {
AxoprojectError::BuildInfoParse {
manifest_path: manifest_path.to_owned(),
details,
}
})?;
let mut binaries = build_manifest
.bin
.into_iter()
.map(|k| k.0)
.collect::<Vec<_>>();
binaries.sort();
let keywords = if manifest.keywords.is_empty() {
None
} else {
Some(manifest.keywords.into_iter().collect::<Vec<String>>())
};
let build_command = if manifest.scripts.contains_key("dist") {
Some(vec![
format!("npm{JS_PROGRAM_EXT}"),
"run".to_owned(),
"dist".to_owned(),
])
} else {
None
};
let mut info = PackageInfo {
true_name: true_package_name,
true_version: version.clone(),
name: package_name,
npm_scope: package_scope,
version,
manifest_path: manifest_path.to_owned(),
dist_manifest_path: None,
package_root: root.clone(),
description: manifest.description,
authors,
license: manifest.license,
publish: true,
repository_url: repository_url.clone(),
homepage_url: manifest.homepage,
keywords,
documentation_url: None,
readme_file: None,
license_files: vec![],
changelog_file: None,
binaries,
out_dir: None,
cdylibs: vec![],
cstaticlibs: vec![],
#[cfg(feature = "cargo-projects")]
cargo_metadata_table: None,
#[cfg(feature = "cargo-projects")]
cargo_package_id: None,
build_command,
axoupdater_versions: Default::default(),
dist: None,
};
crate::merge_auto_includes(&mut info, &root_auto_includes);
let package_info = vec![info];
Ok(WorkspaceStructure {
sub_workspaces: vec![],
packages: package_info,
workspace: WorkspaceInfo {
kind: WorkspaceKind::Javascript,
target_dir,
workspace_dir: root,
manifest_path: manifest_path.to_owned(),
dist_manifest_path: None,
root_auto_includes,
#[cfg(feature = "cargo-projects")]
cargo_metadata_table: None,
#[cfg(feature = "cargo-projects")]
cargo_profiles: crate::rust::CargoProfiles::new(),
},
})
}
fn workspace_manifest(
start_dir: &Utf8Path,
clamp_to_dir: Option<&Utf8Path>,
) -> Result<Utf8PathBuf> {
crate::find_file("package.json", start_dir, clamp_to_dir)
}
fn load_manifest(manifest_path: &Utf8Path) -> Result<Manifest> {
let source = SourceFile::load_local(manifest_path)?;
let manifest = source.deserialize_json()?;
Ok(manifest)
}