UNPKG

keep-a-changelog

Version:

Node package to parse and generate changelogs following the [keepachangelog](https://keepachangelog.com/) format.

175 lines (174 loc) 6.15 kB
"use strict"; // Copyright 2018-2025 the Deno authors. MIT license. Object.defineProperty(exports, "__esModule", { value: true }); exports.MAX_LENGTH = exports.COMPARATOR_REGEXP = exports.OPERATOR_XRANGE_REGEXP = exports.XRANGE = exports.FULL_REGEXP = void 0; exports.compareNumber = compareNumber; exports.checkIdentifier = checkIdentifier; exports.compareIdentifier = compareIdentifier; exports.isValidNumber = isValidNumber; exports.isValidString = isValidString; exports.parsePrerelease = parsePrerelease; exports.parseBuild = parseBuild; exports.parseNumber = parseNumber; exports.isWildcardComparator = isWildcardComparator; function compareNumber(a, b) { if (isNaN(a) || isNaN(b)) { throw new Error("Cannot compare against non-numbers"); } return a === b ? 0 : a < b ? -1 : 1; } function checkIdentifier(v1 = [], v2 = []) { // NOT having a prerelease is > having one // But NOT having a build is < having one if (v1.length && !v2.length) return -1; if (!v1.length && v2.length) return 1; return 0; } function compareIdentifier(v1 = [], v2 = []) { const length = Math.max(v1.length, v2.length); for (let i = 0; i < length; i++) { const a = v1[i]; const b = v2[i]; // same length is equal if (a === undefined && b === undefined) return 0; // longer > shorter if (b === undefined) return 1; // shorter < longer if (a === undefined) return -1; // string > number if (typeof a === "string" && typeof b === "number") return 1; // number < string if (typeof a === "number" && typeof b === "string") return -1; if (a < b) return -1; if (a > b) return 1; // If they're equal, continue comparing segments. } return 0; } /** * A single `0`, or a non-zero digit followed by zero or more digits. */ const NUMERIC_IDENTIFIER = "0|[1-9]\\d*"; /** * Zero or more digits, followed by a letter or hyphen, and then zero or more letters, digits, or hyphens. */ const NON_NUMERIC_IDENTIFIER = "\\d*[a-zA-Z-][a-zA-Z0-9-]*"; /** * Three dot-separated numeric identifiers. */ const VERSION_CORE = `(?<major>${NUMERIC_IDENTIFIER})\\.(?<minor>${NUMERIC_IDENTIFIER})\\.(?<patch>${NUMERIC_IDENTIFIER})`; /** * A numeric identifier, or a non-numeric identifier. */ const PRERELEASE_IDENTIFIER = `(?:${NUMERIC_IDENTIFIER}|${NON_NUMERIC_IDENTIFIER})`; /** * A hyphen, followed by one or more dot-separated pre-release version identifiers. * @example "-pre.release" */ const PRERELEASE = `(?:-(?<prerelease>${PRERELEASE_IDENTIFIER}(?:\\.${PRERELEASE_IDENTIFIER})*))`; /** * Any combination of digits, letters, or hyphens. */ const BUILD_IDENTIFIER = "[0-9A-Za-z-]+"; /** * A plus sign, followed by one or more period-separated build metadata identifiers. * @example "+build.meta" */ const BUILD = `(?:\\+(?<buildmetadata>${BUILD_IDENTIFIER}(?:\\.${BUILD_IDENTIFIER})*))`; /** * A version, followed optionally by a pre-release version and build metadata. */ const FULL_VERSION = `v?${VERSION_CORE}${PRERELEASE}?${BUILD}?`; exports.FULL_REGEXP = new RegExp(`^${FULL_VERSION}$`); /** * A comparator. * @example `=`, `<`, `<=`, `>`, `>=` */ const COMPARATOR = "(?:<|>)?=?"; /** * A wildcard identifier. * @example "x", "X", "*" */ const WILDCARD_IDENTIFIER = `x|X|\\*`; const XRANGE_IDENTIFIER = `${NUMERIC_IDENTIFIER}|${WILDCARD_IDENTIFIER}`; /** * A version that can contain wildcards. * @example "x", "1.x", "1.x.x", "1.2.x", "*", "1.*", "1.*.*", "1.2.*" */ exports.XRANGE = `[v=\\s]*(?<major>${XRANGE_IDENTIFIER})(?:\\.(?<minor>${XRANGE_IDENTIFIER})(?:\\.(?<patch>${XRANGE_IDENTIFIER})${PRERELEASE}?${BUILD}?)?)?`; /** * An operator (`~`, `~>`, `^`, `=`, `<`, `<=`, `>`, or `>=`), followed by an x-range. * @example "~1.x.x", "^1.2.*", ">=1.2.3" */ exports.OPERATOR_XRANGE_REGEXP = new RegExp(`^(?<operator>~>?|\\^|${COMPARATOR})\\s*${exports.XRANGE}$`); /** * An empty string or a comparator (`=`, `<`, `<=`, `>`, or `>=`), followed by a version. * @example ">1.2.3" */ exports.COMPARATOR_REGEXP = new RegExp(`^(?<operator>${COMPARATOR})\\s*(${FULL_VERSION})$|^$`); /** * Returns true if the value is a valid SemVer number. * * Must be a number. Must not be NaN. Can be positive or negative infinity. * Can be between 0 and MAX_SAFE_INTEGER. * @param value The value to check * @returns True if its a valid semver number */ function isValidNumber(value) { return (typeof value === "number" && !Number.isNaN(value) && (!Number.isFinite(value) || (0 <= value && value <= Number.MAX_SAFE_INTEGER))); } exports.MAX_LENGTH = 256; /** * Returns true if the value is a valid semver pre-release or build identifier. * * Must be a string. Must be between 1 and 256 characters long. Must match * the regular expression /[0-9A-Za-z-]+/. * @param value The value to check * @returns True if the value is a valid semver string. */ function isValidString(value) { return (typeof value === "string" && value.length > 0 && value.length <= exports.MAX_LENGTH && /[0-9A-Za-z-]+/.test(value)); } const NUMERIC_IDENTIFIER_REGEXP = new RegExp(`^${NUMERIC_IDENTIFIER}$`); function parsePrerelease(prerelease) { return prerelease .split(".") .filter(Boolean) .map((id) => { if (NUMERIC_IDENTIFIER_REGEXP.test(id)) { const number = Number(id); if (isValidNumber(number)) return number; } return id; }); } function parseBuild(buildmetadata) { return buildmetadata.split(".").filter(Boolean); } function parseNumber(input, errorMessage) { const number = Number(input); if (!isValidNumber(number)) throw new TypeError(errorMessage); return number; } function isWildcardComparator(c) { return (Number.isNaN(c.major) && Number.isNaN(c.minor) && Number.isNaN(c.patch) && (c.prerelease === undefined || c.prerelease.length === 0) && (c.build === undefined || c.build.length === 0)); }