Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
john-mueller committed Feb 10, 2020
0 parents commit 603286c
Show file tree
Hide file tree
Showing 12 changed files with 542 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store
/.build
/.swiftpm
/Packages
/*.xcodeproj
xcuserdata/
Package.resolved
8 changes: 8 additions & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
disabled_rules:
- trailing_comma
- orphaned_doc_comment

excluded:
- .build
- Tests/TidyHTMLPublishStepTests/Helpers/XCTestManifests.swift
- Tests/LinuxMain.swift
21 changes: 21 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

© 2020 John Mueller

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
24 changes: 24 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// swift-tools-version:5.1

/**
* TidyHTMLPublishStep
* © 2020 John Mueller
* MIT license, see LICENSE.md for details
*/

import PackageDescription

let package = Package(
name: "TidyHTMLPublishStep",
products: [
.library(name: "TidyHTMLPublishStep", targets: ["TidyHTMLPublishStep"]),
],
dependencies: [
.package(url: "https://github.com/JohnSundell/Publish.git", from: "0.1.0"),
.package(url: "https://github.com/scinfu/SwiftSoup.git", from: "2.0.0"),
],
targets: [
.target(name: "TidyHTMLPublishStep", dependencies: ["Publish", "SwiftSoup"]),
.testTarget(name: "TidyHTMLPublishStepTests", dependencies: ["TidyHTMLPublishStep"]),
]
)
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Tidy HTML step for Publish

A `PublishingStep` for [Publish](https://github.com/JohnSundell/Publish) that nicely formats your website's HTML using [SwiftSoup](https://github.com/scinfu/SwiftSoup).

## Installation

To install the step, add it as a dependency within your `Package.swift` manifest:

```swift
let package = Package(
...
dependencies: [
...
.package(url: "https://github.com/john-mueller/TidyHTMLPublishStep", from: "0.1.0")
],
targets: [
.target(
...
dependencies: [
...
"TidyHTMLPublishStep"
]
)
]
...
)
```

Then import `TidyHTMLPublishStep` where you'd like to use it.

## Usage

The `tidyHTML(withIndentation:)` step should be inserted into your publishing pipeline *after* your HTML is generated. The default indentation is one space, if the parameter is omitted.

```swift
import TidyHTMLPublishStep
...
try DeliciousRecipes().publish(using: [
...
.generateHTML(withTheme: .foundation),
...
.tidyHTML(indentedBy: .spaces(4))
...
])
```

This package also provides an alternate convenience API to the `Website.publish(withTheme:...:additionalSteps:...)` method, replacing `additionalSteps` with `preGenerationSteps` and `postGenerationSteps`. The `tidyHTML` step should be passed to the `postGenerationSteps` parameter:

```swift
import TidyHTMLPublishStep
...
try DeliciousRecipes().publish(
withTheme: theme,
postGenerationSteps: [
.tidyHTML()
]
)
64 changes: 64 additions & 0 deletions Sources/TidyHTMLPublishStep/TidyHTMLPublishStep.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* TidyHTMLPublishStep
* © 2020 John Mueller
* MIT license, see LICENSE.md for details
*/

import Plot
import Publish
import SwiftSoup

extension PublishingStep {
public static func tidyHTML(indentedBy indent: Indentation.Kind? = nil) -> Self {
.step(named: "Tidy HTML") { context in
do {
let root = try context.folder(at: "")

let files = root.files.recursive

for file in files where file.extension == "html" {
let html = try file.readAsString()

let outputSettings = OutputSettings()
switch indent {
case let .spaces(num), let .tabs(num):
outputSettings.indentAmount(indentAmount: UInt(num))
default:
break
}

let doc = try SwiftSoup.parse(html)
doc.outputSettings(outputSettings)

var tidyHTML = try doc.html()

if case .tabs = indent {
var isPreCode = false
tidyHTML = tidyHTML.components(separatedBy: .newlines).map { line in
if isPreCode {
guard line.hasPrefix("</pre></code>") else {
return line
}
isPreCode = false
}

let tabs = line.prefix(while: { $0 == " " }).map { _ in Character("\t") }
let range = line.startIndex ..< line.index(line.startIndex, offsetBy: tabs.count)

if line[range.upperBound...].hasPrefix("<pre><code>") {
isPreCode = true
}

return line.replacingCharacters(in: range, with: String(tabs))
}.joined(separator: "\n")
}

try file.write(tidyHTML)
}
} catch {
print("Error while tidying HTML:")
print(error)
}
}
}
}
61 changes: 61 additions & 0 deletions Sources/TidyHTMLPublishStep/Website+PostGenerationSteps.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* TidyHTMLPublishStep
* © 2019 John Sundell, 2020 John Mueller
* MIT license, see LICENSE.md for details
*
* This file was adapted from the Publish API
*/

import Plot
import Publish

public extension Website {
/// Publish this website using a default pipeline. To build a completely
/// custom pipeline, use the `publish(using:)` method.
/// - parameter theme: The HTML theme to generate the website using.
/// - parameter indentation: How to indent the generated files.
/// - parameter path: Any specific path to generate the website at.
/// - parameter rssFeedSections: What sections to include in the site's RSS feed.
/// - parameter rssFeedConfig: The configuration to use for the site's RSS feed.
/// - parameter deploymentMethod: How to deploy the website.
/// - parameter preGenerationSteps: Additional steps to add to the publishing
/// pipeline. Will be executed right before the HTML generation process begins.
/// - parameter postGenerationSteps: Additional steps to add to the publishing pipeline.
/// Will be executed after generation steps, right before the deployment process begins.
/// - parameter plugins: Plugins to be installed at the start of the publishing process.
/// - parameter file: The file that this method is called from (auto-inserted).
/// - parameter line: The line that this method is called from (auto-inserted).
@discardableResult
func publish(withTheme theme: Theme<Self>,
indentation: Indentation.Kind? = nil,
at path: Path? = nil,
rssFeedSections: Set<SectionID> = Set(SectionID.allCases),
rssFeedConfig: RSSFeedConfiguration? = .default,
deployedUsing deploymentMethod: DeploymentMethod<Self>? = nil,
preGenerationSteps: [PublishingStep<Self>] = [],
postGenerationSteps: [PublishingStep<Self>] = [],
plugins: [Plugin<Self>] = [],
file: StaticString = #file) throws -> PublishedWebsite<Self> {
try publish(
at: path,
using: [
.group(plugins.map(PublishingStep.installPlugin)),
.optional(.copyResources()),
.addMarkdownFiles(),
.sortItems(by: \.date, order: .descending),
.group(preGenerationSteps),
.generateHTML(withTheme: theme, indentation: indentation),
.unwrap(rssFeedConfig) { config in
.generateRSSFeed(
including: rssFeedSections,
config: config
)
},
.generateSiteMap(indentedBy: indentation),
.group(postGenerationSteps),
.unwrap(deploymentMethod, PublishingStep.deploy),
],
file: file
)
}
}
10 changes: 10 additions & 0 deletions Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* TidyHTMLPublishStep
* © 2020 John Mueller
* MIT license, see LICENSE.md for details
*/

import Foundation

fputs("To run tests on Linux, use 'swift test --enable-test-discovery'\n", stderr)
exit(1)
51 changes: 51 additions & 0 deletions Tests/TidyHTMLPublishStepTests/Helpers/HTMLFactoryMock.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* TidyHTMLPublishStep
* © 2019 John Sundell, 2020 John Mueller
* MIT license, see LICENSE.md for details
*
* This file was copied as-is from the Publish test target
*/

import Plot
import Publish

final class HTMLFactoryMock<Site: Website>: HTMLFactory {
typealias Closure<T> = (T, PublishingContext<Site>) throws -> HTML

var makeIndexHTML: Closure<Index> = { _, _ in HTML(.body()) }
var makeSectionHTML: Closure<Section<Site>> = { _, _ in HTML(.body()) }
var makeItemHTML: Closure<Item<Site>> = { _, _ in HTML(.body()) }
var makePageHTML: Closure<Page> = { _, _ in HTML(.body()) }
var makeTagListHTML: Closure<TagListPage>? = { _, _ in HTML(.body()) }
var makeTagDetailsHTML: Closure<TagDetailsPage>? = { _, _ in HTML(.body()) }

func makeIndexHTML(for index: Index,
context: PublishingContext<Site>) throws -> HTML {
try makeIndexHTML(index, context)
}

func makeSectionHTML(for section: Section<Site>,
context: PublishingContext<Site>) throws -> HTML {
try makeSectionHTML(section, context)
}

func makeItemHTML(for item: Item<Site>,
context: PublishingContext<Site>) throws -> HTML {
try makeItemHTML(item, context)
}

func makePageHTML(for page: Page,
context: PublishingContext<Site>) throws -> HTML {
try makePageHTML(page, context)
}

func makeTagListHTML(for page: TagListPage,
context: PublishingContext<Site>) throws -> HTML? {
try makeTagListHTML?(page, context)
}

func makeTagDetailsHTML(for page: TagDetailsPage,
context: PublishingContext<Site>) throws -> HTML? {
try makeTagDetailsHTML?(page, context)
}
}
Loading

0 comments on commit 603286c

Please sign in to comment.