@@ -63,6 +63,15 @@ interface PinnedPackage {
63
63
alias : Nullable < string > ;
64
64
}
65
65
66
+ // Differentiates between dependencyName and packageName to support npm aliases
67
+ interface DependencyInfo {
68
+ dependencyName : string ;
69
+ packageName : string ;
70
+ alias : Nullable < string > ;
71
+ currentVersion ?: string ;
72
+ finalVersion ?: string ;
73
+ }
74
+
66
75
export function parseAliasedPackageName ( alias : string ) : string {
67
76
return alias . replace ( 'npm:' , '' ) . replace ( / @ ( \^ | ~ ) ? [ 0 - 9 ] { 1 , 3 } (?: .[ 0 - 9 ] { 1 , 3 } ) ? (?: .[ 0 - 9 ] { 1 , 3 } ) ? ( .* ?) $ / , '' ) ;
68
77
}
@@ -151,25 +160,105 @@ export class Package extends AsyncOptionalCreatable {
151
160
fs . writeJsonSync ( pkgJsonPath , this . packageJson ) ;
152
161
}
153
162
163
+ public getDistTags ( name : string ) : Record < string , string > {
164
+ const result = exec ( `npm view ${ name } dist-tags ${ this . registry . getRegistryParameter ( ) } --json` , {
165
+ silent : true ,
166
+ } ) ;
167
+ return JSON . parse ( result . stdout ) as Record < string , string > ;
168
+ }
169
+
154
170
public bumpResolutions ( tag : string ) : void {
155
171
if ( ! this . packageJson . resolutions ) {
156
172
throw new SfdxError ( 'Bumping resolutions requires property "resolutions" to be present in package.json' ) ;
157
173
}
158
174
159
175
Object . keys ( this . packageJson . resolutions ) . map ( ( key : string ) => {
160
- const result = exec ( `npm view ${ key } dist-tags ${ this . registry . getRegistryParameter ( ) } --json` , {
161
- silent : true ,
162
- } ) ;
163
- const versions = JSON . parse ( result . stdout ) as Record < string , string > ;
176
+ const versions = this . getDistTags ( key ) ;
164
177
this . packageJson . resolutions [ key ] = versions [ tag ] ;
165
178
} ) ;
166
179
}
167
180
181
+ // Lookup dependency info by package name or npm alias
182
+ // Examples: @salesforce /plugin-info or @sf/info
183
+ // Pass in the dependencies you want to search through (dependencies, devDependencies, resolutions, etc)
184
+ public getDependencyInfo ( name : string , dependencies : Record < string , string > ) : DependencyInfo {
185
+ for ( const [ key , value ] of Object . entries ( dependencies ) ) {
186
+ if ( key === name ) {
187
+ if ( value . startsWith ( 'npm:' ) ) {
188
+ // npm alias was passed in as name, so we need to parse package name and version
189
+ // e.g. passed in: "@sf/login"
190
+ // dependency: "@sf/login": "npm:@salesforce/plugin-login@1.1.1"
191
+ return {
192
+ dependencyName : key ,
193
+ packageName : parseAliasedPackageName ( value ) ,
194
+ alias : value ,
195
+ currentVersion : parsePackageVersion ( value ) ,
196
+ } ;
197
+ } else {
198
+ // package name was passed, so we can use key and value directly
199
+ return {
200
+ dependencyName : key ,
201
+ packageName : key ,
202
+ alias : null ,
203
+ currentVersion : value ,
204
+ } ;
205
+ }
206
+ }
207
+ if ( value . startsWith ( `npm:${ name } ` ) ) {
208
+ // package name was passed in as name, but an alias is used for the dependency
209
+ // e.g. passed in: "@salesforce/plugin-login"
210
+ // dependency: "@sf/login": "npm:@salesforce/plugin-login@1.1.1"
211
+ return {
212
+ dependencyName : key ,
213
+ packageName : name ,
214
+ alias : value ,
215
+ currentVersion : parsePackageVersion ( value ) ,
216
+ } ;
217
+ }
218
+ }
219
+
220
+ cli . error ( `${ name } was not found in the dependencies section of the package.json` ) ;
221
+ }
222
+
223
+ public bumpDependencyVersions ( targetDependencies : string [ ] ) : DependencyInfo [ ] {
224
+ return targetDependencies
225
+ . map ( ( dep ) => {
226
+ // regex for npm package with optional namespace and version
227
+ // https://regex101.com/r/HmIu3N/1
228
+ const npmPackageRegex = / ^ ( (?: @ [ ^ / ] + \/ ) ? [ ^ @ / ] + ) (?: @ ( [ ^ @ / ] + ) ) ? $ / ;
229
+ const [ , name , version ] = npmPackageRegex . exec ( dep ) ;
230
+
231
+ // We will look for packages in dependencies and resolutions
232
+ const { dependencies, resolutions } = this . packageJson ;
233
+
234
+ // find dependency in package.json (could be an npm alias)
235
+ const depInfo = this . getDependencyInfo ( name , { ...dependencies , ...resolutions } ) ;
236
+
237
+ // if a version is not provided, we'll look up the "latest" version
238
+ depInfo . finalVersion = version ?? this . getDistTags ( depInfo . packageName ) . latest ;
239
+
240
+ // return if version did not change
241
+ if ( depInfo . currentVersion === depInfo . finalVersion ) return ;
242
+
243
+ // override final version if npm alias is used
244
+ if ( depInfo . alias ) {
245
+ depInfo . finalVersion = `npm:${ depInfo . packageName } @${ depInfo . finalVersion } ` ;
246
+ }
247
+
248
+ // update dependency (or resolution) in package.json
249
+ if ( dependencies [ depInfo . dependencyName ] ) {
250
+ this . packageJson . dependencies [ depInfo . dependencyName ] = depInfo . finalVersion ;
251
+ } else {
252
+ this . packageJson . resolutions [ depInfo . dependencyName ] = depInfo . finalVersion ;
253
+ }
254
+
255
+ return depInfo ;
256
+ } )
257
+ . filter ( Boolean ) ; // remove falsy values, in this case the `undefined` if version did not change
258
+ }
259
+
168
260
public getNextRCVersion ( tag : string , isPatch = false ) : string {
169
- const result = exec ( `npm view ${ this . packageJson . name } dist-tags ${ this . registry . getRegistryParameter ( ) } --json` , {
170
- silent : true ,
171
- } ) ;
172
- const versions = JSON . parse ( result . stdout ) as Record < string , string > ;
261
+ const versions = this . getDistTags ( this . packageJson . name ) ;
173
262
174
263
const version = semver . parse ( versions [ tag ] ) ;
175
264
return isPatch
@@ -217,10 +306,7 @@ export class Package extends AsyncOptionalCreatable {
217
306
const pinnedPackages : PinnedPackage [ ] = [ ] ;
218
307
deps . forEach ( ( dep ) => {
219
308
// get the 'release' tag version or the version specified by the passed in tag
220
- const result = exec ( `npm view ${ dep . name } dist-tags ${ this . registry . getRegistryParameter ( ) } --json` , {
221
- silent : true ,
222
- } ) ;
223
- const versions = JSON . parse ( result . stdout ) as Record < string , string > ;
309
+ const versions = this . getDistTags ( dep . name ) ;
224
310
let tag = dep . tag ;
225
311
226
312
// if tag is 'latest-rc' and there's no latest-rc release for a package, default to latest
@@ -244,9 +330,9 @@ export class Package extends AsyncOptionalCreatable {
244
330
245
331
// insert the new hardcoded versions into the dependencies in the project's package.json
246
332
if ( dep . alias ) {
247
- this . packageJson [ ' dependencies' ] [ dep . alias ] = `npm:${ dep . name } @${ version } ` ;
333
+ this . packageJson . dependencies [ dep . alias ] = `npm:${ dep . name } @${ version } ` ;
248
334
} else {
249
- this . packageJson [ ' dependencies' ] [ dep . name ] = version ;
335
+ this . packageJson . dependencies [ dep . name ] = version ;
250
336
}
251
337
// accumulate information to return
252
338
pinnedPackages . push ( { name : dep . name , version, tag, alias : dep . alias } ) ;
0 commit comments