Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions doc/api/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -1544,16 +1544,28 @@ should not be relied upon to exist.
added: REPLACEME
-->

Perform a SemVer comparison to the release version.

* `major` {number} The major version to compare.
* `minor` {number} The minor version to compare.
* `patch` {number} The patch version to compare.
* `tag` {string} The pre-release tag to compare.
* Returns: {number} `1` if the given version is lower than the current release
version, `0` if the given version matches the process version, and `-1`
* Returns: {number} `-1` if the given version is lower than the current release
version, `0` if the given version matches the process version, and `1`
if the given version is greater than the release version.

Perform a SemVer comparison to the release version.

## process.release.compareVersion(version)
<!-- YAML
added: REPLACEME
-->

* `version` {string} The semver version to compare.
* Returns: {number} `-1` if the given version is lower than the current release
version, `0` if the given version matches the process version, and `1`
if the given version is greater than the release version.

Perform a SemVer comparison to the release version.

## process.send(message[, sendHandle[, options]][, callback])
<!-- YAML
added: v0.5.9
Expand Down
65 changes: 47 additions & 18 deletions lib/internal/process.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ const {
ERR_ASSERTION,
ERR_CPU_USAGE,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_ARRAY_LENGTH,
ERR_INVALID_OPT_VALUE,
ERR_OUT_OF_RANGE,
ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET,
ERR_UNKNOWN_SIGNAL
}
Expand Down Expand Up @@ -291,34 +293,61 @@ function setupCompareVersion() {
prereleaseTag,
} = process.release;

process.release.compareVersion = (major, minor, patch, tag) => {
if (typeof major !== 'number')
throw new ERR_INVALID_ARG_TYPE('major', 'number', major);
if (typeof minor !== 'number')
throw new ERR_INVALID_ARG_TYPE('minor', 'number', minor);
if (typeof patch !== 'number')
throw new ERR_INVALID_ARG_TYPE('patch', 'number', patch);
if (tag !== undefined && typeof tag !== 'string')
throw new ERR_INVALID_ARG_TYPE('tag', 'string', tag);
function validateNum(value, name) {
if (!Number.isInteger(value)) {
if (typeof value !== 'number')
throw new ERR_INVALID_ARG_TYPE(name, 'number', value);
throw new ERR_OUT_OF_RANGE(name, 'an integer', value);
}
// Always accept 0 as input even though it does not adhere to semver because
// the first Node.js versions did not use the semver notation.
if (value < 0)
throw new ERR_OUT_OF_RANGE(name, '>= 0', value);
}

// The tag is not validated here.
const semverRegExp = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))*?$/;

process.release.compareVersion = function(major, minor, patch, tag) {
if (arguments.length === 1 && typeof major === 'string') {
const match = major.match(semverRegExp);
if (match === null) {
throw new ERR_INVALID_ARG_VALUE('version',
major,
'is not a valid Semantic Version');
}
[, major, minor, patch, tag] = match;
} else {
validateNum(major, 'major');
validateNum(minor, 'minor');
validateNum(patch, 'patch');
if (tag !== undefined && typeof tag !== 'string')
throw new ERR_INVALID_ARG_TYPE('tag', 'string', tag);
}

if (major > majorVersion)
return -1;
if (major < majorVersion)
return 1;
if (major < majorVersion)
return -1;

if (minor > minorVersion)
return -1;
if (minor < minorVersion)
return 1;
if (minor < minorVersion)
return -1;

if (patch > patchVersion)
return -1;
if (patch < patchVersion)
return 1;
if (prereleaseTag)
return prereleaseTag === tag ? 0 : 1;
if (tag)
if (patch < patchVersion)
return -1;
if (prereleaseTag) {
if (prereleaseTag === tag)
return 0;
// Note: we do not fully adhere to semver as we do not check numbers
// properly. It applies to e.g., 'rc2' > 'rc10'.
return prereleaseTag > tag ? -1 : 1;
}
if (tag)
return 1;

return 0;
};
Expand Down
116 changes: 92 additions & 24 deletions test/parallel/test-process-release.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
'use strict';

const common = require('../common');
require('../common');

const assert = require('assert');
const versionParts = process.versions.node.split('.');

assert.strictEqual(process.release.name, 'node');

// it's expected that future LTS release lines will have additional
// It's expected that future LTS release lines will have additional
// branches in here
if (versionParts[0] === '4' && versionParts[1] >= 2) {
assert.strictEqual(process.release.lts, 'Argon');
Expand All @@ -27,31 +27,99 @@ const {
compareVersion,
} = process.release;

assert.strictEqual(compareVersion(major, minor, patch, tag), 0);
[
[major, minor, patch, tag, 0],
[major + 1, minor, patch, tag, 1],
[major - 1, minor, patch, tag, -1],
[major, minor + 1, patch, tag, 1],
[major, minor - 1, patch, tag, -1],
[major, minor, patch + 1, tag, 1],
[major, minor, patch - 1, tag, -1]
].forEach(([major, minor, patch, tag, expected]) => {
// Skip invalid entries.
if (major < 0 || minor < 0 || patch < 0)
return;
assert.strictEqual(compareVersion(major, minor, patch, tag), expected);
const semverStr = `${major}.${minor}.${patch}${tag ? `-${tag}` : ''}`;
assert.strictEqual(compareVersion(semverStr), expected);
});

assert.strictEqual(compareVersion(major + 1, minor, patch, tag), -1);
assert.strictEqual(compareVersion(major - 1, minor, patch, tag), 1);

assert.strictEqual(compareVersion(major, minor + 1, patch, tag), -1);
assert.strictEqual(compareVersion(major, minor - 1, patch, tag), 1);

assert.strictEqual(compareVersion(major, minor, patch + 1, tag), -1);
assert.strictEqual(compareVersion(major, minor, patch - 1, tag), 1);

if (tag)
if (tag) {
assert.strictEqual(compareVersion(major, minor, patch), 1);
else
assert.strictEqual(compareVersion(major, minor, patch, 'notrealtag'), -1);

for (const args of [
['', 0, 0, ''],
[0, '', 0, ''],
[0, 0, '', ''],
[0, 0, 0, 0],
]) {
common.expectsError(() => {
assert.strictEqual(compareVersion(`${major}.${minor}.${patch}`), 1);
assert.strictEqual(compareVersion(major, minor, patch, `${tag}-fake`), 1);
assert.strictEqual(
compareVersion(major, minor, patch, `${tag.slice(0, -1)}`), -1);
} else {
assert.strictEqual(compareVersion(major, minor, patch, 'fake-tag'), 1);
assert.strictEqual(compareVersion(`${major}.${minor}.${patch}-fake-tag`), 1);
assert.strictEqual(compareVersion(major, minor, patch), 0);
}

[
[['1', 0, 0, ''], 'major', 'string'],
[[0, '1', 0, ''], 'minor', 'string'],
[[0, 0, '1', ''], 'patch', 'string'],
[[0, 0, 0, 1], 'tag', 'number'],
[[0, 0, 0, null], 'tag', 'object'],
].forEach(([args, argName, receivedType]) => {
const expectedType = argName === 'tag' ? 'string' : 'number';
assert.throws(() => {
compareVersion(...args);
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError [ERR_INVALID_ARG_TYPE]',
message: `The "${argName}" argument must be of type ${expectedType}. ` +
`Received type ${receivedType}`
});
}
});

[
[-1, 0, 0, 'major'],
[0, -1, 0, 'minor'],
[0, 0, -1, 'patch'],
].forEach(([major, minor, patch, expected]) => {
assert.throws(() => {
compareVersion(major, minor, patch);
}, {
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError [ERR_OUT_OF_RANGE]',
message: `The value of "${expected}" is out of range. ` +
'It must be >= 0. Received -1'
});
});

[
[1.5, 1, 1, 'major'],
[1, 1.5, 1, 'minor'],
[1, 1, 1.5, 'patch'],
].forEach(([major, minor, patch, expected]) => {
assert.throws(() => {
compareVersion(major, minor, patch);
}, {
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError [ERR_OUT_OF_RANGE]',
message: `The value of "${expected}" is out of range. ` +
'It must be an integer. Received 1.5'
});
});

[
'1a.0.0',
'1.0a.0',
'1.0.0a',
'1.0.0.0',
'01.0.0',
'1.01.0',
'1.0',
'1..0',
].forEach((version) => {
assert.throws(() => {
compareVersion(version);
}, {
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError [ERR_INVALID_ARG_VALUE]',
message: "The argument 'version' is not a valid Semantic Version. " +
`Received '${version}'`
});
});