• This repository has been archived on 17/Oct/2021
  • Stars
    star
    225
  • Rank 177,187 (Top 4 %)
  • Language
    Swift
  • License
    MIT License
  • Created almost 5 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Generate HTML, XML, and other web content using Swift string literal interpolation

HypertextLiteral

CI Documentation

HypertextLiteral is a Swift package for generating HTML, XML, and other SGML dialects.

It uses custom string interpolation to append and escape values based on context, with built-in affordances for common patterns and an extensible architecture for defining your own behavior.

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 is small and self-contained with no external dependencies. You can get up to speed in just a few minutes, without needing to learn any new APIs or domain-specific languages (DSLs). Less time fighting your tools means more time spent generating web content.

This project is inspired by and borrows implementation details from Hypertext Literal by Mike Bostock (@mbostock). You can read more about it here.

Requirements

  • Swift 5.1+

Installation

Swift Package Manager

Add the HypertextLiteral package to your target dependencies in Package.swift:

import PackageDescription

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

Then run the swift build command to build your project.

Usage

Hypertext literals automatically escape interpolated values based on the context in which they appear.

  • By default, interpolated content escapes the XML entities <, >, &, ", and ' as named character references (for example, < becomes &lt;)
  • In the context of an attribute value, quotation marks are escaped with a backslash (\")
  • In a context of comment, any start and end delimiters (<!-- and -->) are removed

Interpolating Content

To get a better sense of how this works in practice, consider the following examples:

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;

Interpolation for an element's name in part or whole work as intended, but interpolation of the tag itself causes the string to have its angle bracket (< and >) escaped.

When you don't want this to happen, such as when you're embedding HTML content in a template, you can either pass that content as an HTML value or interpolate using the unsafeUnescaped argument label.

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>

Note: Interpolation with the unsafeUnescaped argument label appends the provided literal directly, which may lead to invalid results. For this reason, use of HTML values for composition is preferred.

Interpolating Attribute Values

Attributes in hypertext literals may be interchangeably specified with or without quotation marks, either single (') or double (").

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

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

Some attributes have special, built-in rules for value interpolation.

When you interpolate an array of strings for an element's class attribute, the resulting value is a space-delimited list.

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

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

If you interpolate a dictionary for the value of an element's style attribute, it's automatically converted to 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>

The Boolean value true interpolates to different values depending the attribute.

"""
<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"/> */

Interpolating Attributes with Dictionaries

You can specify multiple attributes at once by interpolating dictionaries keyed by strings.

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> */

Attributes with a common aria- or data- prefix can be specified with a nested dictionary.

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> */

Support for Other Formats

In addition to HTML, you can use hypertext literals for XML and other SGML formats. Below is an example of how HypertextLiteral can be used to generate an SVG document.

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>
"""#

License

MIT

Contact

Mattt (@mattt)

More Repositories

1

articles

Articles for NSHipster.com
1,209
star
2

ConfettiView

A view that emits confetti 🎉
Swift
544
star
3

nshipster.com

A journal of the overlooked bits in Objective-C, Swift, and Cocoa.
SCSS
281
star
4

PasswordRules

A Swift library for defining strong password generation rules
Swift
278
star
5

clangwarnings.com

A list of Clang warnings and their descriptions.
276
star
6

SwiftSyntaxHighlighter

A syntax highlighter for Swift code that uses SwiftSyntax to generate Pygments-compatible HTML.
Swift
275
star
7

DictionaryKit

An Objective-C Wrapper for Private Dictionary Services on Mac OS X
Objective-C
241
star
8

AVSpeechSynthesizer-Example

A companion project to the NSHipster article about AVSpeechSynthesizer
Swift
160
star
9

XcodeBuildSettings.com

A convenient reference of available build settings for Xcode projects.
SCSS
152
star
10

articles-zh-Hans

Articles for NSHipster.cn
119
star
11

DynamicDesktop

Companion playgrounds to the NSHipster article about macOS Dynamic Desktops.
Swift
102
star
12

DBSCAN

Density-based spatial clustering of applications with noise
Swift
86
star
13

ibcolortool

List all colors in Storyboards and XIB files
Swift
80
star
14

Image-Resizing-Example

Swift
78
star
15

swift-log-github-actions

GitHub Actions workflow logging for Swift
Swift
72
star
16

swift-gyb

Evaluates and runs a Swift GYB script
Swift
66
star
17

ContactTracing-Framework-Interface

Objective-C header and synthesized Swift interface for Apple & Google's Contact Tracing Framework
Objective-C
58
star
18

xcderiveddata

A command-line utility that prints the path of the derived data directory for the current Xcode project
Shell
55
star
19

uti

A command-line utility that prints the Uniform Type Identifier for files.
Shell
31
star
20

Swift-Documentation-Example

A companion project to the NSHipster article about Swift Documentation
Swift
29
star
21

JavaScriptCore-JSExport-Example

An example of using Swift with JavaScriptCore's JSExport Protocol
Swift
25
star
22

Cycle

A sequence that cycles between each of the items in a given sequence.
Swift
24
star
23

nshipster.cn

NSHipster 关注被忽略的 Objective-C、Swift 和 Cocoa 特性。每周更新。
CSS
23
star
24

MapKitJS-Demo

Swift
22
star
25

homebrew-formulae

Collection of Homebrew Formulae
Ruby
21
star
26

articles-ko

Articles for NSHipster.co.kr
21
star
27

update-homebrew-formula-action

Synchronizes a Homebrew formula with a GitHub release
Ruby
21
star
28

UITableViewHeaderFooterView-Demo

Swift
13
star
29

articles-es

Articles for NSHipster.es
6
star
30

articles-fr

Articles for NSHipster.fr
2
star