UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

360 lines (359 loc) • 12.3 kB
import { regEx } from "../../../util/regex.js"; //#region lib/modules/versioning/maven/compare.ts const PREFIX_DOT = "PREFIX_DOT"; const PREFIX_HYPHEN = "PREFIX_HYPHEN"; const ALPHA_SUFFIX = "-alpha"; const TYPE_NUMBER = "TYPE_NUMBER"; const TYPE_QUALIFIER = "TYPE_QUALIFIER"; function iterateChars(str, cb) { let prev = null; let next = null; for (let i = 0; i < str.length; i += 1) { next = str.charAt(i); cb(prev, next); prev = next; } cb(prev, null); } function isDigit(char) { return regEx(/^\d$/).test(char); } function isLetter(char) { return regEx(/^[a-z_+]$/i).test(char); } function isTransition(prevChar, nextChar) { return isDigit(prevChar) && isLetter(nextChar) || isLetter(prevChar) && isDigit(nextChar); } function iterateTokens(versionStr, cb) { let currentPrefix = ""; let currentVal = ""; function yieldToken(transition = false) { const val = currentVal || "0"; if (regEx(/^\d+$/).test(val)) cb({ prefix: currentPrefix, type: TYPE_NUMBER, val: parseInt(val, 10), isTransition: transition }); else cb({ prefix: currentPrefix, type: TYPE_QUALIFIER, val, isTransition: transition }); } iterateChars(versionStr, (prevChar, nextChar) => { if (nextChar === null) yieldToken(); else if (nextChar === "-") { yieldToken(); currentPrefix = PREFIX_HYPHEN; currentVal = ""; } else if (nextChar === ".") { yieldToken(); currentPrefix = PREFIX_DOT; currentVal = ""; } else if (prevChar === null && (nextChar === "v" || nextChar === "V")) currentPrefix = nextChar; else if (currentVal !== "" && isTransition(prevChar, nextChar)) { yieldToken(true); currentPrefix = PREFIX_HYPHEN; currentVal = nextChar; } else if (currentVal === "" && (currentPrefix === "v" || currentPrefix === "V") && !isDigit(nextChar)) { currentVal = currentVal.concat(currentPrefix, nextChar); currentPrefix = ""; } else currentVal = currentVal.concat(nextChar); }); } function isNull(token) { const val = token.val; return val === 0 || val === "" || val === "final" || val === "ga" || val === "release" || val === "latest" || val === "sr"; } function tokenize(versionStr, preserveMinorZeroes = false) { let buf = []; let result = []; let leadingZero = true; iterateTokens(versionStr.toLowerCase(), (token) => { if (token.prefix === "PREFIX_HYPHEN" || token.type === "TYPE_QUALIFIER") buf = []; buf.push(token); if (!isNull(token)) { leadingZero = false; result = result.concat(buf); buf = []; } else if (leadingZero || preserveMinorZeroes) { result = result.concat(buf); buf = []; } }); return result; } function nullFor(token) { return token.type === "TYPE_NUMBER" ? { prefix: token.prefix, type: TYPE_NUMBER, val: 0 } : { prefix: token.prefix, type: TYPE_QUALIFIER, val: "" }; } function commonOrder(token) { if (token.type === "TYPE_QUALIFIER") return 1; if (token.prefix === "PREFIX_HYPHEN" && token.type === "TYPE_NUMBER") return 2; return 3; } const QualifierTypes = { Alpha: 1, Beta: 2, Milestone: 3, RC: 4, Snapshot: 5, Release: 6, SP: 7 }; function qualifierType(token) { const val = token.val; if (val === "alpha" || token.isTransition && val === "a") return QualifierTypes.Alpha; if (val === "beta" || token.isTransition && val === "b") return QualifierTypes.Beta; if (val === "milestone" || token.isTransition && val === "m") return QualifierTypes.Milestone; if (val === "rc" || val === "cr" || val === "preview") return QualifierTypes.RC; if (val === "snapshot" || val === "snap") return QualifierTypes.Snapshot; if (val === "" || val === "final" || val === "ga" || val === "release" || val === "latest" || val === "sr") return QualifierTypes.Release; if (val === "sp") return QualifierTypes.SP; return null; } function qualifierCmp(left, right) { const leftOrder = qualifierType(left); const rightOrder = qualifierType(right); if (leftOrder && rightOrder) { if (leftOrder < rightOrder) return -1; if (leftOrder > rightOrder) return 1; return 0; } if (leftOrder && leftOrder < QualifierTypes.Release) return -1; if (rightOrder && rightOrder < QualifierTypes.Release) return 1; if (left.val < right.val) return -1; if (left.val > right.val) return 1; return 0; } function tokenCmp(left, right) { const leftOrder = commonOrder(left); const rightOrder = commonOrder(right); if (leftOrder < rightOrder) return -1; if (leftOrder > rightOrder) return 1; if (left.type === "TYPE_NUMBER" && right.type === "TYPE_NUMBER") { if (left.val < right.val) return -1; if (left.val > right.val) return 1; return 0; } return qualifierCmp(left, right); } function compare(left, right) { const leftTokens = tokenize(left); const rightTokens = tokenize(right); const length = Math.max(leftTokens.length, rightTokens.length); for (let idx = 0; idx < length; idx += 1) { const cmpResult = tokenCmp(leftTokens[idx] || nullFor(rightTokens[idx]), rightTokens[idx] || nullFor(leftTokens[idx])); if (cmpResult !== 0) return cmpResult; } return 0; } function isVersion(version) { if (!version || typeof version !== "string") return false; if (!regEx(/^[-.a-z_+0-9]+$/i).test(version)) return false; if (regEx(/^[.-]/).test(version)) return false; if (regEx(/[.-]$/).test(version)) return false; if (["latest", "release"].includes(version.toLowerCase())) return false; return !!tokenize(version).length; } const INCLUDING_POINT = "INCLUDING_POINT"; const EXCLUDING_POINT = "EXCLUDING_POINT"; function parseRange(rangeStr) { function emptyInterval() { return { leftType: null, leftValue: null, leftBracket: null, rightType: null, rightValue: null, rightBracket: null }; } const commaSplit = rangeStr.split(regEx(/\s*,\s*/)); let ranges = []; let interval = emptyInterval(); commaSplit.forEach((subStr) => { if (!ranges) return; if (interval.leftType === null) if (regEx(/^\[.*]$/).test(subStr)) { const ver = subStr.slice(1, -1); ranges.push({ leftType: INCLUDING_POINT, leftValue: ver, leftBracket: "[", rightType: INCLUDING_POINT, rightValue: ver, rightBracket: "]" }); interval = emptyInterval(); } else if (subStr.startsWith("[")) { const ver = subStr.slice(1); interval.leftType = INCLUDING_POINT; interval.leftValue = ver; interval.leftBracket = "["; } else if (subStr.startsWith("(") || subStr.startsWith("]")) { const ver = subStr.slice(1); interval.leftType = EXCLUDING_POINT; interval.leftValue = ver; interval.leftBracket = subStr[0]; } else ranges = null; else if (subStr.endsWith("]")) { const ver = subStr.slice(0, -1); interval.rightType = INCLUDING_POINT; interval.rightValue = ver; interval.rightBracket = "]"; ranges.push(interval); interval = emptyInterval(); } else if (subStr.endsWith(")") || subStr.endsWith("[")) { const ver = subStr.slice(0, -1); interval.rightType = EXCLUDING_POINT; interval.rightValue = ver; interval.rightBracket = subStr.endsWith(")") ? ")" : "["; ranges.push(interval); interval = emptyInterval(); } else ranges = null; }); if (interval.leftType) return null; if (!ranges?.length) return null; const lastIdx = ranges.length - 1; let prevValue = null; const result = []; for (let idx = 0; idx < ranges.length; idx += 1) { const range = ranges[idx]; const { leftType, leftValue, rightType, rightValue } = range; if (idx === 0 && leftValue === "") { if (leftType === "EXCLUDING_POINT" && isVersion(rightValue)) { prevValue = rightValue; result.push({ ...range, leftValue: null }); continue; } return null; } if (idx === lastIdx && rightValue === "") { if (rightType === "EXCLUDING_POINT" && isVersion(leftValue)) { if (prevValue && compare(prevValue, leftValue) === 1) return null; result.push({ ...range, rightValue: null }); continue; } return null; } if (isVersion(leftValue) && isVersion(rightValue)) { if (compare(leftValue, rightValue) === 1) return null; if (prevValue && compare(prevValue, leftValue) === 1) return null; prevValue = rightValue; result.push(range); continue; } return null; } return result; } function isValid(str) { if (!str) return false; return isVersion(str) || !!parseRange(str); } function rangeToStr(fullRange) { if (fullRange === null) return null; const valToStr = (val) => val ?? ""; if (fullRange.length === 1) { const { leftBracket, rightBracket, leftValue, rightValue } = fullRange[0]; if (leftValue === rightValue && leftBracket === "[" && rightBracket === "]") return `[${valToStr(leftValue)}]`; } return fullRange.map((val) => [ val.leftBracket, valToStr(val.leftValue), ",", valToStr(val.rightValue), val.rightBracket ].join("")).join(","); } function tokensToStr(tokens) { let isTransition = false; return tokens.reduce((result, token) => { let prefix; if (token.prefix === "PREFIX_DOT") prefix = "."; else if (token.prefix === "PREFIX_HYPHEN") prefix = isTransition === true ? "" : "-"; else prefix = token.prefix; isTransition = token.isTransition; return `${result}${prefix}${token.val}`; }, ""); } function coerceRangeValue(prev, next) { const prevTokens = tokenize(prev, true); const nextTokens = tokenize(next, true); const resultTokens = nextTokens.slice(0, prevTokens.length); const align = Math.max(0, prevTokens.length - nextTokens.length); if (align > 0) resultTokens.push(...prevTokens.slice(prevTokens.length - align)); return tokensToStr(resultTokens); } function incrementRangeValue(value) { const tokens = tokenize(value); const lastToken = tokens[tokens.length - 1]; if (typeof lastToken.val === "number") { lastToken.val += 1; return coerceRangeValue(value, tokensToStr(tokens)); } return value; } function autoExtendMavenRange(currentRepresentation, newValue) { const range = parseRange(currentRepresentation); if (!range) return currentRepresentation; const isPoint = (vals) => { if (vals.length !== 1) return false; const { leftType, leftValue, rightType, rightValue } = vals[0]; return leftType === "INCLUDING_POINT" && leftType === rightType && leftValue === rightValue; }; if (isPoint(range)) return `[${newValue}]`; const interval = [...range].reverse().find((elem) => { const { rightType, rightValue } = elem; return rightValue === null || rightType === "INCLUDING_POINT" && compare(rightValue, newValue) === -1 || rightType === "EXCLUDING_POINT" && compare(rightValue, newValue) !== 1; }); if (!interval) return currentRepresentation; const { leftValue, rightValue } = interval; if (leftValue !== null && rightValue !== null && incrementRangeValue(leftValue) === rightValue) { if (compare(newValue, leftValue) !== -1) { interval.leftValue = coerceRangeValue(leftValue, newValue); interval.rightValue = incrementRangeValue(interval.leftValue); } } else if (leftValue !== null && rightValue !== null && incrementRangeValue(leftValue) + ALPHA_SUFFIX === rightValue) { interval.leftValue = coerceRangeValue(leftValue, newValue); interval.rightValue = incrementRangeValue(interval.leftValue) + ALPHA_SUFFIX; } else if (rightValue !== null) if (interval.rightType === "INCLUDING_POINT") { const tokens = tokenize(rightValue); if (typeof tokens[tokens.length - 1].val === "number") interval.rightValue = coerceRangeValue(rightValue, newValue); else interval.rightValue = newValue; } else interval.rightValue = incrementRangeValue(coerceRangeValue(rightValue, newValue)); else if (leftValue !== null) interval.leftValue = coerceRangeValue(leftValue, newValue); return rangeToStr(range); } function isSubversion(majorVersion, minorVersion) { const majorTokens = tokenize(majorVersion); const minorTokens = tokenize(minorVersion); let result = true; const len = majorTokens.length; for (let idx = 0; idx < len; idx += 1) { const major = majorTokens[idx]; if (tokenCmp(major, minorTokens[idx] || nullFor(majorTokens[idx])) !== 0) { result = false; break; } } return result; } //#endregion export { EXCLUDING_POINT, QualifierTypes, TYPE_NUMBER, TYPE_QUALIFIER, autoExtendMavenRange, compare, isSubversion, isValid, isVersion, parseRange, qualifierType, rangeToStr, tokenize }; //# sourceMappingURL=compare.js.map