Skip to content

Commit

Permalink
Add support for loading FS templates/CSS
Browse files Browse the repository at this point in the history
The templates still exist in source, but the main copy
are now the .mustache files (and Site.css) in the
package root.

Custom template directories can now be specified using
the `-t`/`--templates` argument for full customization.
  • Loading branch information
helje5 committed Jul 6, 2021
1 parent a127d14 commit 34a0d09
Show file tree
Hide file tree
Showing 23 changed files with 714 additions and 26 deletions.
3 changes: 2 additions & 1 deletion Sources/DocCHTMLExporter/DZRenderingContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ open class DZRenderingContext {
}

public struct Templates {
// refer to them using this struct, to make them configurable later on.

public init() {}

/// Arguments:
/// relativePathToRoot, highlightCDN, contentHTML, footerHTML, title,
Expand Down
25 changes: 25 additions & 0 deletions Sources/DocCHTMLExporter/Templates/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<h2>docc2html Templates (Code)
<img src="http://zeezide.com/img/docz/docc2html100.png"
align="right" width="100" height="100" />
</h2>

This folder contains the default Mustache templates used,
plus the `Site.css` containing the default CSS.

It is used as a fallback if the `Templates` (resource) directory in the package root cannot
be reached, or not `-t/--templates` option was specified.
When PR'ing changes, please do so in the resource directory.


### Who

**docc2html** is brought to you by
the
[Always Right Institute](http://www.alwaysrightinstitute.com)
and
[ZeeZide](http://zeezide.de).
We like
[feedback](https://twitter.com/ar_institute),
GitHub stars,
cool [contract work](http://zeezide.com/en/services/services.html),
presumably any form of praise you can think of.
20 changes: 14 additions & 6 deletions Sources/DocCStaticExporter/DocCStaticExporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,22 @@ open class DocCStaticExporter {
public let options : Options
public let target : DocCStaticExportTarget
public let archiveURLs : [ URL ]
public let stylesheet = DZRenderingContext.defaultStyleSheet

public let stylesheet : String
public let templates : DZRenderingContext.Templates?

public var dataFolderPathes = Set<String>()

public init(target: DocCStaticExportTarget, archivePathes: [ String ],
options: Options, logger: Logger = Logger(label: "docc2html"))
public init(target : DocCStaticExportTarget, archivePathes: [ String ],
options : Options,
templates : DZRenderingContext.Templates? = nil,
stylesheet : String? = nil,
logger : Logger = Logger(label: "docc2html"))
{
self.target = target
self.archiveURLs = archivePathes.map(URL.init(fileURLWithPath:))
self.options = options
self.templates = templates
self.stylesheet = stylesheet ?? DZRenderingContext.defaultStyleSheet
self.logger = logger
}

Expand Down Expand Up @@ -170,7 +176,8 @@ open class DocCStaticExporter {
references : document.references,
isIndex : true,
dataFolderPathes : dataFolderPathes,
indexLinks : true
indexLinks : true,
templates : templates
)

let html = try ctx.buildDocument(document, in: folder)
Expand All @@ -191,7 +198,8 @@ open class DocCStaticExporter {
references : document.references,
isIndex : false,
dataFolderPathes : dataFolderPathes,
indexLinks : buildIndex
indexLinks : buildIndex,
templates : templates
)

let html = try ctx.buildDocument(document, in: folder)
Expand Down
1 change: 1 addition & 0 deletions Sources/docc2html/ExitCode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ enum ExitCode: Int32, Swift.Error {
case targetDirectoryExists = 4
case couldNotLoadStaticResource = 5
case couldNotCopyStaticResource = 6
case couldNotFindTemplatesDir = 7
}

extension ExitCode {
Expand Down
60 changes: 60 additions & 0 deletions Sources/docc2html/LoadTemplates.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// LoadTemplates.swift
// docc2html
//
// Created by Helge Heß.
// Copyright © 2021 ZeeZide GmbH. All rights reserved.
//

import mustache
import Foundation
import DocCHTMLExporter // @docczz/docc2html

func loadTemplatesAndStylesheet(from path: String)
-> ( templates: DZRenderingContext.Templates, stylesheet: String )
{
let baseURL = URL(fileURLWithPath: path)
let cssURL = baseURL.appendingPathComponent("site.css")

let stylesheet = (try? String(contentsOf: cssURL))
?? DZRenderingContext.defaultStyleSheet

var templates = DZRenderingContext.Templates()

func load(_ name: String) -> Mustache? {
let url = baseURL.appendingPathComponent(name)
.appendingPathExtension("mustache")
guard FileManager.default.fileExists(atPath: url.path) else {
logger.trace("No filesystem template for:", name)
return nil
}

do {
let s = try String(contentsOf: url)
let m = Mustache(s)
logger.trace("Did load template:", name, "from:", url.path)
return m
}
catch {
logger.error("Failed to load template:", name, error)
return nil
}
}

if let m = load("Page") { templates.htmlWrapper = m }
if let m = load("DocumentContent") { templates.documentContent = m }
if let m = load("Navigation") { templates.navigation = m }
if let m = load("PrimaryContent") { templates.primaryContentGrid = m }
if let m = load("CodeListing") { templates.codeListing = m }
if let m = load("StepContent") { templates.step = m }
if let m = load("DeclarationSection") { templates.declarationSection = m }
if let m = load("ParametersSection") { templates.parametersSection = m }
if let m = load("Sections") { templates.contentTableSection = m }
if let m = load("TopicTitle") { templates.topicTitle = m }
if let m = load("Hero") { templates.hero = m }
if let m = load("Task") { templates.task = m }
if let m = load("TaskIntro") { templates.taskIntro = m }
if let m = load("Volume") { templates.volume = m }

return ( templates: templates, stylesheet: stylesheet )
}
71 changes: 53 additions & 18 deletions Sources/docc2html/Options.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@ func usage() {
Options:
-f/--force: overwrite/merge target directories and files
-s/--silent: silent logging
-v/--verbose: verbose logging
--keep-hash: keep hashes in resource names
--copy-css: copy the CSS folder in the archive to the target
-f/--force: overwrite/merge target directories and files
-s/--silent: silent logging
-v/--verbose: verbose logging
-t/--templates <directory>: directory containing template files
"""
)
}
Expand All @@ -39,28 +38,64 @@ struct Options {
= [ .buildIndex, .buildAPIDocs, .buildTutorials ]
let archivePathes : [ String ]
let targetPath : String
let templatesPath : String?
let logFactory : ( String ) -> LogHandler

var targetURL : URL { URL(fileURLWithPath: targetPath) }

init?(argv: [ String ]) {
if argv.contains("--force") || argv.contains("-f") {
exportOptions.insert(.force)
var argv = argv

func processFlag(_ long: String, _ short: String = "") -> Bool {
let ok = argv.contains(long)
|| (short.isEmpty ? false : argv.contains(short))
argv.removeAll(where: { $0 == long || $0 == short })
return ok
}
if argv.contains("--keep-hash") { exportOptions.insert(.keepHash ) }
if argv.contains("--copy-css") { exportOptions.insert(.copySystemCSS) }

let silent = argv.contains("--silent") || argv.contains("-s")
let verbose = argv.contains("--verbose") || argv.contains("-v")

logFactory = { label in
var handler = StreamLogHandler.standardOutput(label: label)
if verbose { handler.logLevel = .trace }
else if silent { handler.logLevel = .error }
return handler
if processFlag("--force", "-f") { exportOptions.insert(.force ) }
if processFlag("--keep-hash") { exportOptions.insert(.keepHash ) }
if processFlag("--copy-css") { exportOptions.insert(.copySystemCSS) }

if let idx = argv.firstIndex(of: "-t")
?? argv.firstIndex(of: "--templates"),
(idx + 1) < argv.count
{
argv.remove(at: idx)
templatesPath = argv[idx]
argv.remove(at: idx)
}
else {
let binURL = URL(fileURLWithPath: #filePath)
let pkgURL = binURL.deletingLastPathComponent()
.deletingLastPathComponent()
.deletingLastPathComponent()
let url = pkgURL.appendingPathComponent("Templates")
var isDir : ObjCBool = false
let fm = FileManager.default
if fm.fileExists(atPath: url.path, isDirectory: &isDir), isDir.boolValue {
templatesPath = url.path
}
else {
templatesPath = nil
}
}

do { // Logging
let silent = processFlag("--silent", "-s")
let verbose = processFlag("--verbose", "-v")

logFactory = { label in
var handler = StreamLogHandler.standardOutput(label: label)
if verbose { handler.logLevel = .trace }
else if silent { handler.logLevel = .error }
return handler
}
}

let pathes = argv.dropFirst().filter { !$0.hasPrefix("-") }
// TODO: scan for unprocessed `-` and throw an error?

let pathes = argv.dropFirst().filter { !$0.hasPrefix("-") }
guard pathes.count > 1 else { return nil }
archivePathes = Array(pathes.dropLast())
targetPath = pathes.last ?? ""
Expand Down
32 changes: 31 additions & 1 deletion Sources/docc2html/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,48 @@
import Foundation
import Logging // @apple/swift-log
import DocCStaticExporter // @docczz/docc2html
import DocCHTMLExporter // @docczz/docc2html


// MARK: - Process Commandline Arguments and Setup Logging

guard let options = Options(argv: CommandLine.arguments) else {
usage()
exit(ExitCode.notEnoughArguments.rawValue)
}

LoggingSystem.bootstrap(options.logFactory)
let logger = Logger(label: "docc2html")


// MARK: - Load Templates, if available

let templates : DZRenderingContext.Templates?
let stylesheet : String?
if let path = options.templatesPath {
guard FileManager.default.fileExists(atPath: path) else {
logger.error("Did not find templates directory:", path)
exit(ExitCode.couldNotFindTemplatesDir)
}

logger.trace("Using templates directory:", path)
( templates, stylesheet ) = loadTemplatesAndStylesheet(from: path)
}
else {
templates = nil
stylesheet = nil
}


// MARK: - Setup Site Exporter and Run

let exporter = DocCStaticExporter(
target : DocCFileSystemExportTarget(targetPath: options.targetPath),
archivePathes : options.archivePathes,
options : options.exportOptions
options : options.exportOptions,
templates : templates,
stylesheet : stylesheet,
logger : logger
)

do {
Expand Down
6 changes: 6 additions & 0 deletions Templates/CodeListing.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div data-syntax="{{syntax}}" class="code-listing">
<div class="container-general">
<pre><code>{{#lines}}<span class="code-line-container"><span data-line-number="{{{line}}}" class="code-number" style="display: none;"></span><span class="code-line">{{code}}</span></span>
{{/lines}}</code></pre>
</div>
</div>
6 changes: 6 additions & 0 deletions Templates/DeclarationSection.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<section id='declaration' class='declaration'>
<h2>{{title}}</h2>
<div class='declaration-group'>
<pre class='source indented'><code>{{{tokensHTML}}}</code></pre>
</div>
</section>
10 changes: 10 additions & 0 deletions Templates/DocumentContent.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{{{navigationHTML}}}
<main id="main" role="main" class="main">
{{{topicTitleHTML}}}
{{{primaryContentHTML}}}
{{#sectionsContentHTML}}
<div class="sections">{{{sectionsContentHTML}}}</div>
{{/sectionsContentHTML}}
{{{topicSectionsHTML}}}
{{{seeAlsoHTML}}}
</main>
29 changes: 29 additions & 0 deletions Templates/Hero.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<div id="introduction" class="tutorial-hero">
<div class="hero dark">
{{#backgroundImage}}
<div class="bg"
style="background-image: url(&quot;{{backgroundImage}}&quot;);"></div>
{{/backgroundImage}}

<div class="row">
<div class="col large-7 medium-9 small-12">
<div class="headline"><span class="eyebrow">{{eyebrow}}</span>
<h1 class="heading">{{title}}</h1>
</div>
</div>
<div class="intro">
<div class="content">{{{contentHTML}}}</div>
</div>
<div class="metadata">
<div class="item">
<div class="content">
<div class="duration">{{duration}}
<div class="minutes">min</div>
</div>
</div>
<div class="bottom">Estimated Time</div>
</div>
</div>
</div>
</div>
</div>
26 changes: 26 additions & 0 deletions Templates/Navigation.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<nav role="navigation" class="nav documentation-nav">
<div class="nav__wrapper">
<div class="nav__background"></div>
<div class="nav-content">
<div class="nav-title">
<span class="nav-title-link inactive">{{title}}</span>
</div>
<div class="nav-menu">
<div class="nav-menu-tray">
<ul class="nav-menu-items hierarchy">
{{#navitems}}
<li class="nav-menu-item hierarchy-item">
{{#isCurrent}}
<span class="current item">{{title}}</span>
{{/isCurrent}}
{{^isCurrent}}
<a href="{{link}}" class="item">{{title}}</a>
{{/isCurrent}}
</li>
{{/navitems}}
</ul>
</div>
</div>
</div>
</div>
</nav>
Loading

0 comments on commit 34a0d09

Please sign in to comment.