Discovery System
The discovery system is responsible for finding Swift types that should have JSON Schemas generated. It uses two complementary strategies that are coordinated by DiscoveryCoordinator.
DiscoveryCoordinator
File: Sources/Discovery/DiscoveryCoordinator.swift
DiscoveryCoordinator is the entry point for the discovery phase. It:
- Decides which Swift files to scan (all files, or
--macro-onlyfiltered) - Runs
MacroDiscoveryandProtocolDiscoveryin sequence - Merges and deduplicates results from both strategies
- Returns a
[ModelNode]list to the next pipeline stage
class DiscoveryCoordinator {
func discover(in sourcePath: String, macroOnly: Bool) throws -> [ModelNode]
}
--macro-only Filtering
When --macro-only is passed, DiscoveryCoordinator uses FileScanner to pre-filter source files. Only files containing the literal string @ChimeraSchema are included in the scan set. This is a plain text search — no parsing required — and can dramatically reduce scan time for large codebases.
MacroDiscovery
File: Sources/Discovery/MacroDiscovery.swift
MacroDiscovery uses SwiftSyntax to parse source files and find @ChimeraSchema annotations. It delegates to MacroDiscoveryVisitor to walk the AST.
MacroDiscoveryVisitor
File: Sources/Parser/Syntax/MacroDiscoveryVisitor.swift
A SyntaxVisitor subclass that visits every StructDeclSyntax and ClassDeclSyntax. For each type declaration:
- Checks attributes for
@ChimeraSchema(key:)and@ChimeraMetaData(description:) - If found, creates a partial
ModelNodestub with the type name, key, description, and source location - Does not resolve properties at this stage (that happens in parsing)
What MacroDiscovery Finds
- Any
structorclassannotated with@ChimeraSchema - Associated
@ChimeraMetaDataannotation on the same type - The source file path for later parsing
ProtocolDiscovery
File: Sources/Discovery/ProtocolDiscovery.swift
ProtocolDiscovery finds types implementing protocols annotated with @PolymorphicMapping. Because protocol conformances can be split across extensions in different files, it uses IndexStoreDB (via InheritanceRepository) rather than pure text scanning.
How It Works
- Calls
InheritanceRepositoryto look up all types conforming to each polymorphic protocol - For each conforming type, checks whether it also has
@ChimeraSchema(viaMacroRepository) - Builds the
PolymorphicInfostructure for the protocol, mapping discriminator values to concreteModelNodestubs
KnotDiscovery
File: Sources/Discovery/KnotDiscovery.swift
An alternative discovery strategy for a custom annotation format (useful if your project uses a non-macro approach). Operates similarly to MacroDiscovery but looks for a different annotation pattern.
Repository Layer
The discovery system is backed by two repositories:
SourceFileRepository
File: Sources/Repository/FileSystem/SourceFileRepository.swift
Provides the list of Swift source files to scan. Accepts a root sourcePath and returns all .swift files recursively. Handles symlinks and ignores hidden directories.
InheritanceRepository
File: Sources/Repository/IndexStore/InheritanceRepository.swift
Wraps IndexStoreDB to look up type inheritance relationships:
class InheritanceRepository {
func conformingTypes(to protocolName: String) throws -> [String]
func superclassChain(for typeName: String) throws -> [String]
}
IndexStoreDB is a compile-time artifact generated by the Swift compiler. It stores symbol relationships (inheritance, method calls, etc.) in a binary database at .build/debug/index/.
MacroRepository
File: Sources/Repository/MacroRepository.swift
A cache of @ChimeraSchema annotations indexed by type name. Populated during MacroDiscovery and queried by ProtocolDiscovery to check whether a protocol conformer is also a @ChimeraSchema.
SymbolRepository
File: Sources/Repository/IndexStore/SymbolRepository.swift
Used to resolve symbol locations for types encountered as property types. When a property is typed as SomeModel, SymbolRepository maps "SomeModel" to its source file path so it can be parsed.
Discovery Output
After discovery, the pipeline has a flat [ModelNode] array where each node has:
typeName— Swift type namekey—@ChimeraSchema(key:)valuedescription— From@ChimeraMetaData, ornilsourceFile— Absolute path to the Swift fileproperties— Empty at this stage (filled by the Parser stage)polymorphicInfo— Filled if the type is used in a@PolymorphicMapping
The Parser stage takes this list and fills in properties for each node.