@tinymce/beehive-flow
Version:
A CLI tool implementing the beehive flow git branching process
110 lines (95 loc) • 3.51 kB
text/typescript
import * as E from 'fp-ts/Either';
import * as PromiseUtils from '../utils/PromiseUtils';
import { Comparison, chain, chainN, compareNative } from '../utils/Comparison';
export interface Version {
readonly major: number;
readonly minor: number;
readonly patch: number;
readonly preRelease?: string;
readonly buildMetaData?: string;
}
export interface MajorMinorVersion {
readonly major: number;
readonly minor: number;
}
export const parseVersionE = (input: string): E.Either<string, Version> => {
// based on https://semver.org/
// eslint-disable-next-line max-len
const regexp = /^(?<major>0|[1-9]\d*)\.(?<minor>0|[1-9]\d*)\.(?<patch>0|[1-9]\d*)(?:-(?<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
const r = regexp.exec(input);
if (r === null || r.groups === undefined) {
return E.left('Could not parse version string: ' + input);
} else {
const g = r.groups;
// The regexp should guarantee that these are positive integers
const major = parseInt(g.major, 10);
const minor = parseInt(g.minor, 10);
const patch = parseInt(g.patch, 10);
const preRelease = r.groups.prerelease;
const buildMetaData = r.groups.buildmetadata;
return E.right({
major,
minor,
patch,
preRelease,
buildMetaData
});
}
};
export const parseVersion = async (input: string): Promise<Version> =>
PromiseUtils.eitherToPromise(parseVersionE(input));
export const parseMajorMinorVersion = async (input: string): Promise<MajorMinorVersion> => {
const regexp = /^(?<major>0|[1-9]\d*)\.(?<minor>0|[1-9]\d*)$/;
const r = regexp.exec(input);
if (r === null || r.groups === undefined) {
return PromiseUtils.fail('Could not parse major.minor version string');
} else {
const g = r.groups;
const major = parseInt(g.major, 10);
const minor = parseInt(g.minor, 10);
return { major, minor };
}
};
export const majorMinorVersionToString = (v: MajorMinorVersion): string =>
`${v.major}.${v.minor}`;
export const versionToString = (v: Version): string => {
const preBit = v.preRelease === undefined ? '' : '-' + v.preRelease;
const metaBit = v.buildMetaData === undefined ? '' : '+' + v.buildMetaData;
return [ v.major, v.minor, v.patch ].join('.') + preBit + metaBit;
};
export const compareMajorMinorVersions = (a: MajorMinorVersion, b: MajorMinorVersion): Comparison =>
chain(
compareNative(a.major, b.major),
compareNative(a.minor, b.minor)
);
export const isPreRelease = (v: Version): boolean =>
v.preRelease !== undefined;
/**
* Compares two versions on their major, minor and prerelease versions.
* Per semver:
* - released versions beat prerelease versions for the same major.minor.patch
* - buildMeta is not considered
*
* Might not be suitable for sorting.
*
* @param a
* @param b
*/
export const compareVersions = (a: Version, b: Version): Comparison =>
chainN(
compareNative(a.major, b.major),
compareNative(a.minor, b.minor),
compareNative(a.patch, b.patch),
comparePreReleases(a, b)
);
const comparePreReleases = (a: Version, b: Version): Comparison => {
if (a.preRelease === b.preRelease) {
return Comparison.EQ;
} else if (a.preRelease === undefined) {
// a is a release, b is a prerelease - a > b
return Comparison.GT;
} else {
// a is a prerelease, b is a release - a < b
return Comparison.LT;
}
};