| const debug = require('../internal/debug') |
| const { MAX_LENGTH, MAX_SAFE_INTEGER } = require('../internal/constants') |
| const { re, t } = require('../internal/re') |
| |
| const { compareIdentifiers } = require('../internal/identifiers') |
| class SemVer { |
| constructor (version, options) { |
| if (!options || typeof options !== 'object') { |
| options = { |
| loose: !!options, |
| includePrerelease: false |
| } |
| } |
| if (version instanceof SemVer) { |
| if (version.loose === !!options.loose && |
| version.includePrerelease === !!options.includePrerelease) { |
| return version |
| } else { |
| version = version.version |
| } |
| } else if (typeof version !== 'string') { |
| throw new TypeError(`Invalid Version: ${version}`) |
| } |
| |
| if (version.length > MAX_LENGTH) { |
| throw new TypeError( |
| `version is longer than ${MAX_LENGTH} characters` |
| ) |
| } |
| |
| debug('SemVer', version, options) |
| this.options = options |
| this.loose = !!options.loose |
| // this isn't actually relevant for versions, but keep it so that we |
| // don't run into trouble passing this.options around. |
| this.includePrerelease = !!options.includePrerelease |
| |
| const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]) |
| |
| if (!m) { |
| throw new TypeError(`Invalid Version: ${version}`) |
| } |
| |
| this.raw = version |
| |
| // these are actually numbers |
| this.major = +m[1] |
| this.minor = +m[2] |
| this.patch = +m[3] |
| |
| if (this.major > MAX_SAFE_INTEGER || this.major < 0) { |
| throw new TypeError('Invalid major version') |
| } |
| |
| if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { |
| throw new TypeError('Invalid minor version') |
| } |
| |
| if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { |
| throw new TypeError('Invalid patch version') |
| } |
| |
| // numberify any prerelease numeric ids |
| if (!m[4]) { |
| this.prerelease = [] |
| } else { |
| this.prerelease = m[4].split('.').map((id) => { |
| if (/^[0-9]+$/.test(id)) { |
| const num = +id |
| if (num >= 0 && num < MAX_SAFE_INTEGER) { |
| return num |
| } |
| } |
| return id |
| }) |
| } |
| |
| this.build = m[5] ? m[5].split('.') : [] |
| this.format() |
| } |
| |
| format () { |
| this.version = `${this.major}.${this.minor}.${this.patch}` |
| if (this.prerelease.length) { |
| this.version += `-${this.prerelease.join('.')}` |
| } |
| return this.version |
| } |
| |
| toString () { |
| return this.version |
| } |
| |
| compare (other) { |
| debug('SemVer.compare', this.version, this.options, other) |
| if (!(other instanceof SemVer)) { |
| if (typeof other === 'string' && other === this.version) { |
| return 0 |
| } |
| other = new SemVer(other, this.options) |
| } |
| |
| if (other.version === this.version) { |
| return 0 |
| } |
| |
| return this.compareMain(other) || this.comparePre(other) |
| } |
| |
| compareMain (other) { |
| if (!(other instanceof SemVer)) { |
| other = new SemVer(other, this.options) |
| } |
| |
| return ( |
| compareIdentifiers(this.major, other.major) || |
| compareIdentifiers(this.minor, other.minor) || |
| compareIdentifiers(this.patch, other.patch) |
| ) |
| } |
| |
| comparePre (other) { |
| if (!(other instanceof SemVer)) { |
| other = new SemVer(other, this.options) |
| } |
| |
| // NOT having a prerelease is > having one |
| if (this.prerelease.length && !other.prerelease.length) { |
| return -1 |
| } else if (!this.prerelease.length && other.prerelease.length) { |
| return 1 |
| } else if (!this.prerelease.length && !other.prerelease.length) { |
| return 0 |
| } |
| |
| let i = 0 |
| do { |
| const a = this.prerelease[i] |
| const b = other.prerelease[i] |
| debug('prerelease compare', i, a, b) |
| if (a === undefined && b === undefined) { |
| return 0 |
| } else if (b === undefined) { |
| return 1 |
| } else if (a === undefined) { |
| return -1 |
| } else if (a === b) { |
| continue |
| } else { |
| return compareIdentifiers(a, b) |
| } |
| } while (++i) |
| } |
| |
| compareBuild (other) { |
| if (!(other instanceof SemVer)) { |
| other = new SemVer(other, this.options) |
| } |
| |
| let i = 0 |
| do { |
| const a = this.build[i] |
| const b = other.build[i] |
| debug('prerelease compare', i, a, b) |
| if (a === undefined && b === undefined) { |
| return 0 |
| } else if (b === undefined) { |
| return 1 |
| } else if (a === undefined) { |
| return -1 |
| } else if (a === b) { |
| continue |
| } else { |
| return compareIdentifiers(a, b) |
| } |
| } while (++i) |
| } |
| |
| // preminor will bump the version up to the next minor release, and immediately |
| // down to pre-release. premajor and prepatch work the same way. |
| inc (release, identifier) { |
| switch (release) { |
| case 'premajor': |
| this.prerelease.length = 0 |
| this.patch = 0 |
| this.minor = 0 |
| this.major++ |
| this.inc('pre', identifier) |
| break |
| case 'preminor': |
| this.prerelease.length = 0 |
| this.patch = 0 |
| this.minor++ |
| this.inc('pre', identifier) |
| break |
| case 'prepatch': |
| // If this is already a prerelease, it will bump to the next version |
| // drop any prereleases that might already exist, since they are not |
| // relevant at this point. |
| this.prerelease.length = 0 |
| this.inc('patch', identifier) |
| this.inc('pre', identifier) |
| break |
| // If the input is a non-prerelease version, this acts the same as |
| // prepatch. |
| case 'prerelease': |
| if (this.prerelease.length === 0) { |
| this.inc('patch', identifier) |
| } |
| this.inc('pre', identifier) |
| break |
| |
| case 'major': |
| // If this is a pre-major version, bump up to the same major version. |
| // Otherwise increment major. |
| // 1.0.0-5 bumps to 1.0.0 |
| // 1.1.0 bumps to 2.0.0 |
| if ( |
| this.minor !== 0 || |
| this.patch !== 0 || |
| this.prerelease.length === 0 |
| ) { |
| this.major++ |
| } |
| this.minor = 0 |
| this.patch = 0 |
| this.prerelease = [] |
| break |
| case 'minor': |
| // If this is a pre-minor version, bump up to the same minor version. |
| // Otherwise increment minor. |
| // 1.2.0-5 bumps to 1.2.0 |
| // 1.2.1 bumps to 1.3.0 |
| if (this.patch !== 0 || this.prerelease.length === 0) { |
| this.minor++ |
| } |
| this.patch = 0 |
| this.prerelease = [] |
| break |
| case 'patch': |
| // If this is not a pre-release version, it will increment the patch. |
| // If it is a pre-release it will bump up to the same patch version. |
| // 1.2.0-5 patches to 1.2.0 |
| // 1.2.0 patches to 1.2.1 |
| if (this.prerelease.length === 0) { |
| this.patch++ |
| } |
| this.prerelease = [] |
| break |
| // This probably shouldn't be used publicly. |
| // 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction. |
| case 'pre': |
| if (this.prerelease.length === 0) { |
| this.prerelease = [0] |
| } else { |
| let i = this.prerelease.length |
| while (--i >= 0) { |
| if (typeof this.prerelease[i] === 'number') { |
| this.prerelease[i]++ |
| i = -2 |
| } |
| } |
| if (i === -1) { |
| // didn't increment anything |
| this.prerelease.push(0) |
| } |
| } |
| if (identifier) { |
| // 1.2.0-beta.1 bumps to 1.2.0-beta.2, |
| // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 |
| if (this.prerelease[0] === identifier) { |
| if (isNaN(this.prerelease[1])) { |
| this.prerelease = [identifier, 0] |
| } |
| } else { |
| this.prerelease = [identifier, 0] |
| } |
| } |
| break |
| |
| default: |
| throw new Error(`invalid increment argument: ${release}`) |
| } |
| this.format() |
| this.raw = this.version |
| return this |
| } |
| } |
| |
| module.exports = SemVer |