Skip to content

Commit ee44242

Browse files
committed
WIP: Record problem if external and local entities conflict
1 parent a92348d commit ee44242

File tree

5 files changed

+30
-11
lines changed

5 files changed

+30
-11
lines changed

Sources/SwiftDocC/Indexing/Navigator/NavigatorIndex.swift

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ public class NavigatorIndex {
5858

5959
/// The navigator index has not been initialized.
6060
case navigatorIndexIsNil
61-
61+
62+
case pageConflict(description: String)
63+
6264
public var errorDescription: String {
6365
switch self {
6466
case .missingBundleIdentifier:
@@ -67,6 +69,8 @@ public class NavigatorIndex {
6769
return "The page has no valid title available."
6870
case .navigatorIndexIsNil:
6971
return "The NavigatorIndex is Nil and can't be processed."
72+
case .pageConflict(let description):
73+
return "The page conflicts with another page. \(description)"
7074
}
7175
}
7276
}
@@ -709,7 +713,18 @@ extension NavigatorIndex {
709713
.normalizedNavigatorIndexIdentifier(forLanguage: language.mask)
710714

711715
guard identifierToNode[normalizedIdentifier] == nil else {
712-
return nil // skip as item exists already.
716+
// Because we're associating the external node with the **current** bundle's bundle ID, rather than its real bundle ID, there can be identifier clashes with local nodes.
717+
// Additionally, since the navigator index ultimately maps nodes to a relative path, paths across bundles may clash.
718+
// In the long term, we should move towards using unique identifiers for pages so that there can be no clash across bundles.
719+
//
720+
// Here we prefer local nodes over external nodes in the index, and report these issues to the author so that they're aware of the impact on the navigator,
721+
// with a suggestion of how they can workaround this issue.
722+
if !identifierToNode[normalizedIdentifier]!.item.isExternal && external {
723+
let externalIdentifier = renderNode.metadata.externalID ?? renderNode.identifier.absoluteString
724+
throw Error.pageConflict(description: "External reference \(externalIdentifier.singleQuoted) resolves to the same path as a local page, \(normalizedIdentifier.path.singleQuoted), and so can't have a usable entry in the index. Whenever \(externalIdentifier.singleQuoted) is referenced in the navigator, the local page will be used instead. Replace external references to \(externalIdentifier.singleQuoted) with a local reference.")
725+
} else {
726+
return nil // skip as item exists already.
727+
}
713728
}
714729

715730
guard let title = usePageTitle ? renderNode.metadata.title : renderNode.navigatorTitle() else {

Sources/SwiftDocC/Infrastructure/ConvertActionConverter.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,18 @@ package enum ConvertActionConverter {
164164

165165
guard !Task.isCancelled else { return [] }
166166

167-
try signposter.withIntervalSignpost("Index external links", id: signposter.makeSignpostID()) {
167+
// Here we're associating the external node with the **current** bundle's bundle ID.
168+
// This is needed because nodes are only considered children if the parent and child's bundle ID match.
169+
// Otherwise, the node will be considered as a separate root node and displayed separately.
170+
//
171+
// This opens up possibilities of clashes between local and external links with the same navigation path.
172+
// To be able to properly resolve any potential clashes, we index external links after local entities have been indexed.
173+
// and record any problems caused by conflicts.
174+
signposter.withIntervalSignpost("Index external links", id: signposter.makeSignpostID()) {
168175
// Consume external links and add them into the sidebar.
169176
for externalLink in context.externalCache {
170-
// Here we're associating the external node with the **current** bundle's bundle ID.
171-
// This is needed because nodes are only considered children if the parent and child's bundle ID match.
172-
// Otherwise, the node will be considered as a separate root node and displayed separately.
173177
let externalRenderNode = ExternalRenderNode(externalEntity: externalLink.value, bundleIdentifier: context.inputs.id)
174-
try outputConsumer.consume(externalRenderNode: externalRenderNode)
178+
outputConsumer.consume(externalRenderNode: externalRenderNode)
175179
}
176180
}
177181

Sources/SwiftDocC/Infrastructure/ConvertOutputConsumer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,5 +112,5 @@ package struct _Deprecated<Consumer: ConvertOutputConsumer>: _DeprecatedConsumeP
112112
package protocol ExternalNodeConsumer {
113113
/// Consumes a external render node that was generated during a conversion.
114114
/// > Warning: This method might be called concurrently.
115-
func consume(externalRenderNode: ExternalRenderNode) throws
115+
func consume(externalRenderNode: ExternalRenderNode)
116116
}

Sources/SwiftDocCUtilities/Action/Actions/Convert/ConvertFileWritingConsumer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ struct ConvertFileWritingConsumer: ConvertOutputConsumer, ExternalNodeConsumer {
6868
indexer?.index(renderNode)
6969
}
7070

71-
func consume(externalRenderNode: ExternalRenderNode) throws {
71+
func consume(externalRenderNode: ExternalRenderNode) {
7272
// Index the external node, if indexing is enabled.
7373
indexer?.index(externalRenderNode)
7474
}

Sources/SwiftDocCUtilities/Action/Actions/Convert/Indexer.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ extension ConvertAction {
5757
} catch {
5858
self.problems.append(error.problem(source: renderNode.identifier.url,
5959
severity: .warning,
60-
summaryPrefix: "RenderNode indexing process failed"))
60+
summaryPrefix: "Navigator indexing process for local pages failed:"))
6161
}
6262
})
6363
}
@@ -73,7 +73,7 @@ extension ConvertAction {
7373
} catch {
7474
self.problems.append(error.problem(source: renderNode.identifier.url,
7575
severity: .warning,
76-
summaryPrefix: "External render node indexing process failed"))
76+
summaryPrefix: "Navigator indexing process for external references failed:"))
7777
}
7878
})
7979
}

0 commit comments

Comments
 (0)