comparator.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. const ANY = Symbol('SemVer ANY')
  2. // hoisted class for cyclic dependency
  3. class Comparator {
  4. static get ANY () {
  5. return ANY
  6. }
  7. constructor (comp, options) {
  8. options = parseOptions(options)
  9. if (comp instanceof Comparator) {
  10. if (comp.loose === !!options.loose) {
  11. return comp
  12. } else {
  13. comp = comp.value
  14. }
  15. }
  16. debug('comparator', comp, options)
  17. this.options = options
  18. this.loose = !!options.loose
  19. this.parse(comp)
  20. if (this.semver === ANY) {
  21. this.value = ''
  22. } else {
  23. this.value = this.operator + this.semver.version
  24. }
  25. debug('comp', this)
  26. }
  27. parse (comp) {
  28. const r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]
  29. const m = comp.match(r)
  30. if (!m) {
  31. throw new TypeError(`Invalid comparator: ${comp}`)
  32. }
  33. this.operator = m[1] !== undefined ? m[1] : ''
  34. if (this.operator === '=') {
  35. this.operator = ''
  36. }
  37. // if it literally is just '>' or '' then allow anything.
  38. if (!m[2]) {
  39. this.semver = ANY
  40. } else {
  41. this.semver = new SemVer(m[2], this.options.loose)
  42. }
  43. }
  44. toString () {
  45. return this.value
  46. }
  47. test (version) {
  48. debug('Comparator.test', version, this.options.loose)
  49. if (this.semver === ANY || version === ANY) {
  50. return true
  51. }
  52. if (typeof version === 'string') {
  53. try {
  54. version = new SemVer(version, this.options)
  55. } catch (er) {
  56. return false
  57. }
  58. }
  59. return cmp(version, this.operator, this.semver, this.options)
  60. }
  61. intersects (comp, options) {
  62. if (!(comp instanceof Comparator)) {
  63. throw new TypeError('a Comparator is required')
  64. }
  65. if (this.operator === '') {
  66. if (this.value === '') {
  67. return true
  68. }
  69. return new Range(comp.value, options).test(this.value)
  70. } else if (comp.operator === '') {
  71. if (comp.value === '') {
  72. return true
  73. }
  74. return new Range(this.value, options).test(comp.semver)
  75. }
  76. options = parseOptions(options)
  77. // Special cases where nothing can possibly be lower
  78. if (options.includePrerelease &&
  79. (this.value === '<0.0.0-0' || comp.value === '<0.0.0-0')) {
  80. return false
  81. }
  82. if (!options.includePrerelease &&
  83. (this.value.startsWith('<0.0.0') || comp.value.startsWith('<0.0.0'))) {
  84. return false
  85. }
  86. // Same direction increasing (> or >=)
  87. if (this.operator.startsWith('>') && comp.operator.startsWith('>')) {
  88. return true
  89. }
  90. // Same direction decreasing (< or <=)
  91. if (this.operator.startsWith('<') && comp.operator.startsWith('<')) {
  92. return true
  93. }
  94. // same SemVer and both sides are inclusive (<= or >=)
  95. if (
  96. (this.semver.version === comp.semver.version) &&
  97. this.operator.includes('=') && comp.operator.includes('=')) {
  98. return true
  99. }
  100. // opposite directions less than
  101. if (cmp(this.semver, '<', comp.semver, options) &&
  102. this.operator.startsWith('>') && comp.operator.startsWith('<')) {
  103. return true
  104. }
  105. // opposite directions greater than
  106. if (cmp(this.semver, '>', comp.semver, options) &&
  107. this.operator.startsWith('<') && comp.operator.startsWith('>')) {
  108. return true
  109. }
  110. return false
  111. }
  112. }
  113. module.exports = Comparator
  114. const parseOptions = require('../internal/parse-options')
  115. const { re, t } = require('../internal/re')
  116. const cmp = require('../functions/cmp')
  117. const debug = require('../internal/debug')
  118. const SemVer = require('./semver')
  119. const Range = require('./range')