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 parseOptions = require('../internal/parse-options')
6 const { compareIdentifiers } = require('../internal/identifiers')
8 constructor (version, options) {
9 options = parseOptions(options)
11 if (version instanceof SemVer) {
12 if (version.loose === !!options.loose &&
13 version.includePrerelease === !!options.includePrerelease) {
16 version = version.version
18 } else if (typeof version !== 'string') {
19 throw new TypeError(`Invalid Version: ${version}`)
22 if (version.length > MAX_LENGTH) {
24 `version is longer than ${MAX_LENGTH} characters`
28 debug('SemVer', version, options)
29 this.options = options
30 this.loose = !!options.loose
31 // this isn't actually relevant for versions, but keep it so that we
32 // don't run into trouble passing this.options around.
33 this.includePrerelease = !!options.includePrerelease
35 const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL])
38 throw new TypeError(`Invalid Version: ${version}`)
43 // these are actually numbers
48 if (this.major > MAX_SAFE_INTEGER || this.major < 0) {
49 throw new TypeError('Invalid major version')
52 if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) {
53 throw new TypeError('Invalid minor version')
56 if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) {
57 throw new TypeError('Invalid patch version')
60 // numberify any prerelease numeric ids
64 this.prerelease = m[4].split('.').map((id) => {
65 if (/^[0-9]+$/.test(id)) {
67 if (num >= 0 && num < MAX_SAFE_INTEGER) {
75 this.build = m[5] ? m[5].split('.') : []
80 this.version = `${this.major}.${this.minor}.${this.patch}`
81 if (this.prerelease.length) {
82 this.version += `-${this.prerelease.join('.')}`
92 debug('SemVer.compare', this.version, this.options, other)
93 if (!(other instanceof SemVer)) {
94 if (typeof other === 'string' && other === this.version) {
97 other = new SemVer(other, this.options)
100 if (other.version === this.version) {
104 return this.compareMain(other) || this.comparePre(other)
107 compareMain (other) {
108 if (!(other instanceof SemVer)) {
109 other = new SemVer(other, this.options)
113 compareIdentifiers(this.major, other.major) ||
114 compareIdentifiers(this.minor, other.minor) ||
115 compareIdentifiers(this.patch, other.patch)
120 if (!(other instanceof SemVer)) {
121 other = new SemVer(other, this.options)
124 // NOT having a prerelease is > having one
125 if (this.prerelease.length && !other.prerelease.length) {
127 } else if (!this.prerelease.length && other.prerelease.length) {
129 } else if (!this.prerelease.length && !other.prerelease.length) {
135 const a = this.prerelease[i]
136 const b = other.prerelease[i]
137 debug('prerelease compare', i, a, b)
138 if (a === undefined && b === undefined) {
140 } else if (b === undefined) {
142 } else if (a === undefined) {
144 } else if (a === b) {
147 return compareIdentifiers(a, b)
152 compareBuild (other) {
153 if (!(other instanceof SemVer)) {
154 other = new SemVer(other, this.options)
159 const a = this.build[i]
160 const b = other.build[i]
161 debug('prerelease compare', i, a, b)
162 if (a === undefined && b === undefined) {
164 } else if (b === undefined) {
166 } else if (a === undefined) {
168 } else if (a === b) {
171 return compareIdentifiers(a, b)
176 // preminor will bump the version up to the next minor release, and immediately
177 // down to pre-release. premajor and prepatch work the same way.
178 inc (release, identifier) {
181 this.prerelease.length = 0
185 this.inc('pre', identifier)
188 this.prerelease.length = 0
191 this.inc('pre', identifier)
194 // If this is already a prerelease, it will bump to the next version
195 // drop any prereleases that might already exist, since they are not
196 // relevant at this point.
197 this.prerelease.length = 0
198 this.inc('patch', identifier)
199 this.inc('pre', identifier)
201 // If the input is a non-prerelease version, this acts the same as
204 if (this.prerelease.length === 0) {
205 this.inc('patch', identifier)
207 this.inc('pre', identifier)
211 // If this is a pre-major version, bump up to the same major version.
212 // Otherwise increment major.
213 // 1.0.0-5 bumps to 1.0.0
214 // 1.1.0 bumps to 2.0.0
218 this.prerelease.length === 0
227 // If this is a pre-minor version, bump up to the same minor version.
228 // Otherwise increment minor.
229 // 1.2.0-5 bumps to 1.2.0
230 // 1.2.1 bumps to 1.3.0
231 if (this.patch !== 0 || this.prerelease.length === 0) {
238 // If this is not a pre-release version, it will increment the patch.
239 // If it is a pre-release it will bump up to the same patch version.
240 // 1.2.0-5 patches to 1.2.0
241 // 1.2.0 patches to 1.2.1
242 if (this.prerelease.length === 0) {
247 // This probably shouldn't be used publicly.
248 // 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction.
250 if (this.prerelease.length === 0) {
251 this.prerelease = [0]
253 let i = this.prerelease.length
255 if (typeof this.prerelease[i] === 'number') {
261 // didn't increment anything
262 this.prerelease.push(0)
266 // 1.2.0-beta.1 bumps to 1.2.0-beta.2,
267 // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
268 if (this.prerelease[0] === identifier) {
269 if (isNaN(this.prerelease[1])) {
270 this.prerelease = [identifier, 0]
273 this.prerelease = [identifier, 0]
279 throw new Error(`invalid increment argument: ${release}`)
282 this.raw = this.version
287 module.exports = SemVer