Skip to content

Commit

Permalink
Something to just demonstrate
Browse files Browse the repository at this point in the history
  • Loading branch information
jimisaacs committed Sep 20, 2023
1 parent 57d2b4f commit 78a1a91
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 41 deletions.
105 changes: 85 additions & 20 deletions Sources/ApolloCodegenLib/ApolloCodegen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class ApolloCodegen {
case invalidSchemaName(_ name: String, message: String)
case targetNameConflict(name: String)
case typeNameConflict(name: String, conflictingName: String, containingObject: String)
case failedToComputeOperationIdentifier(type: String, name: String, error: Swift.Error)

public var errorDescription: String? {
switch self {
Expand Down Expand Up @@ -64,6 +65,8 @@ public class ApolloCodegen {
Recommend using a field alias for one of these fields to resolve this conflict. \
For more info see: https://www.apollographql.com/docs/ios/troubleshooting/codegen-troubleshooting#typenameconflict
"""
case let .failedToComputeOperationIdentifier(type, name, error):
return "Received failure while computing operation identifier for \(type) named '\(name)', Error: \(error.localizedDescription)"
}
}
}
Expand Down Expand Up @@ -91,6 +94,9 @@ public class ApolloCodegen {

}

/// A `nil` result is treated as a cancellation, and the default operationIdentifier is used
public typealias ComputeOperationIdentifier = (any IROperation, @escaping (Result<String, Swift.Error>?) -> Void) -> Void

/// Executes the code generation engine with a specified configuration.
///
/// - Parameters:
Expand All @@ -103,16 +109,18 @@ public class ApolloCodegen {
public static func build(
with configuration: ApolloCodegenConfiguration,
withRootURL rootURL: URL? = nil,
itemsToGenerate: ItemsToGenerate = [.code]
itemsToGenerate: ItemsToGenerate = [.code],
computeOperationIdentifier: ComputeOperationIdentifier? = nil
) throws {
try build(with: configuration, rootURL: rootURL, itemsToGenerate: itemsToGenerate)
try build(with: configuration, rootURL: rootURL, itemsToGenerate: itemsToGenerate, computeOperationIdentifier: computeOperationIdentifier)
}

internal static func build(
with configuration: ApolloCodegenConfiguration,
rootURL: URL? = nil,
fileManager: ApolloFileManager = .default,
itemsToGenerate: ItemsToGenerate
itemsToGenerate: ItemsToGenerate,
computeOperationIdentifier: ComputeOperationIdentifier? = nil
) throws {

let configContext = ConfigurationContext(
Expand All @@ -131,29 +139,34 @@ public class ApolloCodegen {

let ir = IR(compilationResult: compilationResult)

var existingGeneratedFilePaths: Set<String>?

if itemsToGenerate.contains(.code) && configuration.options.pruneGeneratedFiles {
existingGeneratedFilePaths = try findExistingGeneratedFilePaths(
let generate: () throws -> Void = {
try generateFiles(
compilationResult: compilationResult,
ir: ir,
config: configContext,
fileManager: fileManager
fileManager: fileManager,
itemsToGenerate: itemsToGenerate,
computeOperationIdentifier: computeOperationIdentifier
)
}

try generateFiles(
compilationResult: compilationResult,
ir: ir,
config: configContext,
fileManager: fileManager,
itemsToGenerate: itemsToGenerate
)

if var existingGeneratedFilePaths {
let generateWithPruning: () throws -> Void = {
var existingGeneratedFilePaths = try findExistingGeneratedFilePaths(
config: configContext,
fileManager: fileManager
)
try generate()
try deleteExtraneousGeneratedFiles(
from: &existingGeneratedFilePaths,
afterCodeGenerationUsing: fileManager
)
}

if itemsToGenerate.contains(.code) && configuration.options.pruneGeneratedFiles {
try generateWithPruning()
} else {
try generate()
}
}

// MARK: Internal
Expand Down Expand Up @@ -412,7 +425,8 @@ public class ApolloCodegen {
ir: IR,
config: ConfigurationContext,
fileManager: ApolloFileManager = .default,
itemsToGenerate: ItemsToGenerate
itemsToGenerate: ItemsToGenerate,
computeOperationIdentifier: ComputeOperationIdentifier? = nil
) throws {

if itemsToGenerate.contains(.code) {
Expand All @@ -431,9 +445,40 @@ public class ApolloCodegen {
operationIDsFileGenerator = OperationManifestFileGenerator(config: config)
}

for operation in compilationResult.operations {
let irOperations = compilationResult.operations.map { ir.build(operation: $0) }
var results = [Result<String, Swift.Error>?](repeating: nil, count: irOperations.count)

if let computeOperationIdentifier {
let dispatchGroup = DispatchGroup()
DispatchQueue.concurrentPerform(iterations: irOperations.count) { index in
let irOperation = irOperations[index]
var sources: [String] = [irOperation.definition.source.convertedToSingleLine()]
for fragment in irOperation.referencedFragments {
sources.append(fragment.definition.source.convertedToSingleLine())
}
dispatchGroup.enter()
computeOperationIdentifier(irOperation) { result in
results[index] = result
dispatchGroup.leave()
}
}
dispatchGroup.wait()
}

for (index, irOperation) in irOperations.enumerated() {
try autoreleasepool {
let irOperation = ir.build(operation: operation)
if let result = results[index] {
switch result {
case .success(let operationIdentifier):
irOperation.operationIdentifier = operationIdentifier
case .failure(let error):
throw Error.failedToComputeOperationIdentifier(
type: irOperation.definition.operationType.rawValue,
name: irOperation.definition.name,
error: error
)
}
}

if itemsToGenerate.contains(.code) {
try validateTypeConflicts(for: irOperation.rootField.selectionSet, with: config, in: irOperation.definition.name)
Expand Down Expand Up @@ -611,4 +656,24 @@ public class ApolloCodegen {

}

public protocol IROperation: AnyObject {
var filePath: String { get }
var name: String { get }
var source: String { get }
var type: CompilationResult.OperationType { get }
}

extension IR.Operation: IROperation {
public var filePath: String { definition.filePath }
public var name: String { definition.name }
public var source: String {
var sources: [String] = [definition.source.convertedToSingleLine()]
for fragment in referencedFragments {
sources.append(fragment.definition.source.convertedToSingleLine())
}
return sources.joined(separator: "\n")
}
public var type: CompilationResult.OperationType { definition.operationType }
}

#endif
22 changes: 11 additions & 11 deletions Sources/ApolloCodegenLib/Frontend/CompilationResult.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import JavaScriptCore

/// The output of the frontend compiler.
public class CompilationResult: JavaScriptObject {
public final class CompilationResult: JavaScriptObject {
private enum Constants {
static let LocalCacheMutationDirectiveName = "apollo_client_ios_localCacheMutation"
}
Expand All @@ -15,15 +15,15 @@ public class CompilationResult: JavaScriptObject {

lazy var schemaDocumentation: String? = self["schemaDocumentation"]

public class RootTypeDefinition: JavaScriptObject {
public final class RootTypeDefinition: JavaScriptObject {
lazy var queryType: GraphQLNamedType = self["queryType"]

lazy var mutationType: GraphQLNamedType? = self["mutationType"]

lazy var subscriptionType: GraphQLNamedType? = self["subscriptionType"]
}

public class OperationDefinition: JavaScriptObject, Hashable {
public final class OperationDefinition: JavaScriptObject, Hashable {
lazy var name: String = self["name"]

lazy var operationType: OperationType = self["operationType"]
Expand Down Expand Up @@ -94,15 +94,15 @@ public class CompilationResult: JavaScriptObject {
}
}

public class VariableDefinition: JavaScriptObject {
public final class VariableDefinition: JavaScriptObject {
lazy var name: String = self["name"]

lazy var type: GraphQLType = self["type"]

lazy var defaultValue: GraphQLValue? = self["defaultValue"]
}

public class FragmentDefinition: JavaScriptObject, Hashable {
public final class FragmentDefinition: JavaScriptObject, Hashable {
lazy var name: String = self["name"]

lazy var type: GraphQLCompositeType = self["typeCondition"]
Expand Down Expand Up @@ -132,7 +132,7 @@ public class CompilationResult: JavaScriptObject {
}
}

public class SelectionSet: JavaScriptWrapper, Hashable, CustomDebugStringConvertible {
public final class SelectionSet: JavaScriptWrapper, Hashable, CustomDebugStringConvertible {
lazy var parentType: GraphQLCompositeType = self["parentType"]

lazy var selections: [Selection] = self["selections"]
Expand Down Expand Up @@ -165,7 +165,7 @@ public class CompilationResult: JavaScriptObject {
}
}

public class InlineFragment: JavaScriptObject, Hashable {
public final class InlineFragment: JavaScriptObject, Hashable {
lazy var selectionSet: SelectionSet = self["selectionSet"]

lazy var inclusionConditions: [InclusionCondition]? = self["inclusionConditions"]
Expand All @@ -187,7 +187,7 @@ public class CompilationResult: JavaScriptObject {

/// Represents an individual selection that includes a named fragment in a selection set.
/// (ie. `...FragmentName`)
public class FragmentSpread: JavaScriptObject, Hashable {
public final class FragmentSpread: JavaScriptObject, Hashable {
lazy var fragment: FragmentDefinition = self["fragment"]

lazy var inclusionConditions: [InclusionCondition]? = self["inclusionConditions"]
Expand Down Expand Up @@ -253,7 +253,7 @@ public class CompilationResult: JavaScriptObject {
}
}

public class Field: JavaScriptWrapper, Hashable, CustomDebugStringConvertible {
public final class Field: JavaScriptWrapper, Hashable, CustomDebugStringConvertible {
lazy var name: String = self["name"]!

lazy var alias: String? = self["alias"]
Expand Down Expand Up @@ -330,7 +330,7 @@ public class CompilationResult: JavaScriptObject {
}
}

public class Argument: JavaScriptObject, Hashable {
public final class Argument: JavaScriptObject, Hashable {
lazy var name: String = self["name"]

lazy var type: GraphQLType = self["type"]
Expand All @@ -352,7 +352,7 @@ public class CompilationResult: JavaScriptObject {
}
}

public class Directive: JavaScriptObject, Hashable {
public final class Directive: JavaScriptObject, Hashable {
lazy var name: String = self["name"]

lazy var arguments: [Argument]? = self["arguments"]
Expand Down
12 changes: 6 additions & 6 deletions Sources/ApolloCodegenLib/IR/IR.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import OrderedCollections
import CryptoKit

class IR {
final class IR {

let compilationResult: CompilationResult

Expand Down Expand Up @@ -85,7 +85,7 @@ class IR {
///
/// Multiple `SelectionSet`s may select fields on the same `Entity`. All `SelectionSet`s that will
/// be selected on the same object share the same `Entity`.
class Entity {
final class Entity {

/// Represents the location within a GraphQL definition (operation or fragment) of an `Entity`.
struct Location: Hashable {
Expand Down Expand Up @@ -170,7 +170,7 @@ class IR {
}
}

class Operation {
final class Operation {
let definition: CompilationResult.OperationDefinition

/// The root field of the operation. This field must be the root query, mutation, or
Expand Down Expand Up @@ -218,7 +218,7 @@ class IR {
}
}

class NamedFragment: Hashable, CustomDebugStringConvertible {
final class NamedFragment: Hashable, CustomDebugStringConvertible {
let definition: CompilationResult.FragmentDefinition
let rootField: EntityField

Expand Down Expand Up @@ -264,7 +264,7 @@ class IR {

/// Represents an Inline Fragment that has been "spread into" another SelectionSet using the
/// spread operator (`...`).
class InlineFragmentSpread: Hashable, CustomDebugStringConvertible {
final class InlineFragmentSpread: Hashable, CustomDebugStringConvertible {
/// The `SelectionSet` representing the inline fragment that has been "spread into" its
/// enclosing operation/fragment.
let selectionSet: SelectionSet
Expand Down Expand Up @@ -310,7 +310,7 @@ class IR {
///
/// While a `NamedFragment` can be shared between operations, a `NamedFragmentSpread` represents a
/// `NamedFragment` included in a specific operation.
class NamedFragmentSpread: Hashable, CustomDebugStringConvertible {
final class NamedFragmentSpread: Hashable, CustomDebugStringConvertible {

/// The `NamedFragment` that this fragment refers to.
///
Expand Down
3 changes: 2 additions & 1 deletion Sources/CodegenCLI/Commands/Generate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ public struct Generate: ParsableCommand {
try codegenProvider.build(
with: configuration,
withRootURL: rootOutputURL(for: inputs),
itemsToGenerate: itemsToGenerate
itemsToGenerate: itemsToGenerate,
computeOperationIdentifier: nil
)
}

Expand Down
3 changes: 2 additions & 1 deletion Sources/CodegenCLI/Commands/GenerateOperationManifest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ public struct GenerateOperationManifest: ParsableCommand {
try codegenProvider.build(
with: configuration,
withRootURL: rootOutputURL(for: inputs),
itemsToGenerate: [.operationManifest]
itemsToGenerate: [.operationManifest],
computeOperationIdentifier: nil
)
}

Expand Down
3 changes: 2 additions & 1 deletion Sources/CodegenCLI/Protocols/CodegenProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ public protocol CodegenProvider {
static func build(
with configuration: ApolloCodegenConfiguration,
withRootURL rootURL: URL?,
itemsToGenerate: ApolloCodegen.ItemsToGenerate
itemsToGenerate: ApolloCodegen.ItemsToGenerate,
computeOperationIdentifier: ApolloCodegen.ComputeOperationIdentifier?
) throws
}

Expand Down
3 changes: 2 additions & 1 deletion Tests/CodegenCLITests/Support/MockApolloCodegen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ class MockApolloCodegen: CodegenProvider {
static func build(
with configuration: ApolloCodegenConfiguration,
withRootURL rootURL: URL?,
itemsToGenerate: ApolloCodegen.ItemsToGenerate
itemsToGenerate: ApolloCodegen.ItemsToGenerate,
computeOperationIdentifier: ApolloCodegen.ComputeOperationIdentifier? = nil
) throws {
guard let handler = buildHandler else {
fatalError("You must set buildHandler before calling \(#function)!")
Expand Down

0 comments on commit 78a1a91

Please sign in to comment.