Skip to content

Include user provided theme-settings.json file in compiled output #339

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Sources/SwiftDocC/Infrastructure/DocumentationBundle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ public struct DocumentationBundle {
/// Custom HTML file to use as the footer for rendered output.
public let customFooter: URL?

/// JSON settings file used to theme renderer output.
public let themeSettings: URL?

/// Default syntax highlighting to use for code samples in this bundle.
@available(*, deprecated, message: "Use 'info.defaultCodeListingLanguage' instead.")
public var defaultCodeListingLanguage: String? {
Expand Down Expand Up @@ -156,7 +159,8 @@ public struct DocumentationBundle {
markupURLs: [URL],
miscResourceURLs: [URL],
customHeader: URL? = nil,
customFooter: URL? = nil
customFooter: URL? = nil,
themeSettings: URL? = nil
) {
self.info = info
self.baseURL = baseURL
Expand All @@ -166,6 +170,7 @@ public struct DocumentationBundle {
self.miscResourceURLs = miscResourceURLs
self.customHeader = customHeader
self.customFooter = customFooter
self.themeSettings = themeSettings
self.rootReference = ResolvedTopicReference(bundleIdentifier: info.identifier, path: "/", sourceLanguage: .swift)
self.documentationRootReference = ResolvedTopicReference(bundleIdentifier: info.identifier, path: NodeURLGenerator.Path.documentationFolder, sourceLanguage: .swift)
self.tutorialsRootReference = ResolvedTopicReference(bundleIdentifier: info.identifier, path: NodeURLGenerator.Path.tutorialsFolder, sourceLanguage: .swift)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,12 @@ public enum DocumentationBundleFileTypes {
public static func isCustomFooter(_ url: URL) -> Bool {
return url.lastPathComponent == customFooterFileName
}

private static let themeSettingsFileName = "theme-settings.json"
/// Checks if a file is `theme-settings.json`.
/// - Parameter url: The file to check.
/// - Returns: Whether or not the file at `url` is `theme-settings.json`.
public static func isThemeSettingsFile(_ url: URL) -> Bool {
return url.lastPathComponent == themeSettingsFileName
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,17 @@ extension DocumentationWorkspaceDataProvider where Self: FileSystemProvider {

let customHeader = findCustomHeader(bundleChildren)?.url
let customFooter = findCustomFooter(bundleChildren)?.url
let themeSettings = findThemeSettings(bundleChildren)?.url

return DocumentationBundle(info: info, symbolGraphURLs: symbolGraphFiles, markupURLs: markupFiles, miscResourceURLs: miscResources, customHeader: customHeader, customFooter: customFooter)
return DocumentationBundle(
info: info,
symbolGraphURLs: symbolGraphFiles,
markupURLs: markupFiles,
miscResourceURLs: miscResources,
customHeader: customHeader,
customFooter: customFooter,
themeSettings: themeSettings
)
}

/// Performs a shallow search for the first Info.plist file in the given list of files and directories.
Expand Down Expand Up @@ -111,6 +120,10 @@ extension DocumentationWorkspaceDataProvider where Self: FileSystemProvider {
private func findCustomFooter(_ bundleChildren: [FSNode]) -> FSNode.File? {
return bundleChildren.firstFile { DocumentationBundleFileTypes.isCustomFooter($0.url) }
}

private func findThemeSettings(_ bundleChildren: [FSNode]) -> FSNode.File? {
return bundleChildren.firstFile { DocumentationBundleFileTypes.isThemeSettingsFile($0.url) }
}
}

fileprivate extension Array where Element == FSNode {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,17 @@ struct ConvertFileWritingConsumer: ConvertOutputConsumer {
if let customFooter = bundle.customFooter, enableCustomTemplates {
try injectCustomTemplate(customFooter, identifiedBy: .footer)
}

// Copy the `theme-settings.json` file into the output directory if one
// is provided. It will override any default `theme-settings.json` file
// that the renderer template may already contain.
if let themeSettings = bundle.themeSettings {
let targetFile = targetFolder.appendingPathComponent(themeSettings.lastPathComponent, isDirectory: false)
if fileManager.fileExists(atPath: targetFile.path) {
try fileManager.removeItem(at: targetFile)
}
try fileManager.copyItem(at: themeSettings, to: targetFile)
}
}

func consume(linkableElementSummaries summaries: [LinkDestinationSummary]) throws {
Expand Down
35 changes: 35 additions & 0 deletions Tests/SwiftDocCTests/Infrastructure/BundleDiscoveryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,9 @@ class BundleDiscoveryTests: XCTestCase {
// Ensure that `customFooter` is `nil` if no top level `footer.html`
// file was found in the bundle
XCTAssertNil(bundle.customFooter)
// Ensure that `themeSettings` is `nil` if no `theme-settings.json`
// file was found in the bundle
XCTAssertNil(bundle.themeSettings)
}

func testCustomTemplatesFound() throws {
Expand Down Expand Up @@ -325,4 +328,36 @@ class BundleDiscoveryTests: XCTestCase {
// `footer.html` file if one is found in the bundle
XCTAssertEqual(bundle.customFooter?.lastPathComponent, "footer.html")
}

func testThemeSettingsFound() throws {
let workspace = Folder(name: "TestBundle.docc", content:
allFiles.map { CopyOfFile(original: $0) } + [
TextFile(name: "theme-settings.json", utf8Content: """
{
"meta": {},
"theme": {
"colors": {
"text": "#ff0000"
}
},
"features": {}
}
"""),
]
)

let tempURL = try createTemporaryDirectory()

let workspaceURL = try workspace.write(inside: tempURL)
let dataProvider = try LocalFileSystemDataProvider(rootURL: workspaceURL)

let bundles = try dataProvider.bundles(options: BundleDiscoveryOptions())

XCTAssertEqual(bundles.count, 1)
guard let bundle = bundles.first else { return }

// Ensure that `themeSettings` points to the location of a
// `theme-settings.json` file if one is found in the bundle
XCTAssertEqual(bundle.themeSettings?.lastPathComponent, "theme-settings.json")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,18 @@ class DocumentationBundleFileTypesTests: XCTestCase {
XCTAssertTrue(DocumentationBundleFileTypes.isCustomFooter(
URL(fileURLWithPath: "DocC.docc/footer.html")))
}

func testIsThemeSettingsFile() {
XCTAssertTrue(DocumentationBundleFileTypes.isThemeSettingsFile(
URL(fileURLWithPath: "theme-settings.json")))
XCTAssertTrue(DocumentationBundleFileTypes.isThemeSettingsFile(
URL(fileURLWithPath: "/a/b/theme-settings.json")))

XCTAssertFalse(DocumentationBundleFileTypes.isThemeSettingsFile(
URL(fileURLWithPath: "theme-settings.txt")))
XCTAssertFalse(DocumentationBundleFileTypes.isThemeSettingsFile(
URL(fileURLWithPath: "not-theme-settings.json")))
XCTAssertFalse(DocumentationBundleFileTypes.isThemeSettingsFile(
URL(fileURLWithPath: "/a/theme-settings.json/bar")))
}
}
58 changes: 58 additions & 0 deletions Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2498,6 +2498,64 @@ class ConvertActionTests: XCTestCase {
let expectedOutput = Folder(name: ".docc-build", content: [expectedIndex])
expectedOutput.assertExist(at: result.outputs[0], fileManager: FileManager.default)
}

func testConvertWithThemeSettings() throws {
let info = InfoPlist(displayName: "TestConvertWithThemeSettings", identifier: "com.test.example")
let index = TextFile(name: "index.html", utf8Content: """
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
</head>
<body data-color-scheme="auto"><p>hello</p></body>
</html>
""")
let themeSettings = TextFile(name: "theme-settings.json", utf8Content: """
{
"meta": {},
"theme": {
"colors": {
"text": "#ff0000"
}
},
"features": {}
}
""")
let template = Folder(name: "template", content: [index])
let bundle = Folder(name: "TestConvertWithThemeSettings.docc", content: [
info,
themeSettings,
])

let tempURL = try createTemporaryDirectory()
let targetURL = tempURL.appendingPathComponent("target", isDirectory: true)

let bundleURL = try bundle.write(inside: tempURL)
let templateURL = try template.write(inside: tempURL)

let dataProvider = try LocalFileSystemDataProvider(rootURL: bundleURL)

var action = try ConvertAction(
documentationBundleURL: bundleURL,
outOfProcessResolver: nil,
analyze: false,
targetDirectory: targetURL,
htmlTemplateDirectory: templateURL,
emitDigest: false,
currentPlatforms: nil,
dataProvider: dataProvider,
fileManager: FileManager.default,
temporaryDirectory: createTemporaryDirectory(),
experimentalEnableCustomTemplates: true
)
let result = try action.perform(logHandle: .standardOutput)

let expectedOutput = Folder(name: ".docc-build", content: [
index,
themeSettings,
])
expectedOutput.assertExist(at: result.outputs[0], fileManager: FileManager.default)
}

private func uniformlyPrintDiagnosticMessages(_ problems: [Problem]) -> String {
return problems.sorted(by: { (lhs, rhs) -> Bool in
Expand Down