import { logger } from '../../../../logger/index.ts'; import { EQUAL, GT, GTE, LT, LTE, NOT_EQUAL, PGTE } from '../operator.ts'; import { parseRanges, satisfiesRange, stringifyRanges } from '../range.ts'; import type { Range } from '../types.ts'; import { adapt, decrement, floor, increment } from '../version.ts'; // Common logic for replace, widen, and bump strategies // It basically makes the range stick to the new version. export function replacePart(part: Range, to: string): Range { const { operator, version: ver, companion } = part; switch (operator) { case LT: return { ...part, version: increment(ver, to) }; case LTE: return { ...part, version: to }; case PGTE: if (companion) { return { ...part, version: floor(adapt(to, ver)), companion: { ...companion, version: to }, }; } return { ...part, version: floor(adapt(to, ver)) }; case GT: return { ...part, version: decrement(to) }; case GTE: case EQUAL: return { ...part, version: to }; case NOT_EQUAL: return part; // istanbul ignore next default: logger.warn({ operator }, `Unsupported ruby versioning operator`); return { operator: '', delimiter: ' ', version: '' }; } } export default ({ range, to }: { range: string; to: string }): string => { const parts = parseRanges(range).map((part): Range => { if (satisfiesRange(to, part)) { // The new version satisfies the range. Keep it as-is. // Note that consecutive `~>` and `>=` parts are combined into one Range object, // therefore both parts are updated if the new version violates one of them. return part; } if (part.version.split('.').length > to.split('.').length) { const diff = part.version.split('.').length - to.split('.').length; const versionToReplace = [to, ...Array(diff).fill('0')].join('.'); const replacement = replacePart(part, versionToReplace); return { ...replacement, version: replacement.version.split('.').slice(0, -diff).join('.'), }; } return replacePart(part, to); }); return stringifyRanges(parts); };