Skip to main content

Semantic Version (SemVer) comparison for JavaScript. Compare one or more semantic version numbers to a reference version number.

/*!
 * 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;
    };
})();