comparator.js 3.5 KB

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