Skip to content

Commit 454d21b

Browse files
stuff
- added 3 macros (anyHTML, rawHTML, anyRawHTML) - removed static string equality - removed some type annotations - minor code cleanups - update README
1 parent e0c3c52 commit 454d21b

30 files changed

+498
-326
lines changed

Package.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// swift-tools-version:5.9
2-
// The swift-tools-version declares the minimum version of Swift required to build this package.
32

43
import PackageDescription
54
import CompilerPluginSupport

README.md

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,45 @@ Write HTML using Swift Macros. Supports HTMX via global attributes.
2828
<details>
2929
<summary>How do I use this library?</summary>
3030

31-
Use the `#html(encoding:attributes:innerHTML:)` macro. All parameters, for the macro and default HTML elements, are optional by default. The default HTML elements are generated by an internal macro.
31+
Use the `#html(encoding:lookupFiles:innerHTML:)` macro. All parameters, for the macro and default HTML elements, are optional by default. The default HTML elements are generated by an internal macro.
3232

33-
#### HTML Macro
33+
#### Macros
34+
35+
<details>
36+
<summary>html</summary>
37+
38+
Requires explicit type annotation due to returning the inferred concrete type.
3439

3540
```swift
3641

37-
#html(
42+
#html<T: CustomStringConvertible>(
3843
encoding: HTMLEncoding = .string,
39-
attributes: [<global attribute>] = [],
40-
<element specific attribute>: <element specific attribute value>? = nil,
41-
_ innerHTML: CustomStringConvertible...
44+
lookupFiles: [StaticString] = [],
45+
_ innerHTML: CustomStringConvertible & Sendable...
46+
) -> T
47+
48+
```
49+
50+
</details>
51+
52+
<details>
53+
54+
<summary>anyHTML</summary>
55+
56+
Same as `#html` but returning an existential.
57+
58+
```swift
59+
60+
#anyHTML(
61+
encoding: HTMLEncoding = .string,
62+
lookupFiles: [StaticString] = [],
63+
_ innerHTML: CustomStringConvertible & Sendable...
4264
)
4365

4466
```
4567

68+
</details>
69+
4670
#### HTMLElement
4771

4872
All default HTML elements conform to the `HTMLElement` protocol and contain their appropriate element attributes. They can be declared when you initialize the element or be changed after initialization by accessing the attribute variable directly.
@@ -54,7 +78,7 @@ The default initializer for creating an HTML Element follows this syntax:
5478
<html element name>(
5579
attributes: [<global attribute>] = [],
5680
<element specific attribute>: <value>? = nil,
57-
_ innerHTML: CustomStringConvertible...
81+
_ innerHTML: CustomStringConvertible & Sendable...
5882
)
5983

6084
```
@@ -272,18 +296,40 @@ Declare the encoding you want in the `#html` macro.
272296

273297
[Currently supported types](https://github.com/RandomHashTags/swift-htmlkit/blob/main/Sources/HTMLKitUtilities/HTMLEncoding.swift):
274298
- `string` -> `String`/`StaticString`
275-
- `utf8Bytes` -> `[UInt8]`
276-
- `utf16Bytes` -> `[UInt16]`
299+
- `utf8Bytes` -> An array of `UInt8` (supports any collection `where Element == UInt8`)
300+
- `utf16Bytes` -> An array of `UInt16` (supports any collection `where Element == UInt16`)
277301
- `utf8CString` -> `ContiguousArray<CChar>`
278-
- `foundationData` -> `Foundation.Data`
279-
- You need to `import Foundation` to use this!
302+
- `foundationData` -> `Foundation.Data`/`FoundationEssentials.Data`
303+
- You need to `import Foundation` or `import FoundationEssentials` to use this!
280304
- `byteBuffer` -> `NIOCore.ByteBuffer`
281305
- You need to `import NIOCore` to use this! Swift HTMLKit does not depend on `swift-nio`!
282306
- `custom("<encoding logic>")` -> A custom type conforming to `CustomStringConvertible`
283307
- Use `$0` to reference the compiled HTML (as a String without the delimiters)
284308

285309
</details>
286310

311+
<details>
312+
313+
<summary>I need to use raw HTML!</summary>
314+
315+
Use the `#rawHTML(encoding:lookupFiles:innerHTML:)` and `#anyRawHTML(encoding:lookupFiles:innerHTML:)` macros.
316+
317+
#### Examples
318+
319+
```swift
320+
321+
var expected = "<!DOCTYPE html><html>dude&dude</html>"
322+
var result:String = #rawHTML("<!DOCTYPE html><html>dude&dude</html>")
323+
#expect(expected == result)
324+
325+
expected = "<!DOCTYPE html><html><p>test&lt;&gt;</p>dude&dude bro&amp;bro</html>"
326+
result = #html(html(#anyRawHTML(p("test<>"), "dude&dude"), " bro&bro"))
327+
#expect(expected == result)
328+
329+
```
330+
331+
</details>
332+
287333
### HTMX
288334

289335
<details>

Sources/CSS/CSSStyle.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ extension CSSStyle {
268268
@inlinable
269269
public func htmlValue(encoding: HTMLEncoding, forMacro: Bool) -> String? {
270270
func get<T : HTMLInitializable>(_ value: T?) -> String? {
271-
guard let v:String = value?.htmlValue(encoding: encoding, forMacro: forMacro) else { return nil }
271+
guard let v = value?.htmlValue(encoding: encoding, forMacro: forMacro) else { return nil }
272272
return key + ":" + v
273273
}
274274
switch self {

Sources/CSS/CSSUnit.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public enum CSSUnit : HTMLInitializable { // https://www.w3schools.com/cssref/cs
8888
.viewportMax(let v),
8989
.percent(let v):
9090
guard let v:Float = v else { return nil }
91-
var s:String = String(describing: v)
91+
var s = String(describing: v)
9292
while s.last == "0" {
9393
s.removeLast()
9494
}
@@ -131,7 +131,7 @@ extension CSSUnit : HTMLParsable {
131131
public init?(context: HTMLExpansionContext) {
132132
func float() -> Float? {
133133
guard let expression:ExprSyntax = context.expression,
134-
let s:String = expression.integerLiteral?.literal.text ?? expression.floatLiteral?.literal.text
134+
let s = expression.integerLiteral?.literal.text ?? expression.floatLiteral?.literal.text
135135
else {
136136
return nil
137137
}

Sources/CSS/styles/Color.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,8 @@ extension CSSStyle {
175175
switch self {
176176
case .hex(let hex): return "#" + hex
177177
case .rgb(let r, let g, let b, let a):
178-
var string:String = "rbg(\(r),\(g),\(b)"
179-
if let a:Swift.Float = a {
178+
var string = "rbg(\(r),\(g),\(b)"
179+
if let a {
180180
string += ",\(a)"
181181
}
182182
return string + ")"

Sources/HTMLAttributes/HTMLAttributes+Extra.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,18 +84,18 @@ extension HTMLAttribute {
8484
extension HTMLAttribute.Extra {
8585
public static func parse(context: HTMLExpansionContext, expr: ExprSyntax) -> (any HTMLInitializable)? {
8686
func get<T : HTMLParsable>(_ type: T.Type) -> T? {
87-
let inner_key:String, arguments:LabeledExprListSyntax
88-
if let function:FunctionCallExprSyntax = expr.functionCall {
89-
inner_key = function.calledExpression.memberAccess!.declName.baseName.text
87+
let innerKey:String, arguments:LabeledExprListSyntax
88+
if let function = expr.functionCall {
89+
innerKey = function.calledExpression.memberAccess!.declName.baseName.text
9090
arguments = function.arguments
91-
} else if let member:MemberAccessExprSyntax = expr.memberAccess {
92-
inner_key = member.declName.baseName.text
91+
} else if let member = expr.memberAccess {
92+
innerKey = member.declName.baseName.text
9393
arguments = LabeledExprListSyntax()
9494
} else {
9595
return nil
9696
}
97-
var c:HTMLExpansionContext = context
98-
c.key = inner_key
97+
var c = context
98+
c.key = innerKey
9999
c.arguments = arguments
100100
return T(context: c)
101101
}

Sources/HTMLElements/CustomElement.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ public struct custom : HTMLElement {
4848

4949
@inlinable
5050
public var description : String {
51-
let attributes_string:String = self.attributes.compactMap({
52-
guard let v:String = $0.htmlValue(encoding: encoding, forMacro: fromMacro) else { return nil }
53-
let delimiter:String = $0.htmlValueDelimiter(encoding: encoding, forMacro: fromMacro)
51+
let attributesString = self.attributes.compactMap({
52+
guard let v = $0.htmlValue(encoding: encoding, forMacro: fromMacro) else { return nil }
53+
let delimiter = $0.htmlValueDelimiter(encoding: encoding, forMacro: fromMacro)
5454
return $0.key + ($0.htmlValueIsVoidable && v.isEmpty ? "" : "=\(delimiter)\(v)\(delimiter)")
5555
}).joined(separator: " ")
5656
let l:String, g:String
@@ -61,6 +61,6 @@ public struct custom : HTMLElement {
6161
l = "<"
6262
g = ">"
6363
}
64-
return l + tag + (isVoid && trailingSlash ? " /" : "") + g + (attributes_string.isEmpty ? "" : " " + attributes_string) + (isVoid ? "" : l + "/" + tag + g)
64+
return l + tag + (isVoid && trailingSlash ? " /" : "") + g + (attributesString.isEmpty ? "" : " " + attributesString) + (isVoid ? "" : l + "/" + tag + g)
6565
}
6666
}

Sources/HTMLElements/svg/svg.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ struct svg : HTMLElement {
4949

5050
@inlinable
5151
public var description : String {
52-
let attributes_string:String = self.attributes.compactMap({
53-
guard let v:String = $0.htmlValue(encoding: encoding, forMacro: fromMacro) else { return nil }
54-
let delimiter:String = $0.htmlValueDelimiter(encoding: encoding, forMacro: fromMacro)
52+
let attributesString = self.attributes.compactMap({
53+
guard let v = $0.htmlValue(encoding: encoding, forMacro: fromMacro) else { return nil }
54+
let delimiter = $0.htmlValueDelimiter(encoding: encoding, forMacro: fromMacro)
5555
return $0.key + ($0.htmlValueIsVoidable && v.isEmpty ? "" : "=\(delimiter)\(v)\(delimiter)")
5656
}).joined(separator: " ")
5757
let l:String, g:String
@@ -62,7 +62,7 @@ struct svg : HTMLElement {
6262
l = "<"
6363
g = ">"
6464
}
65-
return l + tag + (isVoid && trailingSlash ? " /" : "") + g + (attributes_string.isEmpty ? "" : " " + attributes_string) + (isVoid ? "" : l + "/" + tag + g)
65+
return l + tag + (isVoid && trailingSlash ? " /" : "") + g + (attributesString.isEmpty ? "" : " " + attributesString) + (isVoid ? "" : l + "/" + tag + g)
6666
}
6767
}
6868

Sources/HTMLKit/HTMLKit.swift

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,6 @@
1212
@_exported import HTMLKitParse
1313
@_exported import HTMX
1414

15-
// MARK: StaticString equality
16-
extension StaticString {
17-
public static func == (left: Self, right: Self) -> Bool { left.description == right.description }
18-
public static func != (left: Self, right: Self) -> Bool { left.description != right.description }
19-
}
20-
// MARK: StaticString and StringProtocol equality
21-
extension StringProtocol {
22-
public static func == (left: Self, right: StaticString) -> Bool { left == right.description }
23-
public static func == (left: StaticString, right: Self) -> Bool { left.description == right }
24-
}
25-
2615
// MARK: Escape HTML
2716
@freestanding(expression)
2817
public macro escapeHTML(
@@ -31,18 +20,50 @@ public macro escapeHTML(
3120
) -> String = #externalMacro(module: "HTMLKitMacros", type: "EscapeHTML")
3221

3322
// MARK: HTML
23+
/// - Returns: The inferred concrete type that conforms to `CustomStringConvertible & Sendable`.
3424
@freestanding(expression)
35-
public macro html<T: CustomStringConvertible>(
25+
public macro html<T: CustomStringConvertible & Sendable>(
3626
encoding: HTMLEncoding = .string,
3727
lookupFiles: [StaticString] = [],
3828
_ innerHTML: CustomStringConvertible & Sendable...
3929
) -> T = #externalMacro(module: "HTMLKitMacros", type: "HTMLElementMacro")
4030

31+
/// - Returns: An existential conforming to `CustomStringConvertible & Sendable`.
32+
@freestanding(expression)
33+
public macro anyHTML(
34+
encoding: HTMLEncoding = .string,
35+
lookupFiles: [StaticString] = [],
36+
_ innerHTML: CustomStringConvertible & Sendable...
37+
) -> any CustomStringConvertible & Sendable = #externalMacro(module: "HTMLKitMacros", type: "HTMLElementMacro")
38+
4139
// MARK: Unchecked
4240
/// Same as `#html` but ignoring compiler warnings.
41+
///
42+
/// - Returns: The inferred concrete type that conforms to `CustomStringConvertible & Sendable`.
43+
@freestanding(expression)
44+
public macro uncheckedHTML<T: CustomStringConvertible & Sendable>(
45+
encoding: HTMLEncoding = .string,
46+
lookupFiles: [StaticString] = [],
47+
_ innerHTML: CustomStringConvertible & Sendable...
48+
) -> T = #externalMacro(module: "HTMLKitMacros", type: "HTMLElementMacro")
49+
50+
// MARK: Raw
51+
/// Does not escape the `innerHTML`.
52+
///
53+
/// - Returns: The inferred concrete type that conforms to `CustomStringConvertible & Sendable`.
54+
@freestanding(expression)
55+
public macro rawHTML<T: CustomStringConvertible & Sendable>(
56+
encoding: HTMLEncoding = .string,
57+
lookupFiles: [StaticString] = [],
58+
_ innerHTML: CustomStringConvertible & Sendable...
59+
) -> T = #externalMacro(module: "HTMLKitMacros", type: "RawHTML")
60+
61+
/// Does not escape the `innerHTML`.
62+
///
63+
/// - Returns: An existential conforming to `CustomStringConvertible & Sendable`.
4364
@freestanding(expression)
44-
public macro uncheckedHTML<T: CustomStringConvertible>(
65+
public macro anyRawHTML(
4566
encoding: HTMLEncoding = .string,
4667
lookupFiles: [StaticString] = [],
4768
_ innerHTML: CustomStringConvertible & Sendable...
48-
) -> T = #externalMacro(module: "HTMLKitMacros", type: "HTMLElementMacro")
69+
) -> any CustomStringConvertible & Sendable = #externalMacro(module: "HTMLKitMacros", type: "RawHTML")

Sources/HTMLKitMacros/EscapeHTML.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import SwiftSyntaxMacros
1212

1313
enum EscapeHTML : ExpressionMacro {
1414
static func expansion(of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) throws -> ExprSyntax {
15-
let c:HTMLExpansionContext = HTMLExpansionContext(context: context, expansion: node.as(ExprSyntax.self)!.macroExpansion!, ignoresCompilerWarnings: false, encoding: .string, key: "", arguments: node.arguments)
15+
let c = HTMLExpansionContext(context: context, expansion: node.as(ExprSyntax.self)!.macroExpansion!, ignoresCompilerWarnings: false, encoding: .string, key: "", arguments: node.arguments)
1616
return "\"\(raw: HTMLKitUtilities.escapeHTML(context: c))\""
1717
}
1818
}

Sources/HTMLKitMacros/HTMLKitMacros.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import SwiftSyntaxMacros
1212
struct HTMLKitMacros : CompilerPlugin {
1313
let providingMacros:[any Macro.Type] = [
1414
HTMLElementMacro.self,
15-
EscapeHTML.self
15+
EscapeHTML.self,
16+
RawHTML.self
1617
]
1718
}

Sources/HTMLKitMacros/RawHTML.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// RawHTML.swift
3+
//
4+
//
5+
// Created by Evan Anderson on 3/29/25.
6+
//
7+
8+
import HTMLKitParse
9+
import HTMLKitUtilities
10+
import SwiftSyntax
11+
import SwiftSyntaxMacros
12+
13+
enum RawHTML : ExpressionMacro {
14+
static func expansion(of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) throws -> ExprSyntax {
15+
let c = HTMLExpansionContext(context: context, expansion: node.as(ExprSyntax.self)!.macroExpansion!, ignoresCompilerWarnings: false, encoding: .string, key: "", arguments: node.arguments)
16+
return "\"\(raw: HTMLKitUtilities.rawHTML(context: c))\""
17+
}
18+
}

0 commit comments

Comments
 (0)