flow-typed
Version:
A repository of high quality flow type definitions
476 lines (381 loc) • 11.4 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.__parseVersion = _parseVersion;
exports.compareFlowVersionAsc = compareFlowVersionAsc;
exports.determineFlowSpecificVersion = determineFlowSpecificVersion;
exports.disjointVersionsAll = disjointVersionsAll;
exports.extractFlowDirFromFlowDirPath = void 0;
exports.parseDirString = parseDirString;
exports.parseFlowSpecificVer = parseFlowSpecificVer;
exports.toDirString = toDirString;
exports.toSemverString = toSemverString;
var _npmProjectUtils = require("./npm/npmProjectUtils");
var _ValidationError = require("./ValidationError");
function _parseVerNum(numStr, verName, context) {
const num = parseInt(numStr, 10);
if (String(num) !== numStr) {
throw new _ValidationError.ValidationError(`'${context}': Invalid ${verName} number: '${numStr}'. Expected a number.`);
}
return num;
}
function _parseVerNumOrX(numStr, verName, context) {
if (numStr == null) {
return 'x';
}
if (numStr === 'x') {
return numStr;
}
return _parseVerNum(numStr, verName, context);
}
function _parseVersion(verStr, expectPossibleRangeUpper) {
if (verStr[0] !== 'v') {
throw new _ValidationError.ValidationError('Flow version ranges must start with a `v`!');
}
const verParts = verStr.slice(1).match(/^([0-9]+)\.([0-9]+|x)(\.([0-9]+|x))?/);
let majorStr, minorStr, patchStr;
if (verParts == null) {
if (verStr[1] === 'x') {
throw new _ValidationError.ValidationError('The major version of a Flow version string cannot be `x`, it must ' + 'be a number!');
} else {
throw new _ValidationError.ValidationError('Flow versions must be a non-range semver with an exact major version ' + 'and either an exact minor version or an `x` minor ver. Instead got: ' + verStr);
}
} else {
majorStr = verParts[1];
minorStr = verParts[2];
patchStr = verParts[4];
}
const [major, minor, patch] = [_parseVerNum(majorStr, 'major', verStr), _parseVerNumOrX(minorStr, 'minor', verStr), _parseVerNumOrX(patchStr, 'patch', verStr)];
const verAfterParts = verStr.substr(verParts[0].length + 1);
if (patchStr != null && verAfterParts[0] === '-' && verAfterParts[1] != null) {
if (expectPossibleRangeUpper) {
// A `-` could indicate either a range or a prerel. This is technically
// a real ambiguity in our versioning syntax -- but luckily it's rarely
// encountered.
//
// To deal with this, we try parsing as a version. If it parses we assume
// a range. If it doesn't parse as a version, we assume a pre-rel
// identifier.
//
// This is excitingly inefficient but because it operates on tiny inputs
// (and only sometimes) it shouldn't be an issue in practice.
try {
_parseVersion(verAfterParts.substr(1), false);
return [verParts[0].length + 1, {
major,
minor,
patch,
prerel: null
}];
} catch (e) {
// It's possible that a prerel *and* a range co-exist!
// v0.1.x-prerel-v0.2.x
let prerelParts = verAfterParts.substr(1).split('-'); // ['prerel', 'v0.2.x']
// $FlowFixMe[incompatible-type]
let prerel = prerelParts.shift(); // 'prerel'
while (prerelParts.length > 0) {
try {
_parseVersion(prerelParts.join('-'), false);
break;
} catch (e) {
// $FlowFixMe[incompatible-cast]
prerel += '-' + prerelParts.shift();
}
}
return [verParts[0].length + '-'.length + prerel.length + 1, {
major,
minor,
patch,
prerel
}];
}
} else {
// After the `-` must be a prerel
return [verStr.length + 1, {
major,
minor,
patch,
prerel: verAfterParts.substr(1)
}];
}
} else {
return [verParts[0].length + 1, {
major,
minor,
patch,
prerel: null
}];
}
}
function parseDirString(verStr) {
const prefix = 'flow_';
if (!verStr.startsWith(prefix)) {
throw new _ValidationError.ValidationError(`Flow versions must start with \`${prefix}\` but instead got ${verStr}`);
}
const afterPrefix = verStr.substr(verStr.indexOf(prefix) + prefix.length);
if (afterPrefix === 'all' || afterPrefix === 'vx.x.x') {
return {
kind: 'all'
};
} else if (afterPrefix[0] === '-') {
return {
kind: 'ranged',
lower: null,
upper: _parseVersion(verStr.substr(`${prefix}-`.length), false)[1]
};
} else {
const [offset, lowerVer] = _parseVersion(afterPrefix, true);
if (offset === afterPrefix.length) {
return {
kind: 'specific',
ver: lowerVer
};
} else if (afterPrefix[offset] === '-') {
const upperVer = offset + 1 === afterPrefix.length ? null : _parseVersion(afterPrefix.substr(offset + 1), false)[1];
return {
kind: 'ranged',
lower: lowerVer,
upper: upperVer
};
} else {
throw new _ValidationError.ValidationError(`Unexpected trailing characters: ${afterPrefix.substr(offset)}`);
}
}
}
function parseFlowSpecificVer(verStr) {
const flowVer = parseDirString(`flow_${verStr}`);
switch (flowVer.kind) {
case 'specific':
return flowVer.ver;
case 'all':
case 'ranged':
throw new _ValidationError.ValidationError(`This is not a specific Flow version.`);
default:
flowVer;
}
return {
major: -1,
minor: 'x',
patch: 'x',
prerel: null
};
}
async function determineFlowSpecificVersion(cwd, flowVersionArg) {
if (flowVersionArg && typeof flowVersionArg === 'string') {
// Be permissive if the prefix 'v' is left off
let flowVersionStr = flowVersionArg[0] === 'v' ? flowVersionArg : `v${flowVersionArg}`;
if (/^v[0-9]+\.[0-9]+$/.test(flowVersionStr)) {
flowVersionStr = `${flowVersionStr}.0`;
}
return {
kind: 'specific',
ver: parseFlowSpecificVer(flowVersionStr)
};
} else {
return {
kind: 'specific',
ver: await (0, _npmProjectUtils.findFlowSpecificVer)(cwd)
};
}
}
/**
* Given two version ranges a and b, determine whether a is before b.
*/
function lt(n1, n2) {
if (n1 === 'x' || n2 === 'x') return false;
if (n1 < n2) return true;
if (n1 > n2) return false;
return 'maybe';
}
function before(a, b) {
let test = lt(a.major, b.major);
if (test !== 'maybe') return test;
test = lt(a.minor, b.minor);
if (test !== 'maybe') return test;
test = lt(a.patch, b.patch);
if (test !== 'maybe') return test;
return false;
}
/**
* Given a version range, returns the max version satisfying the range.
*/
function maxSat(ver) {
switch (ver.kind) {
case 'all':
return null;
case 'ranged':
return ver.upper;
case 'specific':
return ver.ver;
default:
ver;
throw new Error('Unexpected FlowVersion kind!');
}
}
/**
* Given a version range, returns the min version satisfying the range.
*/
function minSat(ver) {
switch (ver.kind) {
case 'all':
return null;
case 'ranged':
return ver.lower;
case 'specific':
return ver.ver;
default:
ver;
throw new Error('Unexpected FlowVersion kind!');
}
}
/**
* Given two versions, returns whether they are disjoint.
*/
function _before(a, b) {
// If a is undefined, it denotes the maximum version. If b is undefined, it
// denotes the minimum version.
if (a && b) return before(a, b);
return false;
}
function disjointVersions(a, b) {
return _before(maxSat(a), minSat(b)) || _before(maxSat(b), minSat(a));
}
/**
* Given an array of versions, returns whether they are mutually disjoint.
*/
function _disjointVersionsAll(vers, len, i) {
if (i + 1 >= len) return true;
for (let j = i + 1; j < len; j++) {
if (!disjointVersions(vers[i], vers[j])) {
return false;
}
}
return _disjointVersionsAll(vers, len, i + 1);
}
function disjointVersionsAll(vers) {
return _disjointVersionsAll(vers, vers.length, 0);
}
function toDirString(ver) {
switch (ver.kind) {
case 'all':
return 'flow_all';
case 'specific':
{
let str = `flow_v${ver.ver.major}.${ver.ver.minor}`;
if (ver.ver.patch !== null) {
str += `.${ver.ver.patch}`;
if (ver.ver.prerel) {
str += `-${ver.ver.prerel}`;
}
}
return str;
}
case 'ranged':
{
const {
lower,
upper
} = ver;
let str = 'flow_';
if (lower !== null) {
str += `v${lower.major}.${lower.minor}`;
if (lower.patch !== null) {
str += `.${lower.patch}`;
if (lower.prerel !== null) {
str += `-${lower.prerel}`;
}
}
}
str += '-';
if (upper !== null) {
str += `v${upper.major}.${upper.minor}`;
if (upper.patch !== null) {
str += `.${upper.patch}`;
if (upper.prerel !== null) {
str += `-${upper.prerel}`;
}
}
}
return str;
}
default:
ver;
throw new Error('Unexpected FlowVersion kind!');
}
}
function toSemverString(ver) {
switch (ver.kind) {
case 'all':
return 'vx.x.x';
case 'specific':
return toDirString(ver).substr('flow_'.length);
case 'ranged':
{
const {
upper,
lower
} = ver;
let str = '';
if (lower !== null) {
str += `>=v${lower.major}.${lower.minor}`;
if (lower.patch !== null) {
str += `.${lower.patch}`;
if (lower.prerel !== null) {
str += `-${lower.prerel}`;
}
}
if (upper !== null) {
str += ' ';
}
}
if (upper !== null) {
str += `<=v${upper.major}.${upper.minor}`;
if (upper.patch !== null) {
str += `.${upper.patch}`;
if (upper.prerel !== null) {
str += `-${upper.prerel}`;
}
}
}
return str;
}
default:
ver;
throw new Error('Unexpected FlowVersion kind!');
}
}
function compareFlowVersionAsc(a, b) {
if (a.kind === 'all') {
return b.kind === 'all' ? 0 : -1;
}
if (b.kind === 'all') {
return 1;
}
const aLowerVer = a.kind === 'specific' ? a.ver : a.lower;
const bLowerVer = b.kind === 'specific' ? b.ver : b.lower;
if (aLowerVer === null) {
return bLowerVer === null ? 0 : 1;
}
if (bLowerVer === null) {
return -1;
}
const compareMajor = lt(aLowerVer.major, bLowerVer.major);
if (compareMajor !== 'maybe') {
return compareMajor ? 1 : -1;
}
const compareMinor = lt(aLowerVer.minor, bLowerVer.minor);
if (compareMinor !== 'maybe') {
return compareMinor ? 1 : -1;
}
const comparePatch = lt(aLowerVer.patch, bLowerVer.patch);
if (comparePatch !== 'maybe') {
return comparePatch ? 1 : -1;
}
return 0;
}
const extractFlowDirFromFlowDirPath = path => {
const split = path.split('/');
return split[split.length - 1];
}; // Exported for tests
exports.extractFlowDirFromFlowDirPath = extractFlowDirFromFlowDirPath;
;