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 dc4ab0f
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 30 deletions.
112 changes: 92 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,31 @@ public class ApolloCodegen {

}

public protocol IRNamedFragment: AnyObject, Hashable {
associatedtype ReferencedFragment: IRNamedFragment
var definition: CompilationResult.FragmentDefinition { get }
/// All of the fragments that are referenced by this operation's selection set.
var referencedFragments: OrderedSet<ReferencedFragment> { get }
}

public protocol IROperation: AnyObject {
associatedtype ReferencedFragment: IRNamedFragment
var definition: CompilationResult.OperationDefinition { get }
/// All of the fragments that are referenced by this operation's selection set.
var referencedFragments: OrderedSet<ReferencedFragment> { get }
}

extension IROperation {
public var document: String {
var sources: [String] = [definition.source.convertedToSingleLine()]
for fragment in referencedFragments {
sources.append(fragment.definition.source.convertedToSingleLine())
}
return sources.joined(separator: "\n")
}
}

extension IR.NamedFragment: IRNamedFragment {}
extension IR.Operation: IROperation {}

#endif
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 dc4ab0f

Please sign in to comment.