Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
isSemVer - semantic version comparison for JavaScript
/*!
* isSemVer - v0.1 - 9/05/2010
* http://benalman.com/
* http://semver.org/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
// Compare one or more semantic version numbers to a reference version number.
//
// Returns true if all version criteria are met. Comparison operator defaults
// to == if omitted. Valid comparisons are == != < <= > >=.
//
// isSemVer( my.version, '1.2.3' ) // == is assumed if comparison op excluded
// isSemVer( my.version, '>= 1.2.3', '!= 1.2.4alpha' )
// isSemVer( my.version, '< 2.0', '!= 1.2.3', '!= 1.2.4alpha' )
//
var isSemVer = (function(){
// This regexp matches an optional comparison operator, followed by optional
// whitespace and then a valid semantic version per http://semver.org/.
var re = /^(<|>|[=!<>]=)?\s*(\d+(?:\.\d+){0,2})([a-z][a-z0-9\-]*)?$/i;
// Returns a value that can be used in version comparison. Because a semantic
// version is comprised of 1 to 3 dot-separated numbers followed by an
// optional lexicographically-sorted alphanumeric suffix, it is easiest to
// convert the entire value into a single string that can be compared in a
// lexicographic fashion. Each dot-separated number is first left-padded by
// zeroes such that 1.2.3 -> 000000010000000200000003. This allows possible
// major / minor / patch versions between 0-99999999. The optional suffix is
// then appended, and if one was not provided, ~ will be used because it will
// always compare > than any suffix.
//
// If the include_cmp flag is set, the result value will be prepended with
// the specified comparison operator (or == if the operator was omitted).
function get_val( str, include_cmp ) {
// matches[1] = optional comparison operator.
// matches[2] = dot-separated major / minor / patch version.
// matches[3] = alphanumeric "special version" suffix.
var matches = ( str + '' ).match( re );
return matches
// Include matched comparison operator, defaulting to == if necessary.
? ( include_cmp ? ( matches[1] || '==' ) : '' )
// Since this string is going to be evaled, wrap it in quotes.
+ '"'
// In case version passed is like 1 or 1.0, right-pad it with extra .0
+ ( matches[2] + '.0.0' )
// Remove any unnecessary trailing .0
.match( /\d+(?:\.\d+){0,2}/ )[0]
// Replace each dot-separated number with a 0-padded value.
.replace( /(?:^|\.)(\d+)/g, function(a,b){
return Array( 9 - b.length ).join(0) + b;
})
// Append suffix or ~ if suffix was omitted.
+ ( matches[3] || '~' )
// Since this string is going to be evaled, wrap it in quotes.
+ '"'
// Handle an invalid semantic version / comparison.
: ( include_cmp ? '==0' : 1 );
};
return function( base_ver ) {
// Get the comparison value for the base version.
base_ver = get_val( base_ver );
// Iterate over all additional function arguments.
for ( var arg, i = 1; arg = arguments[ i++ ]; ) {
// If any comparison fails, exit immediately with a false value.
if ( !(new Function( 'return ' + base_ver + get_val( arg, 1 ) ))() ) {
return false;
}
}
// All comparisons passed, return true!
return true;
};
})();
/*!
* isSemVer - v0.1 - 9/05/2010
* http://benalman.com/
* http://semver.org/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
var isSemVer=(function(){var a=/^(<|>|[=!<>]=)?\s*(\d+(?:\.\d+){0,2})([a-z][a-z0-9\-]*)?$/i;function b(e,c){var d=(e+"").match(a);return d?(c?(d[1]||"=="):"")+'"'+(d[2]+".0.0").match(/\d+(?:\.\d+){0,2}/)[0].replace(/(?:^|\.)(\d+)/g,function(g,f){return Array(9-f.length).join(0)+f})+(d[3]||"~")+'"':(c?"==0":1)}return function(e){e=b(e);for(var c,d=1;c=arguments[d++];){if(!(new Function("return "+e+b(c,1)))()){return false}}return true}})();
@cowboy

This comment has been minimized.

Copy link
Owner Author

commented Sep 5, 2010

Some basic unit tests:

[
  [ true, ['1','1'] ],
  [ true, ['1','1.0'] ],
  [ true, ['1','1.0.0'] ],
  [ true, ['1.0','1'] ],
  [ true, ['1.0.0','1'] ],
  [ true, ['1.0000','1.0.0000'] ],
  [ true, ['0001.0000.0000','1'] ],
  [ true, ['1','== 1.0.0'] ],
  [ true, ['1','!= 1.0.1'] ],
  [ true, ['1','!= 1.1.0'] ],
  [ true, ['1','!= 0.1.0'] ],
  [ true, ['1','!= 0.1.0alpha'] ],
  [ true, ['1','!= 0.1.0beta'] ],
  [ true, ['1','!= 0.1.0rc1'] ],
  [ true, ['1','> 1alpha'] ],
  [ true, ['1','> 1beta'] ],
  [ true, ['1','> 1rc1'] ],
  [ true, ['1','> 1.0alpha'] ],
  [ true, ['1alpha','>= 1.0alpha'] ],
  [ true, ['1beta','> 1.0alpha'] ],
  [ true, ['1rc1','> 1.0beta'] ],
  [ true, ['1','> 1.0beta'] ],
  [ true, ['1','> 1.0rc1'] ],
  [ true, ['1.10.9','>= 1','<= 2'] ],
  [ true, ['1.10.9','> 1.9.9','> 1.10.9pre'] ],
  [ false, ['1','> 1'] ],
  [ false, ['1','< 1.0'] ],
  [ false, ['1','!= 1.0.0'] ],
  [ false, ['1','1alpha'] ],
  [ false, ['1','== 1beta'] ],
  [ false, ['1','== 1rc1'] ],
  [ false, ['1alpha','< 1.0alpha'] ],
  [ false, ['1alpha','> 1.0alpha'] ],
  [ false, ['1alpha','!= 1.0alpha'] ],
  [ false, ['1beta','< 1.0alpha'] ],
  [ false, ['1.10.9','< 1'] ],
  [ false, ['1.10.9','> 1.9.9','< 1.10.9pre'] ]
].every(function(v){
  return v[0] === isVersion.apply(null, v[1]);
})
@Marak

This comment has been minimized.

Copy link

commented Jan 10, 2011

@cowboy

This comment has been minimized.

Copy link
Owner Author

commented Jan 10, 2011

I like the idea of using - to test "between-ness" and using x in a version like 2.x or 2.x.x as a shorthand for that. I'm not sure I understand why "~" is an alias for "*" though.

@kflorence

This comment has been minimized.

Copy link

commented Jan 10, 2011

@gene1wood

This comment has been minimized.

Copy link

commented Oct 20, 2014

@srescio

This comment has been minimized.

Copy link

commented Apr 14, 2015

used in my phonegap app to notify updates by comparing current version and previous one stored in db, thanks @cowboy :) https://sresc.io/dA4

@Noitidart

This comment has been minimized.

Copy link

commented Feb 21, 2017

Can we get a version without new Function as that makes it fail CSP. We need to pass script-src: 'unsafe-eval' otherwise, and browser extensions dont allow this (Firefox). For example running this on github we get this error - http://i.imgur.com/MtLfhqx.png

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.