Skip to content

Commit cd092e7

Browse files
fixes
1 parent 2095b96 commit cd092e7

File tree

11 files changed

+70
-43
lines changed

11 files changed

+70
-43
lines changed

Sources/HTMLKit/HTMLKit.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@
77

88
@_exported import HTMLKitUtilities
99

10-
#if canImport(Foundation)
11-
import struct Foundation.Data
12-
#endif
13-
1410
// MARK: StaticString equality
1511
public extension StaticString {
1612
static func == (left: Self, right: Self) -> Bool { left.description == right.description }

Sources/HTMLKitUtilities/HTMLElementAttribute.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public enum HTMLElementAttribute : Hashable {
7777
}
7878
func array_string() -> [String] { expression.array?.elements.compactMap({ $0.expression.stringLiteral?.string }) ?? [] }
7979
func float() -> Float? {
80-
guard let s:String = expression.floatLiteral?.literal.text else { return nil }
80+
guard let s:String = expression.integerLiteral?.literal.text ?? expression.floatLiteral?.literal.text else { return nil }
8181
return Float(s)
8282
}
8383
switch key {
@@ -88,7 +88,15 @@ public enum HTMLElementAttribute : Hashable {
8888
case "autofocus": self = .autofocus(boolean())
8989
case "class": self = .class(array_string())
9090
case "contenteditable": self = .contenteditable(enumeration())
91-
case "data": self = .data("", "") // TODO: fix
91+
case "data", "custom":
92+
guard let id:String = string(), let value:String = function.arguments.last?.expression.stringLiteral?.string else {
93+
return nil
94+
}
95+
if key == "data" {
96+
self = .data(id, value)
97+
} else {
98+
self = .custom(id, value)
99+
}
92100
case "dir": self = .dir(enumeration())
93101
case "draggable": self = .draggable(enumeration())
94102
case "enterkeyhint": self = .enterkeyhint(enumeration())
@@ -117,8 +125,11 @@ public enum HTMLElementAttribute : Hashable {
117125
case "writingsuggestions": self = .writingsuggestions(enumeration())
118126
case "trailingSlash": self = .trailingSlash
119127
case "htmx": self = .htmx(enumeration())
120-
case "custom": self = .custom("", "") // TODO: fix
121-
case "event": self = .event(.click, "") // TODO: fix
128+
case "event":
129+
guard let event:HTMLElementAttribute.Extra.event = enumeration(), let value:String = function.arguments.last?.expression.stringLiteral?.string else {
130+
return nil
131+
}
132+
self = .event(event, value)
122133
default: return nil
123134
}
124135
}

Sources/HTMLKitUtilities/HTMLElementAttributeExtra.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ extension HTMLElementAttribute {
6666
case "formmethod": return get(formmethod.self)
6767
case "formtarget": return get(formtarget.self)
6868
case "hidden": return get(hidden.self)
69-
case "httpEquiv": return get(httpequiv.self)
69+
case "httpequiv": return get(httpequiv.self)
7070
case "inputmode": return get(inputmode.self)
7171
case "inputtype": return get(inputtype.self)
7272
case "kind": return get(kind.self)
@@ -89,6 +89,9 @@ extension HTMLElementAttribute {
8989
case "virtualkeyboardpolicy": return get(virtualkeyboardpolicy.self)
9090
case "wrap": return get(wrap.self)
9191
case "writingsuggestions": return get(writingsuggestions.self)
92+
93+
case "width": return get(width.self)
94+
case "height": return get(height.self)
9295
default: return nil
9396
}
9497
}
@@ -191,7 +194,7 @@ public extension HTMLElementAttribute.Extra {
191194
}
192195
func array_string() -> [String] { expression.array?.elements.compactMap({ $0.expression.stringLiteral?.string }) ?? [] }
193196
func float() -> Float? {
194-
guard let s:String = expression.floatLiteral?.literal.text else { return nil }
197+
guard let s:String = expression.integerLiteral?.literal.text ?? expression.floatLiteral?.literal.text else { return nil }
195198
return Float(s)
196199
}
197200
switch key {

Sources/HTMLKitUtilities/HTMLEncoding.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
/// ### Interpolation Promotion
1111
/// Swift HTMLKit tries to [promote](https://github.com/RandomHashTags/swift-htmlkit/blob/94793984763308ef5275dd9f71ea0b5e83fea417/Sources/HTMLKitMacros/HTMLElement.swift#L423) known interpolation at compile time with an equivalent string literal for the best performance, regardless of encoding.
1212
/// It is currently limited due to macro expansions being sandboxed and lexical contexts/AST not being available for macro arguments.
13-
/// This means referencing content known at compile time in a html macro won't get promoted to its expected value.
13+
/// This means referencing content in an html macro won't get promoted to its expected value.
1414
/// [Read more about this limitation](https://forums.swift.org/t/swift-lexical-lookup-for-referenced-stuff-located-outside-scope-current-file/75776/6).
1515
///
1616
/// #### Promotion Example
@@ -26,7 +26,7 @@
2626
/// ```swift
2727
/// let string:StaticString = "Test"
2828
/// let _:StaticString = #html(div(string)) // ❌ promotion cannot be applied; StaticString not allowed
29-
/// let _:String = #html(div(string)) // ⚠️ promotion cannot be applied; compiled as "<div>\(string)</div>"
29+
/// let _:String = #html(div(string)) // ⚠️ promotion cannot be applied; compiles to "<div>\(string)</div>"
3030
/// ```
3131
///
3232
public enum HTMLEncoding {
@@ -62,4 +62,16 @@ public enum HTMLEncoding {
6262
/// ```
6363
///
6464
case custom(_ logic: String)
65+
66+
public init?(rawValue: String) {
67+
switch rawValue {
68+
case "string": self = .string
69+
case "utf8Bytes": self = .utf8Bytes
70+
case "utf8CString": self = .utf8CString
71+
case "utf16Bytes": self = .utf16Bytes
72+
case "foundationData": self = .foundationData
73+
case "byteBuffer": self = .byteBuffer
74+
default: return nil
75+
}
76+
}
6577
}

Sources/HTMLKitUtilities/HTMLKitUtilities.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,9 @@ public extension HTMLElementAttribute {
104104
case percent(_ value: Float?)
105105

106106
public init?(key: String, arguments: LabeledExprListSyntax) {
107+
let expression:ExprSyntax = arguments.first!.expression
107108
func float() -> Float? {
108-
guard let s:String = arguments.first!.expression.floatLiteral?.literal.text else { return nil }
109+
guard let s:String = expression.integerLiteral?.literal.text ?? expression.floatLiteral?.literal.text else { return nil }
109110
return Float(s)
110111
}
111112
switch key {
@@ -168,7 +169,8 @@ public extension HTMLElementAttribute {
168169
.viewportMin(let v),
169170
.viewportMax(let v),
170171
.percent(let v):
171-
return String(describing: v)
172+
guard let v:Float = v else { return nil }
173+
return String(describing: v) + suffix
172174
}
173175
}
174176

Sources/HTMLKitUtilities/HTMX.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public extension HTMLElementAttribute {
6969
}
7070
func array_string() -> [String] { expression.array?.elements.compactMap({ $0.expression.stringLiteral?.string }) ?? [] }
7171
func float() -> Float? {
72-
guard let s:String = expression.floatLiteral?.literal.text else { return nil }
72+
guard let s:String = expression.integerLiteral?.literal.text ?? expression.floatLiteral?.literal.text else { return nil }
7373
return Float(s)
7474
}
7575
switch key {
@@ -240,21 +240,21 @@ public extension HTMLElementAttribute {
240240
case .boost(let value): return value?.rawValue
241241
case .confirm(let value): return value
242242
case .delete(let value): return value
243-
case .disable(_): return ""
243+
case .disable(let value): return value ?? false ? "" : nil
244244
case .disabledElt(let value): return value
245245
case .disinherit(let value): return value
246246
case .encoding(let value): return value
247247
case .ext(let value): return value
248248
case .headers(let js, let headers):
249249
return (js ? "js:" : "") + "{" + headers.map({ "\\\"" + $0.key + "\\\":\\\"" + $0.value + "\\\"" }).joined(separator: ",") + "}"
250250
case .history(let value): return value?.rawValue
251-
case .historyElt(_): return ""
251+
case .historyElt(let value): return value ?? false ? "" : nil
252252
case .include(let value): return value
253253
case .indicator(let value): return value
254254
case .inherit(let value): return value
255255
case .params(let params): return params?.htmlValue
256256
case .patch(let value): return value
257-
case .preserve(_): return ""
257+
case .preserve(let value): return value ?? false ? "" : nil
258258
case .prompt(let value): return value
259259
case .put(let value): return value
260260
case .replaceURL(let url): return url?.htmlValue

Sources/HTMLKitUtilities/LiteralElements.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public struct custom : HTMLElement {
147147
public var attributes:[HTMLElementAttribute]
148148
public var innerHTML:[CustomStringConvertible]
149149

150-
public init(context: some MacroExpansionContext, _ children: SyntaxChildren) { // TODO: fix
150+
public init(context: some MacroExpansionContext, _ children: SyntaxChildren) {
151151
let data:HTMLKitUtilities.ElementData = HTMLKitUtilities.parse_arguments(context: context, children: children)
152152
tag = data.attributes["tag"] as? String ?? ""
153153
isVoid = data.attributes["isVoid"] as? Bool ?? false

Sources/HTMLKitUtilities/ParseData.swift

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ public extension HTMLKitUtilities {
1313
// MARK: Parse Arguments
1414
static func parse_arguments(
1515
context: some MacroExpansionContext,
16-
children: SyntaxChildren
16+
children: SyntaxChildren,
17+
otherAttributes: [String:String] = [:]
1718
) -> ElementData {
1819
var encoding:HTMLEncoding = HTMLEncoding.string
1920
var global_attributes:[HTMLElementAttribute] = []
@@ -26,14 +27,7 @@ public extension HTMLKitUtilities {
2627
if var key:String = child.label?.text {
2728
if key == "encoding" {
2829
if let key:String = child.expression.memberAccess?.declName.baseName.text {
29-
switch key {
30-
case "string": encoding = .string
31-
case "utf8Bytes": encoding = .utf8Bytes
32-
case "utf16Bytes": encoding = .utf16Bytes
33-
case "foundationData": encoding = .foundationData
34-
case "byteBuffer": encoding = .byteBuffer
35-
default: break
36-
}
30+
encoding = HTMLEncoding(rawValue: key) ?? .string
3731
} else if let custom:FunctionCallExprSyntax = child.expression.functionCall {
3832
encoding = .custom(custom.arguments.first!.expression.stringLiteral!.string)
3933
}
@@ -42,10 +36,13 @@ public extension HTMLKitUtilities {
4236
} else if key == "attributes" {
4337
(global_attributes, trailingSlash) = parse_global_attributes(context: context, array: child.expression.array!.elements, lookupFiles: lookupFiles)
4438
} else {
39+
var target_key:String = key
4540
if key == "acceptCharset" {
4641
key = "accept-charset"
42+
} else if let target:String = otherAttributes[key] {
43+
target_key = target
4744
}
48-
if let test:any HTMLInitializable = HTMLElementAttribute.Extra.parse(key: key, expr: child.expression) {
45+
if let test:any HTMLInitializable = HTMLElementAttribute.Extra.parse(key: target_key, expr: child.expression) {
4946
attributes[key] = test
5047
} else if let string:LiteralReturnType = parse_literal_value(context: context, key: key, expression: child.expression, lookupFiles: lookupFiles) {
5148
switch string {
@@ -77,13 +74,14 @@ public extension HTMLKitUtilities {
7774
for element in array {
7875
if let function:FunctionCallExprSyntax = element.expression.functionCall {
7976
let first_expression:ExprSyntax = function.arguments.first!.expression
80-
let key:String = function.calledExpression.memberAccess!.declName.baseName.text
77+
var key:String = function.calledExpression.memberAccess!.declName.baseName.text
8178
if key.contains(" ") {
8279
context.diagnose(Diagnostic(node: first_expression, message: DiagnosticMsg(id: "spacesNotAllowedInAttributeDeclaration", message: "Spaces are not allowed in attribute declaration.")))
8380
} else if keys.contains(key) {
8481
global_attribute_already_defined(context: context, attribute: key, node: first_expression)
8582
} else if let attr:HTMLElementAttribute = HTMLElementAttribute.init(key: key, function) {
8683
attributes.append(attr)
84+
key = attr.key
8785
keys.insert(key)
8886
}
8987
} else if let member:String = element.expression.memberAccess?.declName.baseName.text, member == "trailingSlash" {
@@ -98,8 +96,7 @@ public extension HTMLKitUtilities {
9896
return (attributes, trailingSlash)
9997
}
10098
static func global_attribute_already_defined(context: some MacroExpansionContext, attribute: String, node: some SyntaxProtocol) {
101-
// TODO: reenable
102-
//context.diagnose(Diagnostic(node: node, message: DiagnosticMsg(id: "globalAttributeAlreadyDefined", message: "Global attribute \"" + attribute + "\" is already defined.")))
99+
context.diagnose(Diagnostic(node: node, message: DiagnosticMsg(id: "globalAttributeAlreadyDefined", message: "Global attribute \"" + attribute + "\" is already defined.")))
103100
}
104101
// MARK: Parse innerHTML
105102
static func parse_inner_html(

Sources/HTMLKitUtilityMacros/HTMLElements.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ enum HTMLElements : DeclarationMacro {
4242
//string += "public let isVoid:Bool = false\npublic var attributes:[HTMLElementAttribute] = []"
4343
var attribute_declarations:String = ""
4444
var attributes:[(String, String, String)] = []
45+
var other_attributes:[(String, String)] = []
4546
if let test = item.value.as(ArrayExprSyntax.self)?.elements {
4647
attributes.reserveCapacity(test.count)
4748
for element in test {
@@ -62,6 +63,13 @@ enum HTMLElements : DeclarationMacro {
6263
} else {
6364
var isArray:Bool = false
6465
let (value_type, default_value, value_type_literal):(String, String, HTMLElementValueType) = parse_value_type(isArray: &isArray, key: key, label.expression)
66+
switch value_type_literal {
67+
case .otherAttribute(let other):
68+
other_attributes.append((key, other))
69+
break
70+
default:
71+
break
72+
}
6573
attribute_declarations += "\npublic var \(key):\(value_type)\(default_value.split(separator: "=", omittingEmptySubsequences: false)[0])"
6674
attributes.append((key, value_type, default_value))
6775
}
@@ -90,7 +98,8 @@ enum HTMLElements : DeclarationMacro {
9098
initializers += "self.innerHTML = innerHTML\n}\n"
9199

92100
initializers += "public init?(context: some MacroExpansionContext, _ children: SyntaxChildren) {\n"
93-
initializers += "let data:HTMLKitUtilities.ElementData = HTMLKitUtilities.parse_arguments(context: context, children: children)\n"
101+
let other_attributes_string:String = other_attributes.isEmpty ? "" : ", otherAttributes: [" + other_attributes.map({ "\"" + $0.0 + "\":\"" + $0.1 + "\"" }).joined(separator: ",") + "]"
102+
initializers += "let data:HTMLKitUtilities.ElementData = HTMLKitUtilities.parse_arguments(context: context, children: children\(other_attributes_string))\n"
94103
initializers += "self.attributes = data.globalAttributes\n"
95104
for (key, value_type, _) in attributes {
96105
var value:String = "as? \(value_type)"
@@ -199,7 +208,7 @@ enum HTMLElements : DeclarationMacro {
199208
let value:Bool = expr.as(FunctionCallExprSyntax.self)!.arguments.first!.expression.as(BooleanLiteralExprSyntax.self)!.literal.text == "true"
200209
return ("Bool", "= \(value)", .booleanDefaultValue(value))
201210
case "cssUnit":
202-
return ("HTMLElementAttribute.CSSUnit", isArray ? "" : "? = nil", .string)
211+
return ("HTMLElementAttribute.CSSUnit", isArray ? "" : "? = nil", .cssUnit)
203212
default:
204213
return ("Float", "? = nil", .float)
205214
}

Tests/HTMLKitTests/ElementTests.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,7 @@ struct ElementTests {
2727
string = #html(p("\(unescaped.escapingHTML(escapeAttributes: false))"))
2828
#expect(string == expected_result)
2929

30-
/*expected_result = "<div title=\"&lt;p&gt;\">&lt;p&gt;&lt;/p&gt;</div>"
31-
string = #div(attributes: [.title(StaticString("<p>"))], StaticString("<p></p>")).description
32-
#expect(string == expected_result)*/
33-
30+
expected_result = "<div title=\"&lt;p&gt;\">&lt;p&gt;&lt;/p&gt;</div>"
3431
string = #html(div(attributes: [.title("<p>")], StaticString("<p></p>")))
3532
#expect(string == expected_result)
3633

0 commit comments

Comments
 (0)