Files
renovate/lib/modules/manager/custom/regex/strategies.ts
Sebastian Poxhofer c32e2391a0 refactor(linters): add prefer-coerce-object rule (#45493)
Refs #45216

The object counterpart to `prefer-coerce-array`: `x ?? {}` becomes
`coerceObject(x)`. The rule flags a `??` whose default is a bare `{}`
literal, so a populated default or an asserted `({} as Foo)` still
passes; a `x ?? y ?? {}` chain maps onto `coerceObject(x, y)`.

Every hit across `lib/` converted cleanly, so no disable comments were
needed.
2026-08-28 09:27:59 +00:00

124 lines
3.4 KiB
TypeScript

import { isTruthy } from '@sindresorhus/is';
import { coerceObject } from '../../../../util/object.ts';
import { regEx } from '../../../../util/regex.ts';
import type { PackageDependency } from '../../types.ts';
import { checkIsValidDependency } from '../utils.ts';
import type {
PackageFileInfo,
RecursionParameter,
RegexManagerConfig,
} from './types.ts';
import {
createDependency,
mergeExtractionTemplate,
mergeGroups,
regexMatchAll,
} from './utils.ts';
export function handleAny(
config: RegexManagerConfig,
packageFileInfo: PackageFileInfo,
): PackageDependency[] {
const { content, packageFile } = packageFileInfo;
return config.matchStrings
.map((matchString) => regEx(matchString, 'g'))
.flatMap((regex) => regexMatchAll(regex, content)) // match all regex to content, get all matches, reduce to single array
.map((matchResult) =>
createDependency(
{
groups: coerceObject(matchResult.groups),
replaceString: matchResult[0],
},
config,
packageFileInfo,
),
)
.filter(isTruthy)
.filter((dep: PackageDependency) =>
checkIsValidDependency(dep, packageFile, 'regex'),
);
}
export function handleCombination(
config: RegexManagerConfig,
packageFileInfo: PackageFileInfo,
): PackageDependency[] {
const { content, packageFile } = packageFileInfo;
const matches = config.matchStrings
.map((matchString) => regEx(matchString, 'g'))
.flatMap((regex) => regexMatchAll(regex, content)); // match all regex to content, get all matches, reduce to single array
if (!matches.length) {
return [];
}
const extraction = matches
.map((match) => ({
groups: coerceObject(match.groups),
replaceString:
(match?.groups?.currentValue ?? match?.groups?.currentDigest)
? match[0]
: undefined,
}))
.reduce((base, addition) => mergeExtractionTemplate(base, addition));
return [createDependency(extraction, config, packageFileInfo)]
.filter(isTruthy)
.filter((dep: PackageDependency) =>
checkIsValidDependency(dep, packageFile, 'regex'),
);
}
export function handleRecursive(
config: RegexManagerConfig,
packageFileInfo: PackageFileInfo,
): PackageDependency[] {
const { content, packageFile } = packageFileInfo;
const regexes = config.matchStrings.map((matchString) =>
regEx(matchString, 'g'),
);
return processRecursive({
content,
packageFileInfo,
config,
index: 0,
combinedGroups: {},
regexes,
})
.filter(isTruthy)
.filter((dep: PackageDependency) =>
checkIsValidDependency(dep, packageFile, 'regex'),
);
}
function processRecursive(parameters: RecursionParameter): PackageDependency[] {
const {
content,
index,
combinedGroups,
regexes,
config,
packageFileInfo,
}: RecursionParameter = parameters;
// abort if we have no matchString anymore
if (regexes.length === index) {
const result = createDependency(
{
groups: combinedGroups,
replaceString: content,
},
config,
packageFileInfo,
);
return result ? [result] : /* istanbul ignore next: can this happen? */ [];
}
return regexMatchAll(regexes[index], content).flatMap((match) => {
return processRecursive({
...parameters,
content: match[0],
index: index + 1,
combinedGroups: mergeGroups(combinedGroups, coerceObject(match.groups)),
});
});
}