From 81091445a6fcca08616c060c0e1089272640fed4 Mon Sep 17 00:00:00 2001 From: Mattes Mohr Date: Thu, 11 Nov 2021 20:02:46 +0100 Subject: [PATCH] Update dependencies and other stuff (#45) * Update dependencies and resolve warnings * Add little changes * Outsource the formulas in a own file * Outsource the HTMLContext class in a own file * Outsource the TemplateValueMapping class in a own file and rename the ContextVariable file * Outsource the properties in a own file * Outsource the EnvironmentModifier struct and rename the Localize file * Add a document rendering test * Add more rendering tests * Add the missing tests to AllTests * Add HTMLNode * Change ContentNode to a generic type for elements with a different type of content * Add the comment element * Add HTMLDocument as a node * Clean up * Add some tests * Update swift.yml --- .github/workflows/swift.yml | 4 +- Package.resolved | 4 +- Package.swift | 23 +- Sources/HTMLKit/External/Attributes.swift | 164 ++-- Sources/HTMLKit/External/Components.swift | 78 +- Sources/HTMLKit/External/Elements.swift | 775 +++++++----------- Sources/HTMLKit/External/Functions.swift | 2 + Sources/HTMLKit/External/Types.swift | 17 + Sources/HTMLKit/Internal/Conditions.swift | 112 ++- .../Internal/EnvironmentModifier.swift | 23 + Sources/HTMLKit/Internal/EscapingOption.swift | 5 +- .../Internal/Extensions/DataTypes.swift | 27 +- Sources/HTMLKit/Internal/Formulas.swift | 68 ++ Sources/HTMLKit/Internal/HTMLBuilder.swift | 6 +- Sources/HTMLKit/Internal/HTMLDocument.swift | 56 -- Sources/HTMLKit/Internal/HTMLRenderer.swift | 70 +- .../{Localize.swift => Localized.swift} | 57 +- .../{ => Mapping}/HTMLAttribute.swift | 13 +- .../Internal/{ => Mapping}/HTMLContent.swift | 35 +- .../Internal/Mapping/HTMLContext.swift | 105 +++ .../HTMLKit/Internal/Mapping/HTMLNode.swift | 89 ++ Sources/HTMLKit/Internal/Nodes.swift | 156 ++-- .../Internal/Properties/Conditionable.swift | 18 + .../Internal/Properties/Defineable.swift | 7 + .../Internal/Properties/Localizable.swift | 16 + .../Internal/Properties/Renderable.swift | 52 ++ .../Internal/TemplateValueMapping.swift | 27 + .../Internal/Variables/ContextVariable.swift | 56 ++ .../Variables/ContextualVariable.swift | 234 ------ .../Internal/Variables/DateVariable.swift | 33 +- .../Internal/Variables/StringValue.swift | 9 + .../Internal/Variables/TemplateValue.swift | 15 +- Tests/HTMLKitTests/PerformanceTests.swift | 69 +- Tests/HTMLKitTests/RenderingTests.swift | 87 +- 34 files changed, 1248 insertions(+), 1264 deletions(-) create mode 100644 Sources/HTMLKit/Internal/EnvironmentModifier.swift create mode 100644 Sources/HTMLKit/Internal/Formulas.swift delete mode 100644 Sources/HTMLKit/Internal/HTMLDocument.swift rename Sources/HTMLKit/Internal/{Localize.swift => Localized.swift} (50%) rename Sources/HTMLKit/Internal/{ => Mapping}/HTMLAttribute.swift (74%) rename Sources/HTMLKit/Internal/{ => Mapping}/HTMLContent.swift (53%) create mode 100644 Sources/HTMLKit/Internal/Mapping/HTMLContext.swift create mode 100644 Sources/HTMLKit/Internal/Mapping/HTMLNode.swift create mode 100644 Sources/HTMLKit/Internal/Properties/Conditionable.swift create mode 100644 Sources/HTMLKit/Internal/Properties/Defineable.swift create mode 100644 Sources/HTMLKit/Internal/Properties/Localizable.swift create mode 100644 Sources/HTMLKit/Internal/Properties/Renderable.swift create mode 100644 Sources/HTMLKit/Internal/TemplateValueMapping.swift create mode 100644 Sources/HTMLKit/Internal/Variables/ContextVariable.swift delete mode 100644 Sources/HTMLKit/Internal/Variables/ContextualVariable.swift create mode 100644 Sources/HTMLKit/Internal/Variables/StringValue.swift diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 32d09b1e..44fae0d3 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -24,7 +24,7 @@ jobs: xenial: needs: check-doc-coverage container: - image: swift:5.1.3-xenial + image: swift:5.4.3-xenial runs-on: ubuntu-latest steps: - uses: actions/checkout@v1.2.0 @@ -33,7 +33,7 @@ jobs: bionic: needs: check-doc-coverage container: - image: swift:5.1.3-bionic + image: swift:5.4.3-bionic runs-on: ubuntu-latest steps: - uses: actions/checkout@v1.2.0 diff --git a/Package.resolved b/Package.resolved index 422eca65..3d6b88df 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/miroslavkovac/Lingo.git", "state": { "branch": null, - "revision": "f21f388b04239641b3e88d14f21762125faa9857", - "version": "3.0.5" + "revision": "e4b032f65bde917209a1cb0962c0fd13fa0add2f", + "version": "3.1.0" } } ] diff --git a/Package.swift b/Package.swift index 69fe7697..debdd42c 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.1 +// swift-tools-version:5.4 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -9,26 +9,25 @@ let package = Package( .macOS(.v10_14), ], products: [ - // Products define the executables and libraries produced by a package, and make them visible to other packages. .library( name: "HTMLKit", - targets: ["HTMLKit"]) + targets: ["HTMLKit"] + ) ], dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), - - .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/miroslavkovac/Lingo.git", from: "3.1.0"), ], targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( name: "HTMLKit", - dependencies: ["Lingo"]), + dependencies: ["Lingo"] + ), .testTarget( name: "HTMLKitTests", - dependencies: ["HTMLKit"]), + dependencies: ["HTMLKit"], + resources: [ + .process("Localization") + ] + ) ] ) diff --git a/Sources/HTMLKit/External/Attributes.swift b/Sources/HTMLKit/External/Attributes.swift index 2f6136e2..864f8272 100644 --- a/Sources/HTMLKit/External/Attributes.swift +++ b/Sources/HTMLKit/External/Attributes.swift @@ -44,7 +44,7 @@ public protocol GlobalAttributes { func on(click: HTMLContent) -> Self } -extension GlobalAttributes where Self: AttributeNode { +extension GlobalAttributes where Self: HTMLNode { public func aria(for key: String, value: HTMLContent) -> Self { add(.init(attribute: "aria-" + key, value: value)) @@ -127,7 +127,7 @@ public protocol AcceptAttribute { func accept(_ value: HTMLContent) -> Self } -extension AcceptAttribute where Self: AttributeNode { +extension AcceptAttribute where Self: HTMLNode { public func accept(_ value: HTMLContent) -> Self { add(.init(attribute: "accept", value: value)) @@ -142,7 +142,7 @@ public protocol ActionAttribute { func action(_ value: HTMLContent) -> Self } -extension ActionAttribute where Self: AttributeNode { +extension ActionAttribute where Self: HTMLNode { public func action(_ value: HTMLContent) -> Self { add(.init(attribute: "action", value: value)) @@ -157,7 +157,7 @@ public protocol AlternativeAttribute { func alternative(_ value: HTMLContent) -> Self } -extension AlternativeAttribute where Self: AttributeNode { +extension AlternativeAttribute where Self: HTMLNode { public func alternative(_ value: HTMLContent) -> Self { add(.init(attribute: "alt", value: value)) @@ -172,7 +172,7 @@ public protocol AsyncAttribute { func async() -> Self } -extension AsyncAttribute where Self: AttributeNode { +extension AsyncAttribute where Self: HTMLNode { public func async() -> Self { add(.init(attribute: "async", value: nil)) @@ -187,7 +187,7 @@ public protocol AutoCompleteAttribute { func autoComplete(_ condition: Conditionable) -> Self } -extension AutoCompleteAttribute where Self: AttributeNode { +extension AutoCompleteAttribute where Self: HTMLNode { public func autoComplete(_ condition: Conditionable) -> Self { add(.init(attribute: "autocomplete", value: nil, isIncluded: condition)) @@ -202,7 +202,7 @@ public protocol AutoFocusAttribute { func autoFocus() -> Self } -extension AutoFocusAttribute where Self: AttributeNode { +extension AutoFocusAttribute where Self: HTMLNode { public func autoFocus() -> Self { add(.init(attribute: "autofocus", value: nil)) @@ -217,7 +217,7 @@ public protocol AutoPlayAttribute { func autoPlay() -> Self } -extension AutoPlayAttribute where Self: AttributeNode { +extension AutoPlayAttribute where Self: HTMLNode { public func autoPlay() -> Self { add(.init(attribute: "autoplay", value: nil)) @@ -232,7 +232,7 @@ public protocol CheckedAttribute { func checked() -> Self } -extension CheckedAttribute where Self: AttributeNode { +extension CheckedAttribute where Self: HTMLNode { public func checked() -> Self { add(.init(attribute: "checked", value: nil)) @@ -247,7 +247,7 @@ public protocol CiteAttribute { func cite(_ text: HTMLContent) -> Self } -extension CiteAttribute where Self: AttributeNode { +extension CiteAttribute where Self: HTMLNode { public func cite(_ text: HTMLContent) -> Self { add(.init(attribute: "cite", value: text)) @@ -262,7 +262,7 @@ public protocol ColumnsAttribute { func columns(_ size: Int) -> Self } -extension ColumnsAttribute where Self: AttributeNode { +extension ColumnsAttribute where Self: HTMLNode { public func columns(_ size: Int) -> Self { add(.init(attribute: "cols", value: size)) @@ -277,7 +277,7 @@ public protocol ColumnSpanAttribute { func columnSpan(_ size: Int) -> Self } -extension ColumnSpanAttribute where Self: AttributeNode { +extension ColumnSpanAttribute where Self: HTMLNode { public func columnSpan(_ size: Int) -> Self { add(.init(attribute: "colspan", value: size)) @@ -292,7 +292,7 @@ public protocol ContentAttribute { func content(_ value: HTMLContent) -> Self } -extension ContentAttribute where Self: AttributeNode { +extension ContentAttribute where Self: HTMLNode { public func content(_ value: HTMLContent) -> Self { add(.init(attribute: "content", value: value)) @@ -307,7 +307,7 @@ public protocol ControlsAttribute { func controls() -> Self } -extension ControlsAttribute where Self: AttributeNode { +extension ControlsAttribute where Self: HTMLNode { public func controls() -> Self { add(.init(attribute: "controls", value: nil)) @@ -322,7 +322,7 @@ public protocol CoordinatesAttribute { func coordinates(_ value: HTMLContent) -> Self } -extension CoordinatesAttribute where Self: AttributeNode { +extension CoordinatesAttribute where Self: HTMLNode { public func coordinates(_ value: HTMLContent) -> Self { add(.init(attribute: "coords", value: value)) @@ -337,7 +337,7 @@ public protocol DataAttribute { func data(_ value: HTMLContent) -> Self } -extension DataAttribute where Self: AttributeNode { +extension DataAttribute where Self: HTMLNode { public func data(_ value: HTMLContent) -> Self { add(.init(attribute: "data", value: value)) @@ -352,7 +352,7 @@ public protocol DateTimeAttribute { func dateTime(_ value: HTMLContent) -> Self } -extension DateTimeAttribute where Self: AttributeNode { +extension DateTimeAttribute where Self: HTMLNode { public func dateTime(_ value: HTMLContent) -> Self { add(.init(attribute: "datetime", value: value)) @@ -367,7 +367,7 @@ public protocol DefaultAttribute { func `default`() -> Self } -extension DefaultAttribute where Self: AttributeNode { +extension DefaultAttribute where Self: HTMLNode { public func `default`() -> Self { add(.init(attribute: "default", value: nil)) @@ -382,7 +382,7 @@ public protocol DisabledAttribute { func disabled() -> Self } -extension DisabledAttribute where Self: AttributeNode { +extension DisabledAttribute where Self: HTMLNode { public func disabled() -> Self { add(.init(attribute: "disabled", value: nil)) @@ -397,7 +397,7 @@ public protocol DownloadAttribute { func download() -> Self } -extension DownloadAttribute where Self: AttributeNode { +extension DownloadAttribute where Self: HTMLNode { public func download() -> Self { add(.init(attribute: "download", value: nil)) @@ -412,7 +412,7 @@ public protocol EncodingAttribute { func encoding(_ type: EncodeType) -> Self } -extension EncodingAttribute where Self: AttributeNode { +extension EncodingAttribute where Self: HTMLNode { public func encoding(_ type: EncodeType) -> Self { add(.init(attribute: "enctype", value: type.rawValue)) @@ -427,7 +427,7 @@ public protocol ForAttribute { func `for`(_ value: HTMLContent) -> Self } -extension ForAttribute where Self: AttributeNode { +extension ForAttribute where Self: HTMLNode { public func `for`(_ value: HTMLContent) -> Self { add(.init(attribute: "for", value: value)) @@ -442,7 +442,7 @@ public protocol FormAttribute { func form(_ value: HTMLContent) -> Self } -extension FormAttribute where Self: AttributeNode { +extension FormAttribute where Self: HTMLNode { public func form(_ value: HTMLContent) -> Self { add(.init(attribute: "form", value: value)) @@ -457,7 +457,7 @@ public protocol FormActionAttribute { func formAction(_ value: HTMLContent) -> Self } -extension FormActionAttribute where Self: AttributeNode { +extension FormActionAttribute where Self: HTMLNode { public func formAction(_ value: HTMLContent) -> Self { add(.init(attribute: "formaction", value: value)) @@ -472,7 +472,7 @@ public protocol HeaderAttribute { func header(_ value: HTMLContent) -> Self } -extension HeaderAttribute where Self: AttributeNode { +extension HeaderAttribute where Self: HTMLNode { public func header(_ value: HTMLContent) -> Self { add(.init(attribute: "header", value: value)) @@ -487,7 +487,7 @@ public protocol HeightAttribute { func height(_ height: Int) -> Self } -extension HeightAttribute where Self: AttributeNode { +extension HeightAttribute where Self: HTMLNode { public func height(_ height: Int) -> Self { add(.init(attribute: "height", value: height)) @@ -502,7 +502,7 @@ public protocol HighAttribute { func high(_ height: Int) -> Self } -extension HighAttribute where Self: AttributeNode { +extension HighAttribute where Self: HTMLNode { public func high(_ height: Int) -> Self { add(.init(attribute: "high", value: height)) @@ -517,7 +517,7 @@ public protocol ReferenceAttribute { func reference(_ value: HTMLContent) -> Self } -extension ReferenceAttribute where Self: AttributeNode { +extension ReferenceAttribute where Self: HTMLNode { public func reference(_ value: HTMLContent) -> Self { add(.init(attribute: "href", value: value)) @@ -532,7 +532,7 @@ public protocol ReferenceLanguageAttribute { func referenceLanguage(_ type: LanguageType) -> Self } -extension ReferenceLanguageAttribute where Self: AttributeNode { +extension ReferenceLanguageAttribute where Self: HTMLNode { public func referenceLanguage(_ type: LanguageType) -> Self { add(.init(attribute: "hreflang", value: type.rawValue)) @@ -547,7 +547,7 @@ public protocol KindAttribute { func kind(_ text: HTMLContent) -> Self } -extension KindAttribute where Self: AttributeNode { +extension KindAttribute where Self: HTMLNode { public func kind(_ text: HTMLContent) -> Self { add(.init(attribute: "kind", value: text)) @@ -562,7 +562,7 @@ public protocol LabelAttribute { func label(_ value: HTMLContent) -> Self } -extension LabelAttribute where Self: AttributeNode { +extension LabelAttribute where Self: HTMLNode { public func label(_ value: HTMLContent) -> Self { add(.init(attribute: "label", value: value)) @@ -577,7 +577,7 @@ public protocol ListAttribute { func list(_ value: HTMLContent) -> Self } -extension ListAttribute where Self: AttributeNode { +extension ListAttribute where Self: HTMLNode { public func list(_ value: HTMLContent) -> Self { add(.init(attribute: "list", value: value)) @@ -592,7 +592,7 @@ public protocol LoopAttribute { func loop() -> Self } -extension LoopAttribute where Self: AttributeNode { +extension LoopAttribute where Self: HTMLNode { public func loop() -> Self { add(.init(attribute: "loop", value: nil)) @@ -607,7 +607,7 @@ public protocol MaximumValueAttribute { func maximumValue(value: HTMLContent) -> Self } -extension MaximumValueAttribute where Self: AttributeNode { +extension MaximumValueAttribute where Self: HTMLNode { public func maximumValue(value: HTMLContent) -> Self { add(.init(attribute: "max", value: value)) @@ -622,7 +622,7 @@ public protocol MaximumLengthAttribute { func maximumLength(value: HTMLContent) -> Self } -extension MaximumLengthAttribute where Self: AttributeNode { +extension MaximumLengthAttribute where Self: HTMLNode { public func maximumLength(value: HTMLContent) -> Self { add(.init(attribute: "maxlength", value: value)) @@ -637,7 +637,7 @@ public protocol MediaAttribute { func media(value: HTMLContent) -> Self } -extension MediaAttribute where Self: AttributeNode { +extension MediaAttribute where Self: HTMLNode { public func media(value: HTMLContent) -> Self { add(.init(attribute: "media", value: value)) @@ -652,7 +652,7 @@ public protocol MethodAttribute { func method(_ method: MethodType) -> Self } -extension MethodAttribute where Self: AttributeNode { +extension MethodAttribute where Self: HTMLNode { public func method(_ method: MethodType) -> Self { add(.init(attribute: "method", value: method.rawValue)) @@ -667,7 +667,7 @@ public protocol MinimumValueAttribute { func minimumValue(value: HTMLContent) -> Self } -extension MinimumValueAttribute where Self: AttributeNode { +extension MinimumValueAttribute where Self: HTMLNode { public func minimumValue(value: HTMLContent) -> Self { add(.init(attribute: "min", value: value)) @@ -682,7 +682,7 @@ public protocol MinimumLengthAttribute { func minimumLength(value: HTMLContent) -> Self } -extension MinimumLengthAttribute where Self: AttributeNode { +extension MinimumLengthAttribute where Self: HTMLNode { public func minimumLength(value: HTMLContent) -> Self { add(.init(attribute: "minlength", value: value)) @@ -697,7 +697,7 @@ public protocol MultipleAttribute { func multiple() -> Self } -extension MultipleAttribute where Self: AttributeNode { +extension MultipleAttribute where Self: HTMLNode { public func multiple() -> Self { add(.init(attribute: "multiple", value: nil)) @@ -712,7 +712,7 @@ public protocol MutedAttribute { func muted() -> Self } -extension MutedAttribute where Self: AttributeNode { +extension MutedAttribute where Self: HTMLNode { public func muted() -> Self { add(.init(attribute: "muted", value: nil)) @@ -724,22 +724,14 @@ extension MutedAttribute where Self: AttributeNode { /// public protocol NameAttribute { - associatedtype NameType: RawRepresentable where NameType.RawValue == String - func name(_ value: NameType) -> Self - - func name(_ value: TemplateValue) -> Self } -extension NameAttribute where Self: AttributeNode { +extension NameAttribute where Self: HTMLNode { public func name(_ value: NameType) -> Self { add(.init(attribute: "name", value: value.rawValue)) } - - public func name(_ value: TemplateValue) -> Self { - add(.init(attribute: "name", value: value)) - } } /// The protocol provides @@ -750,7 +742,7 @@ public protocol OpenAttribute { func isOpen(_ condition: Conditionable) -> Self } -extension OpenAttribute where Self: AttributeNode { +extension OpenAttribute where Self: HTMLNode { public func isOpen(_ condition: Conditionable) -> Self { add(.init(attribute: "open", value: nil, isIncluded: condition)) @@ -765,7 +757,7 @@ public protocol OptimumAttribute { func optimum(_ value: HTMLContent) -> Self } -extension OptimumAttribute where Self: AttributeNode { +extension OptimumAttribute where Self: HTMLNode { public func optimum(_ value: HTMLContent) -> Self { add(.init(attribute: "optimum", value: value)) @@ -780,7 +772,7 @@ public protocol PatternAttribute { func pattern(_ regex: String) -> Self } -extension AttributeNode where Self: PatternAttribute { +extension PatternAttribute where Self: HTMLNode { public func pattern(_ regex: String) -> Self { add(.init(attribute: "pattern", value: regex)) @@ -795,7 +787,7 @@ public protocol PingAttribute { func ping(_ value: HTMLContent) -> Self } -extension PingAttribute where Self: AttributeNode { +extension PingAttribute where Self: HTMLNode { public func ping(_ value: HTMLContent) -> Self { add(.init(attribute: "ping", value: value)) @@ -810,7 +802,7 @@ public protocol PlaceholderAttribute { func placeholder(_ text: HTMLContent) -> Self } -extension PlaceholderAttribute where Self: AttributeNode { +extension PlaceholderAttribute where Self: HTMLNode { public func placeholder(_ text: HTMLContent) -> Self { add(.init(attribute: "placeholder", value: text)) @@ -832,7 +824,7 @@ public protocol PosterAttribute { func poster(value: HTMLContent) -> Self } -extension PosterAttribute where Self: AttributeNode { +extension PosterAttribute where Self: HTMLNode { public func poster(value: HTMLContent) -> Self { add(.init(attribute: "poster", value: value)) @@ -847,7 +839,7 @@ public protocol ReadyOnlyAttribute { func readonly() -> Self } -extension ReadyOnlyAttribute where Self: AttributeNode { +extension ReadyOnlyAttribute where Self: HTMLNode { public func readonly() -> Self { add(.init(attribute: "readonly", value: nil)) @@ -862,7 +854,7 @@ public protocol ReferrerPolicyAttribute { func referrerPolicy(_ type: PolicyType) -> Self } -extension ReferrerPolicyAttribute where Self: AttributeNode { +extension ReferrerPolicyAttribute where Self: HTMLNode { public func referrerPolicy(_ type: PolicyType) -> Self { add(.init(attribute: "referrerpolicy", value: type.rawValue)) @@ -874,14 +866,12 @@ extension ReferrerPolicyAttribute where Self: AttributeNode { /// public protocol RelationshipAttribute { - associatedtype RelationshipTypes: RawRepresentable where RelationshipTypes.RawValue == String - - func relationship(_ value: RelationshipTypes) -> Self + func relationship(_ value: RelationshipType) -> Self } -extension RelationshipAttribute where Self: AttributeNode { +extension RelationshipAttribute where Self: HTMLNode { - public func relationship(_ value: RelationshipTypes) -> Self { + public func relationship(_ value: RelationshipType) -> Self { add(.init(attribute: "rel", value: value.rawValue)) } } @@ -894,7 +884,7 @@ public protocol RequiredAttribute { func required() -> Self } -extension RequiredAttribute where Self: AttributeNode { +extension RequiredAttribute where Self: HTMLNode { public func required() -> Self { add(.init(attribute: "required", value: nil)) @@ -909,7 +899,7 @@ public protocol ReversedAttribute { func reversed() -> Self } -extension ReversedAttribute where Self: AttributeNode { +extension ReversedAttribute where Self: HTMLNode { public func reversed() -> Self { add(.init(attribute: "reversed", value: nil)) @@ -924,7 +914,7 @@ public protocol RowsAttribute { func rows(_ size: Int) -> Self } -extension RowsAttribute where Self: AttributeNode { +extension RowsAttribute where Self: HTMLNode { public func rows(_ size: Int) -> Self { add(.init(attribute: "rows", value: size)) @@ -939,7 +929,7 @@ public protocol RowSpanAttribute { func rowSpan(_ size: Int) -> Self } -extension RowSpanAttribute where Self: AttributeNode { +extension RowSpanAttribute where Self: HTMLNode { public func rowSpan(_ size: Int) -> Self { add(.init(attribute: "rowspan", value: size)) @@ -954,7 +944,7 @@ public protocol SandboxAttribute { func sandbox() -> Self } -extension SandboxAttribute where Self: AttributeNode { +extension SandboxAttribute where Self: HTMLNode { public func sandbox() -> Self { add(.init(attribute: "sandbox", value: nil)) @@ -969,7 +959,7 @@ public protocol ScopeAttribute { func scope(_ text: HTMLContent) -> Self } -extension ScopeAttribute where Self: AttributeNode { +extension ScopeAttribute where Self: HTMLNode { public func scope(_ text: HTMLContent) -> Self { add(.init(attribute: "scope", value: text)) @@ -984,7 +974,7 @@ public protocol ShapeAttribute { func shape(_ type: ShapeType) -> Self } -extension ShapeAttribute where Self: AttributeNode { +extension ShapeAttribute where Self: HTMLNode { public func shape(_ type: ShapeType) -> Self { add(.init(attribute: "shape", value: type.rawValue)) @@ -999,7 +989,7 @@ public protocol SizeAttribute { func size(_ size: Int) -> Self } -extension SizeAttribute where Self: AttributeNode { +extension SizeAttribute where Self: HTMLNode { public func size(_ size: Int) -> Self { add(.init(attribute: "size", value: size)) @@ -1014,7 +1004,7 @@ public protocol SizesAttribute { func sizes(_ size: Int) -> Self } -extension SizesAttribute where Self: AttributeNode { +extension SizesAttribute where Self: HTMLNode { public func sizes(_ size: Int) -> Self { add(.init(attribute: "sizes", value: size)) @@ -1029,7 +1019,7 @@ public protocol SpanAttribute { func span(_ size: Int) -> Self } -extension SpanAttribute where Self: AttributeNode { +extension SpanAttribute where Self: HTMLNode { public func span(_ size: Int) -> Self { add(.init(attribute: "span", value: size)) @@ -1044,7 +1034,7 @@ public protocol SourceAttribute { func source(_ link: HTMLContent) -> Self } -extension SourceAttribute where Self: AttributeNode { +extension SourceAttribute where Self: HTMLNode { public func source(_ link: HTMLContent) -> Self { add(.init(attribute: "src", value: link)) @@ -1059,7 +1049,7 @@ public protocol StartAttribute { func start(_ size: Int) -> Self } -extension StartAttribute where Self: AttributeNode { +extension StartAttribute where Self: HTMLNode { public func start(_ size: Int) -> Self { add(.init(attribute: "start", value: size)) @@ -1074,7 +1064,7 @@ public protocol StepAttribute { func step(_ size: Int) -> Self } -extension StepAttribute where Self: AttributeNode { +extension StepAttribute where Self: HTMLNode { public func step(_ size: Int) -> Self { add(.init(attribute: "step", value: size)) @@ -1089,7 +1079,7 @@ public protocol TargetAttribute { func target(_ type: TargetType) -> Self } -extension TargetAttribute where Self: AttributeNode { +extension TargetAttribute where Self: HTMLNode { public func target(_ type: TargetType) -> Self { add(.init(attribute: "target", value: type.rawValue)) @@ -1104,7 +1094,7 @@ public protocol TypeAttribute { func type(_ value: TemplateValue) -> Self } -extension TypeAttribute where Self: AttributeNode { +extension TypeAttribute where Self: HTMLNode { public func type(_ value: TemplateValue) -> Self { add(.init(attribute: "type", value: value)) @@ -1119,7 +1109,7 @@ public protocol ValueAttribute { func value(_ text: HTMLContent) -> Self } -extension ValueAttribute where Self: AttributeNode { +extension ValueAttribute where Self: HTMLNode { public func value(_ text: HTMLContent) -> Self { add(.init(attribute: "value", value: text)) @@ -1134,7 +1124,7 @@ public protocol WidthAttribute { func width(_ width: Int) -> Self } -extension WidthAttribute where Self: AttributeNode { +extension WidthAttribute where Self: HTMLNode { public func width(_ width: Int) -> Self { add(.init(attribute: "width", value: width)) @@ -1149,9 +1139,21 @@ public protocol WrapAttribute { func wrap(_ type: WrapType) -> Self } -extension WrapAttribute where Self: AttributeNode { +extension WrapAttribute where Self: HTMLNode { public func wrap(_ type: WrapType) -> Self { add(.init(attribute: "wrap", value: type.rawValue)) } } + +public protocol PropertyAttribute { + + func property(_ type: OpenGraphType) -> Self +} + +extension PropertyAttribute where Self: HTMLNode { + + public func property(_ type: OpenGraphType) -> Self { + add(.init(attribute: "property", value: type.rawValue)) + } +} diff --git a/Sources/HTMLKit/External/Components.swift b/Sources/HTMLKit/External/Components.swift index ab542518..9e59f8b1 100644 --- a/Sources/HTMLKit/External/Components.swift +++ b/Sources/HTMLKit/External/Components.swift @@ -1,7 +1,9 @@ +// MARK: components + /// The component ist for /// /// -public struct MetaTitle: HTMLComponent, AttributeNode, LocalizableNode { +public struct MetaTitle: HTMLComponent, Localizable { struct Node: ContentNode { public var name: String { "title" } @@ -11,16 +13,27 @@ public struct MetaTitle: HTMLComponent, AttributeNode, LocalizableNode { public var content: HTMLContent } - let content: HTMLContent + public let content: HTMLContent + public var attributes: [HTMLAttribute] - let useOpenGraphMetadata: Conditionable - let useTwitterMetadata: Conditionable + + public let useOpenGraphMetadata: Conditionable + + public let useTwitterMetadata: Conditionable public var body: HTMLContent { [ Node(attributes: attributes, content: content), - IF(useOpenGraphMetadata) { Meta().property("og:title").content(content) }, - IF(useTwitterMetadata) { Meta().name("twitter:title").content(content) } + IF(useOpenGraphMetadata) { + Meta() + .property(.title) + .content(content) + }, + IF(useTwitterMetadata) { + Meta() + .property(.title) + .content(content) + } ] } @@ -45,7 +58,7 @@ public struct MetaTitle: HTMLComponent, AttributeNode, LocalizableNode { useTwitterMetadata = true } - init(attributes: [HTMLAttribute] = [], content: HTMLContent = "", useOpenGraphMetadata: Conditionable = true, useTwitterMetadata: Conditionable = true) { + public init(attributes: [HTMLAttribute] = [], content: HTMLContent = "", useOpenGraphMetadata: Conditionable = true, useTwitterMetadata: Conditionable = true) { self.content = content self.attributes = attributes self.useOpenGraphMetadata = useOpenGraphMetadata @@ -71,7 +84,7 @@ public struct MetaTitle: HTMLComponent, AttributeNode, LocalizableNode { public struct Stylesheet: HTMLComponent { @TemplateValue(String.self) - var url + public var url public init(url: TemplateValue) { self.url = url @@ -92,18 +105,29 @@ public struct Stylesheet: HTMLComponent { /// The component ist for /// /// -public struct MetaDescription: HTMLComponent, LocalizableNode { +public struct MetaDescription: HTMLComponent, Localizable { - var description: HTMLContent + public var description: HTMLContent - let useOpenGraphMetadata: Conditionable - let useTwitterMetadata: Conditionable + public let useOpenGraphMetadata: Conditionable + + public let useTwitterMetadata: Conditionable public var body: HTMLContent { [ - Meta().name(.description).content(description), - IF(useOpenGraphMetadata) { Meta().property("og:description").content(description) }, - IF(useTwitterMetadata) { Meta().name("twitter:description").content(description) } + Meta() + .property(.description) + .content(description), + IF(useOpenGraphMetadata) { + Meta() + .property(.description) + .content(description) + }, + IF(useTwitterMetadata) { + Meta() + .property(.description) + .content(description) + } ] } @@ -125,7 +149,7 @@ public struct MetaDescription: HTMLComponent, LocalizableNode { self.useOpenGraphMetadata = true } - init(description: HTMLContent, useOpenGraphMetadata: Conditionable, useTwitterMetadata: Conditionable) { + public init(description: HTMLContent, useOpenGraphMetadata: Conditionable, useTwitterMetadata: Conditionable) { self.description = description self.useOpenGraphMetadata = useOpenGraphMetadata self.useTwitterMetadata = useTwitterMetadata @@ -145,7 +169,7 @@ public struct MetaDescription: HTMLComponent, LocalizableNode { /// public struct FavIcon: HTMLComponent { - let url: TemplateValue + public let url: TemplateValue public init(url: String) { self.url = .constant(url) @@ -179,8 +203,8 @@ public struct Viewport: HTMLComponent { } } - var mode: WidthMode - var internalScale: Double = 1 + public var mode: WidthMode + public var internalScale: Double = 1 public init(_ mode: WidthMode, internalScale: Double = 1) { self.mode = mode @@ -188,25 +212,29 @@ public struct Viewport: HTMLComponent { } public var body: HTMLContent { - Meta().name(.viewport).content("width=\(mode.width), initial-scale=\(internalScale)") + Meta() + .name(.viewport) + .content("width=\(mode.width), initial-scale=\(internalScale)") } } /// The component ist for /// /// -public struct Author: HTMLComponent, LocalizableNode { +public struct Author: HTMLComponent, Localizable { - var author: HTMLContent + public var author: HTMLContent @TemplateValue(String?.self) - var twitterHandle + public var twitterHandle public var body: HTMLContent { [ Meta().name(.author).content(author), Unwrap(twitterHandle) { handle in - Meta().name("twitter:creator").content(handle) + Meta() + .name(.init(rawValue: "twitter:creator")!) + .content(handle) } ] } @@ -223,7 +251,7 @@ public struct Author: HTMLComponent, LocalizableNode { author = Localized(key: localizedKey, context: context) } - init(author: HTMLContent, handle: TemplateValue) { + public init(author: HTMLContent, handle: TemplateValue) { self.author = author self.twitterHandle = handle } diff --git a/Sources/HTMLKit/External/Elements.swift b/Sources/HTMLKit/External/Elements.swift index e955ba3f..f6e76bb1 100644 --- a/Sources/HTMLKit/External/Elements.swift +++ b/Sources/HTMLKit/External/Elements.swift @@ -1,4 +1,5 @@ // MARK: aliases + public typealias Nav = Navigation public typealias H1 = Heading1 public typealias H2 = Heading2 @@ -51,7 +52,31 @@ public typealias Td = DataCell public typealias Th = HeaderCell public typealias Optgroup = OptionGroup -// MARK: structs +// MARK: elements + +/// The `") } - public func modify(unwrap value: TemplateValue, modifyer: (TemplateValue, Self) -> Self) -> Self { - switch value { - case .constant(let optional): - guard let value = optional else { - return self - } - let emptyNode = self.copy(with: []) - let modified = modifyer(.constant(value), emptyNode) - - return self.add(attributes: modified.attributes) - case .dynamic(let context): - let emptyNode = self.copy(with: []) - var modified: Self! - if context.isMascadingOptional { - modified = modifyer(.dynamic(context.unsafeCast(to: Value.self)), emptyNode) - } else { - modified = modifyer(.dynamic(context.unsafelyUnwrapped), emptyNode) - } - - return self.add(attributes: modified.wrapAttributes(with: context.isDefinded)) - } - } + public func render(with manager: HTMLRenderer.ContextManager) throws -> String { - public func wrapAttributes(with condition: Conditionable) -> [HTMLAttribute] { - attributes.map { attribute in - if let value = attribute.value { - return HTMLAttribute( - attribute: attribute.attribute, - value: value, - isIncluded: condition - ) - } else { - return HTMLAttribute( - attribute: attribute.attribute, - value: nil, - isIncluded: condition - ) - } - } + return "" } } /// The node is for /// /// -public protocol AddableAttributeNode: HTMLContent, GlobalAttributes { - - var attributes: [HTMLAttribute] { get } +public protocol DocumentNode: HTMLContent { + + associatedtype Content: HTMLContent + + var content: Content { get } +} - func add(_ attribute: HTMLAttribute, withSpace: Bool) -> Self +extension DocumentNode { + + public func prerender(_ formula: HTMLRenderer.Formula) throws { + + formula.add(string: "") + } - func add(attributes: [HTMLAttribute], withSpace: Bool) -> Self + public func render(with manager: HTMLRenderer.ContextManager) throws -> String { - func value(of attribute: String) -> HTMLContent? + return "" + } } diff --git a/Sources/HTMLKit/Internal/Properties/Conditionable.swift b/Sources/HTMLKit/Internal/Properties/Conditionable.swift new file mode 100644 index 00000000..35272702 --- /dev/null +++ b/Sources/HTMLKit/Internal/Properties/Conditionable.swift @@ -0,0 +1,18 @@ +/// The protocol defines +/// +/// +public protocol Conditionable: HTMLContent { + + func evaluate(with manager: HTMLRenderer.ContextManager) throws -> Bool +} + +extension Conditionable { + + public func render(with manager: HTMLRenderer.ContextManager) throws -> String { + try evaluate(with: manager).render(with: manager) + } + + public func prerender(_ formula: HTMLRenderer.Formula) throws { + formula.add(mappable: self) + } +} diff --git a/Sources/HTMLKit/Internal/Properties/Defineable.swift b/Sources/HTMLKit/Internal/Properties/Defineable.swift new file mode 100644 index 00000000..957d325c --- /dev/null +++ b/Sources/HTMLKit/Internal/Properties/Defineable.swift @@ -0,0 +1,7 @@ +/// The protocol defines +/// +/// +protocol Defineable { + + var isDefinded: Bool { get } +} diff --git a/Sources/HTMLKit/Internal/Properties/Localizable.swift b/Sources/HTMLKit/Internal/Properties/Localizable.swift new file mode 100644 index 00000000..a639a120 --- /dev/null +++ b/Sources/HTMLKit/Internal/Properties/Localizable.swift @@ -0,0 +1,16 @@ +/// The protocol defines +/// +/// +public protocol Localizable { + + init(_ localizedKey: String) + + init(_ localizedKey: String, with context: TemplateValue) where B: Encodable +} + +extension Localizable { + + public init(_ localizedKey: String, with context: T) where T: Encodable { + self.init(localizedKey, with: TemplateValue.constant(context)) + } +} diff --git a/Sources/HTMLKit/Internal/Properties/Renderable.swift b/Sources/HTMLKit/Internal/Properties/Renderable.swift new file mode 100644 index 00000000..bd097fa2 --- /dev/null +++ b/Sources/HTMLKit/Internal/Properties/Renderable.swift @@ -0,0 +1,52 @@ +/// The protocols defines +/// +/// +public protocol Renderable: AnyObject { + + /// Renders a `HTMLTemplate` formula + /// + /// try renderer.render(raw: WelcomeView.self, with: WelcomeView.Context(...)) + /// + /// - Parameters: + /// - type: The view type to render + /// - context: The needed context to render the view with + /// - Returns: Returns a rendered view in a raw `String` + /// - Throws: If the formula do not exists, or if the rendering process fails + func render(raw type: T.Type, with context: T.Context) throws -> String + + /// Renders a `HTMLPage` formula + /// + /// try renderer.render(raw: WelcomeView.self) + /// + /// - Parameter type: The view type to render + /// - Returns: Returns a rendered view in a raw `String` + /// - Throws: If the formula do not exists, or if the rendering process fails + func render(raw type: T.Type) throws -> String + + /// Adds a forumla to the renderer + /// + /// try renderer.add(view: WelcomeView()) + /// + /// - Parameter view: The view type to add + /// - Throws: If the pre-rendering process fails for some reason + func add(view: T) throws + + /// Adds a forumla to the renderer + /// + /// try renderer.add(view: WelcomeView()) + /// + /// - Parameter view: The view type to add + /// - Throws: If the pre-rendering process fails for some reason + func add(view: T) throws + + /// Registers a localization directory to the renderer + /// + /// try renderer.registerLocalization() // Using default values + /// try renderer.registerLocalization(atPath: "Localization", defaultLocale: "nb") + /// + /// - Parameters: + /// - path: A relative path to the localization folder. This is by *default* set to "Resource/Localization" + /// - defaultLocale: The default locale to use. This is by *default* set to "en" + /// - Throws: If there is an error registrating the lingo + func registerLocalization(atPath path: String, defaultLocale: String) throws +} diff --git a/Sources/HTMLKit/Internal/TemplateValueMapping.swift b/Sources/HTMLKit/Internal/TemplateValueMapping.swift new file mode 100644 index 00000000..bdc6b996 --- /dev/null +++ b/Sources/HTMLKit/Internal/TemplateValueMapping.swift @@ -0,0 +1,27 @@ +/// The class is for +/// +/// +public class TemplateValueMapping { + let variable: TemplateValue + let transform: (B) throws -> C + + init(variable: TemplateValue, transform: @escaping (B) throws -> C) { + self.variable = variable + self.transform = transform + } +} + +extension TemplateValueMapping: HTMLContent where C: HTMLContent { + + public func prerender(_ formula: HTMLRenderer.Formula) throws { + switch variable { + case .constant(let value): try transform(value).prerender(formula) + case .dynamic(_): formula.add(mappable: self) + } + } + + public func render(with manager: HTMLRenderer.ContextManager) throws -> String { + let value = try variable.value(from: manager) + return try transform(value).render(with: manager) + } +} diff --git a/Sources/HTMLKit/Internal/Variables/ContextVariable.swift b/Sources/HTMLKit/Internal/Variables/ContextVariable.swift new file mode 100644 index 00000000..4d2783e0 --- /dev/null +++ b/Sources/HTMLKit/Internal/Variables/ContextVariable.swift @@ -0,0 +1,56 @@ +/// The class is for +/// +/// +@dynamicMemberLookup public class ContextVariable { + + let pathId: String + let rootId: String + + let root: KeyPath + + let escaping: EscapingOption + + init(value: KeyPath, id: String, rootId: String = "", escaping: EscapingOption = .safeHTML) { + self.root = value + self.pathId = id + self.rootId = rootId + self.escaping = escaping + } + + public subscript(dynamicMember keyPath: KeyPath) -> ContextVariable { + let newPath = root.appending(path: keyPath) + return ContextVariable(value: newPath, id: pathId + "-" + String(reflecting: Subject.self), rootId: rootId) + } + + public func escaping(_ option: EscapingOption) -> ContextVariable { + .init(value: root, id: pathId, rootId: rootId, escaping: option) + } + + func append(_ context: ContextVariable) throws -> ContextVariable { + let path = root.appending(path: context.root) + return .init(value: path, id: pathId + "-" + context.pathId, rootId: rootId, escaping: escaping) + } +} + +extension ContextVariable { + public static func root(_ value: T.Type = T.self, rootId: String = "") -> ContextVariable { + ContextVariable(value: \T.self, id: rootId + String(reflecting: T.self), rootId: rootId) + } +} + +extension ContextVariable { + + func applyEscaping(_ render: String) -> String { + switch escaping { + case .safeHTML: + return render + .replacingOccurrences(of: "&", with: "&") + .replacingOccurrences(of: "<", with: "<") + .replacingOccurrences(of: ">", with: ">") + .replacingOccurrences(of: "\"", with: """) + .replacingOccurrences(of: "'", with: "'") + case .unsafeNone: + return render + } + } +} diff --git a/Sources/HTMLKit/Internal/Variables/ContextualVariable.swift b/Sources/HTMLKit/Internal/Variables/ContextualVariable.swift deleted file mode 100644 index 1cc1c4bb..00000000 --- a/Sources/HTMLKit/Internal/Variables/ContextualVariable.swift +++ /dev/null @@ -1,234 +0,0 @@ - - -@propertyWrapper -@dynamicMemberLookup -public class HTMLContext { - - enum Errors: Error { - case unknownKeyPath - } - - let pathId: String - let rootId: String - let escaping: EscapingOption - let isMascadingOptional: Bool - - let keyPath: AnyKeyPath - - public var wrappedValue: HTMLContext { self } - - init(keyPath: KeyPath, id: String, rootId: String = "", escaping: EscapingOption = .safeHTML, isMascadingOptional: Bool = false) { - self.keyPath = keyPath - self.pathId = id - self.rootId = rootId - self.escaping = escaping - self.isMascadingOptional = isMascadingOptional - } - - init(keyPath: AnyKeyPath, id: String, rootId: String = "", escaping: EscapingOption = .safeHTML, isMascadingOptional: Bool = false) { - self.keyPath = keyPath - self.pathId = id - self.rootId = rootId - self.escaping = escaping - self.isMascadingOptional = isMascadingOptional - } - - public init(_ value: Value.Type) { - self.keyPath = \Value.self - self.pathId = "" - self.rootId = "" - self.escaping = .safeHTML - self.isMascadingOptional = false - } - - public func escaping(_ option: EscapingOption) -> HTMLContext { - .init(keyPath: keyPath, id: pathId, rootId: rootId, escaping: option, isMascadingOptional: isMascadingOptional) - } - - public subscript(dynamicMember keyPath: KeyPath) -> HTMLContext { - guard let newPath = self.keyPath.appending(path: keyPath) else { - fatalError() - } - return HTMLContext(keyPath: newPath, id: pathId + "-" + String(reflecting: Subject.self), rootId: rootId, isMascadingOptional: isMascadingOptional) - } - - public static func root(_ type: Value.Type, rootId: String) -> HTMLContext { - .init(keyPath: \Value.self, id: "", rootId: rootId) - } - - public func makeOptional() -> HTMLContext { - .init(keyPath: keyPath, id: pathId, rootId: rootId, escaping: escaping, isMascadingOptional: true) - } - - public func unsafeCast(to type: T.Type) -> HTMLContext { - .init(keyPath: keyPath, id: pathId, rootId: rootId, escaping: escaping) - } -} - -extension HTMLContext: HTMLContent where Value: HTMLContent { - public func render(with manager: HTMLRenderer.ContextManager) throws -> String { - applyEscaping( - try manager.value(for: self) - .render(with: manager) - ) - } - - public func prerender(_ formula: HTMLRenderer.Formula) throws { - formula.add(mappable: self) - } - - func applyEscaping(_ render: String) -> String { - switch escaping { - case .safeHTML: - return render - .reduce(into: "") { string, char in - switch char { - case "&": string += "&" - case "<": string += "<" - case ">": string += ">" - case "\"": string += """ - case "'": string += "'" - default: string += String(char) - } - } - case .unsafeNone: - return render - } - } -} - -@dynamicMemberLookup -public class ContextVariable { - - let pathId: String - let rootId: String - - let root: KeyPath - - let escaping: EscapingOption - - init(value: KeyPath, id: String, rootId: String = "", escaping: EscapingOption = .safeHTML) { - self.root = value - self.pathId = id - self.rootId = rootId - self.escaping = escaping - } - - public subscript(dynamicMember keyPath: KeyPath) -> ContextVariable { - let newPath = root.appending(path: keyPath) - return ContextVariable(value: newPath, id: pathId + "-" + String(reflecting: Subject.self), rootId: rootId) - } - - public func escaping(_ option: EscapingOption) -> ContextVariable { - .init(value: root, id: pathId, rootId: rootId, escaping: option) - } - - func append(_ context: ContextVariable) throws -> ContextVariable { - let path = root.appending(path: context.root) - return .init(value: path, id: pathId + "-" + context.pathId, rootId: rootId, escaping: escaping) - } - -// func cast(to: T.Type) -> ContextVariable { -// let anyPath: AnyKeyPath = root -// switch anyPath { -// case let castRoot as KeyPath: -// return .init(value: castRoot, id: pathId, rootId: rootId, escaping: escaping) -// default: -// fatalError("Can not cast value form \(Value.self) to \(T.self).") -// } -// } -} - -extension ContextVariable { - public static func root(_ value: T.Type = T.self, rootId: String = "") -> ContextVariable { - ContextVariable(value: \T.self, id: rootId + String(reflecting: T.self), rootId: rootId) - } -} - -extension ContextVariable { - - func applyEscaping(_ render: String) -> String { - switch escaping { - case .safeHTML: - return render - .replacingOccurrences(of: "&", with: "&") - .replacingOccurrences(of: "<", with: "<") - .replacingOccurrences(of: ">", with: ">") - .replacingOccurrences(of: "\"", with: """) - .replacingOccurrences(of: "'", with: "'") - case .unsafeNone: - return render - } - } -} - -public class TemplateValueMapping { - let variable: TemplateValue - let transform: (B) throws -> C - - init(variable: TemplateValue, transform: @escaping (B) throws -> C) { - self.variable = variable - self.transform = transform - } -} - -extension TemplateValueMapping: HTMLContent where C: HTMLContent { - - public func prerender(_ formula: HTMLRenderer.Formula) throws { - switch variable { - case .constant(let value): try transform(value).prerender(formula) - case .dynamic(_): formula.add(mappable: self) - } - } - - public func render(with manager: HTMLRenderer.ContextManager) throws -> String { - let value = try variable.value(from: manager) - return try transform(value).render(with: manager) - } -} - -@dynamicMemberLookup -struct StringValue { - public subscript(dynamicMember string: Type) -> StringValue { - return .init() - } -} - -public protocol HTMLPage: HTMLContent { - var body: HTMLContent { get } -} - -public typealias HTMLComponent = HTMLPage - -extension HTMLPage { - public func render(with manager: HTMLRenderer.ContextManager) throws -> String { - try body.render(with: manager) - } - - public func prerender(_ formula: HTMLRenderer.Formula) throws { - try body.prerender(formula) - } - - public var scripts: HTMLContent { body.scripts } -} - -public protocol HTMLTemplate: HTMLContent { - associatedtype Context - var context: TemplateValue { get } - - var body: HTMLContent { get } -} - -extension HTMLTemplate { - public var context: TemplateValue { .root() } - - public func render(with manager: HTMLRenderer.ContextManager) throws -> String { - try body.render(with: manager) - } - - public func prerender(_ formula: HTMLRenderer.Formula) throws { - try body.prerender(formula) - } - - public var scripts: HTMLContent { body.scripts } -} diff --git a/Sources/HTMLKit/Internal/Variables/DateVariable.swift b/Sources/HTMLKit/Internal/Variables/DateVariable.swift index 107ec1dd..e71291a0 100644 --- a/Sources/HTMLKit/Internal/Variables/DateVariable.swift +++ b/Sources/HTMLKit/Internal/Variables/DateVariable.swift @@ -1,13 +1,8 @@ -// -// DateVariable.swift -// HTMLKit -// -// Created by Mats Mollestad on 02/05/2019. -// - import Foundation /// A struct that renders a data in a specified format +/// +/// struct DateVariable: HTMLContent { enum Errors: LocalizedError { @@ -33,12 +28,10 @@ struct DateVariable: HTMLContent { } } - /// The key path to the date to render let dateReference: Reference var format: Format - // View `CompiledTemplate` func render(with manager: HTMLRenderer.ContextManager) throws -> String { var optionalDate: Date? @@ -52,7 +45,6 @@ struct DateVariable: HTMLContent { let formatter = DateFormatter() if let locale = manager.locale { - // settign locale before the format in order to bypass Linux bug formatter.locale = .init(identifier: locale) } switch format { @@ -65,7 +57,6 @@ struct DateVariable: HTMLContent { return formatter.string(from: date) } - // View `CompiledTemplate` func prerender(_ formula: HTMLRenderer.Formula) throws { formula.add(mappable: self) } @@ -84,16 +75,6 @@ extension TemplateValue where Value == Date { return DateVariable(dateReference: .solid(self), format: .style(.init(dateStyle: date, timeStyle: time))) } -// /// Render a date in a formate -// /// -// /// - Parameters: -// /// - datePath: The path to the date -// /// - formatter: The DateFormatter to use when rendering the string -// /// - Returns: A `CompiledTemplate` -// public func formating(with formatter: DateFormatter) -> HTML { -// return DateVariable(formatter: formatter, dateReference: .solid(self)) -// } - /// Render a date in a formate /// /// - Parameters: @@ -118,16 +99,6 @@ extension TemplateValue where Value == Date? { return DateVariable(dateReference: .optional(self), format: .style(.init(dateStyle: date, timeStyle: time))) } -// /// Render a date in a format -// /// -// /// - Parameters: -// /// - datePath: The path to the date -// /// - formatter: The DateFormatter to use when rendering the string -// /// - Returns: A `CompiledTemplate` -// public func formating(with formatter: DateFormatter) -> HTML { -// return DateVariable(formatter: formatter, dateReference: .optional(self)) -// } - /// Render a date in a formate /// /// - Parameters: diff --git a/Sources/HTMLKit/Internal/Variables/StringValue.swift b/Sources/HTMLKit/Internal/Variables/StringValue.swift new file mode 100644 index 00000000..fa030394 --- /dev/null +++ b/Sources/HTMLKit/Internal/Variables/StringValue.swift @@ -0,0 +1,9 @@ +/// The struct is for +/// +/// +@dynamicMemberLookup struct StringValue { + + public subscript(dynamicMember string: Type) -> StringValue { + return .init() + } +} diff --git a/Sources/HTMLKit/Internal/Variables/TemplateValue.swift b/Sources/HTMLKit/Internal/Variables/TemplateValue.swift index ef9d4e46..08142743 100644 --- a/Sources/HTMLKit/Internal/Variables/TemplateValue.swift +++ b/Sources/HTMLKit/Internal/Variables/TemplateValue.swift @@ -1,9 +1,9 @@ - import Foundation -@propertyWrapper -@dynamicMemberLookup -public enum TemplateValue { +/// The enum is for +/// +/// +@propertyWrapper @dynamicMemberLookup public enum TemplateValue { case constant(Value) case dynamic(HTMLContext) @@ -58,18 +58,13 @@ public enum TemplateValue { case .dynamic(let variable): return .dynamic(variable.unsafeCast(to: T.self)) } } - - /// This is used to determine if the value pretends it is a optional + var isMasqueradingOptional: Bool { switch self { case .constant: return false case .dynamic(let variable): return variable.isMascadingOptional } } - -// public func map(transform: @escaping (Value) throws -> T) -> TemplateValueMapping { -// TemplateValueMapping(variable: self, transform: transform) -// } } extension TemplateValue: HTMLContent where Value: HTMLContent { diff --git a/Tests/HTMLKitTests/PerformanceTests.swift b/Tests/HTMLKitTests/PerformanceTests.swift index 3a15220a..f9c520e5 100644 --- a/Tests/HTMLKitTests/PerformanceTests.swift +++ b/Tests/HTMLKitTests/PerformanceTests.swift @@ -73,13 +73,80 @@ final class PerformanceTests: XCTestCase { _ = try! renderer.render(raw: TestPage.self) } } + + func testPerformanceWithAttributes() throws { + + let view = TestPage { + Html { + Body { + Header { + Heading1 { + "Performance Test" + } + .id("test") + .class("class") + .role("role") + } + Main { + Article { + Heading3 { + "Test" + } + .id("test") + .class("class") + .role("role") + } + } + .id("test") + .class("class") + .role("role") + } + } + } + + try renderer.add(view: view) + + measure { + _ = try! renderer.render(raw: TestPage.self) + } + } + + func testPerformanceWithoutAttributes() throws { + + let view = TestPage { + Html { + Body { + Header { + Heading1 { + "Performance Test" + } + } + Main { + Article { + Heading3 { + "Test" + } + } + } + } + } + } + + try renderer.add(view: view) + + measure { + _ = try! renderer.render(raw: TestPage.self) + } + } } extension PerformanceTests { static var allTests = [ ("testPerformanceRenderingWhenUsingDefault", testPerformanceRenderingWhenUsingDefault), - ("testPerformanceRenderingWhenUsingTypealiases", testPerformanceRenderingWhenUsingTypealiases) + ("testPerformanceRenderingWhenUsingTypealiases", testPerformanceRenderingWhenUsingTypealiases), + ("testPerformanceWithAttributes", testPerformanceWithAttributes), + ("testPerformanceWithoutAttributes", testPerformanceWithoutAttributes) ] } diff --git a/Tests/HTMLKitTests/RenderingTests.swift b/Tests/HTMLKitTests/RenderingTests.swift index ffa031bb..daca3184 100644 --- a/Tests/HTMLKitTests/RenderingTests.swift +++ b/Tests/HTMLKitTests/RenderingTests.swift @@ -18,6 +18,32 @@ final class RenderingTests: XCTestCase { var renderer = HTMLRenderer() + func testRenderingDocument() throws { + + let view = TestPage { + Document(type: .html5) + Html { + Body { + Paragraph { + "text" + } + } + } + } + + try renderer.add(view: view) + + XCTAssertEqual(try renderer.render(raw: TestPage.self), + """ + \ + \ + \ +

text

\ + \ + + """ + ) + } func testRenderingContentTag() throws { @@ -55,6 +81,22 @@ final class RenderingTests: XCTestCase { ) } + func testRenderingCommentTag() throws { + + let view = TestPage { + Comment("text") + } + + try renderer.add(view: view) + + XCTAssertEqual(try renderer.render(raw: TestPage.self), + """ + + """ + ) + + } + func testRenderingAttributes() throws { let view = TestPage { @@ -63,7 +105,7 @@ final class RenderingTests: XCTestCase { } .role("role") .class("class") - } + } try renderer.add(view: view) @@ -74,6 +116,45 @@ final class RenderingTests: XCTestCase { ) } + + func testRenderingAttributesWithUnterscore() throws { + + let view = TestPage { + Paragraph { + "text" + } + .role("ro_le") + .class("cl_ass") + } + + try renderer.add(view: view) + + XCTAssertEqual(try renderer.render(raw: TestPage.self), + """ +

text

+ """ + ) + } + + func testRenderingAttributesWithHyphens() throws { + + let view = TestPage { + Paragraph { + "text" + } + .role("ro-le") + .class("cl-ass") + } + + try renderer.add(view: view) + + XCTAssertEqual(try renderer.render(raw: TestPage.self), + """ +

text

+ """ + ) + } + func testNesting() throws { let view = TestPage { @@ -118,9 +199,13 @@ final class RenderingTests: XCTestCase { extension RenderingTests { static var allTests = [ + ("testRenderingDocument", testRenderingDocument), ("testRenderingContentTag", testRenderingContentTag), ("testRenderingEmptyTag", testRenderingEmptyTag), + ("testRenderingCommentTag", testRenderingCommentTag), ("testRenderingAttributes", testRenderingAttributes), + ("testRenderingAttributesWithUnterscore", testRenderingAttributesWithUnterscore), + ("testRenderingAttributesWithHyphens", testRenderingAttributesWithHyphens), ("testNesting", testNesting), ("testEscaping", testEscaping) ]