A collection of utilities for Saga: HTML transformations and useful String extensions.
Include SagaUtils in your Package.swift:
let package = Package(
dependencies: [
.package(url: "https://github.com/loopwerk/Saga", from: "2.0.3"),
.package(url: "https://github.com/loopwerk/SagaUtils", from: "1.0.0"),
],
targets: [
.target(
name: "MyWebsite",
dependencies: ["Saga", "SagaUtils"]),
]
)SagaUtils provides composable HTML transformations powered by SwiftSoup. Each transformation operates on a SwiftSoup Document and can be combined using swiftSoupProcessor:
import SagaUtils
try await Saga(input: "content", output: "deploy")
.register(
folder: "articles",
metadata: ArticleMetadata.self,
readers: [.parsleyMarkdownReader],
itemProcessor: swiftSoupProcessor(generateTOC, convertAsides, processExternalLinks, addCodeBlockTitles),
writers: [.itemWriter(swim(renderArticle))]
)
.run()addHeadingAnchors— Adds<a name="slug"></a>anchors to h1, h2, h3 headings.generateTOC— Replaces a%TOC%placeholder with a<nav class="toc">generated from headings. Also adds heading anchors, so there's no need to also useaddHeadingAnchors. UsegenerateTOC(placeholder: "@TOC")for a custom placeholder.convertAsides— Converts blockquotes with[!TYPE]syntax to<aside class="type">elements. For example,[!WARNING]becomes<aside class="warning">.processExternalLinks— Addstarget="_blank"andrel="nofollow"to external links.
You can also write your own transformations with the signature (Document) throws -> Void and pass them to swiftSoupProcessor.
Useful extensions on String:
// Strip HTML tags, keeping code block content
"<p>Hello <strong>world</strong></p>".plainText // "Hello world"
// Strip HTML tags and code blocks (useful for word counting)
body.textOnly
// Count words
body.textOnly.wordCount
// Truncate with word boundary awareness (inspired by Jinja2)
text.truncate(length: 200)
text.truncate(length: 200, killWords: true, end: "…")