Skip to main content

cuenv_workspaces/
lib.rs

1// Transitive dependencies bring in multiple versions of foldhash and unicode-width
2#![allow(clippy::multiple_crate_versions)]
3// Rust 1.92 compiler bug: false positives for thiserror/miette derive macro fields
4// https://github.com/rust-lang/rust/issues/147648
5#![allow(unused_assignments)]
6
7//! Workspace and dependency resolution for cuenv across multiple package managers.
8//!
9//! This crate provides trait-based abstractions for discovering, parsing, and resolving
10//! workspace configurations and dependencies across different package managers including
11//! npm, Bun, pnpm, Yarn (Classic and Modern), and Cargo.
12//!
13//! # Architecture
14//!
15//! The crate is built around three core traits:
16//!
17//! - [`WorkspaceDiscovery`] - Discovers workspace configuration from a root directory
18//! - [`LockfileParser`] - Parses lockfiles into structured entries
19//! - [`DependencyResolver`] - Builds dependency graphs from workspace and lockfile data
20//!
21//! # Workspace Discovery
22//!
23//! The discovery module provides implementations for finding workspace members from
24//! configuration files.
25//!
26//! ## Feature flags
27//!
28//! ### Discovery Features
29//!
30//! - `discovery-javascript` - Enables npm, Bun, Yarn, and pnpm workspace discovery (**enabled by default**)
31//! - `discovery-rust` - Enables Cargo workspace discovery
32//!
33//! ### Fine-grained Discovery Features
34//!
35//! For minimal dependency footprint:
36//! - `discovery-package-json` - npm/Bun/Yarn discovery via `package.json`
37//! - `discovery-pnpm` - pnpm discovery via `pnpm-workspace.yaml`
38//! - `discovery-cargo` - Cargo discovery via `Cargo.toml`
39//!
40//! ### Default Features
41//!
42//! The crate enables the following features by default:
43//! - `detection` - Package manager detection from lockfiles and commands
44//! - `parsers-javascript` - All JavaScript lockfile parsers
45//! - `discovery-javascript` - All JavaScript workspace discoveries
46//!
47//! This provides a complete out-of-the-box experience for JavaScript/TypeScript projects.
48//! To minimize dependencies for Rust-only or specialized use cases, disable default features:
49//!
50//! ```toml
51//! [dependencies]
52//! cuenv-workspaces = { version = "...", default-features = false, features = ["discovery-rust"] }
53//! ```
54//!
55//! ## Discovery Behavior for Edge Cases
56//!
57//! ### Cargo (`CargoTomlDiscovery`)
58//!
59//! - **Missing `[workspace]` section**: Treated as a valid empty workspace (single-package
60//!   repository). Discovery succeeds with zero members.
61//! - **Missing or malformed member manifests**: Silently skipped during member enumeration.
62//!   Only valid, parseable members are included in the result.
63//!
64//! ### JavaScript (`PackageJsonDiscovery`, `PnpmWorkspaceDiscovery`)
65//!
66//! - **Missing or malformed member manifests**: Silently skipped during member enumeration.
67//!   Only valid, parseable members with a `name` field are included in the result.
68//!
69//! This tolerant behavior ensures that discovery does not fail due to individual member
70//! issues, allowing partial workspace analysis to proceed.
71//!
72//! ## Usage examples
73//!
74//! ```rust,ignore
75//! use cuenv_workspaces::{PackageJsonDiscovery, WorkspaceDiscovery};
76//! use std::path::Path;
77//!
78//! let root = Path::new(".");
79//! let discovery = PackageJsonDiscovery;
80//!
81//! if let Ok(workspace) = discovery.discover(root) {
82//!     println!("Found workspace with {} members", workspace.member_count());
83//! }
84//! ```
85//!
86//! # Example
87//!
88//! ```rust,ignore
89//! use cuenv_workspaces::{WorkspaceDiscovery, PackageManager, Workspace};
90//! use std::path::Path;
91//!
92//! // Discover a workspace
93//! let root = Path::new("/path/to/workspace");
94//! let workspace = some_discovery_impl.discover(root)?;
95//!
96//! // Access workspace information
97//! println!("Found {} members", workspace.member_count());
98//! for member in &workspace.members {
99//!     println!("  - {} at {}", member.name, member.path.display());
100//! }
101//! ```
102//!
103//! # Core Types
104//!
105//! - [`Workspace`] - Represents a discovered workspace with all its members
106//! - [`WorkspaceMember`] - Represents a single package/crate in the workspace
107//! - [`PackageManager`] - Identifies the package manager in use
108//! - [`DependencySpec`] - Describes how a dependency is specified
109//! - [`LockfileEntry`] - Represents a resolved dependency from a lockfile
110//!
111//! # Package Manager Detection
112//!
113//! The detection module provides automatic package manager detection by scanning
114//! for lockfiles and workspace configurations:
115//!
116//! ```rust,ignore
117//! use cuenv_workspaces::detect_package_managers;
118//! use std::path::Path;
119//!
120//! let root = Path::new("/path/to/workspace");
121//! let managers = detect_package_managers(root)?;
122//!
123//! for manager in managers {
124//!     println!("Detected: {}", manager);
125//! }
126//! ```
127//!
128//! You can also detect package managers from command strings:
129//!
130//! ```rust,ignore
131//! use cuenv_workspaces::detect_from_command;
132//!
133//! if let Some(manager) = detect_from_command("cargo build") {
134//!     println!("Command uses: {}", manager);
135//! }
136//! ```
137//!
138//! # Lockfile Parsers
139//!
140//! The parsers module provides implementations for parsing various package manager
141//! lockfiles. Each parser is gated behind a feature flag to minimize binary size.
142//!
143//! ## Recommended feature flags
144//!
145//! Use these aggregate features to enable all parsers for an ecosystem:
146//!
147//! - `parsers-javascript` - Enables all JavaScript parsers (npm, bun, pnpm, yarn classic, yarn modern)
148//! - `parsers-rust` - Enables all Rust parsers (currently only Cargo)
149//!
150//! ## Fine-grained feature flags
151//!
152//! For minimal dependency footprint, you can enable individual parsers:
153//!
154//! - `parser-npm` - npm's `package-lock.json` (v3)
155//! - `parser-bun` - Bun's `bun.lock` (JSONC format)
156//! - `parser-pnpm` - pnpm's `pnpm-lock.yaml`
157//! - `parser-yarn-classic` - Yarn Classic (v1.x) `yarn.lock`
158//! - `parser-yarn-modern` - Yarn Modern (v2+) `yarn.lock`
159//! - `parser-cargo` - Cargo's `Cargo.lock`
160//!
161//! ## Usage examples
162//!
163//! ```rust,ignore
164//! use cuenv_workspaces::{NpmLockfileParser, LockfileParser};
165//! use std::path::Path;
166//!
167//! let parser = NpmLockfileParser;
168//! let entries = parser.parse(Path::new("package-lock.json"))?;
169//!
170//! for entry in entries {
171//!     println!("{} @ {}", entry.name, entry.version);
172//! }
173//! ```
174//!
175//! ```rust,ignore
176//! use cuenv_workspaces::{CargoLockfileParser, LockfileParser};
177//! use std::path::Path;
178//!
179//! let parser = CargoLockfileParser;
180//! let entries = parser.parse(Path::new("Cargo.lock"))?;
181//!
182//! for entry in entries {
183//!     println!("{} @ {}", entry.name, entry.version);
184//! }
185//! ```
186
187#![warn(missing_docs)]
188#![warn(clippy::all, clippy::pedantic)]
189
190pub mod core;
191pub mod error;
192pub mod materializer;
193pub mod resolver;
194
195#[cfg(feature = "detection")]
196pub mod detection;
197
198#[cfg(any(
199    feature = "parsers-javascript",
200    feature = "parsers-rust",
201    feature = "parser-cargo"
202))]
203pub mod parsers;
204
205#[cfg(any(
206    feature = "discovery-package-json",
207    feature = "discovery-pnpm",
208    feature = "discovery-cargo"
209))]
210pub mod discovery;
211
212// Re-export core types
213pub use core::{
214    DependencyRef, DependencySource, DependencySpec, LockfileEntry, PackageManager, Version,
215    VersionReq, Workspace, WorkspaceMember,
216};
217
218// Re-export traits
219pub use core::{DependencyResolver, LockfileParser, WorkspaceDiscovery};
220
221// Re-export resolver types
222pub use resolver::GenericDependencyResolver;
223
224// Re-export error types
225pub use error::{Error, Result};
226
227// Re-export detection functions
228#[cfg(feature = "detection")]
229pub use detection::{detect_from_command, detect_package_managers, detect_with_command_hint};
230
231// Re-export JavaScript discovery types
232#[cfg(feature = "discovery-package-json")]
233pub use discovery::PackageJsonDiscovery;
234
235#[cfg(feature = "discovery-pnpm")]
236pub use discovery::PnpmWorkspaceDiscovery;
237
238// Re-export Rust discovery types
239#[cfg(feature = "discovery-cargo")]
240pub use discovery::CargoTomlDiscovery;
241
242// Re-export JavaScript parser types
243#[cfg(feature = "parser-bun")]
244pub use parsers::javascript::BunLockfileParser;
245#[cfg(feature = "parser-npm")]
246pub use parsers::javascript::NpmLockfileParser;
247#[cfg(feature = "parser-pnpm")]
248pub use parsers::javascript::PnpmLockfileParser;
249#[cfg(feature = "parser-yarn-classic")]
250pub use parsers::javascript::YarnClassicLockfileParser;
251#[cfg(feature = "parser-yarn-modern")]
252pub use parsers::javascript::YarnModernLockfileParser;
253// Re-export Rust parser types
254#[cfg(any(feature = "parsers-rust", feature = "parser-cargo"))]
255pub use parsers::rust::CargoLockfileParser;