index.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. 'use strict'
  2. const figgyPudding = require('figgy-pudding')
  3. const npa = require('npm-package-arg')
  4. const semver = require('semver')
  5. const PickerOpts = figgyPudding({
  6. defaultTag: { default: 'latest' },
  7. enjoyBy: {},
  8. includeDeprecated: { default: false }
  9. })
  10. module.exports = pickManifest
  11. function pickManifest (packument, wanted, opts) {
  12. opts = PickerOpts(opts)
  13. const time = opts.enjoyBy && packument.time && +(new Date(opts.enjoyBy))
  14. const spec = npa.resolve(packument.name, wanted)
  15. const type = spec.type
  16. if (type === 'version' || type === 'range') {
  17. wanted = semver.clean(wanted, true) || wanted
  18. }
  19. const distTags = packument['dist-tags'] || {}
  20. const versions = Object.keys(packument.versions || {}).filter(v => {
  21. return semver.valid(v, true)
  22. })
  23. const policyRestrictions = packument.policyRestrictions
  24. const restrictedVersions = policyRestrictions
  25. ? Object.keys(policyRestrictions.versions) : []
  26. function enjoyableBy (v) {
  27. return !time || (
  28. packument.time[v] && time >= +(new Date(packument.time[v]))
  29. )
  30. }
  31. let err
  32. if (!versions.length && !restrictedVersions.length) {
  33. err = new Error(`No valid versions available for ${packument.name}`)
  34. err.code = 'ENOVERSIONS'
  35. err.name = packument.name
  36. err.type = type
  37. err.wanted = wanted
  38. throw err
  39. }
  40. let target
  41. if (type === 'tag' && enjoyableBy(distTags[wanted])) {
  42. target = distTags[wanted]
  43. } else if (type === 'version') {
  44. target = wanted
  45. } else if (type !== 'range' && enjoyableBy(distTags[wanted])) {
  46. throw new Error('Only tag, version, and range are supported')
  47. }
  48. const tagVersion = distTags[opts.defaultTag]
  49. if (
  50. !target &&
  51. tagVersion &&
  52. packument.versions[tagVersion] &&
  53. enjoyableBy(tagVersion) &&
  54. semver.satisfies(tagVersion, wanted, true)
  55. ) {
  56. target = tagVersion
  57. }
  58. if (!target && !opts.includeDeprecated) {
  59. const undeprecated = versions.filter(v => !packument.versions[v].deprecated && enjoyableBy(v)
  60. )
  61. target = semver.maxSatisfying(undeprecated, wanted, true)
  62. }
  63. if (!target) {
  64. const stillFresh = versions.filter(enjoyableBy)
  65. target = semver.maxSatisfying(stillFresh, wanted, true)
  66. }
  67. if (!target && wanted === '*' && enjoyableBy(tagVersion)) {
  68. // This specific corner is meant for the case where
  69. // someone is using `*` as a selector, but all versions
  70. // are pre-releases, which don't match ranges at all.
  71. target = tagVersion
  72. }
  73. if (
  74. !target &&
  75. time &&
  76. type === 'tag' &&
  77. distTags[wanted] &&
  78. !enjoyableBy(distTags[wanted])
  79. ) {
  80. const stillFresh = versions.filter(v =>
  81. enjoyableBy(v) && semver.lte(v, distTags[wanted], true)
  82. ).sort(semver.rcompare)
  83. target = stillFresh[0]
  84. }
  85. if (!target && restrictedVersions) {
  86. target = semver.maxSatisfying(restrictedVersions, wanted, true)
  87. }
  88. const manifest = (
  89. target &&
  90. packument.versions[target]
  91. )
  92. if (!manifest) {
  93. // Check if target is forbidden
  94. const isForbidden = target && policyRestrictions && policyRestrictions.versions[target]
  95. const pckg = `${packument.name}@${wanted}${
  96. opts.enjoyBy
  97. ? ` with an Enjoy By date of ${
  98. new Date(opts.enjoyBy).toLocaleString()
  99. }. Maybe try a different date?`
  100. : ''
  101. }`
  102. if (isForbidden) {
  103. err = new Error(`Could not download ${pckg} due to policy violations.\n${policyRestrictions.message}\n`)
  104. err.code = 'E403'
  105. } else {
  106. err = new Error(`No matching version found for ${pckg}.`)
  107. err.code = 'ETARGET'
  108. }
  109. err.name = packument.name
  110. err.type = type
  111. err.wanted = wanted
  112. err.versions = versions
  113. err.distTags = distTags
  114. err.defaultTag = opts.defaultTag
  115. throw err
  116. } else {
  117. return manifest
  118. }
  119. }