Skip to content

Commit fedd300

Browse files
committed
Generate inputs from symbol graphs when there is no catalog
1 parent 9a950b1 commit fedd300

File tree

2 files changed

+110
-2
lines changed

2 files changed

+110
-2
lines changed

Sources/SwiftDocC/Infrastructure/Input Discovery/DocumentationInputsProvider.swift

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010

1111
import Foundation
12+
import SymbolKit
1213

1314
extension DocumentationContext {
1415

@@ -160,11 +161,79 @@ extension DocumentationContext.InputsProvider {
160161
}
161162
}
162163

164+
// MARK: Create without catalog
165+
166+
extension DocumentationContext.InputsProvider {
167+
/// Creates a collection of documentation inputs from the symbol graph files and other command line options.
168+
///
169+
/// - Parameter options: Options to configure how the provider creates the documentation inputs.
170+
/// - Returns: Inputs that categorize the files of the given catalog.
171+
package func makeInputsFromSymbolGraphs(options: Options) throws -> DocumentationContext.Inputs? {
172+
guard !options.additionalSymbolGraphFiles.isEmpty else {
173+
return nil
174+
}
175+
176+
// Find all the unique module names from the symbol graph files and generate a top level module page for each of them.
177+
var moduleNames = Set<String>()
178+
for url in options.additionalSymbolGraphFiles {
179+
let data = try fileManager.contents(of: url)
180+
let container = try JSONDecoder().decode(SymbolGraphModuleContainer.self, from: data)
181+
moduleNames.insert(container.module.name)
182+
}
183+
let derivedDisplayName = moduleNames.count == 1 ? moduleNames.first : nil
184+
185+
let info = try DocumentationContext.Inputs.Info(bundleDiscoveryOptions: options, derivedDisplayName: derivedDisplayName)
186+
187+
var topLevelPages: [URL] = []
188+
if moduleNames.count == 1, let moduleName = moduleNames.first, moduleName != info.displayName {
189+
let tempURL = fileManager.uniqueTemporaryDirectory()
190+
try? fileManager.createDirectory(at: tempURL, withIntermediateDirectories: true, attributes: nil)
191+
192+
let url = tempURL.appendingPathComponent("\(moduleName).md")
193+
topLevelPages.append(url)
194+
try fileManager.createFile(
195+
at: url,
196+
contents: Data("""
197+
# ``\(moduleName)``
198+
199+
@Metadata {
200+
@DisplayName("\(info.displayName)")
201+
}
202+
""".utf8),
203+
options: .atomic
204+
)
205+
}
206+
207+
return DocumentationBundle(
208+
info: info,
209+
symbolGraphURLs: options.additionalSymbolGraphFiles,
210+
markupURLs: topLevelPages,
211+
miscResourceURLs: []
212+
)
213+
}
214+
}
215+
216+
/// A wrapper type that decodes only the module in the symbol graph.
217+
private struct SymbolGraphModuleContainer: Decodable {
218+
/// The decoded symbol graph module.
219+
let module: SymbolGraph.Module
220+
221+
typealias CodingKeys = SymbolGraph.CodingKeys
222+
223+
public init(from decoder: Decoder) throws {
224+
let container = try decoder.container(keyedBy: CodingKeys.self)
225+
226+
self.module = try container.decode(SymbolGraph.Module.self, forKey: .module)
227+
}
228+
}
229+
163230
// MARK: Discover and create
164231

165232
extension DocumentationContext.InputsProvider {
166233
/// Traverses the file system from the given starting point to find a documentation catalog and creates a collection of documentation inputs from that catalog.
167234
///
235+
/// If the provider can't find a catalog, it will try to create documentation inputs from the option's symbol graph files.
236+
///
168237
/// - Parameters:
169238
/// - startingPoint: The top of the directory hierarchy that the provider traverses to find a documentation catalog.
170239
/// - allowArbitraryCatalogDirectories: Whether to treat the starting point as a documentation catalog if the provider doesn't find an actual catalog on the file system.
@@ -176,8 +245,10 @@ extension DocumentationContext.InputsProvider {
176245
allowArbitraryCatalogDirectories: Bool = false,
177246
options: Options
178247
) throws -> DocumentationContext.Inputs? {
179-
try findCatalog(startingPoint: startingPoint, allowArbitraryCatalogDirectories: allowArbitraryCatalogDirectories).map {
180-
try makeInputs(contentOf: $0, options: options)
248+
if let catalogURL = try findCatalog(startingPoint: startingPoint, allowArbitraryCatalogDirectories: allowArbitraryCatalogDirectories) {
249+
try makeInputs(contentOf: catalogURL, options: options)
250+
} else {
251+
try makeInputsFromSymbolGraphs(options: options)
181252
}
182253
}
183254
}

Tests/SwiftDocCTests/Infrastructure/Input Discovery/DocumentationInputsProviderTests.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,41 @@ class DocumentationInputsProviderTests: XCTestCase {
190190
)
191191
}
192192
}
193+
194+
func testGeneratesInputsFromSymbolGraphWhenThereIsNoCatalog() throws {
195+
let fileSystem = try TestFileSystem(folders: [
196+
Folder(name: "one", content: [
197+
Folder(name: "two", content: [
198+
// Start search here.
199+
Folder(name: "three", content: [
200+
Folder(name: "four", content: []),
201+
]),
202+
]),
203+
// This catalog is outside the provider's search scope
204+
Folder(name: "OutsideScope.docc", content: []),
205+
206+
]),
207+
208+
Folder(name: "path", content: [
209+
Folder(name: "to", content: [
210+
// The path to this symbol graph file is passed via the options
211+
JSONFile(name: "Something.symbols.json", content: makeSymbolGraph(moduleName: "Something")),
212+
])
213+
])
214+
])
215+
216+
let provider = DocumentationContext.InputsProvider(fileManager: fileSystem)
217+
let startingPoint = URL(fileURLWithPath: "/one/two")
218+
219+
let foundBundle = try provider.inputs(
220+
startingPoint: startingPoint,
221+
options: .init(additionalSymbolGraphFiles: [
222+
URL(fileURLWithPath: "/path/to/Something.symbols.json")])
223+
)
224+
XCTAssertEqual(foundBundle?.displayName, "Something")
225+
XCTAssertEqual(foundBundle?.identifier, "Something")
226+
XCTAssertEqual(foundBundle?.symbolGraphURLs.map(\.path), [
227+
"/path/to/Something.symbols.json",
228+
])
229+
}
193230
}

0 commit comments

Comments
 (0)