From a57cdfdb85197acd455d76f22e928a535e6e9fd1 Mon Sep 17 00:00:00 2001 From: Johannes Feichtner <343448+Churro@users.noreply.github.com> Date: Sat, 28 Oct 2023 23:53:31 +0200 Subject: [PATCH] feat(manager/gradle): add support for Kotlin `import` directives (#25486) --- lib/modules/manager/gradle/parser.spec.ts | 37 ++++++++++++++++ lib/modules/manager/gradle/parser.ts | 3 ++ .../manager/gradle/parser/common.spec.ts | 6 +++ lib/modules/manager/gradle/parser/common.ts | 42 ++++++++++++++++++- lib/modules/manager/gradle/types.ts | 1 + 5 files changed, 88 insertions(+), 1 deletion(-) diff --git a/lib/modules/manager/gradle/parser.spec.ts b/lib/modules/manager/gradle/parser.spec.ts index 222f37186ef55e..6f778f563fe97b 100644 --- a/lib/modules/manager/gradle/parser.spec.ts +++ b/lib/modules/manager/gradle/parser.spec.ts @@ -1053,5 +1053,42 @@ describe('modules/manager/gradle/parser', () => { ], }); }); + + it('imported objects', () => { + const input = codeBlock` + object ModuleConfiguration { + object Build { + object Database { + const val h2Version = "2.0.206" + } + } + } + `; + + const gradleKtsInput = codeBlock` + import ModuleConfiguration.Build.Database + dependencies { + runtimeOnly("com.h2database:h2:\${Database.h2Version}") + } + `; + + const { vars } = parseKotlinSource(input); + const res = parseGradle(gradleKtsInput, vars); + expect(res).toMatchObject({ + vars: { + 'ModuleConfiguration.Build.Database.h2Version': { + key: 'ModuleConfiguration.Build.Database.h2Version', + value: '2.0.206', + }, + }, + deps: [ + { + depName: 'com.h2database:h2', + currentValue: '2.0.206', + groupName: 'ModuleConfiguration.Build.Database.h2Version', + }, + ], + }); + }); }); }); diff --git a/lib/modules/manager/gradle/parser.ts b/lib/modules/manager/gradle/parser.ts index a3824ed8a2d0e7..5d5367baf56179 100644 --- a/lib/modules/manager/gradle/parser.ts +++ b/lib/modules/manager/gradle/parser.ts @@ -3,6 +3,7 @@ import { newlineRegex, regEx } from '../../../util/regex'; import type { PackageDependency } from '../types'; import { qApplyFrom } from './parser/apply-from'; import { qAssignments } from './parser/assignments'; +import { qKotlinImport } from './parser/common'; import { qDependencies, qLongFormDep } from './parser/dependencies'; import { setParseGradleFunc } from './parser/handlers'; import { qKotlinMultiObjectVarAssignment } from './parser/objects'; @@ -29,6 +30,7 @@ const ctx: Ctx = { registryUrls: [], varTokens: [], + tmpKotlinImportStore: [], tmpNestingDepth: [], tmpTokenStore: {}, tokenMap: {}, @@ -51,6 +53,7 @@ export function parseGradle( type: 'root-tree', maxDepth: 32, search: q.alt( + qKotlinImport, qAssignments, qDependencies, qPlugins, diff --git a/lib/modules/manager/gradle/parser/common.spec.ts b/lib/modules/manager/gradle/parser/common.spec.ts index 6915f7339d4b3a..589130e4983b5c 100644 --- a/lib/modules/manager/gradle/parser/common.spec.ts +++ b/lib/modules/manager/gradle/parser/common.spec.ts @@ -30,6 +30,7 @@ describe('modules/manager/gradle/parser/common', () => { registryUrls: [], varTokens: [], + tmpKotlinImportStore: [], tmpNestingDepth: [], tmpTokenStore: {}, tokenMap: {}, @@ -139,6 +140,11 @@ describe('modules/manager/gradle/parser/common', () => { ctx.tmpNestingDepth = []; expect(findVariable('foo', ctx)).toStrictEqual(ctx.globalVars['foo']); + + ctx.tmpKotlinImportStore = [[token, token]]; + expect(findVariable('test.foo3', ctx)).toStrictEqual( + ctx.globalVars['test.test.foo3'] + ); }); it('interpolateString', () => { diff --git a/lib/modules/manager/gradle/parser/common.ts b/lib/modules/manager/gradle/parser/common.ts index a57da4f9fddbdb..da60daa174f284 100644 --- a/lib/modules/manager/gradle/parser/common.ts +++ b/lib/modules/manager/gradle/parser/common.ts @@ -112,6 +112,31 @@ export function coalesceVariable(ctx: Ctx): Ctx { return ctx; } +export function findVariableInKotlinImport( + name: string, + ctx: Ctx, + variables: PackageVariables +): VariableData | undefined { + if (ctx.tmpKotlinImportStore.length && name.includes('.')) { + for (const tokens of ctx.tmpKotlinImportStore) { + const lastToken = tokens[tokens.length - 1]; + if (lastToken && name.startsWith(`${lastToken.value}.`)) { + const prefix = tokens + .slice(0, -1) + .map((token) => token.value) + .join('.'); + const identifier = `${prefix}.${name}`; + + if (variables[identifier]) { + return variables[identifier]; + } + } + } + } + + return undefined; +} + export function findVariable( name: string, ctx: Ctx, @@ -129,7 +154,11 @@ export function findVariable( } } - return variables[name]; + if (variables[name]) { + return variables[name]; + } + + return findVariableInKotlinImport(name, ctx, variables); } export function interpolateString( @@ -271,3 +300,14 @@ export const qValueMatcher = qConcatExpr( qPropertyAccessIdentifier, qVariableAccessIdentifier ); + +// import foo.bar +// runtimeOnly("some:foo:${bar.bazVersion}") +export const qKotlinImport = q + .sym('import') + .join(qVariableAssignmentIdentifier) + .handler((ctx) => { + ctx.tmpKotlinImportStore.push(ctx.varTokens); + return ctx; + }) + .handler(cleanupTempVars); diff --git a/lib/modules/manager/gradle/types.ts b/lib/modules/manager/gradle/types.ts index 7222c36345aa71..74dfd059c98235 100644 --- a/lib/modules/manager/gradle/types.ts +++ b/lib/modules/manager/gradle/types.ts @@ -82,6 +82,7 @@ export interface Ctx { registryUrls: PackageRegistry[]; varTokens: lexer.Token[]; + tmpKotlinImportStore: lexer.Token[][]; tmpNestingDepth: lexer.Token[]; tmpTokenStore: Record; tokenMap: Record;