Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/randomhashtags/swift-htmlkit

Write HTML using Swift Macros.
https://github.com/randomhashtags/swift-htmlkit

dsl html server-side-swift swift swift-library swift-macro swift-macros

Last synced: 2 months ago
JSON representation

Write HTML using Swift Macros.

Awesome Lists containing this project

README

        

Write HTML using Swift Macros.

Requires at least Swift 5.9 Apache 2.0 License

- [Why](#why)
- [Examples](#examples)
- [Basic](#basic)
- [Advanced](#advanced)
- [Benchmarks](#benchmarks)
- [Static](#static)
- [Dynamic](#dynamic)
- [Conclusion](#conclusion)
- [Contributing](#contributing)
- [Funding](#funding)

## Why
- Swift Macros are powerful, efficient and essentially removes any runtime overhead
- Alternative libraries may not fit all situations and may restrict how the html is generated, manipulated, prone to human error, or cost a constant performance overhead (middleware, rendering, result builders, etc)
- HTML macros enforce safety, can be used anywhere, and compile directly to strings
- The output is minified at no performance cost
## Examples
### Basic

How do I use this library?

Syntax: `#(attributes: [], : V?, _ innerHTML: ExpressibleByStringLiteral...)`
#### Examples

```swift
//

Macros are beautiful


#div(attributes: [.class(["dark"])],
#p("Macros are beautiful")
)

//
#a(href: "https://github.com/RandomHashTags/litleagues", target: ._blank)

//
#input(
attributes: [.id("funny-number")],
max: 420,
min: 69,
name: "funny_number",
step: 1,
type: .number,
value: "69"
)

// html example
let test:String = #html(
#body(
#div(
attributes: [
.class(["dark-mode", "row"]),
.draggable(.false),
.hidden(.true),
.inputmode(.email),
.title("Hey, you're pretty cool")
],
"Random text",
#div(),
#a(
#div(
#abbr()
),
#address()
),
#div(),
#button(disabled: true),
#video(autoplay: true, controls: false, preload: .auto, src: "https://github.com/RandomHashTags/litleagues", width: .centimeters(1)),
)
)
)
```

How do I escape HTML?

The output automatically escapes html characters **known only at compile time**.

If you know the data **at compile time** (and not working with HTML macros):
- `#escapeHTML()` macro

If you're working with **runtime** data:
- `.escapeHTML(escapeAttributes:)`
- mutates `self` escaping HTML and, optionally, attribute characters
- `.escapeHTMLAttributes()`
- mutates `self` escaping only attribute characters
- `.escapingHTML(escapeAttributes:)`
- creates a copy of `self` escaping HTML and, optionally, attribute characters
- `.escapingHTMLAttributes()`
- creates a copy of `self` escaping only attribute characters

How do I encode variables?

Using String Interpolation.

> You will get a compiler warning saying *interpolation may introduce raw HTML*.
>
> Its up to you whether or not to suppress this warning or escape the HTML at runtime using an method described above.

#### Example
```swift
let string:String = "any string value", integer:Int = -69, float:Float = 3.14159

// ✅ DO
let _:String = #p("\(string); \(integer); \(float)")
let _:String = #p("\(string)", "; ", String(describing: integer), "; ", float.description)

// ❌ DON'T; compiler error: String Interpolation required
let integer_string:String = String(describing: integer), float_string:String = String(describing: float)
let _:String = #p(string, "; ", integer_string, "; ", float_string)

```

### Advanced

I need a custom element!

Use the `#custom(tag:isVoid:attributes:innerHTML:)` macro.
#### Example
We want to show the [Apple Pay button](https://developer.apple.com/documentation/apple_pay_on_the_web/displaying_apple_pay_buttons_using_javascript#3783424):
```swift
#custom(tag: "apple-pay-button", isVoid: false, attributes: [.custom("buttonstyle", "black"), .custom("type", "buy"), .custom("locale", "el-GR")])
```
becomes
```html

```

I need a custom attribute!

Use `HTMLElementAttribute.custom(id:value:)`
#### Example
We want to show the [Apple Pay button](https://developer.apple.com/documentation/apple_pay_on_the_web/displaying_apple_pay_buttons_using_javascript#3783424):
```swift
#custom(tag: "apple-pay-button", isVoid: false, attributes: [.custom("buttonstyle", "black"), .custom("type", "buy"), .custom("locale", "el-GR")])
```
becomes
```html

```

I need to listen for events!

> WARNING
>
> Inline event handlers are an outdated way to handle events.
>
> General consensus considers this \"bad practice\" and you shouldn't mix your HTML and JavaScript.
>
> This remains deprecated to encourage use of other techniques.
>
> Learn more at https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#inline_event_handlers_—_dont_use_these.

Use the `HTMLElementAttribute.event(, "")`.
#### Example
```swift
#div(attributes: [.event(.click, "doThing()"), .event(.change, "doAnotherThing()")])
```

## Benchmarks
- Libraries tested
- [BinaryBuilds/swift-html](https://github.com/BinaryBirds/swift-html) v1.7.0 (patched version [here](https://github.com/RandomHashTags/fork-bb-swift-html))
- [sliemeobn/elementary](https://github.com/sliemeobn/elementary) v0.3.4
- [JohnSundell/Plot](https://github.com/JohnSundell/Plot) v0.14.0
- [RandomHashTags/swift-htmlkit](https://github.com/RandomHashTags/swift-htmlkit) v0.6.0 (this library)
- [pointfreeco/swift-html](https://github.com/pointfreeco/swift-html) v0.4.1
- [robb/Swim](https://github.com/robb/Swim) v0.4.0 (patched version [here](https://github.com/RandomHashTags/fork-Swim); custom renderer [here](https://github.com/RandomHashTags/swift-htmlkit/blob/main/Benchmarks/Benchmarks/Swim/Swim.swift))
- [vapor-community/HTMLKit](https://github.com/vapor-community/HTMLKit) v2.8.1
- [dokun1/Vaux](https://github.com/dokun1/Vaux) v0.2.0 (patched version [here](https://github.com/RandomHashTags/fork-Vaux); custom renderer [here](https://github.com/RandomHashTags/swift-htmlkit/blob/main/Benchmarks/Benchmarks/Vaux/Vaux.swift))

Test machine: iMac (i9 9900k, 72GB RAM, 2TB) running macOS 15.0 with the Swift 6 compiler.

Executed command: `swift package -c release --allow-writing-to-package-directory benchmark --target Benchmarks --metric throughput --format jmh`

### Static

### Dynamic

### Conclusion
This library is the clear leader in performance & efficiency. Static webpages offer the best performance, while dynamic pages still tops the charts (I am actively researching and testing improvements for dynamic pages).

## Contributing
Create a PR.

## Funding
Love this library? Consider supporting this project by sponsoring the developers.
- [RandomHashTags](https://github.com/sponsors/RandomHashTags)