ruffle/web/packages/core/src/version.ts

229 lines
7.3 KiB
TypeScript

/**
* A representation of a semver 2 compliant version string
*/
export class Version {
private readonly major: number;
private readonly minor: number;
private readonly patch: number;
private readonly prIdent: string[] | null;
private readonly buildIdent: string[] | null;
/**
* Construct a Version from specific components.
*
* If you wish to parse a string into a Version then please use [[fromSemver]].
*
* @param major The major version component.
* @param minor The minor version component.
* @param patch The patch version component.
* @param prIdent A list of pre-release identifiers, if any
* @param buildIdent A list of build identifiers, if any
*/
constructor(
major: number,
minor: number,
patch: number,
prIdent: string[] | null,
buildIdent: string[] | null
) {
this.major = major;
this.minor = minor;
this.patch = patch;
this.prIdent = prIdent;
this.buildIdent = buildIdent;
}
/**
* Construct a version from a semver 2 compliant string.
*
* This function is intended for use with semver 2 compliant strings.
* Malformed strings may still parse correctly, but this result is not
* guaranteed.
*
* @param versionString A semver 2.0.0 compliant version string
* @returns A version object
*/
static fromSemver(versionString: string): Version {
const buildSplit = versionString.split("+"),
prSplit = buildSplit[0].split("-"),
versionSplit = prSplit[0].split(".");
const major = parseInt(versionSplit[0], 10);
let minor = 0;
let patch = 0;
let prIdent = null;
let buildIdent = null;
if (versionSplit[1] != undefined) {
minor = parseInt(versionSplit[1], 10);
}
if (versionSplit[2] != undefined) {
patch = parseInt(versionSplit[2], 10);
}
if (prSplit[1] != undefined) {
prIdent = prSplit[1].split(".");
}
if (buildSplit[1] != undefined) {
buildIdent = buildSplit[1].split(".");
}
return new Version(major, minor, patch, prIdent, buildIdent);
}
/**
* Returns true if a given version is compatible with this one.
*
* Compatibility is defined as having the same nonzero major version
* number, or if both major versions are zero, the same nonzero minor
* version number, or if both minor versions are zero, the same nonzero
* patch version number.
*
* This implements the ^ operator in npm's semver package, with the
* exception of the prerelease exclusion rule.
*
* @param other The other version to test against
* @returns True if compatible
*/
isCompatibleWith(other: Version): boolean {
return (
(this.major !== 0 && this.major === other.major) ||
(this.major === 0 &&
other.major === 0 &&
this.minor !== 0 &&
this.minor === other.minor) ||
(this.major === 0 &&
other.major === 0 &&
this.minor === 0 &&
other.minor === 0 &&
this.patch !== 0 &&
this.patch === other.patch)
);
}
/**
* Returns true if this version has precedence over (is newer than) another
* version.
*
* Precedence is defined as in the Semver 2 spec. This implements the >
* operator in npm's semver package, with the exception of the prerelease
* exclusion rule.
*
* @param other The other version to test against
* @returns True if this version has precedence over the other one
*/
hasPrecedenceOver(other: Version): boolean {
if (this.major > other.major) {
return true;
} else if (this.major < other.major) {
return false;
}
if (this.minor > other.minor) {
return true;
} else if (this.minor < other.minor) {
return false;
}
if (this.patch > other.patch) {
return true;
} else if (this.patch < other.patch) {
return false;
}
if (this.prIdent == null && other.prIdent != null) {
return true;
} else if (this.prIdent != null && other.prIdent != null) {
const isNumeric = /^[0-9]*$/;
for (
let i = 0;
i < this.prIdent.length && i < other.prIdent.length;
i += 1
) {
if (
!isNumeric.test(this.prIdent[i]) &&
isNumeric.test(other.prIdent[i])
) {
return true;
} else if (
isNumeric.test(this.prIdent[i]) &&
isNumeric.test(other.prIdent[i])
) {
if (
parseInt(this.prIdent[i], 10) >
parseInt(other.prIdent[i], 10)
) {
return true;
} else if (
parseInt(this.prIdent[i], 10) <
parseInt(other.prIdent[i], 10)
) {
return false;
}
} else if (
isNumeric.test(this.prIdent[i]) &&
!isNumeric.test(other.prIdent[i])
) {
return false;
} else if (
!isNumeric.test(this.prIdent[i]) &&
!isNumeric.test(other.prIdent[i])
) {
if (this.prIdent[i] > other.prIdent[i]) {
return true;
} else if (this.prIdent[i] < other.prIdent[i]) {
return false;
}
}
}
return this.prIdent.length > other.prIdent.length;
}
return false;
}
/**
* Tests if a given version is equivalent to this one.
*
* Build and prerelease tags are ignored.
*
* @param other The other version to test against
* @returns True if the given version is equivalent
*/
isEqual(other: Version): boolean {
return (
this.major === other.major &&
this.minor === other.minor &&
this.patch === other.patch
);
}
/**
* Tests if a given version is stable or a compatible prerelease for this
* version.
*
* This implements the prerelease exclusion rule of NPM semver: a
* prerelease version can only pass this check if the major/minor/patch
* components of both versions are the same. Otherwise, the prerelease
* version always fails.
*
* @param other The other version to test against
* @returns True if the given version is either stable, or a
* prerelease in the same series as this one.
*/
isStableOrCompatiblePrerelease(other: Version): boolean {
if (other.prIdent == null) {
return true;
} else {
return (
this.major === other.major &&
this.minor === other.minor &&
this.patch === other.patch
);
}
}
}