Skip to content

[4.x] Allow to register multiple compiler plugins #6546

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libraries/apollo-annotations/api/apollo-annotations.api
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public final class com/apollographql/apollo/annotations/ApolloDeprecatedSince$Ve
public static final field v4_0_1 Lcom/apollographql/apollo/annotations/ApolloDeprecatedSince$Version;
public static final field v4_0_2 Lcom/apollographql/apollo/annotations/ApolloDeprecatedSince$Version;
public static final field v4_1_2 Lcom/apollographql/apollo/annotations/ApolloDeprecatedSince$Version;
public static final field v4_2_1 Lcom/apollographql/apollo/annotations/ApolloDeprecatedSince$Version;
public static fun getEntries ()Lkotlin/enums/EnumEntries;
public static fun valueOf (Ljava/lang/String;)Lcom/apollographql/apollo/annotations/ApolloDeprecatedSince$Version;
public static fun values ()[Lcom/apollographql/apollo/annotations/ApolloDeprecatedSince$Version;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ open annotation class com.apollographql.apollo.annotations/ApolloDeprecatedSince
enum entry v4_0_1 // com.apollographql.apollo.annotations/ApolloDeprecatedSince.Version.v4_0_1|null[0]
enum entry v4_0_2 // com.apollographql.apollo.annotations/ApolloDeprecatedSince.Version.v4_0_2|null[0]
enum entry v4_1_2 // com.apollographql.apollo.annotations/ApolloDeprecatedSince.Version.v4_1_2|null[0]
enum entry v4_2_1 // com.apollographql.apollo.annotations/ApolloDeprecatedSince.Version.v4_2_1|null[0]

final val entries // com.apollographql.apollo.annotations/ApolloDeprecatedSince.Version.entries|#static{}entries[0]
final fun <get-entries>(): kotlin.enums/EnumEntries<com.apollographql.apollo.annotations/ApolloDeprecatedSince.Version> // com.apollographql.apollo.annotations/ApolloDeprecatedSince.Version.entries.<get-entries>|<get-entries>#static(){}[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ annotation class ApolloDeprecatedSince(val version: Version) {
v4_0_1,
v4_0_2,
v4_1_2,
v4_2_1,
}
}
43 changes: 40 additions & 3 deletions libraries/apollo-compiler/api/apollo-compiler.api
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ public final class com/apollographql/apollo/compiler/AdapterInitializer$Companio
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class com/apollographql/apollo/compiler/After : com/apollographql/apollo/compiler/Order {
public fun <init> (Ljava/lang/String;)V
public final fun getId ()Ljava/lang/String;
}

public final class com/apollographql/apollo/compiler/ApolloCompiler {
public static final field INSTANCE Lcom/apollographql/apollo/compiler/ApolloCompiler;
public final fun buildCodegenSchema (Ljava/util/List;Lcom/apollographql/apollo/compiler/ApolloCompiler$Logger;Lcom/apollographql/apollo/compiler/CodegenSchemaOptions;Ljava/util/List;)Lcom/apollographql/apollo/compiler/CodegenSchema;
public final fun buildIrOperations (Lcom/apollographql/apollo/compiler/CodegenSchema;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lcom/apollographql/apollo/compiler/IrOptions;Lcom/apollographql/apollo/compiler/DocumentTransform;Lcom/apollographql/apollo/compiler/ApolloCompiler$Logger;)Lcom/apollographql/apollo/compiler/ir/IrOperations;
public final fun buildSchemaAndOperationsSources (Lcom/apollographql/apollo/compiler/CodegenSchema;Ljava/util/List;Lcom/apollographql/apollo/compiler/IrOptions;Lcom/apollographql/apollo/compiler/CodegenOptions;Lcom/apollographql/apollo/compiler/LayoutFactory;Lcom/apollographql/apollo/compiler/OperationOutputGenerator;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/DocumentTransform;Lcom/apollographql/apollo/compiler/ApolloCompiler$Logger;Ljava/io/File;)Lcom/apollographql/apollo/compiler/codegen/SourceOutput;
public final fun buildSchemaAndOperationsSources (Ljava/util/List;Ljava/util/List;Lcom/apollographql/apollo/compiler/CodegenSchemaOptions;Lcom/apollographql/apollo/compiler/IrOptions;Lcom/apollographql/apollo/compiler/CodegenOptions;Lcom/apollographql/apollo/compiler/LayoutFactory;Lcom/apollographql/apollo/compiler/OperationOutputGenerator;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/DocumentTransform;Lcom/apollographql/apollo/compiler/ApolloCompiler$Logger;Ljava/io/File;)Lcom/apollographql/apollo/compiler/codegen/SourceOutput;
public final fun buildIrOperations (Lcom/apollographql/apollo/compiler/CodegenSchema;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lcom/apollographql/apollo/compiler/IrOptions;Lcom/apollographql/apollo/compiler/ExecutableDocumentTransform;Lcom/apollographql/apollo/compiler/ApolloCompiler$Logger;)Lcom/apollographql/apollo/compiler/ir/IrOperations;
public final fun buildSchemaAndOperationsSources (Lcom/apollographql/apollo/compiler/CodegenSchema;Ljava/util/List;Lcom/apollographql/apollo/compiler/IrOptions;Lcom/apollographql/apollo/compiler/CodegenOptions;Lcom/apollographql/apollo/compiler/LayoutFactory;Lcom/apollographql/apollo/compiler/OperationOutputGenerator;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/ExecutableDocumentTransform;Lcom/apollographql/apollo/compiler/ApolloCompiler$Logger;Ljava/io/File;)Lcom/apollographql/apollo/compiler/codegen/SourceOutput;
public final fun buildSchemaAndOperationsSources (Ljava/util/List;Ljava/util/List;Lcom/apollographql/apollo/compiler/CodegenSchemaOptions;Lcom/apollographql/apollo/compiler/IrOptions;Lcom/apollographql/apollo/compiler/CodegenOptions;Lcom/apollographql/apollo/compiler/LayoutFactory;Lcom/apollographql/apollo/compiler/OperationOutputGenerator;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/ExecutableDocumentTransform;Lcom/apollographql/apollo/compiler/ApolloCompiler$Logger;Ljava/io/File;)Lcom/apollographql/apollo/compiler/codegen/SourceOutput;
public final fun buildSchemaAndOperationsSourcesFromIr (Lcom/apollographql/apollo/compiler/CodegenSchema;Lcom/apollographql/apollo/compiler/ir/IrOperations;Lcom/apollographql/apollo/compiler/UsedCoordinates;Ljava/util/List;Lcom/apollographql/apollo/compiler/CodegenOptions;Lcom/apollographql/apollo/compiler/codegen/SchemaAndOperationsLayout;Lcom/apollographql/apollo/compiler/OperationOutputGenerator;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Ljava/io/File;)Lcom/apollographql/apollo/compiler/codegen/SourceOutput;
public final fun buildSchemaSources (Lcom/apollographql/apollo/compiler/CodegenSchema;Lcom/apollographql/apollo/compiler/UsedCoordinates;Lcom/apollographql/apollo/compiler/CodegenOptions;Lcom/apollographql/apollo/compiler/codegen/SchemaLayout;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;)Lcom/apollographql/apollo/compiler/codegen/SourceOutput;
}
Expand All @@ -25,6 +30,7 @@ public final class com/apollographql/apollo/compiler/ApolloCompilerKt {
}

public abstract interface class com/apollographql/apollo/compiler/ApolloCompilerPlugin {
public fun beforeCompilationStep (Lcom/apollographql/apollo/compiler/ApolloCompilerPluginEnvironment;Lcom/apollographql/apollo/compiler/ApolloCompilerRegistry;)V
public fun documentTransform ()Lcom/apollographql/apollo/compiler/DocumentTransform;
public fun foreignSchemas ()Ljava/util/List;
public fun irOperationsTransform ()Lcom/apollographql/apollo/compiler/Transform;
Expand Down Expand Up @@ -52,6 +58,26 @@ public abstract interface class com/apollographql/apollo/compiler/ApolloCompiler
public abstract fun create (Lcom/apollographql/apollo/compiler/ApolloCompilerPluginEnvironment;)Lcom/apollographql/apollo/compiler/ApolloCompilerPlugin;
}

public abstract interface class com/apollographql/apollo/compiler/ApolloCompilerRegistry {
public abstract fun registerExecutableDocumentTransform (Ljava/lang/String;[Lcom/apollographql/apollo/compiler/Order;Lcom/apollographql/apollo/compiler/ExecutableDocumentTransform;)V
public abstract fun registerExtraCodeGenerator (Lcom/apollographql/apollo/compiler/CodeGenerator;)V
public abstract fun registerForeignSchemas (Ljava/util/List;)V
public abstract fun registerIrTransform (Ljava/lang/String;[Lcom/apollographql/apollo/compiler/Order;Lcom/apollographql/apollo/compiler/Transform;)V
public abstract fun registerJavaOutputTransform (Ljava/lang/String;[Lcom/apollographql/apollo/compiler/Order;Lcom/apollographql/apollo/compiler/Transform;)V
public abstract fun registerKotlinOutputTransform (Ljava/lang/String;[Lcom/apollographql/apollo/compiler/Order;Lcom/apollographql/apollo/compiler/Transform;)V
public abstract fun registerLayout (Lcom/apollographql/apollo/compiler/LayoutFactory;)V
public abstract fun registerOperationIdsGenerator (Lcom/apollographql/apollo/compiler/OperationIdsGenerator;)V
}

public final class com/apollographql/apollo/compiler/Before : com/apollographql/apollo/compiler/Order {
public fun <init> (Ljava/lang/String;)V
public final fun getId ()Ljava/lang/String;
}

public abstract interface class com/apollographql/apollo/compiler/CodeGenerator {
public abstract fun generate (Lcom/apollographql/apollo/ast/GQLDocument;Ljava/io/File;)V
}

public final class com/apollographql/apollo/compiler/CodegenMetadata {
public static final field Companion Lcom/apollographql/apollo/compiler/CodegenMetadata$Companion;
public fun <init> (Ljava/lang/String;Lcom/apollographql/apollo/compiler/TargetLanguage;Ljava/util/List;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)V
Expand Down Expand Up @@ -198,6 +224,10 @@ public final class com/apollographql/apollo/compiler/EntrypointsKt {
public static final fun findCodegenSchemaFile (Ljava/lang/Iterable;)Ljava/io/File;
}

public abstract interface class com/apollographql/apollo/compiler/ExecutableDocumentTransform {
public abstract fun transform (Lcom/apollographql/apollo/ast/Schema;Lcom/apollographql/apollo/ast/GQLDocument;Ljava/util/List;)Lcom/apollographql/apollo/ast/GQLDocument;
}

public final class com/apollographql/apollo/compiler/ExpressionAdapterInitializer : com/apollographql/apollo/compiler/AdapterInitializer {
public static final field Companion Lcom/apollographql/apollo/compiler/ExpressionAdapterInitializer$Companion;
public fun <init> (Ljava/lang/String;)V
Expand Down Expand Up @@ -358,6 +388,10 @@ public final class com/apollographql/apollo/compiler/OperationIdGenerator$Sha256
public fun getVersion ()Ljava/lang/String;
}

public abstract interface class com/apollographql/apollo/compiler/OperationIdsGenerator {
public abstract fun generate (Ljava/util/Collection;)Ljava/util/List;
}

public abstract interface class com/apollographql/apollo/compiler/OperationOutputGenerator {
public abstract fun generate (Ljava/util/Collection;)Ljava/util/Map;
public fun getVersion ()Ljava/lang/String;
Expand Down Expand Up @@ -395,6 +429,9 @@ public final class com/apollographql/apollo/compiler/OptionsKt {
public static final fun validate (Lcom/apollographql/apollo/compiler/CodegenOptions;)V
}

public abstract interface class com/apollographql/apollo/compiler/Order {
}

public abstract interface class com/apollographql/apollo/compiler/PackageNameGenerator {
public fun getVersion ()Ljava/lang/String;
public abstract fun packageName (Ljava/lang/String;)Ljava/lang/String;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ object ApolloCompiler {
upstreamCodegenModels: List<String>,
upstreamFragmentDefinitions: List<GQLFragmentDefinition>,
options: IrOptions,
documentTransform: DocumentTransform?,
documentTransform: ExecutableDocumentTransform?,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only breaking change, which I think is OK since it touches the compiler API, which isn't really too much used.

If anyone reads this, the difference is that ExecutableDocumentTransform now runs over all the definitions all at once and gives the upstream fragments.

logger: Logger?,
): IrOperations {
val schema = codegenSchema.schema
Expand Down Expand Up @@ -265,18 +265,12 @@ object ApolloCompiler {
* Step 3, Modify the AST to add typename and key fields
*/
val fragmentDefinitions = (definitions.filterIsInstance<GQLFragmentDefinition>() + upstreamFragmentDefinitions).associateBy { it.name }
val fragments = definitions.filterIsInstance<GQLFragmentDefinition>().map {
addRequiredFields(it, addTypename, schema, fragmentDefinitions).let {
documentTransform?.transform(schema, it) ?: it
}
var fragments = definitions.filterIsInstance<GQLFragmentDefinition>().map {
addRequiredFields(it, addTypename, schema, fragmentDefinitions)
}


val operations = definitions.filterIsInstance<GQLOperationDefinition>().map {
var operations = definitions.filterIsInstance<GQLOperationDefinition>().map {
var operation = addRequiredFields(it, addTypename, schema, fragmentDefinitions)
if (documentTransform != null) {
operation = documentTransform.transform(schema, operation)
}
if (schema.directiveDefinitions.containsKey(Schema.DISABLE_ERROR_PROPAGATION)
&& schema.schemaDefinition?.directives?.any { schema.originalDirectiveName(it.name) == Schema.CATCH_BY_DEFAULT } == true) {
operation = operation.copy(
Expand All @@ -286,6 +280,12 @@ object ApolloCompiler {
operation
}

if (documentTransform != null) {
val transformedDocument = documentTransform.transform(schema, GQLDocument(sourceLocation = null, definitions = fragments + operations), upstreamFragmentDefinitions)
fragments = transformedDocument.definitions.filterIsInstance<GQLFragmentDefinition>()
operations = transformedDocument.definitions.filterIsInstance<GQLOperationDefinition>()
}

// Remember the fragments with the possibly updated fragments
val allFragmentDefinitions = (fragments + upstreamFragmentDefinitions).associateBy { it.name }

Expand Down Expand Up @@ -474,7 +474,7 @@ object ApolloCompiler {
irOperationsTransform: Transform<IrOperations>?,
javaOutputTransform: Transform<JavaOutput>?,
kotlinOutputTransform: Transform<KotlinOutput>?,
documentTransform: DocumentTransform?,
documentTransform: ExecutableDocumentTransform?,
logger: Logger?,
operationManifestFile: File?,
): SourceOutput {
Expand Down Expand Up @@ -515,7 +515,7 @@ object ApolloCompiler {
irOperationsTransform: Transform<IrOperations>?,
javaOutputTransform: Transform<JavaOutput>?,
kotlinOutputTransform: Transform<KotlinOutput>?,
documentTransform: DocumentTransform?,
documentTransform: ExecutableDocumentTransform?,
logger: Logger?,
operationManifestFile: File?,
): SourceOutput {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.apollographql.apollo.compiler

import com.apollographql.apollo.annotations.ApolloDeprecatedSince
import com.apollographql.apollo.annotations.ApolloExperimental
import com.apollographql.apollo.ast.ForeignSchema
import com.apollographql.apollo.ast.GQLDocument
Expand All @@ -15,18 +16,33 @@ import com.apollographql.apollo.compiler.operationoutput.OperationId
import java.io.File

/**
* [ApolloCompilerPlugin] allows to customize the behaviour of the Apollo Compiler.
* [ApolloCompilerPlugin] allows to customize the behavior of the Apollo Compiler.
*
* [ApolloCompilerPlugin] may be instantiated several times in a codegen run. Each instance is create in a
* separate classloader.
* The classloaders contains `apollo-compiler` classes and the runtime classpath of the [ApolloCompilerPlugin].
* You may throw from [ApolloCompilerPlugin] methods to fail the build.
*/
interface ApolloCompilerPlugin {
/**
* This is called before each compilation step. A typical compilation involves different steps:
* - generating the schema
* - generating the IR
* - generating the source code (codegen)
*
* @param environment options and environment for the plugin.
* @param registry the registry where to register transformations.
*/
fun beforeCompilationStep(environment: ApolloCompilerPluginEnvironment, registry: ApolloCompilerRegistry) {

}

/**
* @return the layout or null to use the default layout
* @param codegenSchema the codegenSchema
*/
@Deprecated("Call `registry.registerLayout()` from beforeCompilationStep() instead.")
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_2_1)
fun layout(codegenSchema: CodegenSchema): SchemaAndOperationsLayout? {
return null
}
Expand All @@ -36,6 +52,8 @@ interface ApolloCompilerPlugin {
*
* @return a list of [OperationId] matching an operation name to its id or null to use the SHA256 default
*/
@Deprecated("Call `registry.registerOperationIdsGenerator()` from beforeCompilationStep() instead.")
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_2_1)
fun operationIds(descriptors: List<OperationDescriptor>): List<OperationId>? {
return null
}
Expand Down Expand Up @@ -132,3 +150,61 @@ interface Transform<T> {
*/
fun transform(input: T): T
}

/**
* A [ExecutableDocumentTransform] transforms operations and fragments at build time. [ExecutableDocumentTransform] can add or remove fields automatically, for an example.
*/
fun interface ExecutableDocumentTransform {
/**
* Transforms the given document.
*
* [transform] is called before any validation. Implementation must be robust to invalid fragments, operations and non-executable definitions.
* [transform] is called after any processing done by the Apollo compiler such as adding `__typename`.
*
* @param schema the schema
* @param document the document containing all the operations and fragments defined in this compilation unit.
* @param extraFragmentDefinitions extra fragment definitions from other compilation units.
*/
fun transform(schema: Schema, document: GQLDocument, extraFragmentDefinitions: List<GQLFragmentDefinition>): GQLDocument
}

fun interface OperationIdsGenerator {
fun generate(operationDescriptorList: Collection<OperationDescriptor>): List<OperationId>
}

sealed interface Order

class Before(val id: String): Order
class After(val id: String): Order

interface ApolloCompilerRegistry {
fun registerForeignSchemas(schemas: List<ForeignSchema>)

@ApolloExperimental
fun registerExecutableDocumentTransform(id: String, vararg orders: Order, transform: ExecutableDocumentTransform)
@ApolloExperimental
fun registerIrTransform(id: String, vararg orders: Order, transform: Transform<IrOperations>)

@ApolloExperimental
fun registerLayout(factory: LayoutFactory)
fun registerOperationIdsGenerator(generator: OperationIdsGenerator)
@ApolloExperimental
fun registerJavaOutputTransform(id: String, vararg orders: Order, transform: Transform<JavaOutput>)
@ApolloExperimental
fun registerKotlinOutputTransform(id: String, vararg orders: Order, transform: Transform<KotlinOutput>)
@ApolloExperimental
fun registerExtraCodeGenerator(codeGenerator: CodeGenerator)
}

/**
* A code generator that may write code in [ApolloCompilerPluginEnvironment.outputDirectory]
*
* This is not a kotlin function type because this might be used in environments where those types are
* relocated and might fail to load at runtime. For an example, in a Gradle plugin.
*/
fun interface CodeGenerator {
/**
* Transforms the given input into an output of the same type
*/
fun generate(schema: GQLDocument, outputDirectory: File)
}
Loading
Loading