From 764526b247b02ab711d004f025ef4f967e77933b Mon Sep 17 00:00:00 2001 From: Aryan Bagade Date: Sat, 20 Dec 2025 21:53:27 -0800 Subject: [PATCH] Support @PageImage in in-source documentation comments Previously, @PageImage directives were rejected when used in documentation comments with a warning suggesting to use a documentation extension file instead. This change removes that restriction, allowing developers to specify page images directly in their source code comments. --- .../Semantics/Metadata/Metadata.swift | 4 +- .../Semantics/SymbolTests.swift | 69 ++++++++++++++++--- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/Sources/SwiftDocC/Semantics/Metadata/Metadata.swift b/Sources/SwiftDocC/Semantics/Metadata/Metadata.swift index ca3ae0c296..75b0e48825 100644 --- a/Sources/SwiftDocC/Semantics/Metadata/Metadata.swift +++ b/Sources/SwiftDocC/Semantics/Metadata/Metadata.swift @@ -247,8 +247,7 @@ public final class Metadata: Semantic, AutomaticDirectiveConvertible { validateUnsupportedMetadataDirective(for: titleHeading) validateUnsupportedMetadataDirective(for: redirects) validateUnsupportedMetadataDirective(for: supportedLanguages) - validateUnsupportedMetadataDirective(for: pageImages) - + documentationOptions = nil technologyRoot = nil displayName = nil @@ -258,6 +257,5 @@ public final class Metadata: Semantic, AutomaticDirectiveConvertible { titleHeading = nil redirects = nil supportedLanguages = [] - pageImages = [] } } diff --git a/Tests/SwiftDocCTests/Semantics/SymbolTests.swift b/Tests/SwiftDocCTests/Semantics/SymbolTests.swift index c8843649ff..624f6f0752 100644 --- a/Tests/SwiftDocCTests/Semantics/SymbolTests.swift +++ b/Tests/SwiftDocCTests/Semantics/SymbolTests.swift @@ -1218,14 +1218,69 @@ class SymbolTests: XCTestCase { """, extensionFileContent: nil ) - + XCTAssertEqual(problems.count, 0, "Unexpected problems: \(problems.map(\.diagnostic.summary).sorted())") - + let availability = try XCTUnwrap(node.metadata?.availability.first) XCTAssertEqual(availability.platform, .other("customOS")) XCTAssertEqual(availability.introduced.description, "1.2.3") } - + + func testParsesPageImageDirectiveFromDocComment() async throws { + let (node, problems) = try await makeDocumentationNodeForSymbol( + docComment: """ + The symbol's abstract. + + @Metadata { + @PageImage(source: "my-image.png", purpose: icon) + } + """, + extensionFileContent: nil + ) + + // Filter out resource-not-found warnings since we don't have actual image files in this test. + // The important thing is that there's no "InvalidPageImageInDocumentationComment" warning. + let unexpectedProblems = problems.filter { !$0.diagnostic.summary.contains("couldn't be found") } + XCTAssertEqual(unexpectedProblems.count, 0, "Unexpected problems: \(unexpectedProblems.map(\.diagnostic.summary).sorted())") + + let pageImages = try XCTUnwrap(node.metadata?.pageImages) + XCTAssertEqual(pageImages.count, 1) + + let pageImage = try XCTUnwrap(pageImages.first) + XCTAssertEqual(pageImage.purpose, .icon) + XCTAssertEqual(pageImage.source.path, "my-image.png") + } + + func testParsesMultiplePageImageDirectivesFromDocComment() async throws { + let (node, problems) = try await makeDocumentationNodeForSymbol( + docComment: """ + The symbol's abstract. + + @Metadata { + @PageImage(source: "icon-image.png", purpose: icon) + @PageImage(source: "card-image.png", purpose: card) + } + """, + extensionFileContent: nil + ) + + // Filter out resource-not-found warnings since we don't have actual image files in this test. + // The important thing is that there's no "InvalidPageImageInDocumentationComment" warning. + let unexpectedProblems = problems.filter { !$0.diagnostic.summary.contains("couldn't be found") } + XCTAssertEqual(unexpectedProblems.count, 0, "Unexpected problems: \(unexpectedProblems.map(\.diagnostic.summary).sorted())") + + let pageImages = try XCTUnwrap(node.metadata?.pageImages) + XCTAssertEqual(pageImages.count, 2) + + let iconImage = pageImages.first { $0.purpose == .icon } + let cardImage = pageImages.first { $0.purpose == .card } + + XCTAssertNotNil(iconImage) + XCTAssertNotNil(cardImage) + XCTAssertEqual(iconImage?.source.path, "icon-image.png") + XCTAssertEqual(cardImage?.source.path, "card-image.png") + } + func testEmitsWarningsInMetadataDirectives() async throws { let (_, problems) = try await makeDocumentationNodeForSymbol( docComment: """ @@ -1284,13 +1339,12 @@ class SymbolTests: XCTestCase { @Metadata { @Available("Platform from doc comment", introduced: 1.2.3) @CustomMetadata(key: "key", value: "value") - + @Comment(The directives below this are invalid in documentation comments) - + @DocumentationExtension(mergeBehavior: override) @TechnologyRoot @DisplayName(Title) - @PageImage(source: test, purpose: icon) @CallToAction(url: "https://example.com/sample.zip", purpose: download) @PageKind(sampleCode) @SupportedLanguage(swift) @@ -1301,14 +1355,13 @@ class SymbolTests: XCTestCase { """, extensionFileContent: nil ) - + XCTAssertEqual( Set(problems.map(\.diagnostic.identifier)), [ "org.swift.docc.Metadata.InvalidDocumentationExtensionInDocumentationComment", "org.swift.docc.Metadata.InvalidTechnologyRootInDocumentationComment", "org.swift.docc.Metadata.InvalidDisplayNameInDocumentationComment", - "org.swift.docc.Metadata.InvalidPageImageInDocumentationComment", "org.swift.docc.Metadata.InvalidCallToActionInDocumentationComment", "org.swift.docc.Metadata.InvalidPageKindInDocumentationComment", "org.swift.docc.Metadata.InvalidSupportedLanguageInDocumentationComment",