Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

experimental support for embedded swift #50

Merged
merged 11 commits into from
Oct 12, 2024
Merged
3 changes: 2 additions & 1 deletion .swiftformat
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
--stripunusedargs unnamed-only
--stripunusedargs unnamed-only
--ifdef noindent
2 changes: 1 addition & 1 deletion Examples/HummingbirdDemo/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ let package = Package(
.executable(name: "App", targets: ["App"]),
],
dependencies: [
.package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0-rc.5"),
.package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0"),
.package(path: "../../"),
],
targets: [
Expand Down
2 changes: 1 addition & 1 deletion Examples/HummingbirdDemo/Sources/App/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ struct App {
onServerRunning: { _ in
print("Server running on http://localhost:8080/")
#if DEBUG
browserSyncReload()
browserSyncReload()
#endif
}
)
Expand Down
18 changes: 9 additions & 9 deletions Examples/HummingbirdDemo/Sources/App/BrowserSync.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import Foundation

#if DEBUG
func browserSyncReload() {
let p = Process()
p.executableURL = URL(string: "file:///bin/sh")
p.arguments = ["-c", "browser-sync reload"]
do {
try p.run()
} catch {
print("Could not auto-reload: \(error)")
}
func browserSyncReload() {
let p = Process()
p.executableURL = URL(string: "file:///bin/sh")
p.arguments = ["-c", "browser-sync reload"]
do {
try p.run()
} catch {
print("Could not auto-reload: \(error)")
}
}
#endif
13 changes: 12 additions & 1 deletion [email protected][email protected]
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
// swift-tools-version: 6.0
import Foundation
import PackageDescription

let featureFlags: [SwiftSetting] = [
let shouldBuildForEmbedded = ProcessInfo.processInfo.environment["EXPERIMENTAL_EMBEDDED_WASM"].flatMap(Bool.init) ?? false

var featureFlags: [SwiftSetting] = [
.enableUpcomingFeature("ExistentialAny"),
]

if shouldBuildForEmbedded {
featureFlags.append(
.unsafeFlags([
"-Xfrontend", "-emit-empty-object-file",
])
)
}

let package = Package(
name: "elementary",
platforms: [
Expand Down
2 changes: 2 additions & 0 deletions Sources/Elementary/Core/AsyncForEach.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if !hasFeature(Embedded)
/// An element that lazily renders HTML for each element of an `AsyncSequence`.
///
/// The this element can only be rendered in an async context (ie: by calling ``HTML/render(into:chunkSize:)`` or ``HTML/renderAsync()``).
Expand Down Expand Up @@ -38,3 +39,4 @@ public struct AsyncForEach<Source: AsyncSequence, Content: HTML>: HTML {
}
}
}
#endif
2 changes: 1 addition & 1 deletion Sources/Elementary/Core/AttributeStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public struct _StoredAttribute: Equatable, Sendable {
}
}

public enum _AttributeStorage: Sendable {
public enum _AttributeStorage: Sendable, Equatable {
case none
case single(_StoredAttribute)
case multiple([_StoredAttribute])
Expand Down
4 changes: 3 additions & 1 deletion Sources/Elementary/Core/Environment.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if !hasFeature(Embedded)
/// A property wrapper that reads an environment value from a `TaskLocal`.
///
/// Use `@Environment` to conveniently read a value provided via ``HTML/environment(_:_:)``.
Expand Down Expand Up @@ -105,4 +106,5 @@ public struct _ModifiedTaskLocal<T: Sendable, Content: HTML>: HTML {
}
}

extension _ModifiedTaskLocal: Sendable where Content: Sendable {}
extension _ModifiedTaskLocal: Sendable where Content: Sendable {}
#endif
4 changes: 1 addition & 3 deletions Sources/Elementary/Core/Html+Attributes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ public extension HTMLAttribute {

public struct _AttributedElement<Content: HTML>: HTML {
public var content: Content

@usableFromInline
var attributes: _AttributeStorage
public var attributes: _AttributeStorage

@usableFromInline
init(content: Content, attributes: _AttributeStorage) {
Expand Down
38 changes: 18 additions & 20 deletions Sources/Elementary/Core/Html+Elements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
public struct HTMLElement<Tag: HTMLTagDefinition, Content: HTML>: HTML where Tag: HTMLTrait.Paired {
/// The type of the HTML tag this element represents.
public typealias Tag = Tag
@usableFromInline
var attributes: _AttributeStorage
public var _attributes: _AttributeStorage

// The content of the element.
public var content: Content

/// Creates a new HTML element with the specified content.
/// - Parameter content: The content of the element.
public init(@HTMLBuilder content: () -> Content) {
attributes = .init()
_attributes = .init()
self.content = content()
}

Expand All @@ -21,7 +20,7 @@ public struct HTMLElement<Tag: HTMLTagDefinition, Content: HTML>: HTML where Tag
/// - content: The content of the element.
@inlinable
public init(_ attribute: HTMLAttribute<Tag>, @HTMLBuilder content: () -> Content) {
attributes = .init(attribute)
_attributes = .init(attribute)
self.content = content()
}

Expand All @@ -31,7 +30,7 @@ public struct HTMLElement<Tag: HTMLTagDefinition, Content: HTML>: HTML where Tag
/// - content: The content of the element.
@inlinable
public init(_ attributes: HTMLAttribute<Tag>..., @HTMLBuilder content: () -> Content) {
self.attributes = .init(attributes)
_attributes = .init(attributes)
self.content = content()
}

Expand All @@ -41,24 +40,24 @@ public struct HTMLElement<Tag: HTMLTagDefinition, Content: HTML>: HTML where Tag
/// - content: The content of the element.
@inlinable
public init(attributes: [HTMLAttribute<Tag>], @HTMLBuilder content: () -> Content) {
self.attributes = .init(attributes)
_attributes = .init(attributes)
self.content = content()
}

@_spi(Rendering)
public static func _render<Renderer: _HTMLRendering>(_ html: consuming Self, into renderer: inout Renderer, with context: consuming _RenderingContext) {
html.attributes.append(context.attributes)
html._attributes.append(context.attributes)

renderer.appendToken(.startTag(Tag.name, attributes: html.attributes.flattened(), isUnpaired: false, type: Tag.renderingType))
renderer.appendToken(.startTag(Tag.name, attributes: html._attributes.flattened(), isUnpaired: false, type: Tag.renderingType))
Content._render(html.content, into: &renderer, with: .emptyContext)
renderer.appendToken(.endTag(Tag.name, type: Tag.renderingType))
}

@_spi(Rendering)
public static func _render<Renderer: _AsyncHTMLRendering>(_ html: consuming Self, into renderer: inout Renderer, with context: consuming _RenderingContext) async throws {
html.attributes.append(context.attributes)
html._attributes.append(context.attributes)

try await renderer.appendToken(.startTag(Tag.name, attributes: html.attributes.flattened(), isUnpaired: false, type: Tag.renderingType))
try await renderer.appendToken(.startTag(Tag.name, attributes: html._attributes.flattened(), isUnpaired: false, type: Tag.renderingType))
try await Content._render(html.content, into: &renderer, with: .emptyContext)
try await renderer.appendToken(.endTag(Tag.name, type: Tag.renderingType))
}
Expand All @@ -68,46 +67,45 @@ public struct HTMLElement<Tag: HTMLTagDefinition, Content: HTML>: HTML where Tag
public struct HTMLVoidElement<Tag: HTMLTagDefinition>: HTML where Tag: HTMLTrait.Unpaired {
/// The type of the HTML tag this element represents.
public typealias Tag = Tag
@usableFromInline
var attributes: _AttributeStorage
public var _attributes: _AttributeStorage

/// Creates a new HTML void element.
@inlinable
public init() {
attributes = .init()
_attributes = .init()
}

/// Creates a new HTML void element with the specified attribute.
/// - Parameter attribute: The attribute to apply to the element.
@inlinable
public init(_ attribute: HTMLAttribute<Tag>) {
attributes = .init(attribute)
_attributes = .init(attribute)
}

/// Creates a new HTML void element with the specified attributes.
/// - Parameter attributes: The attributes to apply to the element.
@inlinable
public init(_ attributes: HTMLAttribute<Tag>...) {
self.attributes = .init(attributes)
_attributes = .init(attributes)
}

/// Creates a new HTML void element with the specified attributes.
/// - Parameter attributes: The attributes to apply to the element as an array.
@inlinable
public init(attributes: [HTMLAttribute<Tag>]) {
self.attributes = .init(attributes)
_attributes = .init(attributes)
}

@_spi(Rendering)
public static func _render<Renderer: _HTMLRendering>(_ html: consuming Self, into renderer: inout Renderer, with context: consuming _RenderingContext) {
html.attributes.append(context.attributes)
renderer.appendToken(.startTag(Tag.name, attributes: html.attributes.flattened(), isUnpaired: true, type: Tag.renderingType))
html._attributes.append(context.attributes)
renderer.appendToken(.startTag(Tag.name, attributes: html._attributes.flattened(), isUnpaired: true, type: Tag.renderingType))
}

@_spi(Rendering)
public static func _render<Renderer: _AsyncHTMLRendering>(_ html: consuming Self, into renderer: inout Renderer, with context: consuming _RenderingContext) async throws {
html.attributes.append(context.attributes)
try await renderer.appendToken(.startTag(Tag.name, attributes: html.attributes.flattened(), isUnpaired: true, type: Tag.renderingType))
html._attributes.append(context.attributes)
try await renderer.appendToken(.startTag(Tag.name, attributes: html._attributes.flattened(), isUnpaired: true, type: Tag.renderingType))
}
}

Expand Down
47 changes: 47 additions & 0 deletions Sources/Elementary/Core/HtmlBuilder+Embedded.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// since variadic generics are currently not supported in Embedded, here are a few old-school hand-rolled tuples

#if hasFeature(Embedded)
public extension HTMLBuilder {
static func buildBlock<V0: HTML, V1: HTML>(_ v0: V0, _ v1: V1) -> _HTMLTuple2<V0, V1> {
return _HTMLTuple2(v0: v0, v1: v1)
}

static func buildBlock<V0: HTML, V1: HTML, V2: HTML>(_ v0: V0, _ v1: V1, _ v2: V2) -> _HTMLTuple3<V0, V1, V2> {
return _HTMLTuple3(v0: v0, v1: v1, v2: v2)
}

static func buildBlock<V0: HTML, V1: HTML, V2: HTML, V3: HTML>(_ v0: V0, _ v1: V1, _ v2: V2, _ v3: V3) -> _HTMLTuple4<V0, V1, V2, V3> {
return _HTMLTuple4(v0: v0, v1: v1, v2: v2, v3: v3)
}

static func buildBlock<V0: HTML, V1: HTML, V2: HTML, V3: HTML, V4: HTML>(_ v0: V0, _ v1: V1, _ v2: V2, _ v3: V3, _ v4: V4) -> _HTMLTuple5<V0, V1, V2, V3, V4> {
return _HTMLTuple5(v0: v0, v1: v1, v2: v2, v3: v3, v4: v4)
}
}

public struct _HTMLTuple2<V0: HTML, V1: HTML>: HTML {
public let v0: V0
public let v1: V1
}

public struct _HTMLTuple3<V0: HTML, V1: HTML, V2: HTML>: HTML {
public let v0: V0
public let v1: V1
public let v2: V2
}

public struct _HTMLTuple4<V0: HTML, V1: HTML, V2: HTML, V3: HTML>: HTML {
public let v0: V0
public let v1: V1
public let v2: V2
public let v3: V3
}

public struct _HTMLTuple5<V0: HTML, V1: HTML, V2: HTML, V3: HTML, V4: HTML>: HTML {
public let v0: V0
public let v1: V1
public let v2: V2
public let v3: V3
public let v4: V4
}
#endif
Loading