Files
renovate/lib/modules/manager/maven/update.ts
Sebastian Poxhofer 9717259ffc refactor(oxlint): enable no-else-return and unicorn/no-lonely-if (#44553)
Enable no-else-return (with allowElseIf: false) and unicorn/no-lonely-if
as errors in .oxlintrc.json and fix all 131 pre-existing violations
(91 no-else-return, 40 unicorn/no-lonely-if) across 98 files.

The no-else-return violations were fixed with the oxlint autofix
(iterated to a fixpoint, plus two else-if chains that required
--fix-dangerously) followed by Prettier formatting. One comment dropped
by the autofix in lib/modules/manager/pub/artifacts.ts was restored
manually. The unicorn/no-lonely-if violations were merged by hand,
preserving comments and coverage-ignore hints.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 12:11:21 +00:00

219 lines
6.8 KiB
TypeScript

import { isString } from '@sindresorhus/is';
import type { ReleaseType } from 'semver';
import semver from 'semver';
import { XmlDocument } from 'xmldoc';
import { logger } from '../../../logger/index.ts';
import { replaceAt } from '../../../util/string.ts';
import type {
BumpPackageVersionResult,
UpdateDependencyConfig,
Upgrade,
} from '../types.ts';
export function updateAtPosition(
fileContent: string,
upgrade: Upgrade,
endingAnchor: string,
): string | null {
const { depName, newName, currentValue, newValue, fileReplacePosition } =
upgrade;
let leftPart = fileContent.slice(0, fileReplacePosition);
const rightPart = fileContent.slice(fileReplacePosition);
const versionClosePosition = rightPart.indexOf(endingAnchor);
let restPart = rightPart.slice(versionClosePosition);
const versionPart = rightPart.slice(0, versionClosePosition);
const version = versionPart.trim();
if (newName) {
const blockStart = Math.max(
leftPart.lastIndexOf('<parent'),
leftPart.lastIndexOf('<dependency'),
leftPart.lastIndexOf('<plugin'),
leftPart.lastIndexOf('<extension'),
);
let leftBlock = leftPart.slice(blockStart);
const blockEnd = Math.min(
restPart.indexOf('</parent'),
restPart.indexOf('</dependency'),
restPart.indexOf('</plugin'),
restPart.indexOf('</extension'),
);
let rightBlock = restPart.slice(0, blockEnd);
const [groupId, artifactId] = depName!.split(':', 2);
const [newGroupId, newArtifactId] = newName.split(':', 2);
if (leftBlock.indexOf('<groupId') > 0) {
leftBlock = updateValue(leftBlock, 'groupId', groupId, newGroupId);
} else {
rightBlock = updateValue(rightBlock, 'groupId', groupId, newGroupId);
}
if (leftBlock.indexOf('<artifactId') > 0) {
leftBlock = updateValue(
leftBlock,
'artifactId',
artifactId,
newArtifactId,
);
} else {
rightBlock = updateValue(
rightBlock,
'artifactId',
artifactId,
newArtifactId,
);
}
leftPart = leftPart.slice(0, blockStart) + leftBlock;
restPart = rightBlock + restPart.slice(blockEnd);
} else if (version === newValue) {
return fileContent;
}
if (version === currentValue || upgrade.sharedVariableName) {
// TODO: validate newValue (#22198)
const replacedPart = versionPart.replace(version, newValue!);
return leftPart + replacedPart + restPart;
}
if (
upgrade.datasource === 'docker' ||
upgrade.datasource === 'buildpacks-registry'
) {
// In contrast to maven dependencies, cloud native buildpacks are not contained in specific version tags.
// Instead they are contained in the value of the buildpack tag and we have to update it differently.
let replacedPart = version;
if (currentValue) {
replacedPart = version.replace(currentValue, newValue!);
}
if (upgrade.currentDigest && upgrade.newDigest) {
replacedPart = replacedPart.replace(
upgrade.currentDigest,
upgrade.newDigest,
);
}
if (replacedPart !== version) {
return leftPart + replacedPart + restPart;
}
}
logger.debug({ depName, version, currentValue, newValue }, 'Unknown value');
return null;
}
export function updateDependency({
fileContent,
upgrade,
}: UpdateDependencyConfig): string | null {
const offset = fileContent.indexOf('<');
const spaces = fileContent.slice(0, offset);
const restContent = fileContent.slice(offset);
const updatedContent = updateAtPosition(restContent, upgrade, '</');
if (!updatedContent) {
return null;
}
if (updatedContent === restContent) {
return fileContent;
}
return `${spaces}${updatedContent}`;
}
export function bumpPackageVersion(
content: string,
currentValue: string,
bumpVersion: ReleaseType,
): BumpPackageVersionResult {
logger.debug(
{ bumpVersion, currentValue },
'Checking if we should bump pom.xml version',
);
let bumpedContent = content;
if (!semver.valid(currentValue)) {
logger.warn(
{ currentValue },
'Unable to bump pom.xml version, not a valid semver',
);
return { bumpedContent };
}
try {
const project = new XmlDocument(content);
const versionNode = project.childNamed('version')!;
const startTagPosition = versionNode.startTagPosition!; // TODO: should not be null
const versionPosition = content.indexOf(versionNode.val, startTagPosition);
let newPomVersion: string | null = null;
const currentPrereleaseValue = semver.prerelease(currentValue);
if (isSnapshot(currentPrereleaseValue)) {
// It is already a SNAPSHOT version.
// Therefore the same qualifier (prerelease) will be used as before.
let releaseType = bumpVersion;
if (!bumpVersion.startsWith('pre')) {
releaseType = `pre${bumpVersion}` as ReleaseType;
}
newPomVersion = semver.inc(
currentValue,
releaseType,
currentPrereleaseValue!.join('.'),
false,
);
} else if (currentPrereleaseValue) {
// Some qualifier which is not a SNAPSHOT is present.
// The expected behaviour in this case is unclear and the standard increase will be used.
newPomVersion = semver.inc(currentValue, bumpVersion);
} else {
// A release version without any qualifier is present.
// Therefore the SNAPSHOT qualifier will be added if a prerelease is requested.
// This will do a normal increment, ignoring SNAPSHOT, if a non-prerelease bumpVersion is configured
newPomVersion = semver.inc(currentValue, bumpVersion, 'SNAPSHOT', false);
}
if (!newPomVersion) {
throw new Error('semver inc failed');
}
logger.debug({ newPomVersion });
bumpedContent = replaceAt(
content,
versionPosition,
currentValue,
newPomVersion,
);
if (bumpedContent === content) {
logger.debug('Version was already bumped');
} else {
logger.debug('pom.xml version bumped');
}
} catch {
logger.warn(
{
content,
currentValue,
bumpVersion,
},
'Failed to bumpVersion',
);
}
return { bumpedContent };
}
function isSnapshot(prerelease: readonly (string | number)[] | null): boolean {
const lastPart = prerelease?.at(-1);
return isString(lastPart) && lastPart.endsWith('SNAPSHOT');
}
function updateValue(
content: string,
nodeName: string,
oldValue: string,
newValue: string,
): string {
const elementStart = content.indexOf(`<${nodeName}`);
const start = content.indexOf('>', elementStart) + 1;
const end = content.indexOf(`</${nodeName}`, start);
const elementContent = content.slice(start, end);
if (elementContent.trim() === oldValue) {
return (
content.slice(0, start) +
elementContent.replace(oldValue, newValue) +
content.slice(end)
);
}
logger.debug({ content, nodeName, oldValue, newValue }, 'Unknown value');
return content;
}