Files
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

132 lines
4.0 KiB
TypeScript

import { isNonEmptyArray, isNullOrUndefined } from '@sindresorhus/is';
import { parsePreset } from '../../../config/presets/parse.ts';
import type { ParsedPreset } from '../../../config/presets/types.ts';
import { logger } from '../../../logger/index.ts';
import { coerceArray } from '../../../util/array.ts';
import { getToolConfig } from '../../../util/exec/containerbase.ts';
import { isToolName } from '../../../util/exec/types.ts';
import { coerceObject } from '../../../util/object.ts';
import { GiteaTagsDatasource } from '../../datasource/gitea-tags/index.ts';
import { GithubTagsDatasource } from '../../datasource/github-tags/index.ts';
import { GitlabTagsDatasource } from '../../datasource/gitlab-tags/index.ts';
import type { PackageDependency, PackageFileContent } from '../types.ts';
import { RenovateJson } from './schema.ts';
const supportedPresetSources: Record<string, string> = {
github: GithubTagsDatasource.id,
gitlab: GitlabTagsDatasource.id,
gitea: GiteaTagsDatasource.id,
};
export function extractPackageFile(
content: string,
packageFile: string,
): PackageFileContent | null {
logger.trace(`renovate-config.extractPackageFile(${packageFile})`);
const config = RenovateJson.safeParse(content);
if (!config.success) {
logger.debug({ packageFile, err: config.error }, 'Invalid Renovate Config');
return null;
}
const deps: PackageDependency[] = [];
for (const preset of coerceArray(config.data.extends)) {
if (preset.includes('{{')) {
// templated presets are only resolvable at runtime
continue;
}
let parsedPreset: ParsedPreset;
try {
parsedPreset = parsePreset(preset);
} catch (err) {
logger.debug({ preset, err }, 'Failed to parse preset');
deps.push({
depName: preset,
skipReason: 'invalid-value',
});
continue;
}
const datasource = supportedPresetSources[parsedPreset.presetSource];
if (isNullOrUndefined(datasource)) {
if (parsedPreset.presetSource !== 'internal') {
deps.push({
// relative references have no repository of their own
depName:
parsedPreset.presetSource === 'relative'
? preset
: parsedPreset.repo,
skipReason: 'unsupported-datasource',
});
}
continue;
}
if (isNullOrUndefined(parsedPreset.tag)) {
deps.push({
depName: parsedPreset.repo,
skipReason: 'unspecified-version',
});
continue;
}
deps.push({
depName: parsedPreset.repo,
datasource,
currentValue: parsedPreset.tag,
});
}
for (const [constraint, value] of Object.entries(
coerceObject(config.data.constraints),
)) {
if (isToolName(constraint)) {
const toolConfig = getToolConfig(constraint);
deps.push({
...toolConfig,
depName: constraint,
currentValue: value,
depType: 'tool-constraint',
commitMessageTopic: '{{{depName}}} tool constraint',
});
} else {
deps.push({
depName: constraint,
currentValue: value,
skipReason: 'unsupported',
depType: 'constraint',
commitMessageTopic: '{{{depName}}} constraint',
});
}
}
for (const packageRule of coerceArray(config.data.packageRules)) {
for (const [constraint, value] of Object.entries(
coerceObject(packageRule.constraints),
)) {
if (isToolName(constraint)) {
const toolConfig = getToolConfig(constraint);
deps.push({
...toolConfig,
depName: constraint,
currentValue: value,
depType: 'tool-constraint',
commitMessageTopic: '{{{depName}}} tool constraint',
});
} else {
deps.push({
depName: constraint,
currentValue: value,
skipReason: 'unsupported',
depType: 'constraint',
commitMessageTopic: '{{{depName}}} constraint',
});
}
}
}
return isNonEmptyArray(deps) ? { deps } : null;
}