@@ -25,33 +25,46 @@ export class RegistryClient {
2525 private packageManager : PackageManager ,
2626 private logger : logging . LoggerApi ,
2727 readonly minReleaseAge : number = 0 ,
28+ private getRegistryName ?: ( name : string ) => string ,
2829 ) { }
2930
3031 async getMetadata ( packageName : string ) : Promise < PackageMetadata | null > {
31- let promise = this . metadataCache . get ( packageName ) ;
32+ const registryName = this . getRegistryName ? this . getRegistryName ( packageName ) : packageName ;
33+ let promise = this . metadataCache . get ( registryName ) ;
3234 if ( ! promise ) {
33- promise = this . packageManager . getRegistryMetadata ( packageName ) . catch ( ( e ) => {
34- this . metadataCache . delete ( packageName ) ;
35+ promise = this . packageManager . getRegistryMetadata ( registryName ) . catch ( ( e ) => {
36+ this . metadataCache . delete ( registryName ) ;
3537 throw e ;
3638 } ) ;
37- this . metadataCache . set ( packageName , promise ) ;
39+ this . metadataCache . set ( registryName , promise ) ;
3840 }
3941
40- return promise ;
42+ const metadata = await promise ;
43+ if ( metadata && registryName !== packageName ) {
44+ return { ...metadata , name : packageName } ;
45+ }
46+
47+ return metadata ;
4148 }
4249
4350 async getManifest ( packageName : string , version : string ) : Promise < PackageManifest | null > {
44- const key = `${ packageName } @${ version } ` ;
51+ const registryName = this . getRegistryName ? this . getRegistryName ( packageName ) : packageName ;
52+ const key = `${ registryName } @${ version } ` ;
4553 let promise = this . manifestCache . get ( key ) ;
4654 if ( ! promise ) {
47- promise = this . packageManager . getRegistryManifest ( packageName , version ) . catch ( ( e ) => {
55+ promise = this . packageManager . getRegistryManifest ( registryName , version ) . catch ( ( e ) => {
4856 this . manifestCache . delete ( key ) ;
4957 throw e ;
5058 } ) ;
5159 this . manifestCache . set ( key , promise ) ;
5260 }
5361
54- return promise ;
62+ const manifest = await promise ;
63+ if ( manifest && registryName !== packageName ) {
64+ return { ...manifest , name : packageName } ;
65+ }
66+
67+ return manifest ;
5568 }
5669}
5770
@@ -760,6 +773,20 @@ function _formatVersion(v?: string): string | undefined {
760773 return coerced ? coerced . toString ( ) : undefined ;
761774}
762775
776+ function getRegistryNameAndRange ( name : string , specifier : string ) : { name : string ; range : string } {
777+ try {
778+ const result = npa . resolve ( name , specifier ) ;
779+ if ( result . type === 'alias' && result . subSpec ) {
780+ return {
781+ name : result . subSpec . name ?? name ,
782+ range : result . subSpec . fetchSpec ?? specifier ,
783+ } ;
784+ }
785+ } catch { }
786+
787+ return { name, range : specifier } ;
788+ }
789+
763790function isPkgFromRegistry ( name : string , specifier : string ) : boolean {
764791 const result = npa . resolve ( name , specifier ) ;
765792
@@ -886,7 +913,17 @@ export async function resolveUserUpdatePlan(
886913 const usingYarn = options . packageManager === 'yarn' ;
887914
888915 const minReleaseAge = await packageManager . getMinimumReleaseAge ( ) ;
889- const registryClient = new RegistryClient ( packageManager , logger , minReleaseAge ) ;
916+
917+ const getRegistryName = ( name : string ) : string => {
918+ const specifier = npmDeps . get ( name ) ;
919+ if ( specifier ) {
920+ return getRegistryNameAndRange ( name , specifier ) . name ;
921+ }
922+
923+ return name ;
924+ } ;
925+
926+ const registryClient = new RegistryClient ( packageManager , logger , minReleaseAge , getRegistryName ) ;
890927
891928 await checkCatalogUpdates (
892929 normalizedPackages ,
@@ -1163,8 +1200,31 @@ export async function applyUpdatePlan(
11631200
11641201 const updateDependency = ( deps : Record < string , string > , name : string , newVersion : string ) => {
11651202 const oldVersion = deps [ name ] ;
1166- const execResult = / ^ [ \^ ~ ] / . exec ( oldVersion ) ;
1167- deps [ name ] = `${ execResult ? execResult [ 0 ] : '' } ${ newVersion } ` ;
1203+ const aliasPrefix = 'npm:' ;
1204+
1205+ // If the dependency uses an npm package alias (e.g., "npm:registry-name@version-range"),
1206+ // parse and reconstruct the alias with the new target version while preserving
1207+ // the original alias registry name and any version prefix character (like ^ or ~).
1208+ if ( oldVersion . startsWith ( aliasPrefix ) ) {
1209+ const specifier = oldVersion . slice ( aliasPrefix . length ) ;
1210+ const lastAtIndex = specifier . lastIndexOf ( '@' ) ;
1211+ if ( lastAtIndex > 0 ) {
1212+ const registryName = specifier . slice ( 0 , lastAtIndex ) ;
1213+ const versionRange = specifier . slice ( lastAtIndex + 1 ) ;
1214+ // Retain any semantic versioning operator prefix (e.g., ^ or ~) from the target version range.
1215+ const execResult = / ^ [ \^ ~ ] / . exec ( versionRange ) ;
1216+ deps [ name ] =
1217+ `${ aliasPrefix } ${ registryName } @${ execResult ? execResult [ 0 ] : '' } ${ newVersion } ` ;
1218+ } else {
1219+ // If there's no `@` character defining a version specifier in the alias (e.g. "npm:packageName"),
1220+ // leave it as-is without attempting to inject a version suffix.
1221+ deps [ name ] = oldVersion ;
1222+ }
1223+ } else {
1224+ // Standard dependency formatting, keeping any semantic versioning operator prefix (e.g., ^ or ~).
1225+ const execResult = / ^ [ \^ ~ ] / . exec ( oldVersion ) ;
1226+ deps [ name ] = `${ execResult ? execResult [ 0 ] : '' } ${ newVersion } ` ;
1227+ }
11681228 } ;
11691229
11701230 for ( const [ name , targetVersion ] of plan . packagesToUpdate . entries ( ) ) {
0 commit comments