Skip to content

Conversation

@anferbui
Copy link
Contributor

@anferbui anferbui commented Oct 30, 2025

Bug/issue #, if applicable: rdar://163018922

Summary

The navigation indexing code skips all further nodes after it has already indexed a node with a given identifier 1.

This can result in an odd interaction when there is an external link which is resolved to a path that the bundle serves itself (or more colloquially, a catalog has an external link to itself).

Since external entities are indexed before local entities, they were always be preferred over local entities, resulting in local entities mistakenly being dropped from the navigator altogether. The resulting navigator is incorrect and missing nodes, due to external entities not having hierarchy information. This issue was introduced when support for external links in the navigator was introduced.

This PR updates ConvertActionConverter to always index local entities first. If any external entities clash with local entities, they will be resolved as the local entity (including its hierarchy) in the navigator.

Dependencies

N/A

Testing

Use the test bundle from Example.zip.

Steps:

  1. Run DOCC_LINK_RESOLVER_EXECUTABLE=Example/test-data-external-resolver swift run docc preview Example/Example.docc
  2. Verify that http://localhost:8080/documentation/example-bundle/articlea has two links in the Topics section, and that the first one resolves to a local entity, not the external entity.
  3. Verify the navigation hierarchy contains ArticleC, curated in local node ArticleB.
Screenshot 2025-11-10 at 14 57 52

This can in general be verified by adding an external link to a page's content, which links to an entity that resolves to the same relative presentation URL of a local entity. If the resulting navigator is unchanged when adding this external links vs removing it, then the code is working as expected.

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 N/A

@anferbui
Copy link
Contributor Author

Open question: Do we need any further logging / assertions / checking logic when we skip a node when building the navigator?

guard identifierToNode[normalizedIdentifier] == nil else {
return nil // skip as item exists already.
}

@anferbui anferbui force-pushed the prefer-local-links-over-external-in-navigator branch 2 times, most recently from ee44242 to 9967d12 Compare November 10, 2025 12:23
@anferbui
Copy link
Contributor Author

@swift-ci please test

@anferbui anferbui marked this pull request as ready for review November 10, 2025 15:32
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.

This fix looks good to me but I think it'd be good to find a different location for this test and see if it can use TestExternalReferenceResolver instead of a real on-disk script.

@anferbui anferbui force-pushed the prefer-local-links-over-external-in-navigator branch from 9967d12 to 61f5c90 Compare November 11, 2025 11:50
@anferbui
Copy link
Contributor Author

@d-ronnqvist I've moved the test to Tests/SwiftDocCTests/Indexing/ExternalRenderNodeTests.swift and changed it to not use a link resolver script. Also updated the comment phrasing to match your suggestions.

@anferbui anferbui force-pushed the prefer-local-links-over-external-in-navigator branch from 61f5c90 to 0bbd9b8 Compare November 11, 2025 11:57
@anferbui
Copy link
Contributor Author

@swift-ci please test

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.

This looks good to me.

I think the test can be simplified by reusing an existing test resolver kind but we can make that change in a follow up PR at some points.

The navigation indexing code skips all further nodes after it has already indexed a node with a given identifier [1].

This can result in an odd interaction when there is an external link which is resolved to a path that the bundle serves itself (or more colloquially, a catalog has an external link to itself).

Since external entities are indexed before local entities, they were always be preferred over local entities, resulting in local entities mistakenly being dropped from the navigator altogether.
The resulting navigator is incorrect and missing nodes, due to external entities not having hierarchy information.
This issue was introduced when support for external links in the navigator was introduced.

This commit updates `ConvertActionConverter` to always index local entities first.
If any external entities clash with local entities, they will be resolved as the local entity (including its hierarchy) in the navigator.

Fixes rdar://163018922.

[1]: https://github.com/swiftlang/swift-docc/blob/b27288dd99b0e2715ed1a2d5720cd0f23118c030/Sources/SwiftDocC/Indexing/Navigator/NavigatorIndex.swift#L711-L713
[2]: swiftlang@65aaf92
@anferbui anferbui force-pushed the prefer-local-links-over-external-in-navigator branch from 0bbd9b8 to 30101c9 Compare November 11, 2025 13:08
@anferbui
Copy link
Contributor Author

@swift-ci please test

@anferbui anferbui merged commit 8beb703 into swiftlang:main Nov 11, 2025
2 checks passed
@anferbui anferbui deleted the prefer-local-links-over-external-in-navigator branch November 11, 2025 13:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants