diff --git a/Package.resolved b/Package.resolved index 7bac6c09..422eca65 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,51 +1,6 @@ { "object": { "pins": [ - { - "package": "Console", - "repositoryURL": "https://github.com/vapor/console.git", - "state": { - "branch": null, - "revision": "74cfbea629d4aac34a97cead2447a6870af1950b", - "version": "3.1.1" - } - }, - { - "package": "Core", - "repositoryURL": "https://github.com/vapor/core.git", - "state": { - "branch": null, - "revision": "10d33362a47fab03a067e78fb0791341d9c634fa", - "version": "3.9.3" - } - }, - { - "package": "Crypto", - "repositoryURL": "https://github.com/vapor/crypto.git", - "state": { - "branch": null, - "revision": "df8eb7d8ae51787b3a0628aa3975e67666da936c", - "version": "3.3.3" - } - }, - { - "package": "DatabaseKit", - "repositoryURL": "https://github.com/vapor/database-kit.git", - "state": { - "branch": null, - "revision": "8f352c8e66dab301ab9bfef912a01ce1361ba1e4", - "version": "1.3.3" - } - }, - { - "package": "HTTP", - "repositoryURL": "https://github.com/vapor/http.git", - "state": { - "branch": null, - "revision": "3808ed0401379b6e9f4a053f03090ea9d658caa9", - "version": "3.2.1" - } - }, { "package": "Lingo", "repositoryURL": "https://github.com/miroslavkovac/Lingo.git", @@ -54,114 +9,6 @@ "revision": "f21f388b04239641b3e88d14f21762125faa9857", "version": "3.0.5" } - }, - { - "package": "Multipart", - "repositoryURL": "https://github.com/vapor/multipart.git", - "state": { - "branch": null, - "revision": "f063180d0b84832accd33194e06ed3c41f8609ac", - "version": "3.1.1" - } - }, - { - "package": "Routing", - "repositoryURL": "https://github.com/vapor/routing.git", - "state": { - "branch": null, - "revision": "d76f339c9716785e5079af9d7075d28ff7da3d92", - "version": "3.1.0" - } - }, - { - "package": "Service", - "repositoryURL": "https://github.com/vapor/service.git", - "state": { - "branch": null, - "revision": "fa5b5de62bd68bcde9a69933f31319e46c7275fb", - "version": "1.0.2" - } - }, - { - "package": "swift-nio", - "repositoryURL": "https://github.com/apple/swift-nio.git", - "state": { - "branch": null, - "revision": "ba7970fe396e8198b84c6c1b44b38a1d4e2eb6bd", - "version": "1.14.1" - } - }, - { - "package": "swift-nio-ssl", - "repositoryURL": "https://github.com/apple/swift-nio-ssl.git", - "state": { - "branch": null, - "revision": "0f3999f3e3c359cc74480c292644c3419e44a12f", - "version": "1.4.0" - } - }, - { - "package": "swift-nio-ssl-support", - "repositoryURL": "https://github.com/apple/swift-nio-ssl-support.git", - "state": { - "branch": null, - "revision": "c02eec4e0e6d351cd092938cf44195a8e669f555", - "version": "1.0.0" - } - }, - { - "package": "swift-nio-zlib-support", - "repositoryURL": "https://github.com/apple/swift-nio-zlib-support.git", - "state": { - "branch": null, - "revision": "37760e9a52030bb9011972c5213c3350fa9d41fd", - "version": "1.0.0" - } - }, - { - "package": "TemplateKit", - "repositoryURL": "https://github.com/vapor/template-kit.git", - "state": { - "branch": null, - "revision": "51405c83e95e8adb09565278a5e9b959c605e56c", - "version": "1.4.0" - } - }, - { - "package": "URLEncodedForm", - "repositoryURL": "https://github.com/vapor/url-encoded-form.git", - "state": { - "branch": null, - "revision": "82d8d63bdb76b6dd8febe916c639ab8608dbbaed", - "version": "1.0.6" - } - }, - { - "package": "Validation", - "repositoryURL": "https://github.com/vapor/validation.git", - "state": { - "branch": null, - "revision": "4de213cf319b694e4ce19e5339592601d4dd3ff6", - "version": "2.1.1" - } - }, - { - "package": "Vapor", - "repositoryURL": "https://github.com/vapor/vapor.git", - "state": { - "branch": null, - "revision": "92a58a9a84e4330500b99fe355a94d29f67abe58", - "version": "3.3.1" - } - }, - { - "package": "WebSocket", - "repositoryURL": "https://github.com/vapor/websocket.git", - "state": { - "branch": null, - "revision": "d85e5b6dce4d04065865f77385fc3324f98178f6", - "version": "1.1.2" - } } ] }, diff --git a/Package.swift b/Package.swift index e3181f60..d943d6db 100644 --- a/Package.swift +++ b/Package.swift @@ -9,10 +9,7 @@ let package = Package( // Products define the executables and libraries produced by a package, and make them visible to other packages. .library( name: "HTMLKit", - targets: ["HTMLKit"]), - .library( - name: "HTMLKitVapor", - targets: ["HTMLKitVapor"]), + targets: ["HTMLKit"]) ], dependencies: [ // Dependencies declare other packages that this package depends on. @@ -20,7 +17,6 @@ let package = Package( .package(url: "https://github.com/miroslavkovac/Lingo.git", from: "3.0.5"), // .package(url: "https://github.com/vapor-community/markdown.git", .upToNextMajor(from: "0.4.0")), - .package(url: "https://github.com/vapor/vapor.git", from: "3.0.0") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -28,9 +24,6 @@ let package = Package( .target( name: "HTMLKit", dependencies: ["Lingo"]), - .target( - name: "HTMLKitVapor", - dependencies: ["HTMLKit", "Vapor"]), .testTarget( name: "HTMLKitTests", dependencies: ["HTMLKit"]), diff --git a/Sources/HTMLKit/TemplateValue.swift b/Sources/HTMLKit/TemplateValue.swift index 217cba9b..6e0d5ced 100644 --- a/Sources/HTMLKit/TemplateValue.swift +++ b/Sources/HTMLKit/TemplateValue.swift @@ -1,11 +1,14 @@ import Foundation +@propertyWrapper @dynamicMemberLookup public enum TemplateValue { case constant(Value) case dynamic(ContextVariable) + public var wrappedValue: TemplateValue { self } + public subscript(dynamicMember keyPath: KeyPath) -> TemplateValue { switch self { case .constant(let value): return .constant(value[keyPath: keyPath]) @@ -71,6 +74,12 @@ extension TemplateValue where Value == String, Root == Void { } } +extension TemplateValue where Root == Value { + public init(_ root: Root.Type) { + self = .root() + } +} + extension TemplateValue { public static func root() -> TemplateValue { .dynamic(.root()) diff --git a/Sources/HTMLKit/Unwrap.swift b/Sources/HTMLKit/Unwrap.swift new file mode 100644 index 00000000..3b844013 --- /dev/null +++ b/Sources/HTMLKit/Unwrap.swift @@ -0,0 +1,17 @@ + +public struct Unwrap: HTMLComponent { + + let isDefined: Conditionable + let content: HTML + + public init(_ value: TemplateValue, @HTMLBuilder content: (TemplateValue) -> HTML) { + self.isDefined = value.isDefined + self.content = content(value.unsafelyUnwrapped) + } + + public var body: HTML { + IF(isDefined) { + content + } + } +} diff --git a/Sources/HTMLKitVapor/HTMLRenderable+Vapor.swift b/Sources/HTMLKitVapor/HTMLRenderable+Vapor.swift deleted file mode 100644 index 7546e448..00000000 --- a/Sources/HTMLKitVapor/HTMLRenderable+Vapor.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// HTMLRenderable+Vapor.swift -// HTMLKit - -import Vapor -import HTMLKit - -public struct HTMLKitProvider: Provider { - - public init() {} - - public func register(_ services: inout Services) throws { - services.register(HTMLRenderer()) - } - - /// Called after the container has fully initialized and after `willBoot(_:)`. - public func didBoot(_ container: Container) throws -> Future { - return .done(on: container) - } -} - -///// An extension that implements most of the helper functions -extension HTMLRenderable { - - /// Renders a `TemplateView` formula - /// - /// try renderer.render(WelcomeView.self) - /// - /// - Parameters: - /// - type: The view type to render - /// - context: The needed context to render the view with - /// - Returns: Returns a rendered view in a `HTTPResponse` - /// - Throws: If the formula do not exists, or if the rendering process fails - public func render(_ type: T.Type, with context: T.Context) throws -> HTTPResponse { - return try HTTPResponse(headers: .init([("content-type", "text/html; charset=utf-8")]), body: render(raw: type, with: context)) - } - - /// Renders a `StaticView` formula - /// - /// try renderer.render(WelcomeView.self) - /// - /// - Parameter type: The view type to render - /// - Returns: Returns a rendered view in a `HTTPResponse` - /// - Throws: If the formula do not exists, or if the rendering process fails - public func render(_ type: T.Type) throws -> HTTPResponse where T : HTMLPage { - return try HTTPResponse(headers: .init([("content-type", "text/html; charset=utf-8")]), body: render(raw: type)) - } - - public func render(view type: T.Type) throws -> View where T : HTMLPage { - guard let data = try render(raw: type).data(using: .utf8) else { - throw Abort(.internalServerError) - } - return View(data: data) - } - - public func render(view type: T.Type, with context: T.Context) throws -> View where T : HTMLTemplate { - guard let data = try render(raw: type, with: context).data(using: .utf8) else { - throw Abort(.internalServerError) - } - return View(data: data) - } -} - -extension Request { - - /// Creates a `HTMLRenderer` that can render templates - /// - /// - Returns: A `HTMLRenderer` containing all the templates - /// - Throws: If the shared container could not make the `HTMLRenderer` - public func renderer() throws -> HTMLRenderer { - return try sharedContainer.make(HTMLRenderer.self) - } -} - -extension HTMLRenderer : Service {} - -extension HTMLTemplate { - public func render(with context: Context, for request: Request) -> Future { - return request.future() - .map { _ in - let renderer = try request.renderer() - do { - return try renderer.render(view: Self.self, with: context) - } catch HTMLRenderer.Errors.unableToFindFormula { - try renderer.add(view: self) - return try renderer.render(view: Self.self, with: context) - } - } - } -} - -extension HTMLPage { - public func render(for request: Request) -> Future { - return request.future() - .map { _ in - let renderer = try request.renderer() - do { - return try renderer.render(view: Self.self) - } catch HTMLRenderer.Errors.unableToFindFormula { - try renderer.add(view: self) - return try renderer.render(view: Self.self) - } - } - } -} diff --git a/Tests/HTMLKitTests/HTMLKitTests.swift b/Tests/HTMLKitTests/HTMLKitTests.swift index beb1a92b..39222094 100644 --- a/Tests/HTMLKitTests/HTMLKitTests.swift +++ b/Tests/HTMLKitTests/HTMLKitTests.swift @@ -143,6 +143,35 @@ final class HTMLKitTests: XCTestCase { // _ = try! renderer.render(raw: OptionalDateView.self, with: testDate) // } // } +// } + +// func testView() { +// let formula = HTMLRenderer.Formula() +// try! Test().prerender(formula) +// let context = Test.Context( +// header: true, +// links: [ +// MenuLink(name: "WELKOM", link: "index.html"), +// MenuLink(name: "WIE WIJ ZIJN", link: "overons.html"), +// MenuLink(name: "WAT WIJ DOEN", link: "watwijdoen.html"), +// MenuLink(name: "CONTACT", link: "contact.html") +// ], +// text: """ +// asf;jadgkhjefaldskfdghfiehjsgdfkgh +// fhsfhjksflhjfslkghjfslkgjsfklgjfslkgjsfgkljdfsgklsdj +// """ +// ) +// var isFirst = true +// +// measure { +// for _ in 0...10_000 { +// let output = try! formula.render(with: context, lingo: nil) +// if isFirst { +// print(output) +// isFirst = false +// } +// } +// } // } func testHtmlRenderingTests() throws { diff --git a/Tests/HTMLKitTests/HTMLTestDocuments.swift b/Tests/HTMLKitTests/HTMLTestDocuments.swift index 303c2f74..17f4cba1 100644 --- a/Tests/HTMLKitTests/HTMLTestDocuments.swift +++ b/Tests/HTMLKitTests/HTMLTestDocuments.swift @@ -26,7 +26,8 @@ struct SimpleView: HTMLPage, HTMLTestable { struct StaticEmbedView: HTMLTemplate { - var context: RootValue = .root() + @TemplateValue(SimpleData.self) + var context var body: HTML { Div { @@ -34,9 +35,9 @@ struct StaticEmbedView: HTMLTemplate { P { context.string } - IF(context.int != nil) { + Unwrap(context.int) { int in Small { - context.int + int } } } @@ -1030,3 +1031,86 @@ struct FlashView: HTMLPage { .id("flash") } } + +struct MenuLink { + let name: String + let link: String +} + +struct Test: HTMLTemplate { + + struct Context { + var header: Bool + var links: [MenuLink] + var text: String + } + + var body: HTML { + Document(type: .html5) { + Head { + Title { "Welkom bij Autimatisering" } + Meta().add(.init(attribute: "charset", value: "utf8")) + Meta().add(.init(attribute: "viewport", value: "width=device-width, initial-scale=1")) + Link().add(.init(attribute: "stylesheet", value: "https://autimatisering.nl/styles/template.css")) + Link().add(.init(attribute: "stylesheet", value: "https://autimatisering.nl/styles/content.css")) + } + Body { + IF(context.header) { + Header { + Img(source: "https://autimatisering.nl/img/AMLogo-Full-White.svg") + .id("logo") + } + } + .elseIf(context.text != "Test") { + Anchor { + "Tap here" + } + .href("Some value") + } + .else { + "None of them" + } + + AMMenu(links: context.links) + Header { + Section { + Embed().source("https://autimatisering.nl/styles/svg/pagehead-img-home.svg") + }.id("banner-image-container") + + Article { + H1 { + "WELKOM" + }.id("banner-text-title") + + P { + context.text + context.text + context.text + context.text + }.id("banner-text-body") + }.id("banner-text") + + Section().id("angle") + } + } + } + } + + struct AMMenu: HTMLComponent { + + let links: TemplateValue + + var body: HTML { + Nav { + ForEach(in: links) { link in + Anchor { + link.name + } + .class("selected") + .href(link.link) + } + + }.id("menu") + } + } +}