pub mod npm;
pub mod yarn;
pub mod pnpm;
pub mod lockfile;
pub mod registry;
pub mod auth;
pub use npm::*;
pub use yarn::*;
pub use pnpm::*;
pub use lockfile::*;
pub use registry::*;
pub use auth::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use std::collections::{BTreeMap};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PackageManagerType {
Npm,
Yarn,
Pnpm,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PackageSpec {
Name(String),
NameVersion(String, String),
NameRange(String, String),
Git(String),
Local(PathBuf),
}
#[derive(Debug, Clone)]
pub struct BuildResult {
pub success: bool,
pub output_dir: PathBuf,
pub build_time_ms: u64,
pub warnings: Vec<String>,
pub errors: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PackageManagerConfig {
pub manager_type: PackageManagerType,
pub registry_url: String,
pub cache_dir: PathBuf,
pub timeout_ms: u64,
pub retry_count: u32,
pub auth_token: Option<String>,
pub ca_file: Option<PathBuf>,
pub strict_ssl: bool,
}
impl Default for PackageManagerConfig {
fn default() -> Self {
Self {
manager_type: PackageManagerType::Npm,
registry_url: "https://registry.npmjs.org/".to_string(),
cache_dir: PathBuf::from(".amberjs_cache"),
timeout_ms: 30000,
retry_count: 3,
auth_token: None,
ca_file: None,
strict_ssl: true,
}
}
}
#[derive(Debug, Clone)]
pub struct PackageResolution {
pub package_name: String,
pub version: String,
pub resolved_url: String,
pub integrity: String,
pub dependencies: HashMap<String, String>,
pub peer_dependencies: HashMap<String, String>,
pub optional_dependencies: HashMap<String, String>,
pub bins: HashMap<String, PathBuf>,
pub main: String,
pub types: Option<String>,
pub exports: Option<serde_json::Value>,
}
#[derive(Debug, Clone)]
pub struct InstallOptions {
pub production: bool,
pub dev: bool,
pub optional: bool,
pub global: bool,
pub save: bool,
pub save_dev: bool,
pub save_exact: bool,
pub ignore_scripts: bool,
pub legacy_peer_deps: bool,
}
impl Default for InstallOptions {
fn default() -> Self {
Self {
production: false,
dev: false,
optional: true,
global: false,
save: true,
save_dev: false,
save_exact: false,
ignore_scripts: false,
legacy_peer_deps: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DependencyType {
Regular,
Dev,
Peer,
Optional,
Bundle,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Script {
pub name: String,
pub command: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportsConfig {
pub main: Option<String>,
pub types: Option<String>,
pub module: Option<String>,
pub browser: Option<String>,
pub node: Option<String>,
pub import: Option<String>,
pub require: Option<String>,
pub default: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FilesConfig {
pub include: Vec<String>,
pub exclude: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LifecycleHooks {
pub pre_install: Vec<String>,
pub post_install: Vec<String>,
pub pre_publish: Vec<String>,
pub post_publish: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VersionRange {
Exact(String),
GreaterThan(String),
GreaterThanOrEqual(String),
LessThan(String),
LessThanOrEqual(String),
Compatible(String), Approximate(String), Range(String, String), Or(Vec<VersionRange>),
Wildcard, }
impl VersionRange {
pub fn parse(s: &str) -> Result<Self, String> {
if s == "*" {
return Ok(VersionRange::Wildcard);
}
if let Some(stripped) = s.strip_prefix('^') {
return Ok(VersionRange::Compatible(stripped.to_string());
}
if let Some(stripped) = s.strip_prefix('~') {
return Ok(VersionRange::Approximate(stripped.to_string());
}
if s.contains("||") {
let parts: Vec<VersionRange> = s
.split("||")
.map(|p| VersionRange::parse(p.trim())
.collect::<Result<Vec<_>, _>>()?;
return Ok(VersionRange::Or(parts));
}
if s.contains(" - ") {
let parts: Vec<&str> = s.split(" - ").collect();
if parts.len() == 2 {
return Ok(VersionRange::Range(parts[0].to_string(), parts[1].to_string());
}
}
if let Some(stripped) = s.strip_prefix('>') {
if stripped.starts_with('=') {
return Ok(VersionRange::GreaterThanOrEqual(stripped[1..].to_string());
}
return Ok(VersionRange::GreaterThan(stripped.to_string());
}
if let Some(stripped) = s.strip_prefix('<') {
if stripped.starts_with('=') {
return Ok(VersionRange::LessThanOrEqual(stripped[1..].to_string());
}
return Ok(VersionRange::LessThan(stripped.to_string());
}
Ok(VersionRange::Exact(s.to_string())
}
pub fn matches(&self, version: &str) -> bool {
match self {
VersionRange::Exact(v) => version == v,
VersionRange::Wildcard => true,
VersionRange::Compatible(v) => {
if let Some((major, _)) = version.split('.').next() {
if let Some((req_major, _)) = v.split('.').next() {
return major == req_major;
}
}
false
}
VersionRange::Approximate(v) => {
let v_parts: Vec<&str> = v.split('.').collect();
let ver_parts: Vec<&str> = version.split('.').collect();
if v_parts.len() >= 2 && ver_parts.len() >= 2 {
return v_parts[0] == ver_parts[0] && v_parts[1] == ver_parts[1];
}
false
}
VersionRange::GreaterThan(v) => version > v,
VersionRange::GreaterThanOrEqual(v) => version >= v,
VersionRange::LessThan(v) => version < v,
VersionRange::LessThanOrEqual(v) => version <= v,
VersionRange::Range(start, end) => version >= start && version <= end,
VersionRange::Or(ranges) => ranges.iter().any(|r| r.matches(version)),
}
}
}
impl std::fmt::Display for VersionRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
VersionRange::Exact(v) => write!(f, "{}", v),
VersionRange::Wildcard => write!(f, "*"),
VersionRange::Compatible(v) => write!(f, "^{}", v),
VersionRange::Approximate(v) => write!(f, "~{}", v),
VersionRange::GreaterThan(v) => write!(f, ">{}", v),
VersionRange::GreaterThanOrEqual(v) => write!(f, ">={}", v),
VersionRange::LessThan(v) => write!(f, "<{}", v),
VersionRange::LessThanOrEqual(v) => write!(f, "<={}", v),
VersionRange::Range(start, end) => write!(f, "{} - {}", start, end),
VersionRange::Or(ranges) => {
let strs: Vec<String> = ranges.iter().map(|r| r.to_string()).collect();
write!(f, "{}", strs.join(" || "))
}
}
}
}