From bd25e3cf9cc2cb9d544531af85a5818899765ee8 Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Tue, 22 Sep 2026 10:45:37 +0200 Subject: [PATCH] fix(renderer): Render articles without symbol metadata --- .../client/AppleDocumentationClient+DTO.swift | 6 +-- .../TextTypeDocumentationRenderer.swift | 9 +++-- ...efaultTypeDocumentationRendererTests.swift | 40 +++++++++++++++++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/Sources/CLI/client/AppleDocumentationClient+DTO.swift b/Sources/CLI/client/AppleDocumentationClient+DTO.swift index c627531..e5dbf44 100644 --- a/Sources/CLI/client/AppleDocumentationClient+DTO.swift +++ b/Sources/CLI/client/AppleDocumentationClient+DTO.swift @@ -111,7 +111,7 @@ struct DocumentationMetadataDTO: Decodable, Sendable { let modules: [DocumentationModule] let platforms: [DocumentationPlatform] let roleHeading: String - let symbolKind: String + let symbolKind: String? let title: String private enum CodingKeys: CodingKey { @@ -124,10 +124,10 @@ struct DocumentationMetadataDTO: Decodable, Sendable { init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - modules = try container.decode([DocumentationModule].self, forKey: .modules) + modules = try container.decodeIfPresent([DocumentationModule].self, forKey: .modules) ?? [] platforms = try container.decodeIfPresent([DocumentationPlatform].self, forKey: .platforms) ?? [] roleHeading = try container.decode(String.self, forKey: .roleHeading) - symbolKind = try container.decode(String.self, forKey: .symbolKind) + symbolKind = try container.decodeIfPresent(String.self, forKey: .symbolKind) title = try container.decode(String.self, forKey: .title) } } diff --git a/Sources/CLI/renderer/TextTypeDocumentationRenderer.swift b/Sources/CLI/renderer/TextTypeDocumentationRenderer.swift index 3f827c3..94d0de4 100644 --- a/Sources/CLI/renderer/TextTypeDocumentationRenderer.swift +++ b/Sources/CLI/renderer/TextTypeDocumentationRenderer.swift @@ -5,10 +5,11 @@ struct TextTypeDocumentationRenderer: Sendable { let content = DocumentationContentRenderer(references: page.references) let metadata = ([page.metadata.roleHeading] + page.metadata.modules.map(\.name)) .filter { !$0.isEmpty }.joined(separator: " · ") - var sections = [ - layout.heading(page.metadata.title, prominent: true) + "\n" + metadata - + "\nSymbol kind: " + page.metadata.symbolKind - ] + var header = layout.heading(page.metadata.title, prominent: true) + "\n" + metadata + if let symbolKind = page.metadata.symbolKind { + header += "\nSymbol kind: " + symbolKind + } + var sections = [header] let abstract = content.inlineText(page.abstract) if !abstract.isEmpty { diff --git a/Tests/CLITests/renderer/DefaultTypeDocumentationRendererTests.swift b/Tests/CLITests/renderer/DefaultTypeDocumentationRendererTests.swift index 878d983..9110a80 100644 --- a/Tests/CLITests/renderer/DefaultTypeDocumentationRendererTests.swift +++ b/Tests/CLITests/renderer/DefaultTypeDocumentationRendererTests.swift @@ -76,6 +76,46 @@ struct DefaultTypeDocumentationRendererTests { ) } + @Test("renders articles without module or symbol metadata") + func rendersArticleWithoutSymbolMetadata() throws { + // -- Arrange -- + let rawJSON = """ + { + "kind": "article", + "abstract": [{"text": "Distribute binaries in Swift packages.", "type": "text"}], + "metadata": { + "role": "article", + "roleHeading": "Article", + "title": "Distributing binary frameworks as Swift packages" + }, + "primaryContentSections": [{ + "kind": "content", + "content": [ + {"type": "heading", "level": 2, "text": "Overview"}, + { + "type": "paragraph", + "inlineContent": [{"type": "text", "text": "Create an XCFramework bundle."}] + } + ] + }], + "references": {} + } + """ + let document = try makeDocument(rawJSON) + let renderer = DefaultTypeDocumentationRenderer(output: .text) + + // -- Act -- + let output = try renderer.render(document) + + // -- Assert -- + #expect(output.hasPrefix("Distributing binary frameworks as Swift packages\n")) + #expect(output.contains("\nArticle\n\n Distribute binaries in Swift packages.")) + #expect(output.contains("Overview\n────────\n\n Create an XCFramework bundle.")) + #expect(!output.contains("Symbol kind:")) + #expect(!output.contains("Availability")) + #expect(!output.contains("Declaration")) + } + @Test("returns Apple's DocC JSON unchanged") func rendersRawJSON() throws { // -- Arrange --