comparator.js 3.5 KB

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