import { query as q } from '@renovatebot/good-enough-parser'; import { regEx } from '../../../../util/regex.ts'; import type { Ctx } from '../types.ts'; import { GRADLE_PLUGINS, GRADLE_TEST_SUITES, cleanupTempVars, qArtifactId, qDotOrBraceExpr, qGroupId, qTemplateString, qValueMatcher, qVersion, storeInTokenMap, storeVarToken, } from './common.ts'; import { handleDepString, handleImplicitDep, handleKotlinShortNotationDep, handleLongFormDep, } from './handlers.ts'; // "foo:bar:1.2.3" // "foo:bar:$baz" // "foo" + "${bar}" + baz export const qDependencyStrings = qTemplateString .opt(q.op('+').join(qValueMatcher)) .handler((ctx: Ctx) => storeInTokenMap(ctx, 'templateStringTokens')) .handler(handleDepString) .handler(cleanupTempVars); // dependencySet(group: 'foo', version: bar) { entry 'baz' } const qDependencySet = q .sym('dependencySet', storeVarToken) .handler((ctx) => storeInTokenMap(ctx, 'methodName')) .tree({ type: 'wrapped-tree', maxDepth: 1, startsWith: '(', endsWith: ')', search: q .begin() .sym('group') .alt(q.op(':'), q.op('=')) .join(qGroupId) .op(',') .sym('version') .alt(q.op(':'), q.op('=')) .join(qVersion) .end(), }) .tree({ type: 'wrapped-tree', maxDepth: 1, startsWith: '{', endsWith: '}', search: q .sym('entry') .alt( qArtifactId, q.tree({ type: 'wrapped-tree', maxDepth: 1, startsWith: '(', endsWith: ')', search: q.begin().join(qArtifactId).end(), }), ) .handler(handleLongFormDep), }) .handler(cleanupTempVars); // group: "foo", name: "bar", version: "1.2.3" export const qGroovyMapNotationDependencies = q .sym('group') .op(':') .join(qGroupId) .op(',') .sym('name') .op(':') .join(qArtifactId) .op(',') .sym('version') .op(':') .join(qVersion) .handler(handleLongFormDep) .handler(cleanupTempVars); // kotlin("bom", "1.7.21") const qKotlinShortNotationDependencies = q .sym('kotlin') .tree({ type: 'wrapped-tree', maxDepth: 1, startsWith: '(', endsWith: ')', search: q .begin() .join(qArtifactId) .op(',') .opt(q.sym('version').op('=')) .join(qVersion) .end(), }) .handler(handleKotlinShortNotationDep) .handler(cleanupTempVars); // (group = "foo", name = "bar", version = "1.2.3") const qKotlinMapNotationDependencies = q .tree({ type: 'wrapped-tree', maxDepth: 1, startsWith: '(', endsWith: ')', search: q .begin() .sym('group') .op('=') .join(qGroupId) .op(',') .sym('name') .op('=') .join(qArtifactId) .op(',') .sym('version') .op('=') .join(qVersion), }) .handler(handleLongFormDep) .handler(cleanupTempVars); // someMethod("foo", "bar", "1.2.3") export const qLongFormDep = q .opt( q.sym(storeVarToken).handler((ctx) => storeInTokenMap(ctx, 'methodName')), ) .tree({ type: 'wrapped-tree', maxDepth: 1, maxMatches: 1, startsWith: '(', endsWith: ')', search: q .begin() .join(qGroupId) .op(',') .join(qArtifactId) .op(',') .join(qVersion) .end(), }) .handler(handleLongFormDep) .handler(cleanupTempVars); // pmd { toolVersion = "1.2.3" } const qImplicitGradlePlugin = q .alt( ...Object.keys(GRADLE_PLUGINS).map((implicitDepName) => q .sym(implicitDepName, storeVarToken) .handler((ctx) => storeInTokenMap(ctx, 'implicitDepName')) .tree({ type: 'wrapped-tree', maxDepth: 1, maxMatches: 1, startsWith: '{', endsWith: '}', search: q .sym( GRADLE_PLUGINS[implicitDepName as keyof typeof GRADLE_PLUGINS][0], ) .alt( // toolVersion = "1.2.3" q.opt(q.op('=')).join(qVersion), // toolVersion.set("1.2.3"), toolVersion.value("1.2.3") q .op('.') .sym(regEx(/^(?:set|value)$/)) .tree({ maxDepth: 1, startsWith: '(', endsWith: ')', search: q.begin().join(qVersion).end(), }), ), }), ), ) .handler(handleImplicitDep) .handler(cleanupTempVars); // Match the use* method call with version parameter const qTestSuiteMethod = q .sym( regEx(`^(?:${Object.keys(GRADLE_TEST_SUITES).join('|')})$`), storeVarToken, ) .handler((ctx) => storeInTokenMap(ctx, 'implicitDepName')) .tree({ type: 'wrapped-tree', maxDepth: 1, maxMatches: 1, startsWith: '(', endsWith: ')', search: q.begin().join(qVersion).end(), }); // testing { suites { test { useSpock("1.2.3") } } } // testing.suites.test { useJunit("1.2.3") } // testing { suites.withType(JvmTestSuite).configureEach { useJUnitJupiter("5.13.4") } } // testing { suites.withType(JvmTestSuite::class).configureEach { useSpock("2.3") } } const qImplicitTestSuites = qDotOrBraceExpr( 'testing', qDotOrBraceExpr( 'suites', q.alt( qDotOrBraceExpr('test', qTestSuiteMethod), q .sym('withType') .tree({ type: 'wrapped-tree', maxDepth: 1, startsWith: '(', endsWith: ')', }) .op('.') .sym('configureEach') .tree({ type: 'wrapped-tree', maxDepth: 1, startsWith: '{', endsWith: '}', search: qTestSuiteMethod, }), ), ), ) .handler(handleImplicitDep) .handler(cleanupTempVars); // substitute module("foo:bar:1.2.3") using "baz:qux:4.5.6" // substitute(module("foo:bar:1.2.3")).using("baz:qux:4.5.6") const qIgnoreSubstitutedDependencies = q.sym('substitute').alt( q.sym('module').tree(), q.tree({ type: 'wrapped-tree', maxDepth: 1, startsWith: '(', endsWith: ')', search: q.sym('module').tree(), }), ); // buildConfigField("type", "name", "value") // resValue("type", "name", "value") const qIgnoreAndroidBuildValues = q.alt( q.sym('buildConfigField').tree(), q.sym('resValue').tree(), ); export const qDependencies = q.alt( qDependencyStrings, qDependencySet, qGroovyMapNotationDependencies, qKotlinShortNotationDependencies, qKotlinMapNotationDependencies, qImplicitGradlePlugin, qImplicitTestSuites, // avoid heuristic matching of gradle feature variant capabilities qDotOrBraceExpr('java', q.sym('registerFeature').tree()), // avoid matching substituted dependency modules qIgnoreSubstitutedDependencies, // avoid heuristic matching of Android build values qIgnoreAndroidBuildValues, );