HypertextLiteral

CI Documentation

HypertextLiteral 是一个 Swift 包,用于生成 HTML、XML 和其他 SGML 方言

它使用自定义字符串插值基于上下文追加和转义值,内置了对常见模式的支持,以及一个可扩展的架构来定义您自己的行为。

import HypertextLiteral

let attributes = [
    "style": [
        "background": "yellow",
        "font-weight": "bold"
    ]
]

let html: HTML = "<span \(attributes)>whoa</span>"
// <span style="background: yellow; font-weight: bold;">whoa</span>

HypertextLiteral 体积小巧,自包含,没有外部依赖项。您可以在几分钟内上手,而无需学习任何新的 API 或领域特定语言 (DSL)。减少与工具的斗争意味着有更多时间生成 Web 内容。

这个项目受到 Mike Bostock(@mbostock)的 Hypertext Literal 的启发,并借鉴了其实现细节。您可以在这里阅读更多相关信息。

要求

安装

Swift Package Manager

在您的 Package.swift 文件中,将 HypertextLiteral 包添加到您的目标依赖项中

import PackageDescription

let package = Package(
  name: "YourProject",
  dependencies: [
    .package(
        url: "https://github.com/NSHipster/HypertextLiteral",
        from: "0.0.3"
    ),
  ]
)

然后运行 swift build 命令来构建您的项目。

用法

超文本字面量会根据插值内容出现的上下文自动对其进行转义。

插值内容

为了更好地了解它的实际工作方式,请考虑以下示例

let level: Int = 1
"<h\(level)>Hello, world!</h\(level)>" as HTML
// <h1>Hello, world!</h1>

let elementName: String = "h1"
"<\(elementName)>Hello, world!</\(elementName)>" as HTML
// <h1>Hello, world!</h1>

let startTag: String = "<h1>", endTag: String = "</h1>"
"\(startTag)Hello, world!\(endTag)" as HTML
// &lt;h1&gt;Hello, world!&lt;/h1&gt;

对元素名称的部分或全部进行插值可以按预期工作,但对标签本身进行插值会导致字符串的尖括号 (<>) 被转义。

如果您不希望这种情况发生,例如当您将 HTML 内容嵌入到模板中时,您可以将该内容作为 HTML 值传递,或者使用 unsafeUnescaped 参数标签进行插值。

let startTag: HTML = "<h1>", endTag: HTML = "</h1>"
"\(startTag)Hello, world!\(endTag)" as HTML
// <h1>Hello, world!</h1>

"\(unsafeUnescaped: "<h1>")Hello, world!\(unsafeUnescaped: "</h1>")" as HTML
// <h1>Hello, world!</h1>

注意:使用 unsafeUnescaped 参数标签进行插值会直接追加提供的文字,这可能会导致无效的结果。因此,首选使用 HTML 值进行组合。

插值属性值

超文本字面量中的属性可以用或不用引号指定,可以是单引号 (') 或双引号 (")。

let id: String = "logo
let title: String = #"Swift.org | "Welcome to Swift.org""#
let url = URL(string: "https://swiftlang.cn/")!

#"<a id='\#(logo)' title="\#(title)" href=\#(url)>Swift.org</a>"# as HTML
/* <a id='logo'
      title="Swift.org | \"Welcome to Swift.org\""
      href="https://swiftlang.cn/">Swift.org</a>
*/

某些属性具有特殊的内置值插值规则。

当您为元素的 class 属性插入字符串数组时,生成的值是空格分隔的列表。

let classNames: [String] = ["alpha", "bravo", "charlie"]

"<div class=\(classNames)>…</div>" as HTML
// <div class="alpha bravo charlie">…</div>

如果您为元素的 style 属性的值插入字典,它会自动转换为 CSS。

let style: [String: Any] = [
    "background": "orangered",
    "font-weight": 700
]

"<span style=\(style)>Swift</span>" as HTML
// <span style="background: orangered; font-weight: 700;">Swift</span>

布尔值 true 根据属性的不同插值为不同的值。

"""
<input type="text" aria-enabled=\(true)
                   autocomplete=\(true)
                   spellcheck=\(true)
                   translate=\(true) />
""" as HTML
/* <input type="text" aria-enabled="true"
                      autocomplete="on"
                      spellcheck="spellcheck"
                      translate="yes"/> */

使用字典插值属性

您可以通过插入以字符串为键的字典来一次指定多个属性。

let attributes: [String: Any] = [
    "id": "primary",
    "class": ["alpha", "bravo", "charlie"],
    "style": [
        "font-size": "larger"
    ]
]

"<div \(attributes)>…</div>" as HTML
/* <div id="primary"
        class="alpha bravo charlie"
        style="font-size: larger;">…</div> */

具有通用 aria-data- 前缀的属性可以使用嵌套字典指定。

let attributes: [String: Any] = [
    "id": "article",
    "aria": [
        "role": "article",
    ],
    "data": [
        "index": 1,
        "count": 3,
    ]
]

"<section \(attributes)>…</section>" as HTML
/* <section id="article"
            aria-role="article"
            data-index="1"
            data-count="3">…</section> */

对其他格式的支持

除了 HTML 之外,您还可以将超文本字面量用于 XML 和其他 SGML 格式。 以下是如何使用 HypertextLiteral 生成 SVG 文档的示例。

import HypertextLiteral

typealias SVG = HTML

let groupAttributes: [String: Any] = [
    "stroke-width": 3,
    "stroke": "#FFFFEE"
]

func box(_ rect: CGRect, radius: CGVector = CGVector(dx: 10, dy: 10), attributes: [String: Any] = [:]) -> SVG {
    #"""
    <rect x=\#(rect.origin.x) y=\#(rect.origin.y)
          width=\#(rect.size.width) height=\#(rect.size.height)
          rx=\#(radius.dx) ry=\#(radius.dy)
          \#(attributes)/>
    """#
}

let svg: SVG = #"""
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'>
  <g \#(groupAttributes)>
    \#(box(CGRect(x: 12, y: 28, width: 60, height: 60), attributes: ["fill": "#F06507"]))
    \#(box(CGRect(x: 27, y: 18, width: 55, height: 55), attributes: ["fill": "#F2A02D"]))
    \#(box(CGRect(x: 47, y: 11, width: 40, height: 40), attributes: ["fill": "#FEC352"]))
  </g>
</svg>
"""#

许可证

MIT

联系方式

Mattt (@mattt)