Skip to content

Commit 60535d9

Browse files
emit ExternalLocationReferences into assets digest (#476)
rdar://105226975
1 parent c166a85 commit 60535d9

File tree

7 files changed

+148
-4
lines changed

7 files changed

+148
-4
lines changed

Sources/SwiftDocC/Model/Rendering/References/AssetReferences.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import Foundation
1313
public extension RenderNode {
1414
/// All image, video, file, and download references of this node, grouped by their type.
1515
var assetReferences: [RenderReferenceType: [RenderReference]] {
16-
let assetTypes = [RenderReferenceType.image, .video, .file, .download]
16+
let assetTypes = [RenderReferenceType.image, .video, .file, .download, .externalLocation]
1717
return .init(grouping: references.values.lazy.filter({ assetTypes.contains($0.type) }), by: { $0.type })
1818
}
1919
}

Sources/SwiftDocC/Model/Rendering/References/ExternalLocationReference.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import Foundation
2222
public struct ExternalLocationReference: RenderReference, URLReference {
2323
public static var baseURL: URL = DownloadReference.baseURL
2424

25-
public private(set) var type: RenderReferenceType = .download
25+
public private(set) var type: RenderReferenceType = .externalLocation
2626

2727
public var identifier: RenderReferenceIdentifier
2828

Sources/SwiftDocC/Model/Rendering/References/RenderReference.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public protocol RenderReference: Codable {
2626

2727
/// The type of a reference.
2828
public enum RenderReferenceType: String, Codable, Equatable {
29-
case image, video, file, fileType, xcodeRequirement, topic, section, download, link
29+
case image, video, file, fileType, xcodeRequirement, topic, section, download, link, externalLocation
3030
case unresolvable
3131
}
3232

Sources/SwiftDocC/Model/Rendering/RenderNode/CodableRenderReference.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ struct CodableRenderReference: Codable {
4343
reference = try UnresolvedRenderReference(from: decoder)
4444
case .link:
4545
reference = try LinkReference(from: decoder)
46+
case .externalLocation:
47+
reference = try ExternalLocationReference(from: decoder)
4648
}
4749
}
4850

Sources/SwiftDocC/SwiftDocC.docc/Resources/RenderNode.spec.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1816,7 +1816,7 @@
18161816
"properties": {
18171817
"type": {
18181818
"type": "string",
1819-
"enum": ["download"]
1819+
"enum": ["download", "externalLocation"]
18201820
},
18211821
"identifier": {
18221822
"type": "string"

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,14 @@ struct ConvertFileWritingConsumer: ConvertOutputConsumer {
161161
let assetsURL = targetFolder.appendingPathComponent("assets.json", isDirectory: false)
162162
let data = try encode(digest)
163163
try fileManager.createFile(at: assetsURL, contents: data)
164+
165+
let externalAssetsDigest = Digest.ExternalAssets(
166+
externalLocations: (uniqueAssets[.externalLocation] as? [ExternalLocationReference]) ?? []
167+
)
168+
169+
let externalAssetsURL = targetFolder.appendingPathComponent("external-assets.json", isDirectory: false)
170+
let externalAssetsData = try encode(externalAssetsDigest)
171+
try fileManager.createFile(at: externalAssetsURL, contents: externalAssetsData)
164172
}
165173

166174
func consume(benchmarks: Benchmark) throws {
@@ -217,6 +225,10 @@ enum Digest {
217225
let videos: [VideoReference]
218226
let downloads: [DownloadReference]
219227
}
228+
229+
struct ExternalAssets: Codable {
230+
let externalLocations: [ExternalLocationReference]
231+
}
220232

221233
struct Diagnostic: Codable {
222234
struct Location: Codable {

Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ class ConvertActionTests: XCTestCase {
3131
withExtension: "symbols.json",
3232
subdirectory: "Test Resources"
3333
)!
34+
35+
let projectZipFile = Bundle.module.url(
36+
forResource: "TestBundle", withExtension: "docc", subdirectory: "Test Bundles")!
37+
.appendingPathComponent("project.zip")
3438

3539
/// A symbol graph file that has missing symbols.
3640
let incompleteSymbolGraphFile = TextFile(name: "TechnologyX.symbols.json", utf8Content: """
@@ -1159,6 +1163,132 @@ class ConvertActionTests: XCTestCase {
11591163
XCTAssertEqual(resultAssets.images.map({ $0.identifier.identifier }).sorted(), images.map({ $0.identifier.identifier }).sorted())
11601164
}
11611165

1166+
func testDownloadMetadataIsWritenToOutputFolder() throws {
1167+
let bundle = Folder(name: "unit-test.docc", content: [
1168+
CopyOfFile(original: projectZipFile),
1169+
CopyOfFile(original: imageFile, newName: "referenced-tutorials-image.png"),
1170+
1171+
TextFile(name: "MyTechnology.tutorial", utf8Content: """
1172+
@Tutorial(time: 10, projectFiles: project.zip) {
1173+
@Intro(title: "TechologyX") {}
1174+
1175+
@Section(title: "Section") {
1176+
@Steps {}
1177+
}
1178+
1179+
@Assessments {
1180+
@MultipleChoice {
1181+
text
1182+
@Choice(isCorrect: true) {
1183+
text
1184+
@Justification(reaction: "reaction text") {}
1185+
}
1186+
1187+
@Choice(isCorrect: false) {
1188+
text
1189+
@Justification(reaction: "reaction text") {}
1190+
}
1191+
}
1192+
}
1193+
}
1194+
"""),
1195+
1196+
TextFile(name: "TechnologyX.tutorial", utf8Content: """
1197+
@Tutorials(name: TechnologyX) {
1198+
@Intro(title: "Technology X") {
1199+
Learn about some stuff in Technology X.
1200+
}
1201+
1202+
@Volume(name: "Volume 1") {
1203+
This volume contains Chapter 1.
1204+
1205+
@Image(source: referenced-tutorials-image.png, alt: "Some alt text")
1206+
1207+
@Chapter(name: "Chapter 1") {
1208+
In this chapter, you'll learn about Tutorial 1.
1209+
1210+
@Image(source: referenced-tutorials-image.png, alt: "Some alt text")
1211+
@TutorialReference(tutorial: "doc:MyTechnology")
1212+
}
1213+
}
1214+
}
1215+
"""),
1216+
1217+
TextFile(name: "MySample.md", utf8Content: """
1218+
# My Sample
1219+
1220+
@Metadata {
1221+
@CallToAction(url: "https://example.com/sample.zip", purpose: download)
1222+
}
1223+
1224+
This is a page with a download button.
1225+
"""),
1226+
1227+
TextFile(name: "TestBundle.md", utf8Content: """
1228+
# ``TestBundle``
1229+
1230+
This is a test.
1231+
1232+
## Topics
1233+
1234+
### Pages
1235+
1236+
- <doc:TechnologyX>
1237+
- <doc:MySample>
1238+
"""),
1239+
1240+
// A symbol graph
1241+
CopyOfFile(original: Bundle.module.url(forResource: "TopLevelCuration.symbols", withExtension: "json", subdirectory: "Test Resources")!),
1242+
1243+
InfoPlist(displayName: "TestBundle", identifier: "com.test.example"),
1244+
])
1245+
1246+
let testDataProvider = try TestFileSystem(folders: [bundle, Folder.emptyHTMLTemplateDirectory])
1247+
let targetDirectory = URL(fileURLWithPath: testDataProvider.currentDirectoryPath)
1248+
.appendingPathComponent("target", isDirectory: true)
1249+
1250+
var action = try ConvertAction(
1251+
documentationBundleURL: bundle.absoluteURL,
1252+
outOfProcessResolver: nil,
1253+
analyze: false,
1254+
targetDirectory: targetDirectory,
1255+
htmlTemplateDirectory: Folder.emptyHTMLTemplateDirectory.absoluteURL,
1256+
emitDigest: true,
1257+
currentPlatforms: nil,
1258+
dataProvider: testDataProvider,
1259+
fileManager: testDataProvider,
1260+
temporaryDirectory: createTemporaryDirectory())
1261+
let result = try action.perform(logHandle: .standardOutput)
1262+
1263+
func contentsOfJSONFile<Result: Decodable>(url: URL) -> Result? {
1264+
guard let data = testDataProvider.contents(atPath: url.path) else {
1265+
return nil
1266+
}
1267+
return try? JSONDecoder().decode(Result.self, from: data)
1268+
}
1269+
1270+
// Verify downloads
1271+
guard let resultAssets: Digest.Assets = contentsOfJSONFile(url: result.outputs[0].appendingPathComponent("assets.json")) else {
1272+
XCTFail("Can't find assets.json in output")
1273+
return
1274+
}
1275+
XCTAssertEqual(resultAssets.downloads.count, 1)
1276+
1277+
XCTAssert(resultAssets.downloads.contains(where: {
1278+
$0.identifier.identifier == "project.zip"
1279+
}))
1280+
1281+
guard let externalAssets: Digest.ExternalAssets = contentsOfJSONFile(url: result.outputs[0].appendingPathComponent("external-assets.json")) else {
1282+
XCTFail("Can't find external-assets.json in output")
1283+
return
1284+
}
1285+
XCTAssertEqual(externalAssets.externalLocations.count, 1)
1286+
1287+
XCTAssert(externalAssets.externalLocations.contains(where: {
1288+
$0.identifier.identifier == "https://example.com/sample.zip"
1289+
}))
1290+
}
1291+
11621292
func testMetadataIsWrittenToOutputFolder() throws {
11631293
// Example documentation bundle that contains an image
11641294
let bundle = Folder(name: "unit-test.docc", content: [

0 commit comments

Comments
 (0)