HtmlSnapshotTesting 0.3.0

HtmlSnapshotTesting 0.3.0

Stephen CelisBrandon Williams 维护。



 
依赖项
Html~> 0.3
SnapshotTesting~> 1.3
 

🗺swift-html

Swift 5 iOS/macOS CI Linux CI @pointfreeco

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

目录

动机

目前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>

现在该文档非常严肃认真😂.

安全性

因为我们将我们的DSL嵌入Swift中,我们可以利用一些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属性接受整数,因为将这些属性设置为其他任何内容都是无效的。

为了一个更高级的例子,<li>标签只能放在<ol><ul>标签内部,我们可以通过这种方式表达这一事实,以确保无法构建一个无效的文档

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个case的单个枚举

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化”看起来非常接近原始文档。

常见问题解答(FAQ)

我可以使用这个与现有的Swift网络框架,如Kitura和Vapor一起使用吗?

是的!我们还提供了一些插件库,以降低与Kitura和Vapor使用此库的摩擦。更多信息可以在以下仓库找到

为什么我会选择使用这个而不是模板语言?

模板语言很流行,而且易于上手,但它们有很多缺点

  1. Stringy APIs:模板语言始终是字符串类型的,因为您提供模板时是以一个大的字符串,然后在运行时进行值插值和逻辑执行。这意味着我们我们在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 拖放到你的项目中。

想了解更多吗?

在由 Brandon Williams 和 Stephen Celis 主办的,探讨函数编程和 Swift 的视频系列 Point-Free 的一系列节目中,深入探讨了这些概念(及更多)。

以下节目中探讨了该库的想法:

video poster image

许可证

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