@dev-build-deploy/version-it
Version:
Version Management Library
273 lines (272 loc) • 10.8 kB
JavaScript
"use strict";
/*
* SPDX-FileCopyrightText: 2023 Kevin de Jong <monkaii@hotmail.com>
* SPDX-License-Identifier: MIT
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.CalVer = void 0;
exports.formatFromString = formatFromString;
const modifiers_1 = require("./modifiers");
const utils_1 = require("./utils");
/**
* A simple CalVer implementation
* @class CalVer
* @member major Major version
* @member minor Minor version
* @member micro Micro version
* @member modifier Modifier
* @method toString Returns the CalVer as a string
* @method increment Increments the CalVer by the provided type
*/
class CalVer {
prefix;
major;
minor;
micro;
modifiers;
format;
constructor(format, version, prefix) {
this.format = typeof format === "string" ? formatFromString(format) : format;
this.prefix = version?.prefix ?? prefix;
this.major = version?.major ?? 0;
this.minor = version?.minor ?? 0;
this.micro = version?.micro ?? (this.format.micro ? 0 : undefined);
this.modifiers = version?.modifiers ?? [];
}
/**
* Creates a CalVer object from a string
* @param format CalVer formatting
* @param versin Version string
* @param prefix Prefix associated with the version
* @returns CalVer or null if the version string is invalid
*/
static fromString(format, version, prefix) {
format = typeof format === "string" ? formatFromString(format) : format;
if (prefix !== undefined) {
if (!version.startsWith(prefix))
return null;
version = version.substring(prefix.length);
}
const groups = format.regex.exec(version)?.groups;
if (groups === undefined) {
return null;
}
return new CalVer(format, {
prefix: prefix,
major: parseInt(groups.major),
minor: parseInt(groups.minor),
micro: parseInt(groups.micro),
modifiers: (0, modifiers_1.getModifiers)(groups.modifier),
}, prefix);
}
/**
* Updates versions associated with calendar items (i.e. YYYY, MM, WW, DD)
* in the provided CalVer format.
* @param format The CalVer format
* @returns The updated version based on the current date.
*/
updateCalendar(format) {
if (format === undefined || ["MAJOR", "MINOR", "MICRO"].includes(format))
return;
if (format.includes("Y"))
return new Date().getFullYear();
if (format.includes("M"))
return new Date().getMonth() + 1;
if (format.includes("W"))
return (0, utils_1.getISO8601WeekNumber)();
if (format.includes("D"))
return new Date().getDate() + 1;
}
/**
* Returns a string, where the value is formatted correctly based on the provided format.
* @param value Value to format
* @param format CalVer format to use
* @returns Formatted value
*/
formatVersionCore(value, format) {
if (["MAJOR", "MINOR", "MICRO"].includes(format))
return value.toString();
if (format === "YYYY")
return value.toString().padStart(4, "0");
if (format === "0Y")
return value
.toString()
.substring(value.toString().length - 3)
.padStart(3, "0");
if (format.includes("0"))
return value
.toString()
.substring(value.toString().length - 2)
.padStart(2, "0");
return value.toString().substring(value.toString().length - 2);
}
/**
* Returns the CalVer as a string
* @returns CalVer as a string
*/
toString() {
let version = this.prefix ?? "";
version += `${this.formatVersionCore(this.major, this.format.major)}.${this.formatVersionCore(this.minor, this.format.minor)}`;
if (this.micro !== undefined && this.format.micro !== undefined)
version += `.${this.formatVersionCore(this.micro, this.format.micro)}`;
version += (0, modifiers_1.modifiersToString)(this.modifiers);
return version;
}
/**
* Increments the CalVer by the provided type;
* - "CALENDAR": Increments the calendar (i.e. YYYY, MM, WW, DD)
* - "MAJOR": Increments the major version
* - "MINOR": Increments the minor version
* - "MICRO": Increments the micro version
* - "MODIFIER": Increments the modifier
* @param type Type of increment
* @param modifier Modifier to increment
* @returns Incremented CalVer
*/
increment(type, modifier) {
switch (type) {
case "CALENDAR": {
// Increment the calendar only, this allows us to validate whether the calendar has changed
const newVersion = new CalVer(this.format, {
prefix: this.prefix,
major: this.updateCalendar(this.format.major) ?? this.major,
minor: this.updateCalendar(this.format.minor) ?? this.minor,
micro: this.updateCalendar(this.format.micro) ?? this.micro,
modifiers: this.modifiers,
});
// Return current version in case the calendar has not changed
if (newVersion.isEqualTo(this))
return this;
// If the calendar items are updated, reset MAJOR, MINOR, MICRO...
for (const subtype of ["major", "minor", "micro"]) {
const filter = Object.keys(this.format).filter(key => this.format[key] === subtype.toUpperCase());
if (filter.length !== 1)
continue;
newVersion[filter[0].toLowerCase()] = 0;
}
// ... and reset the MODIFIERS
newVersion.modifiers = [];
return newVersion;
}
case "MAJOR":
case "MINOR":
case "MICRO": {
const filter = Object.keys(this.format).filter(key => this.format[key] === type.toUpperCase());
if (filter.length !== 1)
throw new Error(`Unable to increment ${type} version as it is not part of the format`);
const item = filter[0].toLowerCase();
if (item === "major")
return new CalVer(this.format, { major: this.major + 1, modifiers: [] });
if (item === "minor")
return new CalVer(this.format, { major: this.major, minor: this.minor + 1, modifiers: [] });
if (item === "micro")
return new CalVer(this.format, {
major: this.major,
minor: this.minor,
micro: (this.micro ?? 0) + 1,
modifiers: [],
});
throw new Error("Unknown increment type.");
}
case "MODIFIER":
return new CalVer(this.format, {
...this,
modifiers: (0, modifiers_1.incrementModifier)(this.modifiers, modifier ?? "rc"),
});
}
}
/**
* Confirms that the provided format is identical to the current format
* @param other Other format to compare against
* @returns True if the formats are identical, false otherwise
*/
isFormatIdentical(other) {
return this.format.major === other.major && this.format.minor === other.minor && this.format.micro === other.micro;
}
isEqualTo(other) {
return this.compareTo(other) === 0;
}
isGreaterThan(other) {
return this.compareTo(other) === 1;
}
isLessThan(other) {
return this.compareTo(other) === -1;
}
/**
* Returns 0 when current version is equal to the provided version,
* 1 when current version is greater than the provided version,
* and -1 when current version is less than the provided version.
*
* Resulting in the following ordering (taking `YYYY.0M.MICRO` as an example):
*
* 2022.03.1
* 2022.03.2
* 2022.11.1
* 2022.11.1-alpha.1
* 2022.11.1-alpha.2
* 2022.11.1-beta.1
* 2022.11.2
* 2023.01.1
*
* @param other CalVer to compare to
* @returns
*/
compareTo(other) {
if (!this.isFormatIdentical(other.format))
throw new Error("Unable to compare CalVer with different formats");
const majorCmp = (0, utils_1.compareNumbers)(this.major, other.major);
if (majorCmp !== 0)
return majorCmp;
const minorCmp = (0, utils_1.compareNumbers)(this.minor, other.minor);
if (minorCmp !== 0)
return minorCmp;
if (this.micro !== undefined && other.micro !== undefined) {
const microCmp = (0, utils_1.compareNumbers)(this.micro, other.micro);
if (microCmp !== 0)
return microCmp;
}
return (0, modifiers_1.compareModifiers)(this.modifiers, other.modifiers);
}
}
exports.CalVer = CalVer;
/**
* Generates a regex based on the provided CalVer format
* @param format The CalVer format
* @returns The regex
* @internal
*/
function formatFromString(format) {
const elements = /^(?<major>[A-Z0]+)\.(?<minor>[A-Z0]+)(\.(?<micro>[A-Z0]+))?$/.exec(format)?.groups;
if (elements === undefined)
throw new Error(`Invalid CalVer format: ${format}`);
const usedElements = [];
const versions = [];
for (const key of Object.keys(elements)) {
if (elements[key] === undefined)
continue;
if (usedElements.includes(elements[key])) {
throw new Error(`Duplicate CalVer format element: ${elements[key]}`);
}
if (["MAJOR", "MINOR", "MICRO"].includes(elements[key]))
versions.push(`(?<${key}>\\d+)`);
else if (elements[key] === "YYYY")
versions.push(`(?<${key}>\\d{4})`);
else if (elements[key] === "YY")
versions.push(`(?<${key}>\\d{1,3})`);
else if (elements[key] === "0Y")
versions.push(`(?<${key}>\\d{3})`);
else if (["0M", "0W", "0D"].includes(elements[key]))
versions.push(`(?<${key}>\\d{2})`);
else if (["MM", "WW", "DD"].includes(elements[key]))
versions.push(`(?<${key}>\\d{1,2})`);
else
throw new Error(`Unknown CalVer format element: ${JSON.stringify(elements)}, ${key}, ${elements[key]}`);
usedElements.push(elements[key]);
}
return {
regex: new RegExp(`^${versions.join(".")}(-(?<modifier>[\\w._]+))?$`),
major: elements.major,
minor: elements.minor,
micro: elements.micro,
};
}