Skip to content

Commit a13a8c3

Browse files
added more content to dynamicHTMLs; fixed some implementions and...
- updated dynamic benchmark png - update README a little
1 parent 60854f3 commit a13a8c3

File tree

13 files changed

+85
-31
lines changed

13 files changed

+85
-31
lines changed

Benchmarks/Benchmarks/Benchmarks/Benchmarks.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ let benchmarks = {
2727
"Plot" : PlotTests(),
2828
"Pointfreeco" : SwiftHTMLPFTests(),
2929
"SwiftHTMLKit" : SwiftHTMLKitTests(),
30-
"Swim" : SwimTests(),
30+
"Swim (custom renderer)" : SwimTests(),
3131
"VaporHTMLKit" : VaporHTMLKitTests(),
32-
"Vaux" : VauxTests()
32+
"Vaux (custom renderer)" : VauxTests()
3333
]
3434

3535
/*for (key, value) in libraries {

Benchmarks/Benchmarks/Elementary/Elementary.swift

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ package struct ElementaryTests : HTMLGenerator {
1515
StaticView().render()
1616
}
1717
package func dynamicHTML(_ context: HTMLContext) -> String {
18-
DynamicView(context: context).render()
18+
DynamicView(context: context).rawHTML.render()
1919
}
2020
}
2121

@@ -30,25 +30,31 @@ struct StaticView : HTMLDocument {
3030
}
3131
}
3232

33-
struct DynamicView : HTMLDocument {
34-
var title:String = "DynamicView"
35-
33+
struct DynamicView {
3634
let context:HTMLContext
3735

38-
var head : some HTML {
39-
""
40-
}
41-
42-
var body : some HTML {
43-
h1 { context.heading }
44-
div(attributes: [.id(context.desc_id)]) {
45-
p { context.string }
46-
}
47-
h2 { context.user.details_heading }
48-
h3 { context.user.qualities_heading }
49-
ul(attributes: [.id(context.user.qualities_id)]) {
50-
for quality in context.user.qualities {
51-
li { quality }
36+
// Elementary puts the title element first in the head, which is wrong; it needs to be charset | this is a workaround
37+
@HTMLBuilder var rawHTML : some HTML {
38+
HTMLRaw("<!DOCTYPE html>")
39+
html {
40+
head {
41+
meta(.custom(name: "charset", value: context.charset))
42+
title { "DynamicView" }
43+
meta(.content(context.meta_description), .name("description"))
44+
meta(.content(context.keywords_string), .name("keywords"))
45+
}
46+
body {
47+
h1 { context.heading }
48+
div(attributes: [.id(context.desc_id)]) {
49+
p { context.string }
50+
}
51+
h2 { context.user.details_heading }
52+
h3 { context.user.qualities_heading }
53+
ul(attributes: [.id(context.user.qualities_id)]) {
54+
for quality in context.user.qualities {
55+
li { quality }
56+
}
57+
}
5258
}
5359
}
5460
}

Benchmarks/Benchmarks/Plot/Plot.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ package struct PlotTests : HTMLGenerator {
2424
package func dynamicHTML(_ context: Utilities.HTMLContext) -> String {
2525
return HTML(
2626
.head(
27-
.element(named: "title", text: "DynamicView")
27+
.meta(.attribute(named: "charset", value: context.charset)),
28+
.element(named: "title", text: "DynamicView"),
29+
.meta(.content(context.meta_description), .name("description")),
30+
.meta(.content(context.keywords_string), .name("keywords"))
2831
),
2932
.body(
3033
.component(H1(context.heading)),

Benchmarks/Benchmarks/SwiftHTMLBB/SwiftHTMLBB.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ package struct SwiftHTMLBBTests : HTMLGenerator {
3131
renderer.render(Document(.html) {
3232
Html {
3333
Head {
34+
Meta().charset(context.charset)
3435
Title("DynamicView")
36+
Meta().content(context.meta_description).name("description")
37+
Meta().content(context.keywords_string).name("keywords")
3538
}
3639
Body {
3740
H1(context.heading)

Benchmarks/Benchmarks/SwiftHTMLKit/SwiftHTMLKit.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ package struct SwiftHTMLKitTests : HTMLGenerator {
3131
var qualities:String = ""
3232
for quality in context.user.qualities {
3333
qualities += #li(["\(quality)"])
34-
//qualities += "<li>" + quality + "</li>"
3534
}
36-
//return "<!DOCTYPE html><html><body><h1>" + context.heading + "</h1><div id=\"" + context.desc_id + "\"><p>" + context.string + "</p></div><h2>" + context.user.details_heading + "</h2><h3>" + context.user.qualities_heading + "</h3><ul id=\"" + context.user.qualities_id + "\">" + qualities + "</ul></body></html>"
3735
return #html([
3836
#head([
39-
#title(["DynamicView"])
37+
#meta(charset: "\(context.charset)"),
38+
#title(["DynamicView"]),
39+
#meta(content: "\(context.meta_description)", name: "description"),
40+
#meta(content: "\(context.keywords_string)", name: "keywords")
4041
]),
4142
#body([
4243
#h1(["\(context.heading)"]),

Benchmarks/Benchmarks/SwiftHTMLPF/SwiftHTMLPF.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ package struct SwiftHTMLPFTests : HTMLGenerator {
2929
.document(
3030
.html(
3131
.head(
32-
.title("DynamicView")
32+
.meta(attributes: [.charset(.utf8)]),
33+
.title("DynamicView"),
34+
.meta(attributes: [.init("content", context.meta_description), .init("name", "description")]),
35+
.meta(attributes: [.init("content", context.keywords_string), .init("name", "keywords")])
3336
),
3437
.body(
3538
.h1(.raw(context.heading)),

Benchmarks/Benchmarks/Swim/Swim.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ extension Node {
1616
for (key, value) in attributes {
1717
attributes_string += " " + key + "=\"" + value + "\""
1818
}
19-
return (name == "html" ? "<!DOCTYPE html>" : "") + "<" + name + attributes_string + ">" + (child?.rendered ?? "") + "</" + name + ">"
19+
return (name == "html" ? "<!DOCTYPE html>" : "") + "<" + name + attributes_string + ">" + (child?.rendered ?? "") + (isVoid(name) ? "" : "</" + name + ">")
2020
case .text(let string): return string
2121
case .raw(let string): return string
2222
case .comment(_): return ""
@@ -30,6 +30,12 @@ extension Node {
3030
case .trim: return ""
3131
}
3232
}
33+
func isVoid(_ tag: String) -> Bool {
34+
switch tag {
35+
case "area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "source", "track", "wbr": return true
36+
default: return false
37+
}
38+
}
3339
}
3440

3541
package struct SwimTests : HTMLGenerator {
@@ -54,7 +60,10 @@ package struct SwimTests : HTMLGenerator {
5460
}
5561
return html {
5662
head {
63+
meta(charset: context.charset)
5764
title { "DynamicView" }
65+
meta(customAttributes: ["content":context.meta_description, "name":"description"])
66+
meta(customAttributes: ["content":context.keywords_string, "name":"keywords"])
5867
}
5968
body {
6069
h1 { context.heading }

Benchmarks/Benchmarks/UnitTests/UnitTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ struct UnitTests {
5050
@Test func dynamicHTML() {
5151
let context:HTMLContext = HTMLContext()
5252
let expected_value:String = libraries["SwiftHTMLKit"]!.dynamicHTML(context)
53+
// Plot closes void tags | Swim uses a dictionary for meta values | Vaux is doodoo
5354
for (key, value) in libraries {
5455
#expect(value.dynamicHTML(context) == expected_value, Comment(rawValue: key))
5556
}

Benchmarks/Benchmarks/Utilities/Utilities.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,26 @@ package actor Cache {
3535

3636
// MARK: HTMLContext
3737
package struct HTMLContext {
38+
package let charset:String, keywords:[String], meta_description:String
3839
package let heading:String, desc_id:String
3940
package let string:String, integer:Int, double:Double, float:Float, boolean:Bool
4041
package let now:Date
4142
package let user:User
4243

44+
package var keywords_string : String {
45+
var s:String = ""
46+
for keyword in keywords {
47+
s += "," + keyword
48+
}
49+
s.removeFirst()
50+
return s
51+
}
52+
4353
package init() {
54+
charset = "utf-8"
55+
keywords = ["swift", "html", "benchmark"]
56+
meta_description = "This website is to benchmark the performance of different Swift DSL libraries."
57+
4458
heading = "Dynamic HTML Benchmark"
4559
desc_id = "desc"
4660
// 1 paragraph of lorem ipsum

Benchmarks/Benchmarks/VaporHTMLKit/VaporHTMLKit.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ struct DynamicView : View {
4646
Document(.html5)
4747
Html {
4848
Head {
49+
Meta().charset(.utf8) // TODO: fix? | not dynamic
4950
Title { "DynamicView" }
51+
Meta().content(context.meta_description).name(.description)
52+
Meta().content(context.keywords_string).name(.keywords)
5053
}
5154
Body {
5255
Heading1 { context.heading }

Benchmarks/Benchmarks/Vaux/Vaux.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Utilities
99
import Vaux
1010
import Foundation
1111

12+
// MARK: Custom rendering
1213
extension HTML {
1314
func render(includeTag: Bool) -> (HTMLType, String) {
1415
if let node:HTMLNode = self as? HTMLNode {
@@ -25,6 +26,12 @@ extension HTML {
2526
return (.node, String(describing: self))
2627
}
2728
}
29+
func isVoid(_ tag: String) -> Bool {
30+
switch tag {
31+
case "area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "source", "track", "wbr": return true
32+
default: return false
33+
}
34+
}
2835
}
2936

3037
enum HTMLType {
@@ -35,7 +42,7 @@ extension AttributedNode {
3542
var render : String {
3643
let tag:String = child.getTag()!
3744
let attribute_string:String = " " + attribute.key + (attribute.value != nil ? "=\"" + attribute.value! + "\"" : "")
38-
return "<" + tag + attribute_string + ">" + child.render(includeTag: false).1 + "</" + tag + ">"
45+
return "<" + tag + attribute_string + ">" + child.render(includeTag: false).1 + (isVoid(tag) ? "" : "</" + tag + ">")
3946
}
4047
}
4148

@@ -54,10 +61,11 @@ extension HTMLNode {
5461
break
5562
}
5663
}
57-
return (tag == "html" ? "<!DOCTYPE html>" : "") + (includeTag ? "<" + tag + attributes + ">" : "") + children + (includeTag ? "</" + tag + ">" : "") // Vaux doesn't take void elements into account
64+
return (tag == "html" ? "<!DOCTYPE html>" : "") + (includeTag ? "<" + tag + attributes + ">" : "") + children + (!isVoid(tag) && includeTag ? "</" + tag + ">" : "")
5865
}
5966
}
6067

68+
// MARK: VauxTests
6169
package struct VauxTests : HTMLGenerator {
6270
package init() {}
6371

@@ -77,7 +85,10 @@ package struct VauxTests : HTMLGenerator {
7785
package func dynamicHTML(_ context: HTMLContext) -> String {
7886
html {
7987
head {
88+
meta().attr("charset", context.charset)
8089
title("DynamicView")
90+
meta().attr("content", context.meta_description).attr("name", "description")
91+
meta().attr("content", context.keywords_string).attr("name", "keywords")
8192
}
8293
body {
8394
heading(.h1) { context.heading }

Benchmarks/img/throughput_dynamic.png

4.87 KB
Loading

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,16 +148,16 @@ Use the `HTMLElementAttribute.event(<type>, "<value>")`.
148148

149149
## Benchmarks
150150
- Libraries tested
151-
- [BinaryBuilds/swift-html](https://github.com/BinaryBirds/swift-html) v1.7.0 (patched version [here](https://github.com/RandomHashTags/fork-bb-swift-html))
151+
- [BinaryBuilds/swift-html](https://github.com/BinaryBirds/swift-html) v1.7.0 (patched version [here](https://github.com/RandomHashTags/fork-bb-swift-html); custom renderer [here](https://github.com/RandomHashTags/swift-htmlkit/blob/main/Benchmarks/Benchmarks/Swim/Swim.swift))
152152
- [sliemeobn/elementary](https://github.com/sliemeobn/elementary) v0.3.4
153153
- [JohnSundell/Plot](https://github.com/JohnSundell/Plot) v0.14.0
154154
- [RandomHashTags/swift-htmlkit](https://github.com/RandomHashTags/swift-htmlkit) v0.5.0 (this library)
155155
- [pointfreeco/swift-html](https://github.com/pointfreeco/swift-html) v0.4.1
156156
- [robb/Swim](https://github.com/robb/Swim) v0.4.0 (patched version [here](https://github.com/RandomHashTags/fork-Swim))
157157
- [vapor-community/HTMLKit](https://github.com/vapor-community/HTMLKit) v2.8.1
158-
- [dokun1/Vaux](https://github.com/dokun1/Vaux) v0.2.0 (patched version [here](https://github.com/RandomHashTags/fork-Vaux))
158+
- [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))
159159

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

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

0 commit comments

Comments
 (0)