mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-25 23:11:31 +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
120 lines
3.6 KiB
TypeScript
120 lines
3.6 KiB
TypeScript
import { isEmptyArray, isTruthy } from '@sindresorhus/is';
|
|
import jsonata from 'jsonata';
|
|
import { migrateDatasource } from '../../../../config/migrations/custom/datasource-migration.ts';
|
|
import { logger } from '../../../../logger/index.ts';
|
|
import * as template from '../../../../util/template/index.ts';
|
|
import { parseUrl } from '../../../../util/url.ts';
|
|
import type { PackageDependency } from '../../types.ts';
|
|
import type { ValidMatchFields } from '../types.ts';
|
|
import { checkIsValidDependency, validMatchFields } from '../utils.ts';
|
|
import { QueryResultZod } from './schema.ts';
|
|
import type { JSONataManagerTemplates, JsonataExtractConfig } from './types.ts';
|
|
|
|
export async function handleMatching(
|
|
json: unknown,
|
|
packageFile: string,
|
|
config: JsonataExtractConfig,
|
|
): Promise<PackageDependency[]> {
|
|
let results: Record<string, string>[] = [];
|
|
const { matchStrings: jsonataQueries } = config;
|
|
for (const query of jsonataQueries) {
|
|
// won't fail as this is verified during config validation
|
|
const jsonataExpression = jsonata(query);
|
|
let queryResult: unknown;
|
|
try {
|
|
// this now throws for unknown reasons
|
|
queryResult = await jsonataExpression.evaluate(json);
|
|
} catch (err) {
|
|
logger.warn(
|
|
{ err, jsonataQuery: query, packageFile },
|
|
'Error executing jsonata query. Please check your query.',
|
|
);
|
|
continue;
|
|
}
|
|
|
|
// allows empty dep object cause templates can be used to configure the required fields
|
|
// if some issues arise then the isValidDependency call will catch them later on
|
|
if (!queryResult || isEmptyArray(queryResult)) {
|
|
logger.debug(
|
|
{
|
|
jsonataQuery: query,
|
|
packageFile,
|
|
},
|
|
'The jsonata query returned no matches. Possible error, please check your query. Skipping',
|
|
);
|
|
continue;
|
|
}
|
|
|
|
const parsed = QueryResultZod.safeParse(queryResult);
|
|
if (parsed.success) {
|
|
results = results.concat(parsed.data);
|
|
} else {
|
|
logger.warn(
|
|
{ err: parsed.error, jsonataQuery: query, packageFile, queryResult },
|
|
'Query results failed schema validation',
|
|
);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return results
|
|
.map((dep) => createDependency(dep, config))
|
|
.filter(isTruthy)
|
|
.filter((dep) =>
|
|
checkIsValidDependency(dep, packageFile, 'custom.jsonata'),
|
|
);
|
|
}
|
|
|
|
export function createDependency(
|
|
queryResult: Record<string, string>,
|
|
config: JsonataExtractConfig,
|
|
): PackageDependency | null {
|
|
const dependency: PackageDependency = {};
|
|
|
|
for (const field of validMatchFields) {
|
|
const fieldTemplate = `${field}Template` as keyof JSONataManagerTemplates;
|
|
const tmpl = config[fieldTemplate];
|
|
if (tmpl) {
|
|
try {
|
|
const compiled = template.compile(tmpl, queryResult, false);
|
|
updateDependency(field, compiled, dependency);
|
|
} catch {
|
|
logger.debug(
|
|
{ template: tmpl },
|
|
'Error compiling template for JSONata manager',
|
|
);
|
|
return null;
|
|
}
|
|
} else if (queryResult[field]) {
|
|
updateDependency(field, queryResult[field], dependency);
|
|
}
|
|
}
|
|
return dependency;
|
|
}
|
|
|
|
function updateDependency(
|
|
field: ValidMatchFields,
|
|
value: string,
|
|
dependency: PackageDependency,
|
|
): PackageDependency {
|
|
switch (field) {
|
|
case 'registryUrl': {
|
|
const url = parseUrl(value)?.toString();
|
|
if (!url) {
|
|
logger.debug({ url: value }, 'Invalid JSONata manager registryUrl');
|
|
break;
|
|
}
|
|
dependency.registryUrls = [url];
|
|
break;
|
|
}
|
|
case 'datasource':
|
|
dependency.datasource = migrateDatasource(value);
|
|
break;
|
|
default:
|
|
dependency[field] = value;
|
|
break;
|
|
}
|
|
|
|
return dependency;
|
|
}
|