Skip to content

Commit

Permalink
feat(isSemVer): add semver validator (#1246)
Browse files Browse the repository at this point in the history
* feat: add isSemVer validator to check for Semantic Versioning

* style: modify function docs

* push compiled modules after function docs change

* moe multilineRegexp function to util for re-usability

* add more invalid/bad testcases
  • Loading branch information
hamzahejja authored Feb 11, 2020
1 parent c770678 commit a87165a
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ Validator | Description
**isOctal(str)** | check if the string is a valid octal number.
**isPort(str)** | check if the string is a valid port number.
**isPostalCode(str, locale)** | check if the string is a postal code,<br/><br/>(locale is one of `[ 'AD', 'AT', 'AU', 'BE', 'BG', 'BR', 'CA', 'CH', 'CZ', 'DE', 'DK', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'ID', 'IE' 'IL', 'IN', 'IR', 'IS', 'IT', 'JP', 'KE', 'LI', 'LT', 'LU', 'LV', 'MT', 'MX', 'NL', 'NO', 'NZ', 'PL', 'PR', 'PT', 'RO', 'RU', 'SA', 'SE', 'SI', 'TN', 'TW', 'UA', 'US', 'ZA', 'ZM' ]` OR 'any'. If 'any' is used, function will check if any of the locals match. Locale list is `validator.isPostalCodeLocales`.).
**isSemVer(str)** | check if the string is a Semantic Versioning Specification (SemVer).
**isSurrogatePair(str)** | check if the string contains any surrogate pairs chars.
**isURL(str [, options])** | check if the string is an URL.<br/><br/>`options` is an object which defaults to `{ protocols: ['http','https','ftp'], require_tld: true, require_protocol: false, require_host: true, require_valid_protocol: true, allow_underscores: false, host_whitelist: false, host_blacklist: false, allow_trailing_dot: false, allow_protocol_relative_urls: false, disallow_auth: false }`.<br/><br/>require_protocol - if set as true isURL will return false if protocol is not present in the URL.<br/>require_valid_protocol - isURL will check if the URL's protocol is present in the protocols option.<br/>protocols - valid protocols can be modified with this option.<br/>require_host - if set as false isURL will not check if host is present in the URL.<br/>allow_protocol_relative_urls - if set as true protocol relative URLs will be allowed.
**isUppercase(str)** | check if the string is uppercase.
Expand Down
3 changes: 3 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ var _isVariableWidth = _interopRequireDefault(require("./lib/isVariableWidth"));

var _isMultibyte = _interopRequireDefault(require("./lib/isMultibyte"));

var _isSemVer = _interopRequireDefault(require("./lib/isSemVer"));

var _isSurrogatePair = _interopRequireDefault(require("./lib/isSurrogatePair"));

var _isInt = _interopRequireDefault(require("./lib/isInt"));
Expand Down Expand Up @@ -203,6 +205,7 @@ var validator = {
isHalfWidth: _isHalfWidth.default,
isVariableWidth: _isVariableWidth.default,
isMultibyte: _isMultibyte.default,
isSemVer: _isSemVer.default,
isSurrogatePair: _isSurrogatePair.default,
isInt: _isInt.default,
isFloat: _isFloat.default,
Expand Down
28 changes: 28 additions & 0 deletions lib/isSemVer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"use strict";

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = isSemVer;

var _assertString = _interopRequireDefault(require("./util/assertString"));

var _multilineRegex = _interopRequireDefault(require("./util/multilineRegex"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
* Regular Expression to match
* semantic versioning (SemVer)
* built from multi-line, multi-parts regexp
* Reference: https://semver.org/
*/
var semanticVersioningRegex = (0, _multilineRegex.default)(['^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)', '(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))', '?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$']);

function isSemVer(str) {
(0, _assertString.default)(str);
return semanticVersioningRegex.test(str);
}

module.exports = exports.default;
module.exports.default = exports.default;
23 changes: 23 additions & 0 deletions lib/util/multilineRegex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use strict";

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = multilineRegexp;

/**
* Build RegExp object from an array
* of multiple/multi-line regexp parts
*
* @param {string[]} parts
* @param {string} flags
* @return {object} - RegExp object
*/
function multilineRegexp(parts) {
var flags = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var regexpAsStringLiteral = parts.join('');
return new RegExp(regexpAsStringLiteral, flags);
}

module.exports = exports.default;
module.exports.default = exports.default;
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import isFullWidth from './lib/isFullWidth';
import isHalfWidth from './lib/isHalfWidth';
import isVariableWidth from './lib/isVariableWidth';
import isMultibyte from './lib/isMultibyte';
import isSemVer from './lib/isSemVer';
import isSurrogatePair from './lib/isSurrogatePair';

import isInt from './lib/isInt';
Expand Down Expand Up @@ -137,6 +138,7 @@ const validator = {
isHalfWidth,
isVariableWidth,
isMultibyte,
isSemVer,
isSurrogatePair,
isInt,
isFloat,
Expand Down
20 changes: 20 additions & 0 deletions src/lib/isSemVer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import assertString from './util/assertString';
import multilineRegexp from './util/multilineRegex';

/**
* Regular Expression to match
* semantic versioning (SemVer)
* built from multi-line, multi-parts regexp
* Reference: https://semver.org/
*/
const semanticVersioningRegex = multilineRegexp([
'^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)',
'(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))',
'?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$',
]);

export default function isSemVer(str) {
assertString(str);

return semanticVersioningRegex.test(str);
}
13 changes: 13 additions & 0 deletions src/lib/util/multilineRegex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Build RegExp object from an array
* of multiple/multi-line regexp parts
*
* @param {string[]} parts
* @param {string} flags
* @return {object} - RegExp object
*/
export default function multilineRegexp(parts, flags = '') {
const regexpAsStringLiteral = parts.join('');

return new RegExp(regexpAsStringLiteral, flags);
}
75 changes: 75 additions & 0 deletions test/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -3685,6 +3685,81 @@ describe('Validators', () => {
});
});

it('should validate Semantic Versioning Specification (SemVer) strings', () => {
test({
validator: 'isSemVer',
valid: [
'0.0.4',
'1.2.3',
'10.20.30',
'1.1.2-prerelease+meta',
'1.1.2+meta',
'1.1.2+meta-valid',
'1.0.0-alpha',
'1.0.0-beta',
'1.0.0-alpha.beta',
'1.0.0-alpha.beta.1',
'1.0.0-alpha.1',
'1.0.0-alpha0.valid',
'1.0.0-alpha.0valid',
'1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay',
'1.0.0-rc.1+build.1',
'2.0.0-rc.1+build.123',
'1.2.3-beta',
'10.2.3-DEV-SNAPSHOT',
'1.2.3-SNAPSHOT-123',
'1.0.0',
'2.0.0',
'1.1.7',
'2.0.0+build.1848',
'2.0.1-alpha.1227',
'1.0.0-alpha+beta',
'1.2.3----RC-SNAPSHOT.12.9.1--.12+788',
'1.2.3----R-S.12.9.1--.12+meta',
'1.2.3----RC-SNAPSHOT.12.9.1--.12',
'1.0.0+0.build.1-rc.10000aaa-kk-0.1',
'99999999999999999999999.999999999999999999.99999999999999999',
'1.0.0-0A.is.legal',
],
invalid: [
'-invalid+invalid',
'-invalid.01',
'alpha',
'alpha.beta',
'alpha.beta.1',
'alpha.1',
'alpha+beta',
'alpha_beta',
'alpha.',
'alpha..',
'beta',
'1.0.0-alpha_beta',
'-alpha.',
'1.0.0-alpha..',
'1.0.0-alpha..1',
'1.0.0-alpha...1',
'1.0.0-alpha....1',
'1.0.0-alpha.....1',
'1.0.0-alpha......1',
'1.0.0-alpha.......1',
'01.1.1',
'1.01.1',
'1.1.01',
'1.2',
'1.2.3.DEV',
'1.2-SNAPSHOT',
'1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788',
'1.2-RC-SNAPSHOT',
'-1.0.3-gamma+b7718',
'+justmeta',
'9.8.7+meta+meta',
'9.8.7-whatever+meta+meta',
'99999999999999999999999.999999999999999999.99999999999999999-',
'---RC-SNAPSHOT.12.09.1--------------------------------..12',
],
});
});

it('should validate base32 strings', () => {
test({
validator: 'isBase32',
Expand Down
28 changes: 28 additions & 0 deletions validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,33 @@ function isMultibyte(str) {
return multibyte.test(str);
}

/**
* Build RegExp object from an array
* of multiple/multi-line regexp parts
*
* @param {string[]} parts
* @param {string} flags
* @return {object} - RegExp object
*/
function multilineRegexp(parts) {
var flags = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var regexpAsStringLiteral = parts.join('');
return new RegExp(regexpAsStringLiteral, flags);
}

/**
* Regular Expression to match
* semantic versioning (SemVer)
* built from multi-line, multi-parts regexp
* Reference: https://semver.org/
*/

var semanticVersioningRegex = multilineRegexp(['^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)', '(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))', '?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$']);
function isSemVer(str) {
assertString(str);
return semanticVersioningRegex.test(str);
}

var surrogatePair = /[\uD800-\uDBFF][\uDC00-\uDFFF]/;
function isSurrogatePair(str) {
assertString(str);
Expand Down Expand Up @@ -2354,6 +2381,7 @@ var validator = {
isHalfWidth: isHalfWidth,
isVariableWidth: isVariableWidth,
isMultibyte: isMultibyte,
isSemVer: isSemVer,
isSurrogatePair: isSurrogatePair,
isInt: isInt,
isFloat: isFloat,
Expand Down
2 changes: 1 addition & 1 deletion validator.min.js

Large diffs are not rendered by default.

0 comments on commit a87165a

Please sign in to comment.