@@ -126,202 +126,174 @@ func (npmrc *NpmRC) getRegistryByPackageName(packageName string) *NpmRegistry {
126126 return & npmrc .NpmRegistry
127127}
128128
129- func (npmrc * NpmRC ) getPackageInfo (pkgName string , version string ) (packageJson * npm.PackageJSON , err error ) {
129+ type versionResolver func (metadata * npm.PackageMetadata , version string ) (string , error )
130+
131+ func (npmrc * NpmRC ) fetchPackageMetadata (pkgName string , version string , useVersionSpecificEndpoint bool ) (* npm.PackageMetadata , * npm.PackageJSONRaw , error ) {
130132 reg := npmrc .getRegistryByPackageName (pkgName )
131- getCacheKey := func (pkgName string , pkgVersion string ) string {
132- return reg .Registry + pkgName + "@" + pkgVersion
133+ regUrl := reg .Registry + pkgName
134+
135+ if useVersionSpecificEndpoint && strings .HasPrefix (regUrl , npmRegistry ) {
136+ regUrl += "/" + version
133137 }
134138
135- version = npm .NormalizePackageVersion (version )
136- return withCache (getCacheKey (pkgName , version ), time .Duration (config .NpmQueryCacheTTL )* time .Second , func () (* npm.PackageJSON , string , error ) {
137- // check if the package has been installed
138- if ! npm .IsDistTag (version ) && npm .IsExactVersion (version ) {
139- var raw npm.PackageJSONRaw
140- pkgJsonPath := path .Join (npmrc .StoreDir (), pkgName + "@" + version , "node_modules" , pkgName , "package.json" )
141- if utils .ParseJSONFile (pkgJsonPath , & raw ) == nil {
142- return raw .ToNpmPackage (), "" , nil
143- }
139+ u , err := url .Parse (regUrl )
140+ if err != nil {
141+ return nil , nil , err
142+ }
143+
144+ header := http.Header {}
145+ if reg .Token != "" {
146+ header .Set ("Authorization" , "Bearer " + reg .Token )
147+ } else if reg .User != "" && reg .Password != "" {
148+ header .Set ("Authorization" , "Basic " + base64 .StdEncoding .EncodeToString ([]byte (reg .User + ":" + reg .Password )))
149+ }
150+
151+ fetchClient , recycle := fetch .NewClient ("esmd/" + VERSION , 15 , false , nil )
152+ defer recycle ()
153+
154+ retryTimes := 0
155+ RETRY:
156+ res , err := fetchClient .Fetch (u , header )
157+ if err != nil {
158+ if retryTimes < 3 {
159+ retryTimes ++
160+ time .Sleep (time .Duration (retryTimes ) * 100 * time .Millisecond )
161+ goto RETRY
144162 }
163+ return nil , nil , err
164+ }
165+ defer res .Body .Close ()
145166
146- regUrl := reg .Registry + pkgName
147- // For exact versions and dist tags, use version-specific endpoint for better performance
148- isWellknownVersion := (npm .IsExactVersion (version ) || npm .IsDistTag (version )) && strings .HasPrefix (regUrl , npmRegistry )
149- if isWellknownVersion {
150- // npm registry supports url like `https://registry.npmjs.org/<name>/<version>`
151- regUrl += "/" + version
167+ if res .StatusCode == 404 || res .StatusCode == 401 {
168+ if useVersionSpecificEndpoint {
169+ return nil , nil , fmt .Errorf ("version %s of '%s' not found" , version , pkgName )
170+ } else {
171+ return nil , nil , fmt .Errorf ("package '%s' not found" , pkgName )
152172 }
173+ }
153174
154- u , err := url .Parse (regUrl )
175+ if res .StatusCode != 200 {
176+ return nil , nil , fmt .Errorf ("npm registry returned %d for package '%s'" , res .StatusCode , pkgName )
177+ }
178+
179+ if useVersionSpecificEndpoint {
180+ var raw npm.PackageJSONRaw
181+ err = json .NewDecoder (res .Body ).Decode (& raw )
155182 if err != nil {
156- return nil , "" , err
183+ return nil , nil , err
157184 }
185+ return nil , & raw , nil
186+ }
158187
159- header := http.Header {}
160- if reg .Token != "" {
161- header .Set ("Authorization" , "Bearer " + reg .Token )
162- } else if reg .User != "" && reg .Password != "" {
163- header .Set ("Authorization" , "Basic " + base64 .StdEncoding .EncodeToString ([]byte (reg .User + ":" + reg .Password )))
164- }
188+ var metadata npm.PackageMetadata
189+ err = json .NewDecoder (res .Body ).Decode (& metadata )
190+ if err != nil {
191+ return nil , nil , err
192+ }
165193
166- fetchClient , recycle := fetch .NewClient ("esmd/" + VERSION , 15 , false , nil )
167- defer recycle ()
194+ if len (metadata .Versions ) == 0 {
195+ return nil , nil , fmt .Errorf ("no versions found for package '%s'" , pkgName )
196+ }
197+
198+ return & metadata , nil , nil
199+ }
168200
169- retryTimes := 0
170- RETRY:
171- res , err := fetchClient .Fetch (u , header )
201+ func resolveSemverVersion (metadata * npm.PackageMetadata , version string ) (string , error ) {
202+ CHECK:
203+ distVersion , ok := metadata .DistTags [version ]
204+ if ok {
205+ _ , ok := metadata .Versions [distVersion ]
206+ if ok {
207+ return distVersion , nil
208+ }
209+ } else {
210+ if version == "lastest" {
211+ return "" , fmt .Errorf ("version %s not found" , version )
212+ }
213+ c , err := semver .NewConstraint (version )
172214 if err != nil {
173- if retryTimes < 3 {
174- retryTimes ++
175- time .Sleep (time .Duration (retryTimes ) * 100 * time .Millisecond )
176- goto RETRY
215+ version = "latest"
216+ goto CHECK
217+ }
218+ vs := make ([]* semver.Version , len (metadata .Versions ))
219+ i := 0
220+ for v := range metadata .Versions {
221+ if ! strings .ContainsRune (version , '-' ) && strings .ContainsRune (v , '-' ) {
222+ continue
223+ }
224+ sv , err := semver .NewVersion (v )
225+ if err == nil && c .Check (sv ) {
226+ vs [i ] = sv
227+ i ++
177228 }
178- return nil , "" , err
179229 }
180- defer res .Body .Close ()
181-
182- if res .StatusCode == 404 || res .StatusCode == 401 {
183- if isWellknownVersion {
184- err = fmt .Errorf ("version %s of '%s' not found" , version , pkgName )
185- } else {
186- err = fmt .Errorf ("package '%s' not found" , pkgName )
230+ if i > 0 {
231+ vs = vs [:i ]
232+ if i > 1 {
233+ sort .Sort (semver .Collection (vs ))
187234 }
188- return nil , "" , err
235+ return vs [ i - 1 ]. String (), nil
189236 }
237+ }
238+ return "" , fmt .Errorf ("version %s not found" , version )
239+ }
190240
191- if res .StatusCode != 200 {
192- return nil , "" , fmt .Errorf ("npm registry returned %d for package '%s'" , res .StatusCode , pkgName )
193- }
241+ func (npmrc * NpmRC ) getPackageInfo (pkgName string , version string ) (packageJson * npm.PackageJSON , err error ) {
242+ reg := npmrc .getRegistryByPackageName (pkgName )
243+ getCacheKey := func (pkgName string , pkgVersion string ) string {
244+ return reg .Registry + pkgName + "@" + pkgVersion
245+ }
194246
195- if isWellknownVersion {
196- // for version-specific endpoint, the response is the package.json directly
247+ version = npm .NormalizePackageVersion (version )
248+ return withCache (getCacheKey (pkgName , version ), time .Duration (config .NpmQueryCacheTTL )* time .Second , func () (* npm.PackageJSON , string , error ) {
249+ if ! npm .IsDistTag (version ) && npm .IsExactVersion (version ) {
197250 var raw npm.PackageJSONRaw
198- err = json . NewDecoder ( res . Body ). Decode ( & raw )
199- if err ! = nil {
200- return nil , "" , err
251+ pkgJsonPath := path . Join ( npmrc . StoreDir (), pkgName + "@" + version , "node_modules" , pkgName , "package.json" )
252+ if utils . ParseJSONFile ( pkgJsonPath , & raw ) = = nil {
253+ return raw . ToNpmPackage () , "" , nil
201254 }
202- return raw .ToNpmPackage (), getCacheKey (pkgName , raw .Version ), nil
203255 }
204256
205- // for package-level endpoint, the response contains all versions
206- var metadata npm.PackageMetadata
207- err = json .NewDecoder (res .Body ).Decode (& metadata )
257+ isWellknownVersion := (npm .IsExactVersion (version ) || npm .IsDistTag (version )) && strings .HasPrefix (reg .Registry , npmRegistry )
258+ metadata , raw , err := npmrc .fetchPackageMetadata (pkgName , version , isWellknownVersion )
208259 if err != nil {
209260 return nil , "" , err
210261 }
211262
212- if len (metadata .Versions ) == 0 {
263+ if raw != nil {
264+ return raw .ToNpmPackage (), getCacheKey (pkgName , raw .Version ), nil
265+ }
266+
267+ resolvedVersion , err := resolveSemverVersion (metadata , version )
268+ if err != nil {
213269 return nil , "" , fmt .Errorf ("version %s of '%s' not found" , version , pkgName )
214270 }
215271
216- CHECK:
217- distVersion , ok := metadata .DistTags [version ]
218- if ok {
219- raw , ok := metadata .Versions [distVersion ]
220- if ok {
221- return raw .ToNpmPackage (), getCacheKey (pkgName , raw .Version ), nil
222- }
223- } else {
224- if version == "lastest" {
225- return nil , "" , fmt .Errorf ("version %s of '%s' not found" , version , pkgName )
226- }
227- var c * semver.Constraints
228- c , err = semver .NewConstraint (version )
229- if err != nil {
230- // fallback to latest if semverOrDistTag is not a valid semver
231- version = "latest"
232- goto CHECK
233- }
234- vs := make ([]* semver.Version , len (metadata .Versions ))
235- i := 0
236- for v := range metadata .Versions {
237- // ignore prerelease versions
238- if ! strings .ContainsRune (version , '-' ) && strings .ContainsRune (v , '-' ) {
239- continue
240- }
241- sv , err := semver .NewVersion (v )
242- if err == nil && c .Check (sv ) {
243- vs [i ] = sv
244- i ++
245- }
246- }
247- if i > 0 {
248- vs = vs [:i ]
249- if i > 1 {
250- sort .Sort (semver .Collection (vs ))
251- }
252- raw , ok := metadata .Versions [vs [i - 1 ].String ()]
253- if ok {
254- return raw .ToNpmPackage (), getCacheKey (pkgName , raw .Version ), nil
255- }
256- }
272+ rawData , ok := metadata .Versions [resolvedVersion ]
273+ if ! ok {
274+ return nil , "" , fmt .Errorf ("version %s of '%s' not found" , version , pkgName )
257275 }
258276
259- return nil , "" , fmt . Errorf ( "version %s of '%s' not found" , version , pkgName )
277+ return rawData . ToNpmPackage (), getCacheKey ( pkgName , rawData . Version ), nil
260278 })
261279}
262280
263281func (npmrc * NpmRC ) getPackageInfoByDate (pkgName string , dateVersion string ) (packageJson * npm.PackageJSON , err error ) {
264- // Convert date to time
265282 targetTime , err := npm .ConvertDateVersionToTime (dateVersion )
266283 if err != nil {
267284 return nil , err
268285 }
269286
270- // Create cache key specifically for date-based resolution
271287 reg := npmrc .getRegistryByPackageName (pkgName )
272288 cacheKey := reg .Registry + pkgName + "@date=" + dateVersion
273289
274290 return withCache (cacheKey , time .Duration (config .NpmQueryCacheTTL )* time .Second , func () (* npm.PackageJSON , string , error ) {
275- // Get full package metadata (not version-specific)
276- regUrl := reg .Registry + pkgName
277- u , err := url .Parse (regUrl )
291+ metadata , _ , err := npmrc .fetchPackageMetadata (pkgName , dateVersion , false )
278292 if err != nil {
279293 return nil , "" , err
280294 }
281295
282- header := http.Header {}
283- if reg .Token != "" {
284- header .Set ("Authorization" , "Bearer " + reg .Token )
285- } else if reg .User != "" && reg .Password != "" {
286- header .Set ("Authorization" , "Basic " + base64 .StdEncoding .EncodeToString ([]byte (reg .User + ":" + reg .Password )))
287- }
288-
289- fetchClient , recycle := fetch .NewClient ("esmd/" + VERSION , 15 , false , nil )
290- defer recycle ()
291-
292- retryTimes := 0
293- RETRY:
294- res , err := fetchClient .Fetch (u , header )
295- if err != nil {
296- if retryTimes < 3 {
297- retryTimes ++
298- time .Sleep (time .Duration (retryTimes ) * 100 * time .Millisecond )
299- goto RETRY
300- }
301- return nil , "" , err
302- }
303- defer res .Body .Close ()
304-
305- if res .StatusCode == 404 || res .StatusCode == 401 {
306- return nil , "" , fmt .Errorf ("package '%s' not found" , pkgName )
307- }
308-
309- if res .StatusCode != 200 {
310- return nil , "" , fmt .Errorf ("npm registry returned %d for package '%s'" , res .StatusCode , pkgName )
311- }
312-
313- var metadata npm.PackageMetadata
314- err = json .NewDecoder (res .Body ).Decode (& metadata )
315- if err != nil {
316- return nil , "" , err
317- }
318-
319- if len (metadata .Versions ) == 0 {
320- return nil , "" , fmt .Errorf ("no versions found for package '%s'" , pkgName )
321- }
322-
323- // Resolve version using target time
324- resolvedVersion , err := npm .ResolveVersionByTime (& metadata , targetTime )
296+ resolvedVersion , err := npm .ResolveVersionByTime (metadata , targetTime )
325297 if err != nil {
326298 return nil , "" , fmt .Errorf ("date-based version resolution failed for %s@%s: %s" , pkgName , dateVersion , err .Error ())
327299 }
@@ -331,7 +303,6 @@ func (npmrc *NpmRC) getPackageInfoByDate(pkgName string, dateVersion string) (pa
331303 return nil , "" , fmt .Errorf ("resolved version %s of '%s' not found" , resolvedVersion , pkgName )
332304 }
333305
334- // Cache the result under the exact version for future lookups
335306 exactVersionCacheKey := reg .Registry + pkgName + "@" + raw .Version
336307 return raw .ToNpmPackage (), exactVersionCacheKey , nil
337308 })
0 commit comments