const errors = require('./errors') class Version { constructor(major, minor, patch, opts = {}) { const { prerelease = [], build = [] } = opts this.major = major this.minor = minor this.patch = patch this.prerelease = prerelease this.build = build } compare(version) { return exports.compare(this, version) } toString() { let result = `${this.major}.${this.minor}.${this.patch}` if (this.prerelease.length) { result += '-' + this.prerelease.join('.') } if (this.build.length) { result += '+' + this.build.join('.') } return result } } module.exports = exports = Version exports.parse = function parse( input, state = { position: 0, partial: false, range: false } ) { let i = state.position let c const unexpected = (expected) => { let msg if (i >= input.length) { msg = `Unexpected end of input in '${input}'` } else { msg = `Unexpected token '${input[i]}' in '${input}' at position ${i}` } if (expected) msg += `, ${expected}` throw errors.INVALID_VERSION(msg, unexpected) } const components = [0, 0, 0] let k = 0 while (k < 3) { c = input[i] if (k > 0) { if (c === '.') c = input[++i] else if (state.range) break else unexpected("expected '.'") } if (c === '0') { i++ k++ } else if (c >= '1' && c <= '9') { let j = 0 do c = input[i + ++j] while (c >= '0' && c <= '9') components[k++] = parseInt(input.substring(i, i + j)) i += j } else unexpected('expected /[0-9]/') } const prerelease = [] if (k === 3 && input[i] === '-') { i++ while (true) { c = input[i] let tag = '' let j = 0 while (c >= '0' && c <= '9') c = input[i + ++j] let isNumeric = false if (j) { tag += input.substring(i, i + j) c = input[(i += j)] isNumeric = tag[0] !== '0' || tag.length === 1 } j = 0 while ( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c === '-' ) c = input[i + ++j] if (j) { tag += input.substring(i, i + j) c = input[(i += j)] } else if (!isNumeric) unexpected('expected /[a-zA-Z-]/') prerelease.push(tag) if (c === '.') c = input[++i] else break } } const build = [] if (k === 3 && input[i] === '+') { i++ while (true) { c = input[i] let tag = '' let j = 0 while ( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c === '-' ) c = input[i + ++j] if (j) { tag += input.substring(i, i + j) c = input[(i += j)] } else unexpected('expected /[0-9a-zA-Z-]/') build.push(tag) if (c === '.') c = input[++i] else break } } if (i < input.length && state.partial === false) { unexpected('expected end of input') } state.position = i return new Version(...components, { prerelease, build }) } const integer = /^[0-9]+$/ exports.compare = function compare(a, b) { if (a.major > b.major) return 1 if (a.major < b.major) return -1 if (a.minor > b.minor) return 1 if (a.minor < b.minor) return -1 if (a.patch > b.patch) return 1 if (a.patch < b.patch) return -1 if (a.prerelease.length === 0) return b.prerelease.length === 0 ? 0 : 1 if (b.prerelease.length === 0) return -1 let i = 0 do { let x = a.prerelease[i] let y = b.prerelease[i] if (x === undefined) return y === undefined ? 0 : -1 if (y === undefined) return 1 if (x === y) continue const xInt = integer.test(x) const yInt = integer.test(y) if (xInt && yInt) { x = +x y = +y } else { if (xInt) return -1 if (yInt) return 1 } return x > y ? 1 : -1 } while (++i) }