diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-04 00:16:43 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-04 00:16:43 +0900 |
| commit | 5fc05048a456ce18fd9408ea985031865cf45550 (patch) | |
| tree | 9d01068a076f096874c188f0622f260920d94476 /crates/mozart-registry/src/resolver.rs | |
| parent | 16f8cf26db22c4fb1073b55d57d31110ea9773cc (diff) | |
| download | php-mozart-5fc05048a456ce18fd9408ea985031865cf45550.tar.gz php-mozart-5fc05048a456ce18fd9408ea985031865cf45550.tar.zst php-mozart-5fc05048a456ce18fd9408ea985031865cf45550.zip | |
fix(resolver): rewrite self.version on materialized root aliases
Mirror Composer's `AliasPackage::replaceSelfVersionDependencies`: a base
package's `replace` / `provide` / `conflict` link whose constraint
matches the base's own version (the resolved form of `self.version`) is
duplicated on the alias at the alias's version. A root require like
`a/aliased: dev-next as 4.1.0-RC2` paired with `replace: { foo:
self.version }` previously left the alias with a `dev-next` constraint,
so a transitive `foo ^4.0` requirement saw no numeric provider and the
solver bailed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart-registry/src/resolver.rs')
| -rw-r--r-- | crates/mozart-registry/src/resolver.rs | 35 |
1 files changed, 32 insertions, 3 deletions
diff --git a/crates/mozart-registry/src/resolver.rs b/crates/mozart-registry/src/resolver.rs index a59d330..c592e01 100644 --- a/crates/mozart-registry/src/resolver.rs +++ b/crates/mozart-registry/src/resolver.rs @@ -1429,14 +1429,43 @@ pub async fn resolve(request: &ResolveRequest) -> Result<Vec<ResolvedPackage>, R .is_alias_of .clone() .unwrap_or_else(|| input.version.clone()); + // Extend `self.version`-derived `replace` / `provide` / + // `conflict` links with an extra entry pinned at the + // alias's own version. Mirrors Composer's + // `AliasPackage::replaceSelfVersionDependencies`: a base + // link whose constraint matches the base's own version + // (the resolved form of `self.version`) is duplicated + // under the alias at the alias's version, so a transitive + // require like `a/aliased-replaced ^4.0` can match the + // alias even when the base is at a non-matching dev + // version. Without this, the alias's replace map keeps + // the base's `dev-next` constraint and the requirement + // never sees a numeric provider. + let alias_extra_self_links = |links: &[PoolLink]| -> Vec<PoolLink> { + links + .iter() + .filter(|l| l.constraint == input.version) + .map(|l| PoolLink { + target: l.target.clone(), + constraint: alias.alias_normalized.clone(), + source: l.source.clone(), + }) + .collect() + }; + let mut alias_replaces = input.replaces.clone(); + alias_replaces.extend(alias_extra_self_links(&input.replaces)); + let mut alias_provides = input.provides.clone(); + alias_provides.extend(alias_extra_self_links(&input.provides)); + let mut alias_conflicts = input.conflicts.clone(); + alias_conflicts.extend(alias_extra_self_links(&input.conflicts)); new_aliases.push(PoolPackageInput { name: input.name.clone(), version: alias.alias_normalized.clone(), pretty_version: alias.alias.clone(), requires: input.requires.clone(), - replaces: input.replaces.clone(), - provides: input.provides.clone(), - conflicts: input.conflicts.clone(), + replaces: alias_replaces, + provides: alias_provides, + conflicts: alias_conflicts, is_fixed: false, is_alias_of: Some(target_normalized), }); |
