diff options
Diffstat (limited to 'crates/mozart-registry/src/lockfile.rs')
| -rw-r--r-- | crates/mozart-registry/src/lockfile.rs | 88 |
1 files changed, 78 insertions, 10 deletions
diff --git a/crates/mozart-registry/src/lockfile.rs b/crates/mozart-registry/src/lockfile.rs index 075848f..e422a9a 100644 --- a/crates/mozart-registry/src/lockfile.rs +++ b/crates/mozart-registry/src/lockfile.rs @@ -372,6 +372,20 @@ impl LockFileGenerationRequest { .find(|ipkg| ipkg.name == name && ipkg.version.version_normalized == version_normalized) .map(|ipkg| ipkg.version) } + + /// Look up a `type: composer` repository entry for `name@version_normalized`. + /// Used to short-circuit the Packagist fetch when the resolved package came + /// from a local Composer repo (the test fixtures' file:// case). + fn composer_repo_lookup( + &self, + name: &str, + version_normalized: &str, + ) -> Option<PackagistVersion> { + crate::composer_repo::collect_composer_packages(&self.composer_json.repositories) + .into_iter() + .find(|cpkg| cpkg.name == name && cpkg.version.version_normalized == version_normalized) + .map(|cpkg| cpkg.version) + } } /// Convert a `PackagistSource` to a `LockedSource`. @@ -523,7 +537,15 @@ fn extract_platform_requirements(requirements: &BTreeMap<String, String>) -> ser /// 3. Computes the content-hash /// 4. Assembles the complete `LockFile` struct pub async fn generate_lock_file(request: &LockFileGenerationRequest) -> anyhow::Result<LockFile> { - // 1. Fetch full metadata for all resolved packages. + // Split the resolved set into real packages and alias entries up front. + // Aliases get emitted as a separate `aliases[]` block and never enter the + // metadata fetch loop — their target package carries the real metadata. + let (real_resolved, alias_resolved): (Vec<&ResolvedPackage>, Vec<&ResolvedPackage>) = request + .resolved_packages + .iter() + .partition(|p| p.alias_of_normalized.is_none()); + + // 1. Fetch full metadata for real (non-alias) packages. // // Inline `type: package` repositories carry full metadata in composer.json // — short-circuit those before hitting the network. Everything else goes @@ -531,12 +553,17 @@ pub async fn generate_lock_file(request: &LockFileGenerationRequest) -> anyhow:: // steps will move VCS / inline through the same set. let mut package_metadata: HashMap<String, PackagistVersion> = HashMap::new(); let repo_set = &request.repositories; - for pkg in &request.resolved_packages { + for pkg in &real_resolved { if let Some(inline) = request.inline_lookup(&pkg.name, &pkg.version_normalized) { package_metadata.insert(pkg.name.clone(), inline); continue; } + if let Some(cv) = request.composer_repo_lookup(&pkg.name, &pkg.version_normalized) { + package_metadata.insert(pkg.name.clone(), cv); + continue; + } + let queries = [crate::repository::PackageQuery { name: pkg.name.as_str(), constraint: None, @@ -555,9 +582,19 @@ pub async fn generate_lock_file(request: &LockFileGenerationRequest) -> anyhow:: package_metadata.insert(pkg.name.clone(), matching.version); } - // 2. Classify dev vs non-dev packages + // 2. Classify dev vs non-dev packages (real packages only). + let real_owned: Vec<ResolvedPackage> = real_resolved + .iter() + .map(|p| ResolvedPackage { + name: p.name.clone(), + version: p.version.clone(), + version_normalized: p.version_normalized.clone(), + is_dev: p.is_dev, + alias_of_normalized: None, + }) + .collect(); let dev_only = classify_dev_packages( - &request.resolved_packages, + &real_owned, &request.composer_json.require, &request.composer_json.require_dev, &package_metadata, @@ -566,7 +603,7 @@ pub async fn generate_lock_file(request: &LockFileGenerationRequest) -> anyhow:: // 3. Build LockedPackage lists let mut packages: Vec<LockedPackage> = Vec::new(); let mut packages_dev: Vec<LockedPackage> = Vec::new(); - for pkg in &request.resolved_packages { + for pkg in &real_resolved { let pv = &package_metadata[&pkg.name]; let locked = packagist_version_to_locked_package(&pkg.name, pv); if dev_only.contains(&pkg.name) { @@ -580,14 +617,38 @@ pub async fn generate_lock_file(request: &LockFileGenerationRequest) -> anyhow:: packages.sort_by(|a, b| a.name.cmp(&b.name)); packages_dev.sort_by(|a, b| a.name.cmp(&b.name)); - // 5. Compute content-hash + // 5. Build the aliases[] block. Each alias entry references the target + // package (`package` + `version`) and carries the alias's pretty/normalized + // form (`alias` + `alias_normalized`). Mirrors Composer's + // `Locker::lockPackages` alias dump. + let mut alias_blocks: Vec<LockAlias> = Vec::new(); + for alias in &alias_resolved { + let target_normalized = match &alias.alias_of_normalized { + Some(t) => t.clone(), + None => continue, + }; + let target_pretty = real_resolved + .iter() + .find(|p| p.name == alias.name && p.version_normalized == target_normalized) + .map(|p| p.version.clone()) + .unwrap_or_else(|| target_normalized.clone()); + alias_blocks.push(LockAlias { + package: alias.name.clone(), + version: target_pretty, + alias: alias.version.clone(), + alias_normalized: alias.version_normalized.clone(), + }); + } + alias_blocks.sort_by(|a, b| a.package.cmp(&b.package).then(a.alias.cmp(&b.alias))); + + // 6. Compute content-hash let content_hash = LockFile::compute_content_hash(&request.composer_json_content)?; - // 6. Extract platform requirements + // 7. Extract platform requirements let platform = extract_platform_requirements(&request.composer_json.require); let platform_dev = extract_platform_requirements(&request.composer_json.require_dev); - // 7. Determine minimum-stability and prefer-stable + // 8. Determine minimum-stability and prefer-stable let minimum_stability = request .composer_json .minimum_stability @@ -601,7 +662,7 @@ pub async fn generate_lock_file(request: &LockFileGenerationRequest) -> anyhow:: .and_then(|v| v.as_bool()) .unwrap_or(false); - // 8. Assemble LockFile + // 9. Assemble LockFile Ok(LockFile { readme: LockFile::default_readme(), content_hash, @@ -611,7 +672,7 @@ pub async fn generate_lock_file(request: &LockFileGenerationRequest) -> anyhow:: } else { Some(vec![]) }, - aliases: vec![], + aliases: alias_blocks, minimum_stability, stability_flags: serde_json::json!({}), prefer_stable, @@ -904,24 +965,28 @@ mod tests { version: "1.0.0".to_string(), version_normalized: "1.0.0.0".to_string(), is_dev: false, + alias_of_normalized: None, }, ResolvedPackage { name: "vendor/b".to_string(), version: "1.0.0".to_string(), version_normalized: "1.0.0.0".to_string(), is_dev: false, + alias_of_normalized: None, }, ResolvedPackage { name: "vendor/c".to_string(), version: "1.0.0".to_string(), version_normalized: "1.0.0.0".to_string(), is_dev: false, + alias_of_normalized: None, }, ResolvedPackage { name: "vendor/d".to_string(), version: "1.0.0".to_string(), version_normalized: "1.0.0.0".to_string(), is_dev: false, + alias_of_normalized: None, }, ]; @@ -983,18 +1048,21 @@ mod tests { version: "1.0.0".to_string(), version_normalized: "1.0.0.0".to_string(), is_dev: false, + alias_of_normalized: None, }, ResolvedPackage { name: "vendor/b".to_string(), version: "1.0.0".to_string(), version_normalized: "1.0.0.0".to_string(), is_dev: false, + alias_of_normalized: None, }, ResolvedPackage { name: "vendor/c".to_string(), version: "1.0.0".to_string(), version_normalized: "1.0.0.0".to_string(), is_dev: false, + alias_of_normalized: None, }, ]; |
