UNPKG

flow-typed

Version:

A repository of high quality flow type definitions

476 lines (381 loc) 11.4 kB
"use strict"; 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;