Because I despise nitpicking stuff like indentation and spacing in pull request comments, I'd like to nudge forward our automated style checking, at least for new files being added. I don't want to totally rewrite server.js just to get automated style checking… the blame tracking is just too useful. So let's it's just take care of that when we start splitting it out. More discussion in #948.
176 lines
4.5 KiB
JavaScript
176 lines
4.5 KiB
JavaScript
/**
|
|
* Utilities relating to PHP version numbers. This compares version numbers
|
|
* using the algorithm followed by Composer (see
|
|
* https://getcomposer.org/doc/04-schema.md#version).
|
|
*/
|
|
'use strict';
|
|
|
|
const {listCompare} = require('./version.js');
|
|
const {omitv} = require('./text-formatters.js');
|
|
|
|
// Return a negative value if v1 < v2,
|
|
// zero if v1 = v2,
|
|
// a positive value otherwise.
|
|
//
|
|
// See https://getcomposer.org/doc/04-schema.md#version
|
|
// and https://github.com/badges/shields/issues/319#issuecomment-74411045
|
|
function compare(v1, v2) {
|
|
// Omit the starting `v`.
|
|
const rawv1 = omitv(v1);
|
|
const rawv2 = omitv(v2);
|
|
let v1data, v2data;
|
|
try {
|
|
v1data = numberedVersionData(rawv1);
|
|
v2data = numberedVersionData(rawv2);
|
|
} catch(e) {
|
|
return asciiVersionCompare(rawv1, rawv2);
|
|
}
|
|
|
|
// Compare the numbered part (eg, 1.0.0 < 2.0.0).
|
|
const numbersCompare = listCompare(v1data.numbers, v2data.numbers);
|
|
if (numbersCompare !== 0) {
|
|
return numbersCompare;
|
|
}
|
|
|
|
// Compare the modifiers (eg, alpha < beta).
|
|
if (v1data.modifier < v2data.modifier) {
|
|
return -1;
|
|
} else if (v1data.modifier > v2data.modifier) {
|
|
return 1;
|
|
}
|
|
|
|
// Compare the modifier counts (eg, alpha1 < alpha3).
|
|
if (v1data.modifierCount < v2data.modifierCount) {
|
|
return -1;
|
|
} else if (v1data.modifierCount > v2data.modifierCount) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
exports.compare = compare;
|
|
|
|
function latest(versions) {
|
|
let latest = versions[0];
|
|
for (let i = 1; i < versions.length; i++) {
|
|
if (compare(latest, versions[i]) < 0) {
|
|
latest = versions[i];
|
|
}
|
|
}
|
|
return latest;
|
|
}
|
|
exports.latest = latest;
|
|
|
|
// Whether a version is stable.
|
|
function isStable(version) {
|
|
const rawVersion = omitv(version);
|
|
let versionData;
|
|
try {
|
|
versionData = numberedVersionData(rawVersion);
|
|
} catch(e) {
|
|
return false;
|
|
}
|
|
// normal or patch
|
|
return (versionData.modifier === 3) || (versionData.modifier === 4);
|
|
}
|
|
exports.isStable = isStable;
|
|
|
|
// === Private helper functions ===
|
|
|
|
// Return a negative value if v1 < v2,
|
|
// zero if v1 = v2, a positive value otherwise.
|
|
function asciiVersionCompare(v1, v2) {
|
|
if (v1 < v2) {
|
|
return -1;
|
|
} else if (v1 > v2) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Take a version without the starting v.
|
|
// eg, '1.0.x-beta'
|
|
// Return { numbers: [1,0,something big], modifier: 2, modifierCount: 1 }
|
|
function numberedVersionData(version) {
|
|
// A version has a numbered part and a modifier part
|
|
// (eg, 1.0.0-patch, 2.0.x-dev).
|
|
const parts = version.split('-');
|
|
const numbered = parts[0];
|
|
|
|
// Aliases that get caught here.
|
|
if (numbered === 'dev') {
|
|
return {
|
|
numbers: parts[1],
|
|
modifier: 5,
|
|
modifierCount: 1,
|
|
};
|
|
}
|
|
|
|
let modifierLevel = 3;
|
|
let modifierLevelCount = 0;
|
|
|
|
if (parts.length > 1) {
|
|
const modifier = parts[parts.length - 1];
|
|
const firstLetter = modifier.charCodeAt(0);
|
|
let modifierLevelCountString;
|
|
|
|
// Modifiers: alpha < beta < RC < normal < patch < dev
|
|
if (firstLetter === 97) { // a
|
|
modifierLevel = 0;
|
|
if (/^alpha/.test(modifier)) {
|
|
modifierLevelCountString = + (modifier.slice(5));
|
|
} else {
|
|
modifierLevelCountString = + (modifier.slice(1));
|
|
}
|
|
} else if (firstLetter === 98) { // b
|
|
modifierLevel = 1;
|
|
if (/^beta/.test(modifier)) {
|
|
modifierLevelCountString = + (modifier.slice(4));
|
|
} else {
|
|
modifierLevelCountString = + (modifier.slice(1));
|
|
}
|
|
} else if (firstLetter === 82) { // R
|
|
modifierLevel = 2;
|
|
modifierLevelCountString = + (modifier.slice(2));
|
|
} else if (firstLetter === 112) { // p
|
|
modifierLevel = 4;
|
|
if (/^patch/.test(modifier)) {
|
|
modifierLevelCountString = + (modifier.slice(5));
|
|
} else {
|
|
modifierLevelCountString = + (modifier.slice(1));
|
|
}
|
|
} else if (firstLetter === 100) { // d
|
|
modifierLevel = 5;
|
|
if (/^dev/.test(modifier)) {
|
|
modifierLevelCountString = + (modifier.slice(3));
|
|
} else {
|
|
modifierLevelCountString = + (modifier.slice(1));
|
|
}
|
|
}
|
|
|
|
// If we got the empty string, it defaults to a modifier count of 1.
|
|
if (!modifierLevelCountString) {
|
|
modifierLevelCount = 1;
|
|
} else {
|
|
modifierLevelCount = + modifierLevelCountString;
|
|
}
|
|
}
|
|
|
|
// Try to convert to a list of numbers.
|
|
function toNum (s) {
|
|
let n = +s;
|
|
if (n !== n) { // If n is NaN…
|
|
n = 0xffffffff;
|
|
}
|
|
return n;
|
|
}
|
|
const numberList = numbered.split('.').map(toNum);
|
|
|
|
return {
|
|
numbers: numberList,
|
|
modifier: modifierLevel,
|
|
modifierCount: modifierLevelCount,
|
|
};
|
|
}
|