/ node_modules / semver / classes / semver.js
semver.js
  1  'use strict'
  2  
  3  const debug = require('../internal/debug')
  4  const { MAX_LENGTH, MAX_SAFE_INTEGER } = require('../internal/constants')
  5  const { safeRe: re, t } = require('../internal/re')
  6  
  7  const parseOptions = require('../internal/parse-options')
  8  const { compareIdentifiers } = require('../internal/identifiers')
  9  class SemVer {
 10    constructor (version, options) {
 11      options = parseOptions(options)
 12  
 13      if (version instanceof SemVer) {
 14        if (version.loose === !!options.loose &&
 15          version.includePrerelease === !!options.includePrerelease) {
 16          return version
 17        } else {
 18          version = version.version
 19        }
 20      } else if (typeof version !== 'string') {
 21        throw new TypeError(`Invalid version. Must be a string. Got type "${typeof version}".`)
 22      }
 23  
 24      if (version.length > MAX_LENGTH) {
 25        throw new TypeError(
 26          `version is longer than ${MAX_LENGTH} characters`
 27        )
 28      }
 29  
 30      debug('SemVer', version, options)
 31      this.options = options
 32      this.loose = !!options.loose
 33      // this isn't actually relevant for versions, but keep it so that we
 34      // don't run into trouble passing this.options around.
 35      this.includePrerelease = !!options.includePrerelease
 36  
 37      const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL])
 38  
 39      if (!m) {
 40        throw new TypeError(`Invalid Version: ${version}`)
 41      }
 42  
 43      this.raw = version
 44  
 45      // these are actually numbers
 46      this.major = +m[1]
 47      this.minor = +m[2]
 48      this.patch = +m[3]
 49  
 50      if (this.major > MAX_SAFE_INTEGER || this.major < 0) {
 51        throw new TypeError('Invalid major version')
 52      }
 53  
 54      if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) {
 55        throw new TypeError('Invalid minor version')
 56      }
 57  
 58      if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) {
 59        throw new TypeError('Invalid patch version')
 60      }
 61  
 62      // numberify any prerelease numeric ids
 63      if (!m[4]) {
 64        this.prerelease = []
 65      } else {
 66        this.prerelease = m[4].split('.').map((id) => {
 67          if (/^[0-9]+$/.test(id)) {
 68            const num = +id
 69            if (num >= 0 && num < MAX_SAFE_INTEGER) {
 70              return num
 71            }
 72          }
 73          return id
 74        })
 75      }
 76  
 77      this.build = m[5] ? m[5].split('.') : []
 78      this.format()
 79    }
 80  
 81    format () {
 82      this.version = `${this.major}.${this.minor}.${this.patch}`
 83      if (this.prerelease.length) {
 84        this.version += `-${this.prerelease.join('.')}`
 85      }
 86      return this.version
 87    }
 88  
 89    toString () {
 90      return this.version
 91    }
 92  
 93    compare (other) {
 94      debug('SemVer.compare', this.version, this.options, other)
 95      if (!(other instanceof SemVer)) {
 96        if (typeof other === 'string' && other === this.version) {
 97          return 0
 98        }
 99        other = new SemVer(other, this.options)
100      }
101  
102      if (other.version === this.version) {
103        return 0
104      }
105  
106      return this.compareMain(other) || this.comparePre(other)
107    }
108  
109    compareMain (other) {
110      if (!(other instanceof SemVer)) {
111        other = new SemVer(other, this.options)
112      }
113  
114      return (
115        compareIdentifiers(this.major, other.major) ||
116        compareIdentifiers(this.minor, other.minor) ||
117        compareIdentifiers(this.patch, other.patch)
118      )
119    }
120  
121    comparePre (other) {
122      if (!(other instanceof SemVer)) {
123        other = new SemVer(other, this.options)
124      }
125  
126      // NOT having a prerelease is > having one
127      if (this.prerelease.length && !other.prerelease.length) {
128        return -1
129      } else if (!this.prerelease.length && other.prerelease.length) {
130        return 1
131      } else if (!this.prerelease.length && !other.prerelease.length) {
132        return 0
133      }
134  
135      let i = 0
136      do {
137        const a = this.prerelease[i]
138        const b = other.prerelease[i]
139        debug('prerelease compare', i, a, b)
140        if (a === undefined && b === undefined) {
141          return 0
142        } else if (b === undefined) {
143          return 1
144        } else if (a === undefined) {
145          return -1
146        } else if (a === b) {
147          continue
148        } else {
149          return compareIdentifiers(a, b)
150        }
151      } while (++i)
152    }
153  
154    compareBuild (other) {
155      if (!(other instanceof SemVer)) {
156        other = new SemVer(other, this.options)
157      }
158  
159      let i = 0
160      do {
161        const a = this.build[i]
162        const b = other.build[i]
163        debug('build compare', i, a, b)
164        if (a === undefined && b === undefined) {
165          return 0
166        } else if (b === undefined) {
167          return 1
168        } else if (a === undefined) {
169          return -1
170        } else if (a === b) {
171          continue
172        } else {
173          return compareIdentifiers(a, b)
174        }
175      } while (++i)
176    }
177  
178    // preminor will bump the version up to the next minor release, and immediately
179    // down to pre-release. premajor and prepatch work the same way.
180    inc (release, identifier, identifierBase) {
181      if (release.startsWith('pre')) {
182        if (!identifier && identifierBase === false) {
183          throw new Error('invalid increment argument: identifier is empty')
184        }
185        // Avoid an invalid semver results
186        if (identifier) {
187          const match = `-${identifier}`.match(this.options.loose ? re[t.PRERELEASELOOSE] : re[t.PRERELEASE])
188          if (!match || match[1] !== identifier) {
189            throw new Error(`invalid identifier: ${identifier}`)
190          }
191        }
192      }
193  
194      switch (release) {
195        case 'premajor':
196          this.prerelease.length = 0
197          this.patch = 0
198          this.minor = 0
199          this.major++
200          this.inc('pre', identifier, identifierBase)
201          break
202        case 'preminor':
203          this.prerelease.length = 0
204          this.patch = 0
205          this.minor++
206          this.inc('pre', identifier, identifierBase)
207          break
208        case 'prepatch':
209          // If this is already a prerelease, it will bump to the next version
210          // drop any prereleases that might already exist, since they are not
211          // relevant at this point.
212          this.prerelease.length = 0
213          this.inc('patch', identifier, identifierBase)
214          this.inc('pre', identifier, identifierBase)
215          break
216        // If the input is a non-prerelease version, this acts the same as
217        // prepatch.
218        case 'prerelease':
219          if (this.prerelease.length === 0) {
220            this.inc('patch', identifier, identifierBase)
221          }
222          this.inc('pre', identifier, identifierBase)
223          break
224        case 'release':
225          if (this.prerelease.length === 0) {
226            throw new Error(`version ${this.raw} is not a prerelease`)
227          }
228          this.prerelease.length = 0
229          break
230  
231        case 'major':
232          // If this is a pre-major version, bump up to the same major version.
233          // Otherwise increment major.
234          // 1.0.0-5 bumps to 1.0.0
235          // 1.1.0 bumps to 2.0.0
236          if (
237            this.minor !== 0 ||
238            this.patch !== 0 ||
239            this.prerelease.length === 0
240          ) {
241            this.major++
242          }
243          this.minor = 0
244          this.patch = 0
245          this.prerelease = []
246          break
247        case 'minor':
248          // If this is a pre-minor version, bump up to the same minor version.
249          // Otherwise increment minor.
250          // 1.2.0-5 bumps to 1.2.0
251          // 1.2.1 bumps to 1.3.0
252          if (this.patch !== 0 || this.prerelease.length === 0) {
253            this.minor++
254          }
255          this.patch = 0
256          this.prerelease = []
257          break
258        case 'patch':
259          // If this is not a pre-release version, it will increment the patch.
260          // If it is a pre-release it will bump up to the same patch version.
261          // 1.2.0-5 patches to 1.2.0
262          // 1.2.0 patches to 1.2.1
263          if (this.prerelease.length === 0) {
264            this.patch++
265          }
266          this.prerelease = []
267          break
268        // This probably shouldn't be used publicly.
269        // 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction.
270        case 'pre': {
271          const base = Number(identifierBase) ? 1 : 0
272  
273          if (this.prerelease.length === 0) {
274            this.prerelease = [base]
275          } else {
276            let i = this.prerelease.length
277            while (--i >= 0) {
278              if (typeof this.prerelease[i] === 'number') {
279                this.prerelease[i]++
280                i = -2
281              }
282            }
283            if (i === -1) {
284              // didn't increment anything
285              if (identifier === this.prerelease.join('.') && identifierBase === false) {
286                throw new Error('invalid increment argument: identifier already exists')
287              }
288              this.prerelease.push(base)
289            }
290          }
291          if (identifier) {
292            // 1.2.0-beta.1 bumps to 1.2.0-beta.2,
293            // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
294            let prerelease = [identifier, base]
295            if (identifierBase === false) {
296              prerelease = [identifier]
297            }
298            if (compareIdentifiers(this.prerelease[0], identifier) === 0) {
299              if (isNaN(this.prerelease[1])) {
300                this.prerelease = prerelease
301              }
302            } else {
303              this.prerelease = prerelease
304            }
305          }
306          break
307        }
308        default:
309          throw new Error(`invalid increment argument: ${release}`)
310      }
311      this.raw = this.format()
312      if (this.build.length) {
313        this.raw += `+${this.build.join('.')}`
314      }
315      return this
316    }
317  }
318  
319  module.exports = SemVer