191 lines
3.7 KiB
JavaScript
191 lines
3.7 KiB
JavaScript
const errors = require('./errors')
|
|
|
|
const Version = module.exports = exports = 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
|
|
}
|
|
}
|
|
|
|
exports.parse = function parse (input, state = { position: 0, partial: 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 = []
|
|
|
|
while (components.length < 3) {
|
|
c = input[i]
|
|
|
|
if (components.length > 0) {
|
|
if (c === '.') c = input[++i]
|
|
else unexpected('expected \'.\'')
|
|
}
|
|
|
|
if (c === '0') {
|
|
components.push(0)
|
|
|
|
i++
|
|
} else if (c >= '1' && c <= '9') {
|
|
let j = 0
|
|
do c = input[i + ++j]
|
|
while (c >= '0' && c <= '9')
|
|
|
|
components.push(parseInt(input.substring(i, i + j)))
|
|
|
|
i += j
|
|
} else unexpected('expected /[0-9]/')
|
|
}
|
|
|
|
const prerelease = []
|
|
|
|
if (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 (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)
|
|
}
|