sources/frontend-api/src/org/jetbrains/amper/frontend/schema/dependencies.kt (106 lines of code) (raw):
/*
* Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
package org.jetbrains.amper.frontend.schema
import org.jetbrains.amper.frontend.EnumMap
import org.jetbrains.amper.frontend.MavenCoordinates
import org.jetbrains.amper.frontend.SchemaEnum
import org.jetbrains.amper.frontend.api.EnumOrderSensitive
import org.jetbrains.amper.frontend.api.FromKeyAndTheRestIsNested
import org.jetbrains.amper.frontend.api.SchemaDoc
import org.jetbrains.amper.frontend.api.SchemaNode
import org.jetbrains.amper.frontend.api.Shorthand
import org.jetbrains.amper.frontend.api.StringSemantics
import org.jetbrains.amper.frontend.api.TraceableString
import org.jetbrains.amper.frontend.types.SchemaType.StringType.Semantics
import org.jetbrains.amper.frontend.userGuideUrl
import java.nio.file.Path
private const val dependenciesGuideUrl = "$userGuideUrl/dependencies"
@EnumOrderSensitive
enum class DependencyScope(
override val schemaValue: String,
val runtime: Boolean,
val compile: Boolean,
override val outdated: Boolean = false,
) : SchemaEnum {
COMPILE_ONLY("compile-only", runtime = false, compile = true),
RUNTIME_ONLY("runtime-only", runtime = true, compile = false),
ALL("all", runtime = true, compile = true);
companion object : EnumMap<DependencyScope, String>(DependencyScope::values, DependencyScope::schemaValue)
}
// TODO Break this hierarchy into two:
// - DependencyNotation: MavenNotation, CatalogNotation, LocalNotation (in future replaced as just reference)
// - Dependency: ScopedDependency, BomDependency (if we need any special meaning here for Bom).
// .
// Also, by breaking this hierarchy we can replace KspDependencies by just notation.
// Also, it may contradict "constructor args" approach from AmperLang.
sealed class Dependency : SchemaNode()
sealed class ScopedDependency : Dependency() {
// TODO Replace exported flag by new scope (rethink scopes).
@Shorthand
@SchemaDoc("Whether a dependency should be [visible as a part of a published API]($dependenciesGuideUrl/#transitivity)")
val exported by value(false)
@Shorthand
@SchemaDoc("When the dependency should be used. Read more about the [dependency scopes]($dependenciesGuideUrl/#scopes)")
val scope by value(DependencyScope.ALL)
}
class ExternalMavenDependency : ScopedDependency() {
@SchemaDoc("Dependency on [a Kotlin or Java library]($dependenciesGuideUrl/#external-maven-dependencies) in a Maven repository")
@StringSemantics(Semantics.MavenCoordinates)
@FromKeyAndTheRestIsNested
val coordinates by value<String>()
}
class InternalDependency : ScopedDependency() {
@SchemaDoc("Dependency [on another module]($dependenciesGuideUrl/#module-dependencies) in the codebase")
@FromKeyAndTheRestIsNested
val path by value<Path>()
}
class CatalogDependency : ScopedDependency() {
@SchemaDoc("Dependency from [a library catalog]($dependenciesGuideUrl/#library-catalogs)")
@FromKeyAndTheRestIsNested
val catalogKey by value<String>()
}
/**
* Hierarchical notation for dependencies without scope, that is identical to [ScopedDependency].
*/
// TODO See TODO on [Dependency].
sealed class UnscopedDependency : Dependency()
class UnscopedExternalMavenDependency : UnscopedDependency() {
@FromKeyAndTheRestIsNested
@StringSemantics(Semantics.MavenCoordinates)
val coordinates by value<String>()
}
class UnscopedModuleDependency : UnscopedDependency() {
@FromKeyAndTheRestIsNested
val path by value<Path>()
}
class UnscopedCatalogDependency : UnscopedDependency() {
// Actual usage of this property is indirect and located within [CatalogVersionsSubstitutor] within the tree.
// The value of this property is to provide the schema.
@Suppress("unused")
@FromKeyAndTheRestIsNested
val catalogKey by value<String>()
}
sealed class UnscopedBomDependency : UnscopedDependency()
class UnscopedExternalMavenBomDependency : UnscopedBomDependency() {
@FromKeyAndTheRestIsNested
@StringSemantics(Semantics.MavenCoordinates)
val coordinates by value<String>()
}
class UnscopedCatalogBomDependency : UnscopedBomDependency() {
@FromKeyAndTheRestIsNested
val catalogKey by value<String>()
}
sealed class BomDependency : Dependency()
class ExternalMavenBomDependency : BomDependency() {
@SchemaDoc("Dependency on [a BOM]($dependenciesGuideUrl/#using-a-maven-bom) in a Maven repository")
@FromKeyAndTheRestIsNested
@StringSemantics(Semantics.MavenCoordinates)
val coordinates by value<String>()
}
class CatalogBomDependency : BomDependency() {
@SchemaDoc("BOM dependency from [a library catalog]($dependenciesGuideUrl/#library-catalogs)")
@FromKeyAndTheRestIsNested
val catalogKey by value<String>()
}
/**
* Splits this [TraceableString] into its [MavenCoordinates] components.
*
* This [TraceableString] must respect the full Maven format with 2 to 4 parts delimited with `:`, and with an optional
* packaging type appended after `@` at the end:
*
* ```
* groupId:artifactId[:version][:classifier][@packagingType]
* ```
*/
fun TraceableString.toMavenCoordinates(): MavenCoordinates {
val coordsAndPackaging = value.trim().split("@")
val coords = coordsAndPackaging.first().split(":")
val packagingType = coordsAndPackaging.getOrNull(1)
check(coords.size in 2..4) {
"Coordinates should have between 2 and 4 parts, but got ${coords.size}: $this. " +
"Ensure that the coordinates were properly validated in the parser."
}
return MavenCoordinates(
groupId = coords[0],
artifactId = coords[1],
version = coords.getOrNull(2),
classifier = coords.getOrNull(3),
packagingType = packagingType,
trace = this.trace,
)
}