mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-27 15:58:48 +00:00
e0cec3dc95
feat(lint): add `types-location` rule and enable it for `lib/modules`
Reviewers consistently ask for exported types to be moved into a
colocated `types.ts`. Add a `renovate/types-location` oxlint rule that
reports `export interface` / `export type` declarations outside such a
file, and turn it on across `lib/modules`.
Re-exports (`export type { X }`, `export type *`), file-local types, and
values (enums, classes, functions) are left alone. `schema.ts` files and
`schema/` directories are excluded via config, matching how
`zod-schema-location` is configured — inferred types belong next to the
schema they are derived from.
Relocating the 87 existing violations means:
- new `types.ts` files for the modules that had none
- `terragrunt/common.ts` folded into its `types.ts`, as it held nothing
else
- `RangeBound` in `versioning/gradle/compare.ts` un-exported, since only
a file-local interface referenced it
- `bazel-module/parser/fragments.ts` split from its inferred types, which
now live in `parser/types.ts` and infer through a namespace import so
each schema keeps its merged name
338 lines
8.8 KiB
TypeScript
338 lines
8.8 KiB
TypeScript
import type { RangeStrategy } from '../../../types/versioning.ts';
|
|
import { parseVersion } from '../nuget/parser.ts';
|
|
import type { NugetVersion } from '../nuget/types.ts';
|
|
import type { NewValueConfig, VersioningApi } from '../types.ts';
|
|
import {
|
|
adaptToPrecision,
|
|
bumpAtPrecision,
|
|
ensurePrereleaseMatches,
|
|
intervalOf,
|
|
isLessThanLowerBound,
|
|
matches as matchesRange,
|
|
parseRange,
|
|
rangeToString,
|
|
releaseParts,
|
|
twiddle,
|
|
} from './range.ts';
|
|
import type { PaketConstraint, PaketRange } from './types.ts';
|
|
import { compare, sameReleaseParts } from './version.ts';
|
|
|
|
export const id = 'paket';
|
|
export const displayName = 'Paket';
|
|
export const urls = [
|
|
'[Paket version constraints](https://fsprojects.github.io/Paket/nuget-dependencies.html#Version-constraints)',
|
|
'[NuGet package versioning](https://learn.microsoft.com/nuget/concepts/package-versioning)',
|
|
];
|
|
export const supportsRanges = true;
|
|
export const supportedRangeStrategies: RangeStrategy[] = [
|
|
'bump',
|
|
'widen',
|
|
'replace',
|
|
'update-lockfile',
|
|
];
|
|
|
|
function isVersion(input: string | undefined | null): boolean {
|
|
return !!input && parseVersion(input) !== null;
|
|
}
|
|
|
|
function isValid(input: string): boolean {
|
|
return parseRange(input) !== null;
|
|
}
|
|
|
|
function isSingleVersionRange(range: PaketRange): boolean {
|
|
if (range.constraints.length !== 1 || range.prereleaseTags.length > 0) {
|
|
return false;
|
|
}
|
|
|
|
const { operator } = range.constraints[0];
|
|
return operator === '' || operator === '=' || operator === '==';
|
|
}
|
|
|
|
function isSingleVersion(input: string): boolean {
|
|
const range = parseRange(input);
|
|
return !!range && isSingleVersionRange(range);
|
|
}
|
|
|
|
function isStable(input: string): boolean {
|
|
const version = parseVersion(input);
|
|
if (version) {
|
|
return !version.prerelease;
|
|
}
|
|
|
|
const range = parseRange(input);
|
|
return (
|
|
!!range &&
|
|
isSingleVersionRange(range) &&
|
|
!range.constraints[0].version.prerelease
|
|
);
|
|
}
|
|
|
|
function isCompatible(version: string): boolean {
|
|
return isVersion(version);
|
|
}
|
|
|
|
function getMajor(version: string): number | null {
|
|
return parseVersion(version)?.major ?? null;
|
|
}
|
|
|
|
function getMinor(version: string): number | null {
|
|
return parseVersion(version)?.minor ?? null;
|
|
}
|
|
|
|
function getPatch(version: string): number | null {
|
|
return parseVersion(version)?.patch ?? null;
|
|
}
|
|
|
|
function compareVersions(version: string, other: string): number | null {
|
|
const x = parseVersion(version);
|
|
const y = parseVersion(other);
|
|
return x && y ? compare(x, y) : null;
|
|
}
|
|
|
|
function equals(version: string, other: string): boolean {
|
|
return compareVersions(version, other) === 0;
|
|
}
|
|
|
|
function isGreaterThan(version: string, other: string): boolean {
|
|
const cmp = compareVersions(version, other);
|
|
return cmp !== null && cmp > 0;
|
|
}
|
|
|
|
function sortVersions(version: string, other: string): number {
|
|
return compareVersions(version, other) ?? 0;
|
|
}
|
|
|
|
function matches(version: string, range: string): boolean {
|
|
const v = parseVersion(version);
|
|
const r = parseRange(range);
|
|
return !!v && !!r && matchesRange(v, r);
|
|
}
|
|
|
|
function isLessThanRange(version: string, range: string): boolean {
|
|
const v = parseVersion(version);
|
|
const r = parseRange(range);
|
|
return !!v && !!r && isLessThanLowerBound(v, r);
|
|
}
|
|
|
|
function satisfyingVersions(versions: string[], range: string): string[] {
|
|
return versions.filter((v) => matches(v, range)).sort(sortVersions);
|
|
}
|
|
|
|
function getSatisfyingVersion(
|
|
versions: string[],
|
|
range: string,
|
|
): string | null {
|
|
return satisfyingVersions(versions, range).pop() ?? null;
|
|
}
|
|
|
|
function minSatisfyingVersion(
|
|
versions: string[],
|
|
range: string,
|
|
): string | null {
|
|
return satisfyingVersions(versions, range).shift() ?? null;
|
|
}
|
|
|
|
function getPinnedValue(newVersion: string): string {
|
|
return newVersion;
|
|
}
|
|
|
|
function replaceConstraint(
|
|
constraint: PaketConstraint,
|
|
v: NugetVersion,
|
|
): PaketConstraint {
|
|
const { operator, version } = constraint;
|
|
switch (operator) {
|
|
case '':
|
|
case '=':
|
|
case '==':
|
|
return { operator, version: v };
|
|
case '>':
|
|
case '>=':
|
|
return constraint;
|
|
case '<=':
|
|
return compare(v, version) > 0 ? { operator, version: v } : constraint;
|
|
case '<':
|
|
return compare(v, version) >= 0 || sameReleaseParts(v, version)
|
|
? {
|
|
operator,
|
|
version: bumpAtPrecision(v, releaseParts(version).length),
|
|
}
|
|
: constraint;
|
|
case '~>': {
|
|
const cap = twiddle(version);
|
|
return compare(v, cap) >= 0 || sameReleaseParts(v, cap)
|
|
? {
|
|
operator,
|
|
version: adaptToPrecision(v, releaseParts(version).length),
|
|
}
|
|
: constraint;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Rewrite `~> band` optionally followed by a `>=`/`>` floor: the band follows
|
|
* the new version at its old precision, and the floor sticks to the exact new
|
|
* version unless it becomes redundant.
|
|
*/
|
|
function pessimisticWithFloor(range: PaketRange, v: NugetVersion): PaketRange {
|
|
const [pessimistic] = range.constraints;
|
|
const band = adaptToPrecision(v, releaseParts(pessimistic.version).length);
|
|
const constraints: PaketConstraint[] =
|
|
compare(band, v) === 0
|
|
? [{ operator: '~>', version: band }]
|
|
: [
|
|
{ operator: '~>', version: band },
|
|
{ operator: '>=', version: v },
|
|
];
|
|
return { ...range, constraints };
|
|
}
|
|
|
|
function replaceRange(range: PaketRange, v: NugetVersion): PaketRange {
|
|
const [first, second] = range.constraints;
|
|
if (
|
|
first.operator === '~>' &&
|
|
(second?.operator === '>' || second?.operator === '>=')
|
|
) {
|
|
return pessimisticWithFloor(range, v);
|
|
}
|
|
|
|
return {
|
|
...range,
|
|
constraints: range.constraints.map((c) => replaceConstraint(c, v)),
|
|
};
|
|
}
|
|
|
|
function bumpConstraint(
|
|
constraint: PaketConstraint,
|
|
v: NugetVersion,
|
|
): PaketConstraint {
|
|
const { operator, version } = constraint;
|
|
switch (operator) {
|
|
case '>=':
|
|
return compare(v, version) > 0 ? { operator, version: v } : constraint;
|
|
case '>':
|
|
// the lower bound guard in `getNewValue` ensures `v` is above `version`
|
|
return { operator: '>=', version: v };
|
|
case '~>': {
|
|
const adapted = adaptToPrecision(v, releaseParts(version).length);
|
|
return compare(adapted, version) > 0
|
|
? { operator, version: adapted }
|
|
: constraint;
|
|
}
|
|
default:
|
|
return replaceConstraint(constraint, v);
|
|
}
|
|
}
|
|
|
|
function bumpRange(range: PaketRange, v: NugetVersion): PaketRange {
|
|
const [first, second] = range.constraints;
|
|
if (
|
|
first.operator === '~>' &&
|
|
second?.operator !== '<' &&
|
|
second?.operator !== '<='
|
|
) {
|
|
return pessimisticWithFloor(range, v);
|
|
}
|
|
|
|
return {
|
|
...range,
|
|
constraints: range.constraints.map((c) => bumpConstraint(c, v)),
|
|
};
|
|
}
|
|
|
|
function widenRange(range: PaketRange, v: NugetVersion): PaketRange {
|
|
const interval = intervalOf(range.constraints);
|
|
const [first] = range.constraints;
|
|
|
|
if (interval.kind !== 'range' || first.operator !== '~>') {
|
|
return {
|
|
...range,
|
|
constraints: range.constraints.map((c) => replaceConstraint(c, v)),
|
|
};
|
|
}
|
|
|
|
const { from, fromInclusive, to, toInclusive } = interval;
|
|
const upperViolated = toInclusive
|
|
? compare(v, to) > 0
|
|
: compare(v, to) >= 0 || sameReleaseParts(v, to);
|
|
if (!upperViolated) {
|
|
return range;
|
|
}
|
|
|
|
// The pessimistic operator acts as both bounds, so decompose it into
|
|
// explicit bounds to widen upwards while keeping the lower bound.
|
|
const lower: PaketConstraint = {
|
|
operator: fromInclusive ? '>=' : '>',
|
|
version: from,
|
|
};
|
|
const upper: PaketConstraint = toInclusive
|
|
? { operator: '<=', version: v }
|
|
: { operator: '<', version: bumpAtPrecision(v, releaseParts(to).length) };
|
|
return { ...range, constraints: [lower, upper] };
|
|
}
|
|
|
|
function getNewValue({
|
|
currentValue,
|
|
rangeStrategy,
|
|
newVersion,
|
|
}: NewValueConfig): string | null {
|
|
const v = parseVersion(newVersion);
|
|
if (!v) {
|
|
return null;
|
|
}
|
|
|
|
if (isVersion(currentValue)) {
|
|
return newVersion;
|
|
}
|
|
|
|
const range = parseRange(currentValue);
|
|
if (!range) {
|
|
return null;
|
|
}
|
|
|
|
if (rangeStrategy === 'pin') {
|
|
return `${range.strategy ?? ''}${newVersion}`;
|
|
}
|
|
|
|
if (isLessThanLowerBound(v, range)) {
|
|
return currentValue;
|
|
}
|
|
|
|
let result: PaketRange;
|
|
if (rangeStrategy === 'bump') {
|
|
result = bumpRange(range, v);
|
|
} else if (matchesRange(v, range)) {
|
|
return currentValue;
|
|
} else if (rangeStrategy === 'widen') {
|
|
result = widenRange(range, v);
|
|
} else {
|
|
result = replaceRange(range, v);
|
|
}
|
|
|
|
const newValue = rangeToString(ensurePrereleaseMatches(result, v));
|
|
return newValue === rangeToString(range) ? currentValue : newValue;
|
|
}
|
|
|
|
export const api: VersioningApi = {
|
|
equals,
|
|
getMajor,
|
|
getMinor,
|
|
getPatch,
|
|
getNewValue,
|
|
getPinnedValue,
|
|
getSatisfyingVersion,
|
|
minSatisfyingVersion,
|
|
isCompatible,
|
|
isGreaterThan,
|
|
isLessThanRange,
|
|
isSingleVersion,
|
|
isStable,
|
|
isValid,
|
|
isVersion,
|
|
matches,
|
|
sortVersions,
|
|
};
|
|
|
|
export default api;
|