1 const debug = require('../internal/debug')
2 const { MAX_LENGTH, MAX_SAFE_INTEGER } = require('../internal/constants')
3 const { re, t } = require('../internal/re')
5 const { compareIdentifiers } = require('../internal/identifiers')
7 constructor (version, options) {
8 if (!options || typeof options !== 'object') {
11 includePrerelease: false
14 if (version instanceof SemVer) {
15 if (version.loose === !!options.loose &&
16 version.includePrerelease === !!options.includePrerelease) {
19 version = version.version
21 } else if (typeof version !== 'string') {
22 throw new TypeError(`Invalid Version: ${version}`)
25 if (version.length > MAX_LENGTH) {
27 `version is longer than ${MAX_LENGTH} characters`
31 debug('SemVer', version, options)
32 this.options = options
33 this.loose = !!options.loose
34 // this isn't actually relevant for versions, but keep it so that we
35 // don't run into trouble passing this.options around.
36 this.includePrerelease = !!options.includePrerelease
38 const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL])
41 throw new TypeError(`Invalid Version: ${version}`)
46 // these are actually numbers
51 if (this.major > MAX_SAFE_INTEGER || this.major < 0) {
52 throw new TypeError('Invalid major version')
55 if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) {
56 throw new TypeError('Invalid minor version')
59 if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) {
60 throw new TypeError('Invalid patch version')
63 // numberify any prerelease numeric ids
67 this.prerelease = m[4].split('.').map((id) => {
68 if (/^[0-9]+$/.test(id)) {
70 if (num >= 0 && num < MAX_SAFE_INTEGER) {
78 this.build = m[5] ? m[5].split('.') : []
83 this.version = `${this.major}.${this.minor}.${this.patch}`
84 if (this.prerelease.length) {
85 this.version += `-${this.prerelease.join('.')}`
95 debug('SemVer.compare', this.version, this.options, other)
96 if (!(other instanceof SemVer)) {
97 if (typeof other === 'string' && other === this.version) {
100 other = new SemVer(other, this.options)
103 if (other.version === this.version) {
107 return this.compareMain(other) || this.comparePre(other)
110 compareMain (other) {
111 if (!(other instanceof SemVer)) {
112 other = new SemVer(other, this.options)
116 compareIdentifiers(this.major, other.major) ||
117 compareIdentifiers(this.minor, other.minor) ||
118 compareIdentifiers(this.patch, other.patch)
123 if (!(other instanceof SemVer)) {
124 other = new SemVer(other, this.options)
127 // NOT having a prerelease is > having one
128 if (this.prerelease.length && !other.prerelease.length) {
130 } else if (!this.prerelease.length && other.prerelease.length) {
132 } else if (!this.prerelease.length && !other.prerelease.length) {
138 const a = this.prerelease[i]
139 const b = other.prerelease[i]
140 debug('prerelease compare', i, a, b)
141 if (a === undefined && b === undefined) {
143 } else if (b === undefined) {
145 } else if (a === undefined) {
147 } else if (a === b) {
150 return compareIdentifiers(a, b)
155 compareBuild (other) {
156 if (!(other instanceof SemVer)) {
157 other = new SemVer(other, this.options)
162 const a = this.build[i]
163 const b = other.build[i]
164 debug('prerelease compare', i, a, b)
165 if (a === undefined && b === undefined) {
167 } else if (b === undefined) {
169 } else if (a === undefined) {
171 } else if (a === b) {
174 return compareIdentifiers(a, b)
179 // preminor will bump the version up to the next minor release, and immediately
180 // down to pre-release. premajor and prepatch work the same way.
181 inc (release, identifier) {
184 this.prerelease.length = 0
188 this.inc('pre', identifier)
191 this.prerelease.length = 0
194 this.inc('pre', identifier)
197 // If this is already a prerelease, it will bump to the next version
198 // drop any prereleases that might already exist, since they are not
199 // relevant at this point.
200 this.prerelease.length = 0
201 this.inc('patch', identifier)
202 this.inc('pre', identifier)
204 // If the input is a non-prerelease version, this acts the same as
207 if (this.prerelease.length === 0) {
208 this.inc('patch', identifier)
210 this.inc('pre', identifier)
214 // If this is a pre-major version, bump up to the same major version.
215 // Otherwise increment major.
216 // 1.0.0-5 bumps to 1.0.0
217 // 1.1.0 bumps to 2.0.0
221 this.prerelease.length === 0
230 // If this is a pre-minor version, bump up to the same minor version.
231 // Otherwise increment minor.
232 // 1.2.0-5 bumps to 1.2.0
233 // 1.2.1 bumps to 1.3.0
234 if (this.patch !== 0 || this.prerelease.length === 0) {
241 // If this is not a pre-release version, it will increment the patch.
242 // If it is a pre-release it will bump up to the same patch version.
243 // 1.2.0-5 patches to 1.2.0
244 // 1.2.0 patches to 1.2.1
245 if (this.prerelease.length === 0) {
250 // This probably shouldn't be used publicly.
251 // 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction.
253 if (this.prerelease.length === 0) {
254 this.prerelease = [0]
256 let i = this.prerelease.length
258 if (typeof this.prerelease[i] === 'number') {
264 // didn't increment anything
265 this.prerelease.push(0)
269 // 1.2.0-beta.1 bumps to 1.2.0-beta.2,
270 // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
271 if (this.prerelease[0] === identifier) {
272 if (isNaN(this.prerelease[1])) {
273 this.prerelease = [identifier, 0]
276 this.prerelease = [identifier, 0]
282 throw new Error(`invalid increment argument: ${release}`)
285 this.raw = this.version
290 module.exports = SemVer