一个用于解决构建 Swift Web 框架中各种问题的框架集合。每个框架都专注于一个单一问题,例如 HTML 渲染、CSS 预处理、路由、中间件等等。它们也不依赖于集合中的任何其他框架。您可以选择您想要和不想要的部分,例如您可以单独使用 Html
而不使用 Css
。
这个库应被视为实验性的。如果您觉得它的内容有用,请考虑维护一个分支。
import PackageDescription
let package = Package(
dependencies: [
.package(url: "https://github.com/pointfreeco/swift-web.git", .branch("master")),
]
)
这个库包含一个广泛的测试套件和一组可以探索的 playground。要使其运行
git clone https://github.com/pointfreeco/swift-web.git
cd swift-web
swift package generate-xcodeproj
xed .
一个用于 CSS 预处理器(如 Sass)的 EDSL。一些简单的值类型和函数允许您对大多数 CSS 进行建模,并允许您表达标准 CSS 中不可能实现的新事物。
import Css
let css = body % (
padding(all: .rem(2))
<> background(Color.hsl(60, 0.5, 0.8))
)
render(css: css)
body {
padding-top : 2rem;
padding-right : 2rem;
padding-bottom : 2rem;
padding-left : 2rem;
background : #e6e6b3;
}
一些类型和函数,用于将服务器中间件建模为一个将请求转换为响应的简单函数。它使用 phantom 类型来表达允许您编写状态、标头和响应正文的状态转换。
import HttpPipeline
let middleware = writeStatus(.ok)
>>> writeHeader(.contentType(.html))
>>> closeHeaders
>>> send(render(document).data(using: .utf8))
>>> end
let request = URLRequest(url: URL(string: "/")!)
let conn = connection(from: request).map(const(Data?.none))
▿ Step
ResponseEnded
▿ Request
GET /
(Data, 0 bytes)
▿ Response
Status 200 OK
Content-Type: text/html; charset=utf8
<html><body><p>Hello world!</p><p>Goodbye!</p><a href="/">Home</a></body></html>
一个基于“applicatives”原则构建的路由器,统一了解析请求和打印路由。它具有健壮性、可组合性和类型安全性。它的工作是从浏览器获取传入的、非结构化的 URLRequest
,并将其转换为结构化的值,以便您的应用程序可以执行其需要执行的操作来生成响应。此外,给定一个值,它可以反向操作,生成一个可以在超链接中使用的请求。这个库的大部分想法都来自 这篇论文。
import ApplicativeRouter
struct UserData: Decodable {
let email: String
}
enum Route {
case home
case episodes
case episode(String)
case search(String?)
case signup(UserData?)
}
let router = [
// Matches: GET /
Route.iso.home
<¢> .get <% end,
// Matches: GET /episode/:str
Route.iso.episode
<¢> get %> lit("episode") %> pathParam(.string) <% end,
// Matches: GET /episodes
Route.iso.episodes
<¢> get %> lit("episodes") <% end,
// Matches: GET /search?query=:optional_string
Route.iso.search
<¢> get %> lit("search") %> queryParam("query", opt(.string)) <% end,
// Matches: POST /signup
Route.iso.signup
<¢> post %> jsonBody(Episode.self) <%> lit("signup") %> opt(.jsonBody)) <% end,
]
.reduce(.empty, <|>)
// Match a route given a request
let request = URLRequest(url: URL(string: "https://www.pointfree.co/episode/001-hello-world")!)
let route = router.match(request: request)
// => Route.episode("001-hello-world")
// Generate a string from a route:
router.absoluteString(for: .episode("001-hello-world"))
// => /episode/001-hello-world
添加用于渲染 Html
视图的中间件
import Foundation
import Html
import HttpPipeline
import HttpPipelineHtmlSupport
let view = View(p(["Hello world!"]))
let middleware = writeStatus(.ok)
>>> respond(view)
let conn = connection(from: URLRequest(url: URL(string: "/")!))
middleware(conn).response.description
Status 200
Content-Type: text/html
<p>Hello world!</p>
向 Html
添加元素和属性函数,用于将 Css
值渲染到内部样式表或内联样式。元素函数 style
允许您提供一个 Stylesheet
值,该值将被渲染到内部样式表
import Css
import Html
import HtmlCssSupport
let css = body % background(red)
let document = html([head([style(css)])])
render(document)
<html>
<head>
<style>body{background:#ff0000}</style>
</head>
</html>
属性函数 style
允许您直接在元素上内联渲染样式表
import Css
import Html
import HtmlCssSupport
let anchorStyle = color(.red)
<> textTransform(.capitalize)
let styledDocument = p([
"Go back ",
a([style(anchorStyle)], ["Home"])
])
print(render(styledDocument, config: pretty))
<p>
Go back
<a style="color:#ff0000;text-transform:capitalize">
Home
</a>
</p>
包含一个类型为 Stylesheet
的单值 reset
,它重置网页的所有默认值。它可以与另一个样式表通过 reset <> otherStyles
组合,或者可以通过 render(reset)
直接渲染为样式表字符串。
所有模块均在 MIT 许可证下发布。有关详细信息,请参阅 LICENSE。