Skip to content

Commit db132d4

Browse files
tried adding 2 more libraries to benchmark against
- removed some code left behind when testing different dynamic implementations - made deprecated message when using `HTMLElementAttribute.event` shorter
1 parent 8d09890 commit db132d4

File tree

7 files changed

+141
-40
lines changed

7 files changed

+141
-40
lines changed

Benchmarks/Benchmarks/Benchmarks/Benchmarks.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Utilities
1010

1111
import TestElementary
1212
import TestPlot
13+
import TestSwiftDOM
1314
import TestSwiftHTMLBB
1415
import TestSwiftHTMLKit
1516
import TestSwiftHTMLPF
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//
2+
// SwiftDOM.swift
3+
//
4+
//
5+
// Created by Evan Anderson on 10/13/24.
6+
//
7+
8+
/*
9+
import Utilities
10+
import DOM
11+
import HTML
12+
13+
package struct SwiftDOMTests : HTMLGenerator {
14+
package init() {}
15+
16+
package func staticHTML() -> String {
17+
let document:HTML = .document {
18+
$0[.html] {
19+
$0[.head] { $0[.title] = "StaticView" }
20+
$0[.body] { $0[.h1] = "Swift HTML Benchmarks" }
21+
}
22+
}
23+
return "\(document)"
24+
}
25+
package func dynamicHTML(_ context: HTMLContext) -> String {
26+
let qualities:(inout DOM.HTML.ContentEncoder) throws -> () = {
27+
for quality in context.user.qualities {
28+
$0[.li] = quality
29+
}
30+
}
31+
let document:HTML = .document {
32+
$0[.html] {
33+
$0[.head] {
34+
$0[.meta] { $0[name: .charset] = context.charset }
35+
$0[.title] { $0[name: .title] = context.title }
36+
$0[.meta] {
37+
$0[name: .content] = context.meta_description
38+
$0[name: .name] = "description"
39+
}
40+
$0[.meta] {
41+
$0[name: .content] = context.keywords_string
42+
$0[name: .name] = "keywords"
43+
}
44+
}
45+
$0[.body] {
46+
$0[.h1] = context.heading
47+
$0[.div] {
48+
$0.id = context.desc_id
49+
} content: {
50+
$0[.p] = context.string
51+
}
52+
$0[.h2] = context.user.details_heading
53+
$0[.h3] = context.user.qualities_heading
54+
$0[.ul] {
55+
$0.id = context.user.qualities_id
56+
} content: {
57+
try! qualities(&$0)
58+
}
59+
}
60+
}
61+
}
62+
return "\(document)"
63+
}
64+
}*/
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// Tokamak.swift
3+
//
4+
//
5+
// Created by Evan Anderson on 10/13/24.
6+
//
7+
8+
import Utilities
9+
//import TokamakDOM
10+
//import TokamakStaticHTML
11+
12+
package struct TokamakTests : HTMLGenerator {
13+
package init() {}
14+
15+
package func staticHTML() -> String {
16+
""//HTML()
17+
}
18+
package func dynamicHTML(_ context: HTMLContext) -> String {
19+
""
20+
}
21+
}

Benchmarks/Benchmarks/UnitTests/UnitTests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import SwiftHTMLKit
1111

1212
import TestElementary
1313
import TestPlot
14+
import TestSwiftDOM
1415
import TestSwiftHTMLBB
1516
import TestSwiftHTMLKit
1617
import TestSwiftHTMLPF
@@ -25,6 +26,7 @@ struct UnitTests {
2526
"Elementary" : ElementaryTests(),
2627
"Plot" : PlotTests(),
2728
"Pointfreeco" : SwiftHTMLPFTests(),
29+
//"SwiftDOM" : SwiftDOMTests(),
2830
"SwiftHTMLKit" : SwiftHTMLKitTests(),
2931
"Swim" : SwimTests(),
3032
"VaporHTMLKit" : VaporHTMLKitTests(),

Benchmarks/Package.swift

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ let package = Package(
1313
.package(url: "https://github.com/ordo-one/package-benchmark", from: "1.27.0"),
1414
.package(url: "https://github.com/swiftlang/swift-syntax", from: "600.0.0"),
1515
.package(name: "swift-htmlkit", path: "../"),
16-
//.package(url: "https://github.com/RandomHashTags/swift-htmlkit", from: "0.5.0"),
1716
.package(url: "https://github.com/sliemeobn/elementary", from: "0.4.0"),
1817
.package(url: "https://github.com/vapor-community/HTMLKit", from: "2.8.1"),
1918
.package(url: "https://github.com/pointfreeco/swift-html", from: "0.4.1"),
@@ -22,6 +21,9 @@ let package = Package(
2221
//.package(url: "https://github.com/toucansites/toucan", from: "1.0.0-alpha.1"), // unstable
2322
.package(url: "https://github.com/RandomHashTags/fork-Swim", branch: "main"),
2423
.package(url: "https://github.com/RandomHashTags/fork-Vaux", branch: "master"),
24+
//.package(url: "https://github.com/tayloraswift/swift-dom", from: "1.1.0"), // bad exports
25+
//.package(url: "https://github.com/TokamakUI/Tokamak", from: "0.11.1"), // swift-benchmark problem
26+
2527
.package(url: "https://github.com/vapor/leaf", from: "4.4.0"),
2628

2729
// networking
@@ -57,28 +59,37 @@ let package = Package(
5759
],
5860
path: "Benchmarks/Plot"
5961
),
62+
.target(
63+
name: "TestSwiftDOM",
64+
dependencies: [
65+
"Utilities",
66+
//.product(name: "DOM", package: "swift-dom", moduleAliases: ["DOM":"SwiftDOM"]),
67+
//.product(name: "HTML", package: "swift-dom", moduleAliases: ["HTML":"SwiftDOMHTML"])
68+
],
69+
path: "Benchmarks/SwiftDOM"
70+
),
6071
.target(
6172
name: "TestSwiftHTMLBB",
6273
dependencies: [
6374
"Utilities",
64-
.product(name: "SwiftHtml", package: "fork-bb-swift-html")
75+
.product(name: "SwiftHtml", package: "fork-bb-swift-html", moduleAliases: ["SwiftHtml":"SwiftHTMLBB"])
6576
],
6677
path: "Benchmarks/SwiftHTMLBB"
6778
),
6879
.target(
6980
name: "TestSwiftHTMLKit",
7081
dependencies: [
7182
"Utilities",
72-
.product(name: "HTMLKit", package: "swift-htmlkit", moduleAliases: ["HTMLKit" : "SwiftHTMLKit"]),
73-
.product(name: "HTMLKit", package: "HTMLKit", moduleAliases: ["HTMLKit" : "VaporHTMLKit"])
83+
.product(name: "HTMLKit", package: "swift-htmlkit", moduleAliases: ["HTMLKit":"SwiftHTMLKit"]),
84+
.product(name: "HTMLKit", package: "HTMLKit", moduleAliases: ["HTMLKit":"VaporHTMLKit"]),
7485
],
7586
path: "Benchmarks/SwiftHTMLKit"
7687
),
7788
.target(
7889
name: "TestSwiftHTMLPF",
7990
dependencies: [
8091
"Utilities",
81-
.product(name: "Html", package: "swift-html")
92+
.product(name: "Html", package: "swift-html", moduleAliases: ["Html":"SwiftHTMLPF"])
8293
],
8394
path: "Benchmarks/SwiftHTMLPF"
8495
),
@@ -90,6 +101,15 @@ let package = Package(
90101
],
91102
path: "Benchmarks/Swim"
92103
),
104+
.target(
105+
name: "TestTokamak",
106+
dependencies: [
107+
"Utilities",
108+
//.product(name: "TokamakDOM", package: "Tokamak"),
109+
//.product(name: "TokamakStaticHTML", package: "Tokamak")
110+
],
111+
path: "Benchmarks/Tokamak"
112+
),
93113
.target(
94114
name: "TestToucan",
95115
dependencies: [
@@ -101,8 +121,8 @@ let package = Package(
101121
name: "TestVaporHTMLKit",
102122
dependencies: [
103123
"Utilities",
104-
.product(name: "HTMLKit", package: "swift-htmlkit", moduleAliases: ["HTMLKit" : "SwiftHTMLKit"]),
105-
.product(name: "HTMLKit", package: "HTMLKit", moduleAliases: ["HTMLKit" : "VaporHTMLKit"])
124+
.product(name: "HTMLKit", package: "HTMLKit", moduleAliases: ["HTMLKit":"VaporHTMLKit"]),
125+
.product(name: "HTMLKit", package: "swift-htmlkit", moduleAliases: ["HTMLKit":"SwiftHTMLKit"])
106126
],
107127
path: "Benchmarks/VaporHTMLKit"
108128
),
@@ -122,6 +142,7 @@ let package = Package(
122142
"TestElementary",
123143
"TestLeaf",
124144
"TestPlot",
145+
"TestSwiftDOM",
125146
"TestSwiftHTMLBB",
126147
"TestSwiftHTMLKit",
127148
"TestSwiftHTMLPF",
@@ -145,6 +166,7 @@ let package = Package(
145166
"TestElementary",
146167
"TestLeaf",
147168
"TestPlot",
169+
"TestSwiftDOM",
148170
"TestSwiftHTMLBB",
149171
"TestSwiftHTMLKit",
150172
"TestSwiftHTMLPF",
@@ -164,6 +186,7 @@ let package = Package(
164186
"TestElementary",
165187
"TestLeaf",
166188
"TestPlot",
189+
"TestSwiftDOM",
167190
"TestSwiftHTMLBB",
168191
"TestSwiftHTMLKit",
169192
"TestSwiftHTMLPF",

Sources/HTMLKitMacros/HTMLElement.swift

Lines changed: 22 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,19 @@ import HTMLKitUtilities
1212

1313
struct HTMLElement : ExpressionMacro {
1414
static func expansion(of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) throws -> ExprSyntax {
15-
var dynamicVariables:[String] = []
16-
let (string, isDynamic):(String, Bool) = parse_macro(context: context, expression: node.as(MacroExpansionExprSyntax.self)!, isRoot: true, dynamicVariables: &dynamicVariables)
17-
return isDynamic ? "\(raw: string)" : "\"\(raw: string)\""
15+
return "\"\(raw: parse_macro(context: context, expression: node.as(MacroExpansionExprSyntax.self)!))\""
1816
}
1917
}
2018

2119
private extension HTMLElement {
22-
static func parse_macro(context: some MacroExpansionContext, expression: MacroExpansionExprSyntax, isRoot: Bool, dynamicVariables: inout [String]) -> (String, Bool) {
23-
guard let elementType:HTMLElementType = HTMLElementType(rawValue: expression.macroName.text) else { return ("\(expression)", true) }
20+
static func parse_macro(context: some MacroExpansionContext, expression: MacroExpansionExprSyntax) -> String {
21+
guard let elementType:HTMLElementType = HTMLElementType(rawValue: expression.macroName.text) else { return "\(expression)" }
2422
let childs:SyntaxChildren = expression.arguments.children(viewMode: .all)
2523
if elementType == .escapeHTML {
26-
return (childs.compactMap({
24+
return childs.compactMap({
2725
guard let child:LabeledExprSyntax = $0.labeled else { return nil }
28-
return parse_inner_html(context: context, elementType: elementType, child: child, dynamicVariables: &dynamicVariables)
29-
}).joined(), false)
26+
return parse_inner_html(context: context, elementType: elementType, child: child)
27+
}).joined()
3028
}
3129
let tag:String, isVoid:Bool
3230
var children:Slice<SyntaxChildren>
@@ -40,51 +38,44 @@ private extension HTMLElement {
4038
isVoid = elementType.isVoid
4139
children = childs.prefix(childs.count)
4240
}
43-
let data:ElementData = parse_arguments(context: context, elementType: elementType, children: children, dynamicVariables: &dynamicVariables)
41+
let data:ElementData = parse_arguments(context: context, elementType: elementType, children: children)
4442
var string:String = (elementType == .html ? "<!DOCTYPE html>" : "") + "<" + tag + data.attributes + ">" + data.innerHTML
4543
if !isVoid {
4644
string += "</" + tag + ">"
4745
}
48-
return (string, false)
49-
50-
/*if isRoot {
51-
return dynamicVariables.isEmpty ? (string, false) : ("DynamicString(string: \"" + string + "\").test", true)
52-
//return dynamicVariables.isEmpty ? (string, false) : ("DynamicString(string: \"" + string + "\", values: [" + dynamicVariables.map({ $0.contains("\\(") ? "\"\($0)\"" : $0 }).joined(separator: ",") + "]).test", true)
53-
} else {
54-
return (string, false)
55-
}*/
46+
return string
5647
}
57-
static func parse_arguments(context: some MacroExpansionContext, elementType: HTMLElementType, children: Slice<SyntaxChildren>, dynamicVariables: inout [String]) -> ElementData {
48+
static func parse_arguments(context: some MacroExpansionContext, elementType: HTMLElementType, children: Slice<SyntaxChildren>) -> ElementData {
5849
var attributes:[String] = [], innerHTML:[String] = []
5950
for element in children {
6051
if let child:LabeledExprSyntax = element.labeled {
6152
if var key:String = child.label?.text {
6253
if key == "attributes" {
63-
attributes.append(contentsOf: parse_global_attributes(context: context, elementType: elementType, array: child.expression.array!, dynamicVariables: &dynamicVariables))
54+
attributes.append(contentsOf: parse_global_attributes(context: context, elementType: elementType, array: child.expression.array!))
6455
} else {
6556
if key == "acceptCharset" {
6657
key = "accept-charset"
6758
}
68-
if let string:String = parse_attribute(context: context, elementType: elementType, key: key, argument: child, dynamicVariables: &dynamicVariables) {
59+
if let string:String = parse_attribute(context: context, elementType: elementType, key: key, argument: child) {
6960
attributes.append(key + (string.isEmpty ? "" : "=\\\"" + string + "\\\""))
7061
}
7162
}
7263
// inner html
73-
} else if let inner_html:String = parse_inner_html(context: context, elementType: elementType, child: child, dynamicVariables: &dynamicVariables) {
64+
} else if let inner_html:String = parse_inner_html(context: context, elementType: elementType, child: child) {
7465
innerHTML.append(inner_html)
7566
}
7667
}
7768
}
7869
return ElementData(attributes: attributes, innerHTML: innerHTML)
7970
}
80-
static func parse_global_attributes(context: some MacroExpansionContext, elementType: HTMLElementType, array: ArrayExprSyntax, dynamicVariables: inout [String]) -> [String] {
71+
static func parse_global_attributes(context: some MacroExpansionContext, elementType: HTMLElementType, array: ArrayExprSyntax) -> [String] {
8172
var keys:Set<String> = [], attributes:[String] = []
8273
for element in array.elements {
8374
let function:FunctionCallExprSyntax = element.expression.as(FunctionCallExprSyntax.self)!, key_argument:LabeledExprSyntax = function.arguments.first!, key_element:ExprSyntax = key_argument.expression
8475
var key:String = function.calledExpression.memberAccess!.declName.baseName.text, value:String? = nil
8576
switch key {
8677
case "custom", "data":
87-
var (literalValue, returnType):(String, LiteralReturnType) = parse_literal_value(context: context, elementType: elementType, key: key, argument: function.arguments.last!, dynamicVariables: &dynamicVariables)!
78+
var (literalValue, returnType):(String, LiteralReturnType) = parse_literal_value(context: context, elementType: elementType, key: key, argument: function.arguments.last!)!
8879
if returnType == .string {
8980
literalValue.escapeHTML(escapeAttributes: true)
9081
}
@@ -97,7 +88,7 @@ private extension HTMLElement {
9788
break
9889
case "event":
9990
key = "on" + key_element.memberAccess!.declName.baseName.text
100-
if var (literalValue, returnType):(String, LiteralReturnType) = parse_literal_value(context: context, elementType: elementType, key: key, argument: function.arguments.last!, dynamicVariables: &dynamicVariables) {
91+
if var (literalValue, returnType):(String, LiteralReturnType) = parse_literal_value(context: context, elementType: elementType, key: key, argument: function.arguments.last!) {
10192
if returnType == .string {
10293
literalValue.escapeHTML(escapeAttributes: true)
10394
}
@@ -108,7 +99,7 @@ private extension HTMLElement {
10899
}
109100
break
110101
default:
111-
if let string:String = parse_attribute(context: context, elementType: elementType, key: key, argument: key_argument, dynamicVariables: &dynamicVariables) {
102+
if let string:String = parse_attribute(context: context, elementType: elementType, key: key, argument: key_argument) {
112103
value = string
113104
}
114105
break
@@ -126,14 +117,14 @@ private extension HTMLElement {
126117
}
127118
return attributes
128119
}
129-
static func parse_inner_html(context: some MacroExpansionContext, elementType: HTMLElementType, child: LabeledExprSyntax, dynamicVariables: inout [String]) -> String? {
120+
static func parse_inner_html(context: some MacroExpansionContext, elementType: HTMLElementType, child: LabeledExprSyntax) -> String? {
130121
if let macro:MacroExpansionExprSyntax = child.expression.macroExpansion {
131-
var string:String = parse_macro(context: context, expression: macro, isRoot: false, dynamicVariables: &dynamicVariables).0
122+
var string:String = parse_macro(context: context, expression: macro)
132123
if elementType == .escapeHTML {
133124
string.escapeHTML(escapeAttributes: false)
134125
}
135126
return string
136-
} else if var string:String = parse_literal_value(context: context, elementType: elementType, key: "", argument: child, dynamicVariables: &dynamicVariables)?.value {
127+
} else if var string:String = parse_literal_value(context: context, elementType: elementType, key: "", argument: child)?.value {
137128
string.escapeHTML(escapeAttributes: false)
138129
return string
139130
} else {
@@ -171,9 +162,9 @@ private extension HTMLElement {
171162
}
172163
}
173164

174-
static func parse_attribute(context: some MacroExpansionContext, elementType: HTMLElementType, key: String, argument: LabeledExprSyntax, dynamicVariables: inout [String]) -> String? {
165+
static func parse_attribute(context: some MacroExpansionContext, elementType: HTMLElementType, key: String, argument: LabeledExprSyntax) -> String? {
175166
let expression:ExprSyntax = argument.expression
176-
if var (string, returnType):(String, LiteralReturnType) = parse_literal_value(context: context, elementType: elementType, key: key, argument: argument, dynamicVariables: &dynamicVariables) {
167+
if var (string, returnType):(String, LiteralReturnType) = parse_literal_value(context: context, elementType: elementType, key: key, argument: argument) {
177168
switch returnType {
178169
case .boolean: return string.elementsEqual("true") ? "" : nil
179170
case .string:
@@ -198,7 +189,7 @@ private extension HTMLElement {
198189
default: return " "
199190
}
200191
}
201-
static func parse_literal_value(context: some MacroExpansionContext, elementType: HTMLElementType, key: String, argument: LabeledExprSyntax, dynamicVariables: inout [String]) -> (value: String, returnType: LiteralReturnType)? {
192+
static func parse_literal_value(context: some MacroExpansionContext, elementType: HTMLElementType, key: String, argument: LabeledExprSyntax) -> (value: String, returnType: LiteralReturnType)? {
202193
let expression:ExprSyntax = argument.expression
203194
if let boolean:String = expression.booleanLiteral?.literal.text {
204195
return (boolean, .boolean)
@@ -286,7 +277,6 @@ private extension HTMLElement {
286277
}
287278
}
288279
if returnType == .interpolation || remaining_interpolation > 0 {
289-
//dynamicVariables.append(string)
290280
if !string.contains("\\(") {
291281
string = "\\(" + string + ")"
292282
}

Sources/HTMLKitUtilities/HTMLKitUtilities.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public enum HTMLElementAttribute {
118118

119119
case custom(_ id: any ExpressibleByStringLiteral, _ value: (any ExpressibleByStringLiteral)?)
120120

121-
@available(*, deprecated, message: "Inline event handlers are an outdated way to handle events.\nGeneral consensus considers this \"bad practice\" and you shouldn't mix your HTML and JavaScript.\n\nThis will never be removed and remains deprecated to encourage use of other techniques.\n\nLearn more at https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#inline_event_handlers_—_dont_use_these.")
121+
@available(*, deprecated, message: "General consensus considers this \"bad practice\" and you shouldn't mix your HTML and JavaScript. This will never be removed and 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.")
122122
case event(Extra.event, _ value: (any ExpressibleByStringLiteral)? = nil)
123123
}
124124
public extension HTMLElementAttribute {

0 commit comments

Comments
 (0)