Html 0.3.1

Html 0.3.1

Stephen CelisBrandon Williams 维护。



Html 0.3.1

🗺swift-html

Swift 5 iOS/macOS CI Linux CI @pointfreeco

一种用于类型安全、可扩展和可转换 HTML 文档的 Swift DSL。

目录

动机

如今,Swift 中渲染 HTML 的流行选择是使用模板语言,但它们会使您的应用程序暴露于 运行时错误无效的 HTML。我们的库通过将 HTML 直接嵌入到 Swift 强大的类型系统中,在编译时防止这些运行时问题。

示例

HTML 文档可以像创建嵌套 JSON 文档一样以树形结构创建

import Html

let document: Node = .document(
  .html(
    .body(
      .h1("Welcome!"),
      .p("You’ve found our site!")
    )
  )
)

这些标签函数 htmlbodyh1 等,实际上只是在创建和嵌套 Node 类型的实例,后者是一个简单的 Swift 枚举。因为 Node 只是一个简单的 Swift 类型,我们可以以各种有趣的方式进行转换。例如,如果我们想要从文档中删除所有的感叹号呢?

func unexclaim(_ node: Node) -> Node {
  switch node {
  case .comment:
    // Don't need to transform HTML comments
    return node

  case .doctype:
    // Don't need to transform doctypes
    return node

  case let .element(tag, attrs, children):
    // Recursively transform all of the children of an element
    return .element(tag, attrs, children.map(unexclaim))

  case let .fragment(children):
    // Recursively transform all of the children of a fragment
    return .fragment(children.map(unexclaim))

  case let .raw(string), .text(string):
    // Transform text nodes by replacing exclamation marks with periods.
    return string.replacingOccurrences(of: "!", with: ".")
  }
}

unexclaim(document) // Node

创建完您的文档后,您可以使用 render 函数来渲染它

render(document)
// <!doctype html><html><body><h1>Welcome!</h1><p>You’ve found our site!</p></body></html>

当然,您可以先对文档执行 unexlaim 转换,然后再进行渲染

render(unexclaim(document))
// <!doctype html><html><body><h1>Welcome.</h1><p>You’ve found our site.</p></body></html>

现在文档就非常严肃和认真了😂.

安全

由于我们在 Swift 中嵌入我们的 DSL,我们可以利用一些高级 Swift 特性在构建 HTML 文档时添加额外的安全层。以一个简单的例子来说,我们可以增强许多 HTML API 的真正类型,而不是仅仅依赖字符串。

let imgTag = Node.img(attributes: [.src("cat.jpg"), .width(400), .height(300)])

render(imgTag)
// <img src="cat.jpg" width="400" height="300">

在这里,src 属性接受一个字符串,但 widthheight 接受整数,因为将这些属性放置其他任何内容都是无效的。

对于更复杂的情况,仅可以在 <ol><ul> 标签内放置 <li> 标签,且我们可以表示这个事实,使其无法构建无效文档

let listTag = Node.ul(
  .li("Cat"),
  .li("Dog"),
  .li("Rabbit")
) // ✅ Compiles!

render(listTag)
// <ul><li>Cat</li><li>Dog</li><li>Rabbit</li></ul>

Node.div(
  .li("Cat"),
  .li("Dog"),
  .li("Rabbit")
) // 🛑 Compile error

设计

库的核心是一个具有 6 个情况的单一枚举

public enum Node {
  case comment(String)
  case doctype(String)
  indirect case element(String, [(key: String, value: String?)], Node)
  indirect case fragment([Node])
  case raw(String)
  case text(String)
}

此类型允许您表达可以存在的每一个 HTML 文档。然而,直接使用此类可能会有些不便,因此我们提供了一组辅助函数,以类型安全的方式从整个 HTML 规范构建每个元素和属性

// Not using helper functions
Node.element("html", [], [
  .element("body", [], [
    .element("p", [], [.text("You’ve found our site!")])
    ])
  ])

// versus

// Using helper functions
Node.html(
  .body(
    .h1("Welcome!"),
    .p("You’ve found our site!")
  )
)

这使得 HTML 文档的“Swift化”看起来与原始文档非常相似。

常见问题解答

我是否可以用它与现有的 Swift 网络框架(如 Kitura 和 Vapor)一起使用?

是的!我们甚至提供了插件库,以降低在 Kitura 和 Vapor 上使用此库的摩擦力。更多详细信息请参阅以下存储库

我为什么要在模板语言之外使用这个工具?

模板语言非常受欢迎,入门也简单,但它们有很多缺点

  1. 字符串API:模板语言总是字符串类型的,因为您提供模板作为一个大字符串,然后在运行时插入值和执行逻辑。这意味着在Swift中我们习以为常的许多功能,如编译器捕获笔误和类型不匹配,将不被注意到,直到你运行代码。

  2. 不完整的语言:模板语言只是这样:编程语言。这意味着您应该从这些语言中获得所有其他完整语言(如Swift)的优点。这包括语法高亮、IDE自动完成、静态分析、重构工具、断点、调试器以及让Swift强大的各种功能,如let绑定、条件语句、循环等。然而,现实是没有任何模板语言支持所有这些功能。

  3. 严格:模板语言在某种程度上是严格的,因为它们不允许我们像在Swift中的数据结构上执行的那种组合和变换。您无法简短地遍历构建的文档,并检查或转换访问的节点。这一能力有很多用途,例如能够美化打印或压缩您的HTML输出,或编写允许您将CSS样式表内联到HTML节点的变换。由于模板语言的工作方式,关闭了整个世界。

这个库中的领域特定语言(DSL)修复了所有这些问题,并为模板语言完全关闭的大门打开了。

何时更合适使用模板语言而不是swift-html?

有一些原因可能会让你仍然想使用模板语言

  1. 设计师提供了一个大的HTML文档给你,而你只想添加一点点值插值或逻辑。在这种情况下,你可以直接把HTML复制粘贴到模板中,添加几个插值标记,就可以从您的Web应用程序中服务完整页面。

  2. 您需要渲染非HTML文档。模板语言的优点在于它直接输出到纯文本,因此可以模拟任何类型的文档,无论是HTML、Markdown、XML、RSS、ATOM、LaTeX,还是更多。

  3. 在一个单一代数表达式中创建非常大的文档会导致编译时间上升,而模板不是由Swift编译的,因此不会影响编译时间。幸运的是,这并不是一个常见问题,因为很容易将文档分成你想要那么多的小块,这可能在长远来看会带来更多可重用代码。

安装

Carthage

如果您使用 Carthage,可以在您的 Cartfile 中添加以下依赖项

github "pointfreeco/swift-html" ~> 0.3

CocoaPods

如果您的项目使用 CocoaPods,只需在您的 Podfile 中添加以下内容

pod 'Html', '~> 0.3'

# SnapshotTesting helpers
pod 'HtmlSnapshotTesting', '~> 0.3'

SwiftPM

如果您想在使用 SwiftPM 的项目中使用 swift-html,只需在 Package.swift 中添加一个 dependencies 子句

dependencies: [
  .package(url: "https://github.com/pointfreeco/swift-html.git", from: "0.3.0")
]

然后您可以添加 HtmlHtmlSnapshotTesting 作为目标依赖项。

Xcode 子项目

通过子模块克隆或下载 swift-html,然后将 Html.xcodeproj 拖入您的项目。

想要了解更多吗?

这些概念(以及更多)在 Point-Free 的系列视频中得到了深入探讨,这是一个由 Brandon WilliamsStephen Celis 主持的关于函数式编程和 Swift 的视频系列。

该库的想法在以下几集中进行了探讨

video poster image

许可协议

所有模块均在MIT许可下发布。有关详细信息,请参阅LICENSE