Skip to content

Commit

Permalink
Fix some logic and logistics (#103)
Browse files Browse the repository at this point in the history
* Revise some of the vapor provider

* Rename the vapor provider

The target within the package might get in a naming conflict with the other vapor provider package. As long the other package is not archived, renaming it, prevents the conflict and secures the back compatibility.

* Add support for localization to the provider again

* Revise the vapor provider

* Add a test for the environment modifier

* Update the readme file

* Fix the components plugin
  • Loading branch information
mattesmohr authored Nov 2, 2022
1 parent 52ede3f commit f98464d
Show file tree
Hide file tree
Showing 17 changed files with 500 additions and 384 deletions.
14 changes: 9 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ let package = Package(
products: [
.library(
name: "HTMLKit",
targets: ["HTMLKit", "HTMLKitComponents", "HTMLKitVaporProvider"]
targets: ["HTMLKit", "HTMLKitComponents", "HTMLKitVapor"]
),
.plugin(
name: "ComponentsPlugin",
Expand Down Expand Up @@ -51,10 +51,11 @@ let package = Package(
]
),
.target(
name: "HTMLKitVaporProvider",
name: "HTMLKitVapor",
dependencies: [
.target(name: "HTMLKit"),
.product(name: "Vapor", package: "vapor")
.product(name: "Vapor", package: "vapor"),
.product(name: "Lingo", package: "lingo")
]
),
.testTarget(
Expand Down Expand Up @@ -83,11 +84,14 @@ let package = Package(
]
),
.testTarget(
name: "HTMLKitVaporProviderTests",
name: "HTMLKitVaporTests",
dependencies: [
.target(name: "HTMLKitVaporProvider"),
.target(name: "HTMLKitVapor"),
.target(name: "HTMLKit"),
.product(name: "XCTVapor", package: "vapor")
],
resources: [
.process("Localization")
]
),
.executableTarget(
Expand Down
4 changes: 3 additions & 1 deletion Plugins/ComponentsPlugin/plugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import PackagePlugin
import Foundation

@main
struct ConverterPlugin: CommandPlugin {
struct ComponentsPlugin: CommandPlugin {

func performCommand(context: PluginContext, arguments: [String]) async throws {

Expand All @@ -16,6 +16,8 @@ struct ConverterPlugin: CommandPlugin {

let explanation = """
USAGE: deploy
ARGUMENTS:
"""

print(explanation)
Expand Down
127 changes: 14 additions & 113 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,113 +1,14 @@
# HTMLKit

Render dynamic HTML templates in a typesafe and performant way! By using Swift's powerful language features and a pre-rendering algorithm, HTMLKit will render insanely fast templates but also catch bugs that otherwise might occur with other templating options.

## Getting Started

### Installation

Add the packages as dependecies and targets to your package file.

```swift
/// [Package.swift]

...
dependencies: [
...
///1. Add the packages
.package(name: "HTMLKit", url: "https://github.com/vapor-community/HTMLKit.git", from: "2.4.5"),
.package(name: "HTMLKitVaporProvider", url: "https://github.com/vapor-community/htmlkit-vapor-provider.git", from: "1.2.1")
],
targets: [
.target(
...
dependencies: [
...
/// 2. Add the products
.product(name: "HTMLKit", package: "HTMLKit"),
.product(name: "HTMLKitVaporProvider", package: "HTMLKitVaporProvider")
]
),
...
```

Read the [installation instructions](/Instructions/Installation.md) for more information.

### Definition

Create a new file in your project. Add the import at the top of your file and declare a new structure. Extend your structure by adding a [layout definition](/Instructions/Essential/Layouts.md) to adopt the required properties and methods. Add your content to the `body` property.

```swift
/// [SimplePage.swift]

/// 1. Add the import
import HTMLKit

/// 2. Define a new structure
struct SimplePage: Page {

/// 3. Add some content
var body: AnyContent {
Document(type: .html5)
Html {
Head {
Title {
"SimplePage"
}
}
Body {
Paragraph {
"Hello World!"
}
}
}
}
}
```

### Implementation

Call the structure you have created in your controller handler and use the render method to render the view for the request.

```swift
/// [SimpleController.swift]

...
final class SimpleController {
...
func getPage(req: Request) throws -> EventLoopFuture<View> {
/// 1. Call the structure
return SimplePage().render(for: req)
}
...
}
```

## Features

### Localization

HTMLKit offers localization by the help of the [Lingo framework](https://github.com/miroslavkovac/Lingo.git). See the [instructions](/Instructions/Features/Localization.md) for more information.

### Conversion

You dont have to rewrite your whole codebase to use HTMLKit. HTMLKit offers a converter to translate HTML into HTMLKit. See the [instructions](/Instructions/Features/Conversion.md) for more information.

### Validation

HTMLKit is build with the intention to lead you writing valid code. So you don't need to think about it in particular.

## Resources

### Instructions

See the [instructions](/Instructions/Overview.md) to learn more about the package and the features.

### Components

See the [package](https://github.com/vapor-community/HTMLKit-Components) to extend your experience with HTMLKit. The package is still work in progress, but
it will be available soon.

### Live Example

See the [package](https://github.com/mattesmohr/Website) of our contributor, how he uses HTMLKit in his website.
<div align="center">
<img src="https://avatars.githubusercontent.com/u/26165732?s=200&v=4" width="100" height="100" alt="avatar" />
<h1/>HTMLKit</h1>
<p>Render dynamic HTML templates in a typesafe and performant way! By using Swift's powerful language features and a pre-rendering algorithm, HTMLKit will render insanely fast templates but also catch bugs that otherwise might occur with other templating options.</p>
<a href="https://swiftpackageindex.com/vapor-community/htmlkit/documentation">
<img src="https://img.shields.io/badge/Documentation-17.10.2022-red" alt="documentation" />
</a>
<a href="https://swiftpackageindex.com/vapor-community/HTMLKit">
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fvapor-community%2FHTMLKit%2Fbadge%3Ftype%3Dswift-versions" alt="versions" />
</a>
<a href="https://swiftpackageindex.com/vapor-community/HTMLKit">
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fvapor-community%2FHTMLKit%2Fbadge%3Ftype%3Dplatforms" alt="platforms" />
</a>
</div>
3 changes: 2 additions & 1 deletion Sources/Commands/Components/DeployCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ internal struct DeployCommand {
.appendingPathComponent("Resources", isDirectory: true)

let distributionFile = URL(fileURLWithPath: targetPath)
.appendingPathComponent("Public")
.appendingPathComponent("Public", isDirectory: true)
.appendingPathComponent("HtmlKit", isDirectory: true)
.appendingPathComponent("css")
.appendingPathComponent("all")
.appendingPathExtension("css")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
/// The modifier is for
///
///
public struct EnvironmentModifier: AnyContent {
public struct EnvironmentModifier: GlobalElement {

public let view: AnyContent

Expand Down
91 changes: 91 additions & 0 deletions Sources/HTMLKitVapor/Extensions/Vapor+HTMLKit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
Abstract:
The file contains the extensions of some Vapor directives.
*/

import HTMLKit
import Vapor

extension Application.Views.Provider {

/// Access to the view renderer
public static var htmlkit: Self {
return .init {
$0.views.use {
$0.htmlkit.renderer
}
}
}
}

extension Application {

/// Access to the vapor provider
public var htmlkit: HtmlKit {
return .init(application: self)
}

/// The vapor provider
public struct HtmlKit {

internal struct CacheStorageKey: StorageKey {

public typealias Value = ViewCache
}

/// The view cache
public var views: ViewCache {

if let cache = self.application.storage[CacheStorageKey.self] {
return cache
}

let cache = ViewCache()

self.application.storage[CacheStorageKey.self] = cache

return cache
}

internal struct LingoStorageKey: StorageKey {

public typealias Value = LingoConfiguration
}

/// The view localization
public var lingo: LingoConfiguration {

if let configuration = self.application.storage[LingoStorageKey.self] {
return configuration
}

let configuration = LingoConfiguration()

self.application.storage[LingoStorageKey.self] = configuration

return configuration
}

/// The view renderer
internal var renderer: ViewRenderer {
return .init(eventLoop: self.application.eventLoopGroup.next(), cache: self.views, lingo: lingo)
}

/// The application dependency
public let application: Application

/// Creates the provider
public init(application: Application) {

self.application = application
}
}
}

extension Request {

/// Access to the view renderer
public var htmlkit: ViewRenderer {
return .init(eventLoop: self.eventLoop, cache: self.application.htmlkit.views, lingo: self.application.htmlkit.lingo)
}
}
47 changes: 47 additions & 0 deletions Sources/HTMLKitVapor/LingoConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
Abstract:
The file contains the configuration for Lingo.
*/

import Foundation

/// The localization
public class LingoConfiguration {

/// A enumeration of possible locale identifier
public enum Locale: String {

case arabic = "ar"
case english = "en"
case french = "fr"
case german = "de"
case hindi = "es"
case bengali = "bn"
case russian = "ru"
case portuguese = "pt"
case indonesian = "id"
}

/// The root path
internal var defaultDirectory: String

/// The locale indentifier
internal var defaultLocale: String

/// Creates a configuration
internal init() {

self.defaultDirectory = "Resources/Localization"
self.defaultLocale = "en"
}

/// Sets the root path
public func set(directory: URL) {
self.defaultDirectory = directory.path
}

/// Sets the default locale indentifier
public func set(locale: Locale) {
self.defaultLocale = locale.rawValue
}
}
50 changes: 50 additions & 0 deletions Sources/HTMLKitVapor/ViewCache.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
Abstract:
The file contains the cache of the Vapor renderer.
*/

import HTMLKit
import Vapor

/// The cache
public class ViewCache {

/// The cache storage
internal var storage: [String: HTMLKit.Formula]

/// Creates the cache
internal init() {
self.storage = [:]
}

/// Retrieves a formula from the storage
internal func retrieve(name: String, on loop: EventLoop) -> EventLoopFuture<HTMLKit.Formula?> {

if let cache = self.storage[name] {
return loop.makeSucceededFuture(cache)

} else {
return loop.makeSucceededFuture(nil)
}
}

/// Sets or updates a formula at the storage
internal func upsert(name: String, formula: HTMLKit.Formula) {
self.storage.updateValue(formula, forKey: name)
}

/// Removes a formula from the storage
internal func remove(name: String) {
self.storage.removeValue(forKey: name)
}

/// Adds a view to the storage
public func add<T: HTMLKit.AnyLayout>(view: T) {

let formula = HTMLKit.Formula()

try? view.prerender(formula)

self.storage.updateValue(formula, forKey: String(reflecting: T.self))
}
}
Loading

0 comments on commit f98464d

Please sign in to comment.