From e76108502fc95420ece6bc3300f63c4d702cf76b Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Fri, 30 May 2025 19:42:57 +0200 Subject: [PATCH 1/4] Backport v5 compiler plugins in v4 --- .../api/apollo-annotations.api | 1 + .../api/apollo-annotations.klib.api | 1 + .../annotations/ApolloDeprecatedSince.kt | 1 + .../apollo-compiler/api/apollo-compiler.api | 43 ++- .../apollo/compiler/ApolloCompiler.kt | 24 +- .../apollo/compiler/ApolloCompilerPlugin.kt | 78 ++++- .../apollo/compiler/entrypoints.kt | 98 +++--- .../internal/DefaultApolloCompilerRegistry.kt | 289 ++++++++++++++++++ .../apollo/compiler/internal/legacy.kt | 73 +++++ .../operationoutput/OperationOutput.kt | 7 + .../apollo/gradle/internal/serviceloader.kt | 47 --- .../src/main/kotlin/hooks/TestPlugin.kt | 1 + 12 files changed, 550 insertions(+), 113 deletions(-) create mode 100644 libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/DefaultApolloCompilerRegistry.kt create mode 100644 libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/legacy.kt delete mode 100644 libraries/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo/gradle/internal/serviceloader.kt diff --git a/libraries/apollo-annotations/api/apollo-annotations.api b/libraries/apollo-annotations/api/apollo-annotations.api index b3b706e9b12..487c1f27486 100644 --- a/libraries/apollo-annotations/api/apollo-annotations.api +++ b/libraries/apollo-annotations/api/apollo-annotations.api @@ -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; diff --git a/libraries/apollo-annotations/api/apollo-annotations.klib.api b/libraries/apollo-annotations/api/apollo-annotations.klib.api index f2215467422..6fe52143fca 100644 --- a/libraries/apollo-annotations/api/apollo-annotations.klib.api +++ b/libraries/apollo-annotations/api/apollo-annotations.klib.api @@ -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 (): kotlin.enums/EnumEntries // com.apollographql.apollo.annotations/ApolloDeprecatedSince.Version.entries.|#static(){}[0] diff --git a/libraries/apollo-annotations/src/commonMain/kotlin/com/apollographql/apollo/annotations/ApolloDeprecatedSince.kt b/libraries/apollo-annotations/src/commonMain/kotlin/com/apollographql/apollo/annotations/ApolloDeprecatedSince.kt index c8df0079c37..c2633884e21 100644 --- a/libraries/apollo-annotations/src/commonMain/kotlin/com/apollographql/apollo/annotations/ApolloDeprecatedSince.kt +++ b/libraries/apollo-annotations/src/commonMain/kotlin/com/apollographql/apollo/annotations/ApolloDeprecatedSince.kt @@ -34,5 +34,6 @@ annotation class ApolloDeprecatedSince(val version: Version) { v4_0_1, v4_0_2, v4_1_2, + v4_2_1, } } diff --git a/libraries/apollo-compiler/api/apollo-compiler.api b/libraries/apollo-compiler/api/apollo-compiler.api index 0a16964a83c..400844ca28f 100644 --- a/libraries/apollo-compiler/api/apollo-compiler.api +++ b/libraries/apollo-compiler/api/apollo-compiler.api @@ -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 (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; } @@ -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; @@ -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 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 abstract fun registerOperationsTransform (Ljava/lang/String;[Lcom/apollographql/apollo/compiler/Order;Lcom/apollographql/apollo/compiler/ExecutableDocumentTransform;)V +} + +public final class com/apollographql/apollo/compiler/Before : com/apollographql/apollo/compiler/Order { + public fun (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 (Ljava/lang/String;Lcom/apollographql/apollo/compiler/TargetLanguage;Ljava/util/List;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)V @@ -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 (Ljava/lang/String;)V @@ -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; @@ -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; diff --git a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ApolloCompiler.kt b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ApolloCompiler.kt index a3c2ba5b9e5..291b89d5c26 100644 --- a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ApolloCompiler.kt +++ b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ApolloCompiler.kt @@ -178,7 +178,7 @@ object ApolloCompiler { upstreamCodegenModels: List, upstreamFragmentDefinitions: List, options: IrOptions, - documentTransform: DocumentTransform?, + documentTransform: ExecutableDocumentTransform?, logger: Logger?, ): IrOperations { val schema = codegenSchema.schema @@ -265,18 +265,12 @@ object ApolloCompiler { * Step 3, Modify the AST to add typename and key fields */ val fragmentDefinitions = (definitions.filterIsInstance() + upstreamFragmentDefinitions).associateBy { it.name } - val fragments = definitions.filterIsInstance().map { - addRequiredFields(it, addTypename, schema, fragmentDefinitions).let { - documentTransform?.transform(schema, it) ?: it - } + var fragments = definitions.filterIsInstance().map { + addRequiredFields(it, addTypename, schema, fragmentDefinitions) } - - val operations = definitions.filterIsInstance().map { + var operations = definitions.filterIsInstance().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( @@ -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() + operations = transformedDocument.definitions.filterIsInstance() + } + // Remember the fragments with the possibly updated fragments val allFragmentDefinitions = (fragments + upstreamFragmentDefinitions).associateBy { it.name } @@ -474,7 +474,7 @@ object ApolloCompiler { irOperationsTransform: Transform?, javaOutputTransform: Transform?, kotlinOutputTransform: Transform?, - documentTransform: DocumentTransform?, + documentTransform: ExecutableDocumentTransform?, logger: Logger?, operationManifestFile: File?, ): SourceOutput { @@ -515,7 +515,7 @@ object ApolloCompiler { irOperationsTransform: Transform?, javaOutputTransform: Transform?, kotlinOutputTransform: Transform?, - documentTransform: DocumentTransform?, + documentTransform: ExecutableDocumentTransform?, logger: Logger?, operationManifestFile: File?, ): SourceOutput { diff --git a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ApolloCompilerPlugin.kt b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ApolloCompilerPlugin.kt index 1c0471123f9..46f529b89c4 100644 --- a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ApolloCompilerPlugin.kt +++ b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ApolloCompilerPlugin.kt @@ -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 @@ -15,7 +16,7 @@ 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. @@ -23,10 +24,25 @@ import java.io.File * 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 } @@ -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): List? { return null } @@ -132,3 +150,61 @@ interface Transform { */ 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): GQLDocument +} + +fun interface OperationIdsGenerator { + fun generate(operationDescriptorList: Collection): List +} + +sealed interface Order + +class Before(val id: String): Order +class After(val id: String): Order + +interface ApolloCompilerRegistry { + fun registerForeignSchemas(schemas: List) + + @ApolloExperimental + fun registerOperationsTransform(id: String, vararg orders: Order, transform: ExecutableDocumentTransform) + @ApolloExperimental + fun registerIrTransform(id: String, vararg orders: Order, transform: Transform) + + @ApolloExperimental + fun registerLayout(factory: LayoutFactory) + fun registerOperationIdsGenerator(generator: OperationIdsGenerator) + @ApolloExperimental + fun registerJavaOutputTransform(id: String, vararg orders: Order, transform: Transform) + @ApolloExperimental + fun registerKotlinOutputTransform(id: String, vararg orders: Order, transform: Transform) + @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) +} \ No newline at end of file diff --git a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/entrypoints.kt b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/entrypoints.kt index 138efd1b5b2..7b50f246873 100644 --- a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/entrypoints.kt +++ b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/entrypoints.kt @@ -3,7 +3,9 @@ package com.apollographql.apollo.compiler import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.compiler.codegen.SchemaAndOperationsLayout import com.apollographql.apollo.compiler.codegen.writeTo +import com.apollographql.apollo.compiler.internal.DefaultApolloCompilerRegistry import com.apollographql.apollo.compiler.internal.GradleCompilerPluginLogger +import com.apollographql.apollo.compiler.internal.LegacyOperationIdsGenerator import com.apollographql.apollo.compiler.operationoutput.OperationDescriptor import com.apollographql.apollo.compiler.operationoutput.OperationId import com.apollographql.apollo.compiler.operationoutput.OperationOutput @@ -30,17 +32,17 @@ class EntryPoints { codegenSchemaOptionsFile: File, codegenSchemaFile: File, ) { - val plugin = apolloCompilerPlugin( + val registry = apolloCompilerRegistry( arguments, logLevel, - warnIfNotFound + warnIfNotFound, ) ApolloCompiler.buildCodegenSchema( schemaFiles = normalizedSchemaFiles.toInputFiles(), logger = warning.toLogger(), codegenSchemaOptions = codegenSchemaOptionsFile.toCodegenSchemaOptions(), - foreignSchemas = plugin?.foreignSchemas().orEmpty() + foreignSchemas = registry.foreignSchemas() ).writeTo(codegenSchemaFile) } @@ -54,7 +56,7 @@ class EntryPoints { warning: Consumer, irOperationsFile: File, ) { - val plugin = apolloCompilerPlugin(arguments, logLevel) + val registry = apolloCompilerRegistry(arguments, logLevel, false) val upstream = upstreamIrOperations.toInputFiles().map { it.file.toIrOperations() } ApolloCompiler.buildIrOperations( @@ -62,7 +64,7 @@ class EntryPoints { codegenSchema = codegenSchemaFiles.toInputFiles().map { it.file }.findCodegenSchemaFile().toCodegenSchema(), upstreamCodegenModels = upstream.map { it.codegenModels }, upstreamFragmentDefinitions = upstream.flatMap { it.fragmentDefinitions }, - documentTransform = plugin?.documentTransform(), + documentTransform = registry.executableDocumentTransform(), options = irOptionsFile.toIrOptions(), logger = warning.toLogger(), ).writeTo(irOperationsFile) @@ -81,7 +83,7 @@ class EntryPoints { outputDir: File, metadataOutputFile: File? ) { - val plugin = apolloCompilerPlugin( + val registry = apolloCompilerRegistry( arguments, logLevel, warnIfNotFound @@ -97,16 +99,16 @@ class EntryPoints { downstreamUsedCoordinates = downstreamUsedCoordinates.toUsedCoordinates(), upstreamCodegenMetadata = upstreamCodegenMetadata, codegenOptions = codegenOptions.toCodegenOptions(), - layout = plugin?.layout(codegenSchema), - irOperationsTransform = plugin?.irOperationsTransform(), - javaOutputTransform = plugin?.javaOutputTransform(), - kotlinOutputTransform = plugin?.kotlinOutputTransform(), + layout = registry.layout(codegenSchema), + irOperationsTransform = registry.irOperationsTransform(), + javaOutputTransform = registry.javaOutputTransform(), + kotlinOutputTransform = registry.kotlinOutputTransform(), operationManifestFile = operationManifestFile, - operationOutputGenerator = plugin?.toOperationOutputGenerator(), + operationOutputGenerator = registry.toOperationOutputGenerator(), ).writeTo(outputDir, true, metadataOutputFile) if (upstreamCodegenMetadata.isEmpty()) { - plugin?.schemaListener()?.onSchema(codegenSchema.schema, outputDir) + registry.extraCodeGenerator().generate(codegenSchema.schema.toGQLDocument(), outputDir) } } @@ -123,7 +125,7 @@ class EntryPoints { operationManifestFile: File?, outputDir: File ) { - val plugin = apolloCompilerPlugin( + val registry = apolloCompilerRegistry( arguments, logLevel, warnIfNotFound @@ -132,7 +134,7 @@ class EntryPoints { val codegenSchema = ApolloCompiler.buildCodegenSchema( schemaFiles = schemaFiles.toInputFiles(), codegenSchemaOptions = codegenSchemaOptions.toCodegenSchemaOptions(), - foreignSchemas = plugin?.foreignSchemas().orEmpty(), + foreignSchemas = registry.foreignSchemas(), logger = warning.toLogger() ) @@ -144,18 +146,18 @@ class EntryPoints { logger = warning.toLogger(), layoutFactory = object : LayoutFactory { override fun create(codegenSchema: CodegenSchema): SchemaAndOperationsLayout? { - return plugin?.layout(codegenSchema) + return registry.layout(codegenSchema) } }, - operationOutputGenerator = plugin?.toOperationOutputGenerator(), - irOperationsTransform = plugin?.irOperationsTransform(), - javaOutputTransform = plugin?.javaOutputTransform(), - kotlinOutputTransform = plugin?.kotlinOutputTransform(), - documentTransform = plugin?.documentTransform(), + operationOutputGenerator = registry.toOperationOutputGenerator(), + irOperationsTransform = registry.irOperationsTransform(), + javaOutputTransform = registry.javaOutputTransform(), + kotlinOutputTransform = registry.kotlinOutputTransform(), + documentTransform = registry.executableDocumentTransform(), operationManifestFile = operationManifestFile, ).writeTo(outputDir, true, null) - plugin?.schemaListener()?.onSchema(codegenSchema.schema, outputDir) + registry.extraCodeGenerator().generate(codegenSchema.schema.toGQLDocument(), outputDir) } } @@ -193,46 +195,42 @@ fun Iterable.findCodegenSchemaFile(): File { } ?: error("Cannot find CodegenSchema in $this") } -internal fun apolloCompilerPlugin( + + +internal fun apolloCompilerRegistry( arguments: Map, logLevel: Int, - warnIfNotFound: Boolean = false, -): ApolloCompilerPlugin? { + warnIfNotFound: Boolean = false +): DefaultApolloCompilerRegistry { + val registry = DefaultApolloCompilerRegistry() + val environment = ApolloCompilerPluginEnvironment( + arguments, + GradleCompilerPluginLogger(logLevel), + ) + var hasPlugin = false val plugins = ServiceLoader.load(ApolloCompilerPlugin::class.java, ApolloCompilerPlugin::class.java.classLoader).toList() - - if (plugins.size > 1) { - error("Apollo: only a single compiler plugin is allowed") + plugins.forEach { + hasPlugin = true + it.beforeCompilationStep(environment, registry) + registry.registerPlugin(it) } - val plugin = plugins.singleOrNull() - if (plugin != null) { - error("Apollo: use ApolloCompilerPluginProvider instead of ApolloCompilerPlugin directly. ApolloCompilerPluginProvider allows arguments and logging") + val pluginProviders = ServiceLoader.load(ApolloCompilerPluginProvider::class.java, ApolloCompilerPluginProvider::class.java.classLoader).toList() + pluginProviders.forEach { + println("Apollo: using ApolloCompilerPluginProvider is deprecated. Please use ApolloCompilerPlugin directly.") + hasPlugin = true + val plugin = it.create(environment) + plugin.beforeCompilationStep(environment, registry) + registry.registerPlugin(plugin) } - val pluginProviders = ServiceLoader.load(ApolloCompilerPluginProvider::class.java, ApolloCompilerPlugin::class.java.classLoader).toList() - - if (pluginProviders.size > 1) { - error("Apollo: only a single compiler plugin provider is allowed") + if (!hasPlugin && warnIfNotFound) { + println("Apollo: a compiler plugin was added with `Service.plugin()` but no plugin was loaded by the ServiceLoader. Check your META-INF/services/com.apollographql.apollo.compiler.ApolloCompilerPlugin file.") } - if (pluginProviders.isEmpty() && warnIfNotFound) { - println("Apollo: a compiler plugin was added with `Service.plugin()` but could not be loaded by the ServiceLoader. Check your META-INF/services/com.apollographql.apollo.compiler.ApolloCompilerPluginProvider file.") - } - - val provider = pluginProviders.singleOrNull() - if (provider != null) { - return provider.create( - ApolloCompilerPluginEnvironment( - arguments, - GradleCompilerPluginLogger(logLevel) - ) - ) - } - - return plugins.singleOrNull() + return registry } - internal fun List.toInputFiles(): List = buildList { val iterator = this@toInputFiles.iterator() while (iterator.hasNext()) { diff --git a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/DefaultApolloCompilerRegistry.kt b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/DefaultApolloCompilerRegistry.kt new file mode 100644 index 00000000000..dbca54b1550 --- /dev/null +++ b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/DefaultApolloCompilerRegistry.kt @@ -0,0 +1,289 @@ +@file:Suppress("DEPRECATION") + +package com.apollographql.apollo.compiler.internal + +import com.apollographql.apollo.ast.ForeignSchema +import com.apollographql.apollo.compiler.After +import com.apollographql.apollo.compiler.ApolloCompilerPlugin +import com.apollographql.apollo.compiler.ApolloCompilerRegistry +import com.apollographql.apollo.compiler.Before +import com.apollographql.apollo.compiler.CodeGenerator +import com.apollographql.apollo.compiler.CodegenSchema +import com.apollographql.apollo.compiler.LayoutFactory +import com.apollographql.apollo.compiler.OperationIdsGenerator +import com.apollographql.apollo.compiler.OperationOutputGenerator +import com.apollographql.apollo.compiler.ExecutableDocumentTransform +import com.apollographql.apollo.compiler.Order +import com.apollographql.apollo.compiler.Transform +import com.apollographql.apollo.compiler.codegen.SchemaAndOperationsLayout +import com.apollographql.apollo.compiler.codegen.java.JavaOutput +import com.apollographql.apollo.compiler.codegen.kotlin.KotlinOutput +import com.apollographql.apollo.compiler.ir.IrOperations +import com.apollographql.apollo.compiler.operationoutput.OperationDescriptor +import com.apollographql.apollo.compiler.operationoutput.OperationId +import com.apollographql.apollo.compiler.operationoutput.OperationOutput +import com.apollographql.apollo.compiler.operationoutput.toOperationOutput +import java.nio.charset.StandardCharsets +import java.security.MessageDigest + +internal class Registration(val id: String, val transform: T, val orders: Array) + +private fun List>.toNodes(): Collection> { + val map = mutableMapOf>() + + forEach { + if (map.contains(it.id)) { + error("Apollo: id '${it.id}' is used multiple times for '${it.transform}") + } + map.put(it.id, Node(it.id, it.transform)) + } + + forEach { + val self = map.get(it.id)!! + it.orders.forEach { + when (it) { + is After -> { + val other = map.get(it.id) + if (other != null) { + self.dependencies.add(other) + } + } + + is Before -> { + val other = map.get(it.id) + if (other != null) { + other.dependencies.add(self) + } + } + } + } + } + + return map.values +} + +private class Node(val id: String, val transform: T) { + val dependencies = mutableListOf>() +} + +internal class DefaultApolloCompilerRegistry : ApolloCompilerRegistry { + private val foreignSchemas = mutableListOf() + private val executableDocumentTransforms = mutableListOf>() + private val irTransforms = mutableListOf>>() + private val layoutFactories = mutableListOf() + private val operationIdsGenerators = mutableListOf() + private val javaOutputTransforms = mutableListOf>>() + private val kotlinOutputTransforms = mutableListOf>>() + private val extraCodeGenerators = mutableListOf() + + @Suppress("DEPRECATION") + fun registerPlugin(plugin: ApolloCompilerPlugin) { + val layoutFactory = LegacyLayoutFactory(plugin) + registerLayout(layoutFactory) + + val operationIdsGenerator = LegacyOperationIdsGenerator(plugin) + registerOperationIdsGenerator(operationIdsGenerator) + + val javaOutputTransform = plugin.javaOutputTransform() + if (javaOutputTransform != null) { + println("Apollo: using ApolloCompilerPlugin.javaOutputTransform() is deprecated. Please use registry.registerJavaOutputTransform() from beforeCompilationStep() instead.") + registerJavaOutputTransform(id = "${plugin.javaClass.name}.javaOutputTransform", transform = javaOutputTransform) + } + + val kotlinOutputTransform = plugin.kotlinOutputTransform() + if (kotlinOutputTransform != null) { + println("Apollo: using ApolloCompilerPlugin.kotlinOutputTransform() is deprecated. Please use registry.registerKotlinOutputTransform() from beforeCompilationStep() instead.") + registerKotlinOutputTransform(id = "${plugin.javaClass.name}.kotlinOutputTransform", transform = kotlinOutputTransform) + } + + val documentTransform = plugin.documentTransform() + if (documentTransform != null) { + println("Apollo: using ApolloCompilerPlugin.documentTransform() is deprecated. Please use registry.registerOperationsTransform() from beforeCompilationStep() instead.") + registerOperationsTransform(id = "${plugin.javaClass.name}.documentTransform", transform = LegacyExecutableDocumentTransform(documentTransform)) + } + + val irOperationsTransform = plugin.irOperationsTransform() + if (irOperationsTransform != null) { + println("Apollo: using ApolloCompilerPlugin.irOperationsTransform() is deprecated. Please use registry.registerIrTransform() from beforeCompilationStep() instead.") + registerIrTransform(id = "${plugin.javaClass.name}.irOperationsTransform", transform = irOperationsTransform) + } + + val foreignSchemas = plugin.foreignSchemas() + if (foreignSchemas.isNotEmpty()) { + println("Apollo: using ApolloCompilerPlugin.foreignSchemas() is deprecated. Please use registry.registerForeignSchemas() from beforeCompilationStep() instead.") + registerForeignSchemas(foreignSchemas) + } + + val schemaListener = plugin.schemaListener() + if (schemaListener != null) { + println("Apollo: using ApolloCompilerPlugin.schemaListener() is deprecated. Please use registry.registerExtraCodeGenerator() from beforeCompilationStep() instead.") + registerExtraCodeGenerator(LegacyExtraCodeGenerator(schemaListener)) + } + } + + override fun registerForeignSchemas(schemas: List) { + foreignSchemas.addAll(schemas) + } + + override fun registerOperationsTransform( + id: String, + vararg orders: Order, + transform: ExecutableDocumentTransform, + ) { + executableDocumentTransforms.add(Registration(id, transform, orders)) + } + + override fun registerIrTransform( + id: String, + vararg orders: Order, + transform: Transform, + ) { + irTransforms.add(Registration(id, transform, orders)) + } + + override fun registerLayout(factory: LayoutFactory) { + layoutFactories.add(factory) + } + + override fun registerOperationIdsGenerator(generator: OperationIdsGenerator) { + operationIdsGenerators.add(generator) + } + + override fun registerJavaOutputTransform( + id: String, + vararg orders: Order, + transform: Transform, + ) { + javaOutputTransforms.add(Registration(id, transform, orders)) + } + + override fun registerKotlinOutputTransform( + id: String, + vararg orders: Order, + transform: Transform, + ) { + kotlinOutputTransforms.add(Registration(id, transform, orders)) + } + + override fun registerExtraCodeGenerator(codeGenerator: CodeGenerator) { + extraCodeGenerators.add(codeGenerator) + } + + fun foreignSchemas() = foreignSchemas + + fun executableDocumentTransform(): ExecutableDocumentTransform { + val nodes = executableDocumentTransforms.toNodes().sort() + return ExecutableDocumentTransform { schema, document, fragmentDefinitions -> + nodes.fold(document) { acc, node -> + node.transform.transform(schema, acc, fragmentDefinitions) + } + } + } + + fun layout(codegenSchema: CodegenSchema): SchemaAndOperationsLayout? { + if (layoutFactories.isEmpty()) { + return null + } + if (layoutFactories.size > 1) { + error("Apollo: multiple layouts registered. Check your compiler plugins.") + } + return layoutFactories.single().create(codegenSchema) + } + + fun irOperationsTransform(): Transform { + val nodes = irTransforms.toNodes().sort() + return object : Transform { + override fun transform(input: IrOperations): IrOperations { + return nodes.fold(input) { acc, node -> + node.transform.transform(acc) + } + } + } + } + + fun javaOutputTransform(): Transform { + val nodes = javaOutputTransforms.toNodes().sort() + return object : Transform { + override fun transform(input: JavaOutput): JavaOutput { + return nodes.fold(input) { acc, node -> + node.transform.transform(acc) + } + } + } + } + + fun kotlinOutputTransform(): Transform { + val nodes = kotlinOutputTransforms.toNodes().sort() + return object : Transform { + override fun transform(input: KotlinOutput): KotlinOutput { + return nodes.fold(input) { acc, node -> + node.transform.transform(acc) + } + } + } + } + + @Suppress("DEPRECATION") + fun toOperationOutputGenerator(): OperationOutputGenerator? { + return object : OperationOutputGenerator { + override fun generate(operationDescriptorList: Collection): OperationOutput { + val candidates = operationIdsGenerators.mapNotNull { + when (val operationIds = it.generate(operationDescriptorList)) { + LegacyOperationIdsGenerator.NoList -> null + else -> operationIds + } + } + val operationIds = if (candidates.isEmpty()) { + operationDescriptorList.map { + OperationId(it.source.sha256(), it.name) + } + } else if (candidates.size == 1) { + candidates.single() + } else { + error("Apollo: multiple operationIdGenerators are registered, please check your compiler plugins.") + } + + return operationDescriptorList.toList().toOperationOutput(operationIds) + } + } + } + + fun extraCodeGenerator(): CodeGenerator { + return CodeGenerator { document, outputDirectory -> + extraCodeGenerators.forEach { + it.generate(document, outputDirectory) + } + } + } +} + +internal fun String.sha256(): String { + val bytes = toByteArray(charset = StandardCharsets.UTF_8) + val md = MessageDigest.getInstance("SHA-256") + val digest = md.digest(bytes) + return digest.fold("") { str, it -> str + "%02x".format(it) } +} + +private fun Collection>.sort(): List> { + val visited = mutableSetOf>() + val result = mutableListOf>() + val visiting = mutableSetOf>() + + fun visit(node: Node) { + if (node in visiting) { + throw kotlin.IllegalArgumentException("Apollo: circular dependency detected on transform '${node.id}'") + } + if (node in visited) { + return + } + + visiting.add(node) + node.dependencies.forEach { visit(it) } + visiting.remove(node) + visited.add(node) + result.add(node) + } + + forEach { visit(it) } + return result.reversed() // Topological sort results in reverse order +} diff --git a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/legacy.kt b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/legacy.kt new file mode 100644 index 00000000000..c19a7e40ab4 --- /dev/null +++ b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/legacy.kt @@ -0,0 +1,73 @@ +@file:Suppress("DEPRECATION") + +package com.apollographql.apollo.compiler.internal + +import com.apollographql.apollo.ast.GQLDocument +import com.apollographql.apollo.ast.GQLFragmentDefinition +import com.apollographql.apollo.ast.GQLOperationDefinition +import com.apollographql.apollo.ast.Schema +import com.apollographql.apollo.ast.toSchema +import com.apollographql.apollo.compiler.ApolloCompilerPlugin +import com.apollographql.apollo.compiler.CodeGenerator +import com.apollographql.apollo.compiler.CodegenSchema +import com.apollographql.apollo.compiler.DocumentTransform +import com.apollographql.apollo.compiler.LayoutFactory +import com.apollographql.apollo.compiler.OperationIdsGenerator +import com.apollographql.apollo.compiler.ExecutableDocumentTransform +import com.apollographql.apollo.compiler.SchemaListener +import com.apollographql.apollo.compiler.codegen.SchemaAndOperationsLayout +import com.apollographql.apollo.compiler.operationoutput.OperationDescriptor +import com.apollographql.apollo.compiler.operationoutput.OperationId +import java.io.File +import kotlin.collections.toList + +internal class LegacyOperationIdsGenerator(private val plugin: ApolloCompilerPlugin) : OperationIdsGenerator { + override fun generate(operationDescriptorList: Collection): List { + @Suppress("DEPRECATION") + val operationIds = plugin.operationIds(operationDescriptorList.toList()) + if (operationIds != null) { + println("Apollo: using ApolloCompiler.operationIds() is deprecated. Please use registry.registerOperationIdsGenerator() from beforeCompilationStep() instead.") + return operationIds + } + return NoList + } + + companion object { + internal val NoList: List = emptyList() + } +} + + +internal class LegacyLayoutFactory(private val plugin: ApolloCompilerPlugin) : LayoutFactory { + override fun create(codegenSchema: CodegenSchema): SchemaAndOperationsLayout? { + val layout = plugin.layout(codegenSchema) + if (layout != null) { + println("Apollo: using ApolloCompilerPlugin.layout() is deprecated. Please use registry.registerLayout() from beforeCompilationStep() instead.") + } + return layout + } +} + +internal class LegacyExecutableDocumentTransform(private val documentTransform: DocumentTransform): ExecutableDocumentTransform { + override fun transform( + schema: Schema, + document: GQLDocument, + extraFragmentDefinitions: List, + ): GQLDocument { + return document.copy( + definitions = document.definitions.map { + when (it) { + is GQLFragmentDefinition -> documentTransform.transform(schema, it) + is GQLOperationDefinition -> documentTransform.transform(schema, it) + else -> it + } + } + ) + } +} + +internal class LegacyExtraCodeGenerator(private val schemaListener: SchemaListener): CodeGenerator { + override fun generate(schema: GQLDocument, outputDirectory: File) { + schemaListener.onSchema(schema.toSchema(), outputDirectory) + } +} \ No newline at end of file diff --git a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/operationoutput/OperationOutput.kt b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/operationoutput/OperationOutput.kt index c2913ffc1b7..41953ad75cc 100644 --- a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/operationoutput/OperationOutput.kt +++ b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/operationoutput/OperationOutput.kt @@ -49,3 +49,10 @@ internal fun OperationOutput.findOperationId(name: String): String { return id } +internal fun List.toOperationOutput(operationIds: List): OperationOutput { + return associateBy { descriptor -> + val operationId = operationIds.firstOrNull { it.name == descriptor.name } ?: error("No id found for operation ${descriptor.name}") + operationId.id + } +} + diff --git a/libraries/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo/gradle/internal/serviceloader.kt b/libraries/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo/gradle/internal/serviceloader.kt deleted file mode 100644 index 51fcb72ab99..00000000000 --- a/libraries/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo/gradle/internal/serviceloader.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.apollographql.apollo.gradle.internal - -import com.apollographql.apollo.compiler.ApolloCompilerPlugin -import com.apollographql.apollo.compiler.ApolloCompilerPluginEnvironment -import com.apollographql.apollo.compiler.ApolloCompilerPluginProvider -import com.apollographql.apollo.compiler.internal.GradleCompilerPluginLogger -import java.util.ServiceLoader - -internal fun apolloCompilerPlugin( - arguments: Map, - logLevel: Int, - warnIfNotFound: Boolean = false, -): ApolloCompilerPlugin? { - val plugins = ServiceLoader.load(ApolloCompilerPlugin::class.java).toList() - - if (plugins.size > 1) { - error("Apollo: only a single compiler plugin is allowed") - } - - val plugin = plugins.singleOrNull() - if (plugin != null) { - error("Apollo: use ApolloCompilerPluginProvider instead of ApolloCompilerPlugin directly. ApolloCompilerPluginProvider allows arguments and logging") - } - - val pluginProviders = ServiceLoader.load(ApolloCompilerPluginProvider::class.java).toList() - - if (pluginProviders.size > 1) { - error("Apollo: only a single compiler plugin provider is allowed") - } - - if (pluginProviders.isEmpty() && warnIfNotFound) { - println("Apollo: a compiler plugin was added with `Service.plugin()` but could not be loaded by the ServiceLoader. Check your META-INF/services/com.apollographql.apollo.compiler.ApolloCompilerPluginProvider file.") - } - - val provider = pluginProviders.singleOrNull() - if (provider != null) { - return provider.create( - ApolloCompilerPluginEnvironment( - arguments, - GradleCompilerPluginLogger(logLevel) - ) - ) - } - - return plugins.singleOrNull() -} - diff --git a/tests/compiler-plugins/prefix-names/src/main/kotlin/hooks/TestPlugin.kt b/tests/compiler-plugins/prefix-names/src/main/kotlin/hooks/TestPlugin.kt index f38a0cb94c6..3e96be94c4f 100644 --- a/tests/compiler-plugins/prefix-names/src/main/kotlin/hooks/TestPlugin.kt +++ b/tests/compiler-plugins/prefix-names/src/main/kotlin/hooks/TestPlugin.kt @@ -15,6 +15,7 @@ class TestPlugin( logger.info("TestPlugin.prefix=$prefix") } + @Deprecated("deprecated") override fun layout(codegenSchema: CodegenSchema): SchemaAndOperationsLayout { val delegate = SchemaAndOperationsLayout( codegenSchema = codegenSchema, From 4768468a89e838fd7338c58d8925337d32c761be Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Fri, 30 May 2025 20:15:01 +0200 Subject: [PATCH 2/4] turn warnings into errors --- .../apollo/compiler/ApolloCompilerPlugin.kt | 2 +- .../internal/DefaultApolloCompilerRegistry.kt | 28 +++--- .../apollo/compiler/internal/legacy.kt | 37 +------- .../src/main/kotlin/hooks/TestPlugin.kt | 43 +++++---- .../src/main/kotlin/hooks/TestPlugin.kt | 88 ++++++++++--------- .../src/main/kotlin/hooks/TestPlugin.kt | 12 ++- .../src/main/kotlin/hooks/TestPlugin.kt | 68 +++++++------- .../src/main/kotlin/hooks/TestPlugin.kt | 12 ++- .../src/main/kotlin/hooks/TestPlugin.kt | 85 +++++++++--------- .../src/main/kotlin/schema/TestPlugin.kt | 58 ++++++------ .../src/main/kotlin/hooks/TestPlugin.kt | 10 ++- 11 files changed, 217 insertions(+), 226 deletions(-) diff --git a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ApolloCompilerPlugin.kt b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ApolloCompilerPlugin.kt index 46f529b89c4..718ad588899 100644 --- a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ApolloCompilerPlugin.kt +++ b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ApolloCompilerPlugin.kt @@ -181,7 +181,7 @@ interface ApolloCompilerRegistry { fun registerForeignSchemas(schemas: List) @ApolloExperimental - fun registerOperationsTransform(id: String, vararg orders: Order, transform: ExecutableDocumentTransform) + fun registerExecutableDocumentTransform(id: String, vararg orders: Order, transform: ExecutableDocumentTransform) @ApolloExperimental fun registerIrTransform(id: String, vararg orders: Order, transform: Transform) diff --git a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/DefaultApolloCompilerRegistry.kt b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/DefaultApolloCompilerRegistry.kt index dbca54b1550..5bf543c8833 100644 --- a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/DefaultApolloCompilerRegistry.kt +++ b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/DefaultApolloCompilerRegistry.kt @@ -86,38 +86,32 @@ internal class DefaultApolloCompilerRegistry : ApolloCompilerRegistry { val javaOutputTransform = plugin.javaOutputTransform() if (javaOutputTransform != null) { - println("Apollo: using ApolloCompilerPlugin.javaOutputTransform() is deprecated. Please use registry.registerJavaOutputTransform() from beforeCompilationStep() instead.") - registerJavaOutputTransform(id = "${plugin.javaClass.name}.javaOutputTransform", transform = javaOutputTransform) + error("Apollo: using ApolloCompilerPlugin.javaOutputTransform() is deprecated. Please use registry.registerJavaOutputTransform() from beforeCompilationStep() instead.") } val kotlinOutputTransform = plugin.kotlinOutputTransform() if (kotlinOutputTransform != null) { - println("Apollo: using ApolloCompilerPlugin.kotlinOutputTransform() is deprecated. Please use registry.registerKotlinOutputTransform() from beforeCompilationStep() instead.") - registerKotlinOutputTransform(id = "${plugin.javaClass.name}.kotlinOutputTransform", transform = kotlinOutputTransform) + error("Apollo: using ApolloCompilerPlugin.kotlinOutputTransform() is deprecated. Please use registry.registerKotlinOutputTransform() from beforeCompilationStep() instead.") } val documentTransform = plugin.documentTransform() if (documentTransform != null) { - println("Apollo: using ApolloCompilerPlugin.documentTransform() is deprecated. Please use registry.registerOperationsTransform() from beforeCompilationStep() instead.") - registerOperationsTransform(id = "${plugin.javaClass.name}.documentTransform", transform = LegacyExecutableDocumentTransform(documentTransform)) + error("Apollo: using ApolloCompilerPlugin.documentTransform() is deprecated. Please use registry.registerOperationsTransform() from beforeCompilationStep() instead.") } val irOperationsTransform = plugin.irOperationsTransform() if (irOperationsTransform != null) { - println("Apollo: using ApolloCompilerPlugin.irOperationsTransform() is deprecated. Please use registry.registerIrTransform() from beforeCompilationStep() instead.") - registerIrTransform(id = "${plugin.javaClass.name}.irOperationsTransform", transform = irOperationsTransform) + error("Apollo: using ApolloCompilerPlugin.irOperationsTransform() is deprecated. Please use registry.registerIrTransform() from beforeCompilationStep() instead.") } val foreignSchemas = plugin.foreignSchemas() if (foreignSchemas.isNotEmpty()) { - println("Apollo: using ApolloCompilerPlugin.foreignSchemas() is deprecated. Please use registry.registerForeignSchemas() from beforeCompilationStep() instead.") - registerForeignSchemas(foreignSchemas) + error("Apollo: using ApolloCompilerPlugin.foreignSchemas() is deprecated. Please use registry.registerForeignSchemas() from beforeCompilationStep() instead.") } val schemaListener = plugin.schemaListener() if (schemaListener != null) { - println("Apollo: using ApolloCompilerPlugin.schemaListener() is deprecated. Please use registry.registerExtraCodeGenerator() from beforeCompilationStep() instead.") - registerExtraCodeGenerator(LegacyExtraCodeGenerator(schemaListener)) + error("Apollo: using ApolloCompilerPlugin.schemaListener() is deprecated. Please use registry.registerExtraCodeGenerator() from beforeCompilationStep() instead.") } } @@ -125,7 +119,7 @@ internal class DefaultApolloCompilerRegistry : ApolloCompilerRegistry { foreignSchemas.addAll(schemas) } - override fun registerOperationsTransform( + override fun registerExecutableDocumentTransform( id: String, vararg orders: Order, transform: ExecutableDocumentTransform, @@ -181,13 +175,11 @@ internal class DefaultApolloCompilerRegistry : ApolloCompilerRegistry { } fun layout(codegenSchema: CodegenSchema): SchemaAndOperationsLayout? { - if (layoutFactories.isEmpty()) { - return null - } - if (layoutFactories.size > 1) { + val candidates = layoutFactories.mapNotNull { it.create(codegenSchema) } + if (candidates.size > 1) { error("Apollo: multiple layouts registered. Check your compiler plugins.") } - return layoutFactories.single().create(codegenSchema) + return candidates.singleOrNull() } fun irOperationsTransform(): Transform { diff --git a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/legacy.kt b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/legacy.kt index c19a7e40ab4..e786ebca1aa 100644 --- a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/legacy.kt +++ b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/legacy.kt @@ -2,24 +2,13 @@ package com.apollographql.apollo.compiler.internal -import com.apollographql.apollo.ast.GQLDocument -import com.apollographql.apollo.ast.GQLFragmentDefinition -import com.apollographql.apollo.ast.GQLOperationDefinition -import com.apollographql.apollo.ast.Schema -import com.apollographql.apollo.ast.toSchema import com.apollographql.apollo.compiler.ApolloCompilerPlugin -import com.apollographql.apollo.compiler.CodeGenerator import com.apollographql.apollo.compiler.CodegenSchema -import com.apollographql.apollo.compiler.DocumentTransform import com.apollographql.apollo.compiler.LayoutFactory import com.apollographql.apollo.compiler.OperationIdsGenerator -import com.apollographql.apollo.compiler.ExecutableDocumentTransform -import com.apollographql.apollo.compiler.SchemaListener import com.apollographql.apollo.compiler.codegen.SchemaAndOperationsLayout import com.apollographql.apollo.compiler.operationoutput.OperationDescriptor import com.apollographql.apollo.compiler.operationoutput.OperationId -import java.io.File -import kotlin.collections.toList internal class LegacyOperationIdsGenerator(private val plugin: ApolloCompilerPlugin) : OperationIdsGenerator { override fun generate(operationDescriptorList: Collection): List { @@ -42,32 +31,8 @@ internal class LegacyLayoutFactory(private val plugin: ApolloCompilerPlugin) : L override fun create(codegenSchema: CodegenSchema): SchemaAndOperationsLayout? { val layout = plugin.layout(codegenSchema) if (layout != null) { - println("Apollo: using ApolloCompilerPlugin.layout() is deprecated. Please use registry.registerLayout() from beforeCompilationStep() instead.") + error("Apollo: using ApolloCompilerPlugin.layout() is deprecated. Please use registry.registerLayout() from beforeCompilationStep() instead.") } return layout } } - -internal class LegacyExecutableDocumentTransform(private val documentTransform: DocumentTransform): ExecutableDocumentTransform { - override fun transform( - schema: Schema, - document: GQLDocument, - extraFragmentDefinitions: List, - ): GQLDocument { - return document.copy( - definitions = document.definitions.map { - when (it) { - is GQLFragmentDefinition -> documentTransform.transform(schema, it) - is GQLOperationDefinition -> documentTransform.transform(schema, it) - else -> it - } - } - ) - } -} - -internal class LegacyExtraCodeGenerator(private val schemaListener: SchemaListener): CodeGenerator { - override fun generate(schema: GQLDocument, outputDirectory: File) { - schemaListener.onSchema(schema.toSchema(), outputDirectory) - } -} \ No newline at end of file diff --git a/tests/compiler-plugins/add-field/src/main/kotlin/hooks/TestPlugin.kt b/tests/compiler-plugins/add-field/src/main/kotlin/hooks/TestPlugin.kt index 801d7a23baa..d88ff942166 100644 --- a/tests/compiler-plugins/add-field/src/main/kotlin/hooks/TestPlugin.kt +++ b/tests/compiler-plugins/add-field/src/main/kotlin/hooks/TestPlugin.kt @@ -11,10 +11,10 @@ import com.apollographql.apollo.ast.definitionFromScope import com.apollographql.apollo.ast.rawType import com.apollographql.apollo.ast.responseName import com.apollographql.apollo.ast.rootTypeDefinition -import com.apollographql.apollo.compiler.DocumentTransform import com.apollographql.apollo.compiler.ApolloCompilerPlugin import com.apollographql.apollo.compiler.ApolloCompilerPluginEnvironment import com.apollographql.apollo.compiler.ApolloCompilerPluginProvider +import com.apollographql.apollo.compiler.ApolloCompilerRegistry class TestPluginProvider: ApolloCompilerPluginProvider { override fun create(environment: ApolloCompilerPluginEnvironment): ApolloCompilerPlugin { @@ -23,22 +23,6 @@ class TestPluginProvider: ApolloCompilerPluginProvider { } class TestPlugin : ApolloCompilerPlugin { - override fun documentTransform(): DocumentTransform { - return object : DocumentTransform { - override fun transform(schema: Schema, operation: GQLOperationDefinition): GQLOperationDefinition { - return operation.copy( - selections = operation.selections.alwaysGreet(schema, operation.rootTypeDefinition(schema)!!.name) - ) - } - - override fun transform(schema: Schema, fragment: GQLFragmentDefinition): GQLFragmentDefinition { - return fragment.copy( - selections = fragment.selections.alwaysGreet(schema, fragment.typeCondition.name) - ) - } - } - } - private fun List.alwaysGreet(schema: Schema, parentType: String): List { val selections = this.map { when (it) { @@ -58,4 +42,29 @@ class TestPlugin : ApolloCompilerPlugin { selections } } + + override fun beforeCompilationStep( + environment: ApolloCompilerPluginEnvironment, + registry: ApolloCompilerRegistry, + ) { + registry.registerExecutableDocumentTransform("test"){ schema, document, fragments -> + document.copy( + definitions = document.definitions.map { + when (it) { + is GQLOperationDefinition -> { + it.copy( + selections = it.selections.alwaysGreet(schema, it.rootTypeDefinition(schema)!!.name) + ) + } + is GQLFragmentDefinition -> { + it.copy( + selections = it.selections.alwaysGreet(schema, it.typeCondition.name) + ) + } + else -> it + } + } + ) + } + } } diff --git a/tests/compiler-plugins/capitalize-enum-values/src/main/kotlin/hooks/TestPlugin.kt b/tests/compiler-plugins/capitalize-enum-values/src/main/kotlin/hooks/TestPlugin.kt index 538da4201fb..fbf5597f0d3 100644 --- a/tests/compiler-plugins/capitalize-enum-values/src/main/kotlin/hooks/TestPlugin.kt +++ b/tests/compiler-plugins/capitalize-enum-values/src/main/kotlin/hooks/TestPlugin.kt @@ -3,6 +3,7 @@ package hooks import com.apollographql.apollo.compiler.ApolloCompilerPlugin import com.apollographql.apollo.compiler.ApolloCompilerPluginEnvironment import com.apollographql.apollo.compiler.ApolloCompilerPluginProvider +import com.apollographql.apollo.compiler.ApolloCompilerRegistry import com.apollographql.apollo.compiler.Transform import com.apollographql.apollo.compiler.codegen.kotlin.KotlinOutput import com.squareup.kotlinpoet.FunSpec @@ -15,56 +16,57 @@ class TestPluginProvider: ApolloCompilerPluginProvider { } class TestPlugin : ApolloCompilerPlugin { - override fun kotlinOutputTransform(): Transform { - return object : Transform { + override fun beforeCompilationStep( + environment: ApolloCompilerPluginEnvironment, + registry: ApolloCompilerRegistry, + ) { + registry.registerKotlinOutputTransform("test", transform = object : Transform{ override fun transform(input: KotlinOutput): KotlinOutput { - return KotlinOutput( + return KotlinOutput( fileSpecs = input.fileSpecs.map { - it.toBuilder() - .apply { - members.replaceAll { member -> - if (member is TypeSpec && member.isEnum) { - member.toBuilder() - .apply { - val capitalizedEnumConstants = enumConstants.mapKeys { (key, _) -> - key.uppercase() - } - enumConstants.clear() - enumConstants.putAll(capitalizedEnumConstants) + it.toBuilder() + .apply { + members.replaceAll { member -> + if (member is TypeSpec && member.isEnum) { + member.toBuilder() + .apply { + val capitalizedEnumConstants = enumConstants.mapKeys { (key, _) -> + key.uppercase() + } + enumConstants.clear() + enumConstants.putAll(capitalizedEnumConstants) - // knownValues is in the companion object - typeSpecs.replaceAll { typeSpec -> - typeSpec.toBuilder() - .apply { - propertySpecs.replaceAll { propertySpec -> - if (propertySpec.name == "knownEntries") { - propertySpec.toBuilder() - .getter(FunSpec.getterBuilder() - .addStatement("return listOf(%L)", capitalizedEnumConstants.keys.filterNot { it == "UNKNOWN__" }.joinToString()) + // knownValues is in the companion object + typeSpecs.replaceAll { typeSpec -> + typeSpec.toBuilder() + .apply { + propertySpecs.replaceAll { propertySpec -> + if (propertySpec.name == "knownEntries") { + propertySpec.toBuilder() + .getter(FunSpec.getterBuilder() + .addStatement("return listOf(%L)", capitalizedEnumConstants.keys.filterNot { it == "UNKNOWN__" }.joinToString()) + .build() + ) .build() - ) - .build() - } else { - propertySpec + } else { + propertySpec + } + } } - } - } - .build() - } - } - .build() - } else { - member + .build() + } + } + .build() + } else { + member + } + } } - } - } - .build() - }, - codegenMetadata = input.codegenMetadata + .build() + }, + codegenMetadata = input.codegenMetadata ) } - - } + }) } - } diff --git a/tests/compiler-plugins/custom-flatten/src/main/kotlin/hooks/TestPlugin.kt b/tests/compiler-plugins/custom-flatten/src/main/kotlin/hooks/TestPlugin.kt index 2aac72ba2ca..b13ffa76180 100644 --- a/tests/compiler-plugins/custom-flatten/src/main/kotlin/hooks/TestPlugin.kt +++ b/tests/compiler-plugins/custom-flatten/src/main/kotlin/hooks/TestPlugin.kt @@ -3,18 +3,22 @@ package hooks import com.apollographql.apollo.compiler.ApolloCompilerPlugin import com.apollographql.apollo.compiler.ApolloCompilerPluginEnvironment import com.apollographql.apollo.compiler.ApolloCompilerPluginProvider +import com.apollographql.apollo.compiler.ApolloCompilerRegistry import com.apollographql.apollo.compiler.Transform import com.apollographql.apollo.compiler.ir.IrOperations -class TestPluginProvider: ApolloCompilerPluginProvider { +class TestPluginProvider : ApolloCompilerPluginProvider { override fun create(environment: ApolloCompilerPluginEnvironment): ApolloCompilerPlugin { return TestPlugin() } } class TestPlugin : ApolloCompilerPlugin { - override fun irOperationsTransform(): Transform { - return object : Transform { + override fun beforeCompilationStep( + environment: ApolloCompilerPluginEnvironment, + registry: ApolloCompilerRegistry, + ) { + registry.registerIrTransform("test", transform = object : Transform { override fun transform(input: IrOperations): IrOperations { return input.copy( operations = input.operations.map { @@ -24,6 +28,6 @@ class TestPlugin : ApolloCompilerPlugin { } ) } - } + }) } } diff --git a/tests/compiler-plugins/default-null-values/src/main/kotlin/hooks/TestPlugin.kt b/tests/compiler-plugins/default-null-values/src/main/kotlin/hooks/TestPlugin.kt index 153a88bae85..4a2d5615dc4 100644 --- a/tests/compiler-plugins/default-null-values/src/main/kotlin/hooks/TestPlugin.kt +++ b/tests/compiler-plugins/default-null-values/src/main/kotlin/hooks/TestPlugin.kt @@ -3,6 +3,7 @@ package hooks import com.apollographql.apollo.compiler.ApolloCompilerPlugin import com.apollographql.apollo.compiler.ApolloCompilerPluginEnvironment import com.apollographql.apollo.compiler.ApolloCompilerPluginProvider +import com.apollographql.apollo.compiler.ApolloCompilerRegistry import com.apollographql.apollo.compiler.Transform import com.apollographql.apollo.compiler.codegen.kotlin.KotlinOutput import com.squareup.kotlinpoet.CodeBlock @@ -16,8 +17,11 @@ class TestPluginProvider: ApolloCompilerPluginProvider { } class TestPlugin: ApolloCompilerPlugin { - override fun kotlinOutputTransform(): Transform { - return object : Transform { + override fun beforeCompilationStep( + environment: ApolloCompilerPluginEnvironment, + registry: ApolloCompilerRegistry, + ) { + registry.registerKotlinOutputTransform("test", transform = object : Transform { override fun transform(input: KotlinOutput): KotlinOutput { return KotlinOutput( fileSpecs = input.fileSpecs.map { @@ -36,36 +40,36 @@ class TestPlugin: ApolloCompilerPlugin { codegenMetadata = input.codegenMetadata ) } + }) + } +} - private fun TypeSpec.addDefaultValueToNullableProperties(): TypeSpec { - return toBuilder() - .apply { - // Only care about data classes - if (modifiers.contains(KModifier.DATA)) { - primaryConstructor( - primaryConstructor!!.toBuilder() - .apply { - parameters.replaceAll { param -> - if (param.type.isNullable) { - param.toBuilder() - .defaultValue(CodeBlock.of("null")) - .build() - } else { - param - } - } - } - .build() - ) - } +private fun TypeSpec.addDefaultValueToNullableProperties(): TypeSpec { + return toBuilder() + .apply { + // Only care about data classes + if (modifiers.contains(KModifier.DATA)) { + primaryConstructor( + primaryConstructor!!.toBuilder() + .apply { + parameters.replaceAll { param -> + if (param.type.isNullable) { + param.toBuilder() + .defaultValue(CodeBlock.of("null")) + .build() + } else { + param + } + } + } + .build() + ) + } - // Recurse on nested types - typeSpecs.replaceAll { typeSpec -> - typeSpec.addDefaultValueToNullableProperties() - } - } - .build() + // Recurse on nested types + typeSpecs.replaceAll { typeSpec -> + typeSpec.addDefaultValueToNullableProperties() + } } - } - } -} + .build() +} \ No newline at end of file diff --git a/tests/compiler-plugins/getters-and-setters/src/main/kotlin/hooks/TestPlugin.kt b/tests/compiler-plugins/getters-and-setters/src/main/kotlin/hooks/TestPlugin.kt index fcff6972c85..472a6cd9535 100644 --- a/tests/compiler-plugins/getters-and-setters/src/main/kotlin/hooks/TestPlugin.kt +++ b/tests/compiler-plugins/getters-and-setters/src/main/kotlin/hooks/TestPlugin.kt @@ -3,6 +3,7 @@ package hooks import com.apollographql.apollo.compiler.ApolloCompilerPlugin import com.apollographql.apollo.compiler.ApolloCompilerPluginEnvironment import com.apollographql.apollo.compiler.ApolloCompilerPluginProvider +import com.apollographql.apollo.compiler.ApolloCompilerRegistry import com.apollographql.apollo.compiler.Transform import com.apollographql.apollo.compiler.capitalizeFirstLetter import com.apollographql.apollo.compiler.codegen.java.JavaOutput @@ -15,9 +16,13 @@ class TestPluginProvider: ApolloCompilerPluginProvider { return TestPlugin() } } + class TestPlugin : ApolloCompilerPlugin { - override fun javaOutputTransform(): Transform { - return object : Transform { + override fun beforeCompilationStep( + environment: ApolloCompilerPluginEnvironment, + registry: ApolloCompilerRegistry, + ) { + registry.registerJavaOutputTransform("test", transform = object : Transform { override fun transform(input: JavaOutput): JavaOutput { return JavaOutput( javaFiles = input.javaFiles.map { javaFile -> @@ -66,6 +71,7 @@ class TestPlugin : ApolloCompilerPlugin { codegenMetadata = input.codegenMetadata ) } - } + + }) } } diff --git a/tests/compiler-plugins/prefix-names/src/main/kotlin/hooks/TestPlugin.kt b/tests/compiler-plugins/prefix-names/src/main/kotlin/hooks/TestPlugin.kt index 3e96be94c4f..1999ad03255 100644 --- a/tests/compiler-plugins/prefix-names/src/main/kotlin/hooks/TestPlugin.kt +++ b/tests/compiler-plugins/prefix-names/src/main/kotlin/hooks/TestPlugin.kt @@ -4,64 +4,67 @@ import com.apollographql.apollo.compiler.ApolloCompilerPlugin import com.apollographql.apollo.compiler.ApolloCompilerPluginEnvironment import com.apollographql.apollo.compiler.ApolloCompilerPluginLogger import com.apollographql.apollo.compiler.ApolloCompilerPluginProvider +import com.apollographql.apollo.compiler.ApolloCompilerRegistry import com.apollographql.apollo.compiler.CodegenSchema +import com.apollographql.apollo.compiler.LayoutFactory import com.apollographql.apollo.compiler.codegen.SchemaAndOperationsLayout -class TestPlugin( - val prefix: String, - logger: ApolloCompilerPluginLogger, -) : ApolloCompilerPlugin { - init { - logger.info("TestPlugin.prefix=$prefix") - } +class TestPlugin() : ApolloCompilerPlugin { + override fun beforeCompilationStep( + environment: ApolloCompilerPluginEnvironment, + registry: ApolloCompilerRegistry, + ) { + val prefix = environment.arguments.get("prefix") as String - @Deprecated("deprecated") - override fun layout(codegenSchema: CodegenSchema): SchemaAndOperationsLayout { - val delegate = SchemaAndOperationsLayout( - codegenSchema = codegenSchema, - packageName = "hooks.prefixnames.kotlin", - rootPackageName = null, - useSemanticNaming = null, - decapitalizeFields = null, - generatedSchemaName = null - ) + registry.registerLayout(object : LayoutFactory { + override fun create(codegenSchema: CodegenSchema): SchemaAndOperationsLayout? { + val delegate = SchemaAndOperationsLayout( + codegenSchema = codegenSchema, + packageName = "hooks.prefixnames.kotlin", + rootPackageName = null, + useSemanticNaming = null, + decapitalizeFields = null, + generatedSchemaName = null + ) - return object : SchemaAndOperationsLayout by delegate { + return object : SchemaAndOperationsLayout by delegate { - override fun schemaTypeName(schemaTypeName: String): String { - return delegate.schemaTypeName(schemaTypeName).prefixed() - } + override fun schemaTypeName(schemaTypeName: String): String { + return delegate.schemaTypeName(schemaTypeName).prefixed() + } - override fun schemaName(): String { - return delegate.schemaName().prefixed() - } + override fun schemaName(): String { + return delegate.schemaName().prefixed() + } - override fun assertionsName(): String { - return delegate.assertionsName().prefixed() - } + override fun assertionsName(): String { + return delegate.assertionsName().prefixed() + } - override fun paginationName(): String { - return delegate.paginationName().prefixed() - } + override fun paginationName(): String { + return delegate.paginationName().prefixed() + } - override fun operationName(name: String, capitalizedOperationType: String): String { - return delegate.operationName(name, capitalizedOperationType).prefixed() - } + override fun operationName(name: String, capitalizedOperationType: String): String { + return delegate.operationName(name, capitalizedOperationType).prefixed() + } - override fun fragmentName(name: String): String { - return delegate.fragmentName(name).prefixed() - } + override fun fragmentName(name: String): String { + return delegate.fragmentName(name).prefixed() + } - private fun String.prefixed(): String { - return "${prefix}${this}" + private fun String.prefixed(): String { + return "${prefix}${this}" + } + } } - } + }) } } + class TestPluginProvider : ApolloCompilerPluginProvider { override fun create(environment: ApolloCompilerPluginEnvironment): ApolloCompilerPlugin { - return TestPlugin(environment.arguments.get("prefix") as String, environment.logger) + return TestPlugin() } - } diff --git a/tests/compiler-plugins/schema-codegen/src/main/kotlin/schema/TestPlugin.kt b/tests/compiler-plugins/schema-codegen/src/main/kotlin/schema/TestPlugin.kt index 278f09db226..67606f60e0f 100644 --- a/tests/compiler-plugins/schema-codegen/src/main/kotlin/schema/TestPlugin.kt +++ b/tests/compiler-plugins/schema-codegen/src/main/kotlin/schema/TestPlugin.kt @@ -2,12 +2,14 @@ package schema import com.apollographql.apollo.ast.ForeignSchema import com.apollographql.apollo.ast.GQLIntValue +import com.apollographql.apollo.ast.GQLTypeDefinition import com.apollographql.apollo.ast.Schema import com.apollographql.apollo.ast.parseAsGQLDocument import com.apollographql.apollo.compiler.ApolloCompilerPlugin import com.apollographql.apollo.compiler.ApolloCompilerPluginEnvironment import com.apollographql.apollo.compiler.ApolloCompilerPluginLogger import com.apollographql.apollo.compiler.ApolloCompilerPluginProvider +import com.apollographql.apollo.compiler.ApolloCompilerRegistry import com.apollographql.apollo.compiler.SchemaListener import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.FileSpec @@ -23,8 +25,11 @@ class TestPlugin( logger.info("TestPlugin") } - override fun foreignSchemas(): List { - return listOf( + override fun beforeCompilationStep( + environment: ApolloCompilerPluginEnvironment, + registry: ApolloCompilerRegistry, + ) { + registry.registerForeignSchemas(listOf( ForeignSchema( "cache", "v0.1", @@ -32,34 +37,31 @@ class TestPlugin( it.parseAsGQLDocument().getOrThrow().definitions } ) - ) - } + )) - override fun schemaListener(): SchemaListener { - return object : SchemaListener { - override fun onSchema(schema: Schema, outputDirectory: File) { - val maxAge = schema.typeDefinition("Menu") - .directives - .single { - it.name == "cacheControl" - } - .arguments - .single { - it.name == "maxAge" - } - .value - .let { it as GQLIntValue } - .value + registry.registerExtraCodeGenerator { schema, outputDirectory -> + val maxAge = schema.definitions.filterIsInstance() + .first { it.name == "Menu" } + .directives + .single { + it.name == "cacheControl" + } + .arguments + .single { + it.name == "maxAge" + } + .value + .let { it as GQLIntValue } + .value - FileSpec.builder("hooks.generated", "cache") - .addProperty( - PropertySpec.builder("menuMaxAge", ClassName("kotlin", "String")) - .initializer("%S", maxAge) - .build() - ) - .build() - .writeTo(outputDirectory) - } + FileSpec.builder("hooks.generated", "cache") + .addProperty( + PropertySpec.builder("menuMaxAge", ClassName("kotlin", "String")) + .initializer("%S", maxAge) + .build() + ) + .build() + .writeTo(outputDirectory) } } } diff --git a/tests/compiler-plugins/typename-interface/src/main/kotlin/hooks/TestPlugin.kt b/tests/compiler-plugins/typename-interface/src/main/kotlin/hooks/TestPlugin.kt index b37f6bbf9e0..1a622e6a2b2 100644 --- a/tests/compiler-plugins/typename-interface/src/main/kotlin/hooks/TestPlugin.kt +++ b/tests/compiler-plugins/typename-interface/src/main/kotlin/hooks/TestPlugin.kt @@ -3,6 +3,7 @@ package hooks import com.apollographql.apollo.compiler.ApolloCompilerPlugin import com.apollographql.apollo.compiler.ApolloCompilerPluginEnvironment import com.apollographql.apollo.compiler.ApolloCompilerPluginProvider +import com.apollographql.apollo.compiler.ApolloCompilerRegistry import com.apollographql.apollo.compiler.Transform import com.apollographql.apollo.compiler.codegen.kotlin.KotlinOutput import com.squareup.kotlinpoet.ClassName @@ -18,8 +19,11 @@ class TestPluginProvider: ApolloCompilerPluginProvider { class TestPlugin : ApolloCompilerPlugin { private val interfaceName = "hooks.typenameinterface.HasTypeName" - override fun kotlinOutputTransform(): Transform { - return object : Transform { + override fun beforeCompilationStep( + environment: ApolloCompilerPluginEnvironment, + registry: ApolloCompilerRegistry, + ) { + registry.registerKotlinOutputTransform("test", transform = object : Transform { override fun transform(input: KotlinOutput): KotlinOutput { return KotlinOutput( fileSpecs = input.fileSpecs.map { @@ -38,7 +42,7 @@ class TestPlugin : ApolloCompilerPlugin { codegenMetadata = input.codegenMetadata ) } - } + }) } private fun TypeSpec.addSuperInterfaceOnType(): TypeSpec { From c468ba018a9d53f204e87745e27503c32238c0b0 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Fri, 30 May 2025 21:12:46 +0200 Subject: [PATCH 3/4] update apiDump --- libraries/apollo-compiler/api/apollo-compiler.api | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/apollo-compiler/api/apollo-compiler.api b/libraries/apollo-compiler/api/apollo-compiler.api index 400844ca28f..453640cc83a 100644 --- a/libraries/apollo-compiler/api/apollo-compiler.api +++ b/libraries/apollo-compiler/api/apollo-compiler.api @@ -59,6 +59,7 @@ public abstract interface class com/apollographql/apollo/compiler/ApolloCompiler } 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 @@ -66,7 +67,6 @@ public abstract interface class com/apollographql/apollo/compiler/ApolloCompiler 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 abstract fun registerOperationsTransform (Ljava/lang/String;[Lcom/apollographql/apollo/compiler/Order;Lcom/apollographql/apollo/compiler/ExecutableDocumentTransform;)V } public final class com/apollographql/apollo/compiler/Before : com/apollographql/apollo/compiler/Order { From c1b4cbacc50c82ecfcc39f2a2b86b0ab05df0c4d Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Fri, 30 May 2025 21:38:47 +0200 Subject: [PATCH 4/4] fix sort --- .../internal/DefaultApolloCompilerRegistry.kt | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/DefaultApolloCompilerRegistry.kt b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/DefaultApolloCompilerRegistry.kt index 5bf543c8833..e45f43bd1cb 100644 --- a/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/DefaultApolloCompilerRegistry.kt +++ b/libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/internal/DefaultApolloCompilerRegistry.kt @@ -257,25 +257,26 @@ internal fun String.sha256(): String { } private fun Collection>.sort(): List> { - val visited = mutableSetOf>() + val visited = mutableListOf>() val result = mutableListOf>() - val visiting = mutableSetOf>() fun visit(node: Node) { - if (node in visiting) { - throw kotlin.IllegalArgumentException("Apollo: circular dependency detected on transform '${node.id}'") - } - if (node in visited) { - return - } - - visiting.add(node) - node.dependencies.forEach { visit(it) } - visiting.remove(node) visited.add(node) + node.dependencies.forEach { + if (!visited.contains(it)) { + visit(it) + } else if (!result.contains(it)){ + throw IllegalArgumentException("Apollo: circular dependency detected on transform '${node.id}': ${visited.map { it.id } + node.id}") + } + } result.add(node) } - forEach { visit(it) } - return result.reversed() // Topological sort results in reverse order + forEach { + if (!visited.contains(it)) { + visit(it) + } + } + return result } +