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

Fix some logic and logistics #103

Merged
merged 7 commits into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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