Skip to content

Conversation

@jrturton
Copy link

Issue #1301

Summary

This PR addresses the changes proposed in #1301. It involves a new translator / semantic visitor / markup walker to create the new outputs from DocumentationNode. This is done for each node, after the render JSON is generated.

Dependencies

None

Testing

There are extensive unit tests in MarkdownOutputTests.swift. Testers can also run the convert command against any catalog with the --enable-experimental-markdown-output and --enable-experimental-markdown-output-manifest flags set.

Checklist

Make sure you check off the following items. If they cannot be completed, provide a reason.

  • Added tests
  • Ran the ./bin/test script and it succeeded
  • Updated documentation if necessary

@jrturton jrturton linked an issue Sep 26, 2025 that may be closed by this pull request
Copy link
Contributor

@d-ronnqvist d-ronnqvist left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot to unpack here and I've only had the time to skim thought the code but these are a few things that stand out to me:

  • If this is intended as experimental feature, then it can't add actual public API because consumers of SwiftDocC would have no way of knowing what API is stable and what is experimental and could still have breaking changes or be removed.

    If a goal of this API is that clients can use it while it's still experimental then it needs to be marked as SPI (using @_spi(SomeName) so that those clients make a deliberate decision to use it and acknowledge that it may have breaking changes.

  • The added tests follow a pattern not seen elsewhere in the repo. I left some more specific comments about that.

@jrturton jrturton requested a review from d-ronnqvist December 9, 2025 10:16
Comment on lines 206 to 214
var manifestRelationship: MarkdownOutputManifest.RelationshipSubType? {
// Structured like this to cause a compiler error if a new case is added
switch self {
case .conformingTypes: .conformingTypes
case .conformsTo: .conformsTo
case .inheritsFrom: .inheritsFrom
case .inheritedBy: .inheritedBy
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: what's the goal of this code? Should this enumeration be defined so that new callers need to handle unknown cases?

Comment on lines 69 to 70
/// The URI of the document
public let uri: String
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm.. maybe a better question is "what is a caller meant to do with this value"?

}
}

public func children(of parent: Document) -> Set<Document> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please note that the added comment doesn't fully answer my question.

  • What does it mean for a "document" to be the "parent" of another document?
    • Is this based on the developer defined documentation hierarchy or is it based on the base symbol hierarchy?
  • What is a caller meant to use this function for?
    • If the caller is meant to process the document's hierarchically, would it make more sense to have the data also have a hierarchy?

public let modules: [String]

public enum CodingKeys: String, CodingKey {
case kindDisplayName = "kind"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: why not also use the more descriptive name in the file?

Comment on lines 42 to 43
public let introduced: String?
public let deprecated: String?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should clarify; I wasn't asking about how this is represented on the page or in the file. I was asking about the type of this property. That's why I asked what a caller is meant to do with the version information and if the caller is meant to inspect or process the information somehow.

public var role: String?
public let uri: String
public var title: String
public let framework: String
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "location" and what do you mean by "document"? Is a "document" the same thing as a "documentation page"?

Assuming that a symbol is a kind of "document";

  • if "SomeModule" extends a type in "ExtendedModule" to add a doSomething() function. What's would be the "location" for that document?
  • if a cross import overlay between "ModuleA" and "ModuleB" defines a doSomething() function. What's would be the "location" for that document?

Alternatively, if that distinction isn't relevant for this property, what is a caller meant to do with this information?

Comment on lines 57 to 64
/// One or more protocols to which a type conforms.
case conformsTo
/// One or more types that conform to a protocol.
case conformingTypes
/// One or more types that are parents of the symbol.
case inheritsFrom
/// One or more types that are children of the symbol.
case inheritedBy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to be a subset of the cases in Relationship.

Is there a reason for not including the other cases and is there a reason for not using the already existing type?

return data
}
/// Data for this node to be rendered to disk as a markdown file. This method renders the metadata as a JSON header wrapped in an HTML comment block, then includes the document content.
public func generateDataRepresentation() throws -> Data {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: is the client meant to do anything else with this raw data or is it only "be rendered to disk as a markdown file"?

Alternatively, is the client meant to write these files at all? I was sort of under the impression that it was DocC that created these files and the the client would only read them. If the client doesn't create and write its own files, does this need to be SPI or could it be package or internal access?

import Markdown

/// Performs any markup processing necessary to build the final output markdown
internal struct MarkdownOutputMarkupWalker: MarkupWalker {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI (not blocking): it's redundant to specify internal here because it's the default access level.

}

func consume(markdownManifest: MarkdownOutputManifest) throws {
let url = targetFolder.appendingPathComponent("\(markdownManifest.title)-markdown-manifest.json", isDirectory: false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: what's the reason for including the title in the manifest file?

Have I misunderstood that archives would only have a single manifest file?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One archive will only have a single manifest file, but if you are processing the output of multiple archives this allows you to distinguish them

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't the caller already know what archive the manifest belongs to based on what archive the manifest file is inside?

Comment on lines +43 to +46
## Overview
Nothing to see here
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This is just noise (and just slightly malformed because the rendered page would have two "Overview" sections)

@jrturton jrturton requested a review from d-ronnqvist December 17, 2025 12:11
Copy link
Contributor

@d-ronnqvist d-ronnqvist left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There continues to be lot of API with a higher access level than what's needed and a handful of unused API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs forum discussion Needs to be discussed in the Swift Forums

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LLM-accessible output

2 participants