mirror of
https://github.com/renovatebot/renovate.git
synced 2026-11-01 18:19:51 +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
132 lines
4.7 KiB
TypeScript
132 lines
4.7 KiB
TypeScript
import { regEx } from '../../../util/regex.ts';
|
|
import { GenericVersioningApi } from '../generic.ts';
|
|
import type { VersioningApi } from '../types.ts';
|
|
import type { DebVersion } from './types.ts';
|
|
|
|
export const id = 'deb';
|
|
export const displayName = 'Deb version';
|
|
export const urls = [
|
|
'[Debian policy - Version](https://www.debian.org/doc/debian-policy/ch-controlfields.html#version)',
|
|
'[deb-version man page](https://manpages.debian.org/unstable/dpkg-dev/deb-version.7.en.html)',
|
|
];
|
|
export const supportsRanges = false;
|
|
|
|
const epochPattern = regEx(/^\d+$/);
|
|
const upstreamVersionPattern = regEx(/^[-+.:~A-Za-z\d]+$/);
|
|
const debianRevisionPattern = regEx(/^[+.~A-Za-z\d]*$/);
|
|
const numericPattern = regEx(/\d+/g);
|
|
const characterOrder =
|
|
'~ ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-.:';
|
|
const numericChars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
|
|
|
class DebVersioningApi extends GenericVersioningApi {
|
|
protected _parse(version: string): DebVersion | null {
|
|
/* Splitting version into "[epoch:]upstream-version[-debian-revision]"
|
|
All found numbers are exported as release info */
|
|
|
|
// split of first element by `:` as epoch:
|
|
const epochSplit = version.split(':');
|
|
const epochStr = epochSplit.length > 1 ? epochSplit.shift()! : '0';
|
|
const remainingVersion = epochSplit.join(':');
|
|
|
|
// split of last element by `-`
|
|
if (remainingVersion.endsWith('-')) {
|
|
// Forbid debian revision (it would result in `2.4.0-` == `2.4.0`)
|
|
return null;
|
|
}
|
|
const debianSplit = remainingVersion.split('-');
|
|
const debianRevision = debianSplit.length > 1 ? debianSplit.pop()! : '';
|
|
const upstreamVersion = debianSplit.join('-');
|
|
|
|
if (
|
|
!epochPattern.test(epochStr) ||
|
|
!upstreamVersionPattern.test(upstreamVersion) ||
|
|
!debianRevisionPattern.test(debianRevision)
|
|
) {
|
|
return null;
|
|
}
|
|
const release = [...remainingVersion.matchAll(numericPattern)].map((m) =>
|
|
parseInt(m[0], 10),
|
|
);
|
|
return {
|
|
epoch: parseInt(epochStr, 10),
|
|
upstreamVersion,
|
|
debianRevision,
|
|
release,
|
|
suffix: debianRevision,
|
|
};
|
|
}
|
|
|
|
protected _compare_string(a: string, b: string): number {
|
|
/* Special string sorting based on official specification:
|
|
* https://manpages.debian.org/unstable/dpkg-dev/deb-version.7.en.html#Sorting_algorithm
|
|
* The string is compared by continuous blocks of a) non-digit and b) digit characters.
|
|
* Non-digit blocks are compared lexicographically with a custom character order.
|
|
* Digit blocks are compared numerically.
|
|
* We are alternating between both modes until a difference is found.
|
|
*/
|
|
let charPos = 0;
|
|
while (charPos < a.length || charPos < b.length) {
|
|
const aChar = a.charAt(charPos);
|
|
const bChar = b.charAt(charPos);
|
|
if (numericChars.includes(aChar) && numericChars.includes(bChar)) {
|
|
// numeric comparison of the whole block
|
|
let aNumericEnd = charPos + 1;
|
|
while (numericChars.includes(a.charAt(aNumericEnd))) {
|
|
aNumericEnd += 1;
|
|
}
|
|
let bNumericEnd = charPos + 1;
|
|
while (numericChars.includes(b.charAt(bNumericEnd))) {
|
|
bNumericEnd += 1;
|
|
}
|
|
const numericCmp = a
|
|
.substring(charPos, aNumericEnd)
|
|
.localeCompare(b.substring(charPos, bNumericEnd), undefined, {
|
|
numeric: true,
|
|
});
|
|
if (numericCmp !== 0) {
|
|
return numericCmp;
|
|
}
|
|
charPos = aNumericEnd; // the same as bNumericEnd as both are the same
|
|
continue;
|
|
}
|
|
if (aChar !== bChar) {
|
|
// Lexicographical comparison
|
|
// numeric character is treated like end of string (they are part of a new block)
|
|
const aPriority = characterOrder.indexOf(
|
|
numericChars.includes(aChar) || aChar === '' ? ' ' : aChar,
|
|
);
|
|
const bPriority = characterOrder.indexOf(
|
|
numericChars.includes(bChar) || bChar === '' ? ' ' : bChar,
|
|
);
|
|
return Math.sign(aPriority - bPriority);
|
|
}
|
|
charPos += 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
protected override _compare(version: string, other: string): number {
|
|
const parsed1 = this._parse(version);
|
|
const parsed2 = this._parse(other);
|
|
if (!(parsed1 && parsed2)) {
|
|
return 1;
|
|
}
|
|
if (parsed1.epoch !== parsed2.epoch) {
|
|
return Math.sign(parsed1.epoch - parsed2.epoch);
|
|
}
|
|
const upstreamVersionDifference = this._compare_string(
|
|
parsed1.upstreamVersion,
|
|
parsed2.upstreamVersion,
|
|
);
|
|
if (upstreamVersionDifference !== 0) {
|
|
return upstreamVersionDifference;
|
|
}
|
|
return this._compare_string(parsed1.debianRevision, parsed2.debianRevision);
|
|
}
|
|
}
|
|
|
|
export const api: VersioningApi = new DebVersioningApi();
|
|
|
|
export default api;
|