swift-web

CI Swift 5.3 @pointfreeco

一个用于解决构建 Swift Web 框架中各种问题的框架集合。每个框架都专注于一个单一问题,例如 HTML 渲染、CSS 预处理、路由、中间件等等。它们也不依赖于集合中的任何其他框架。您可以选择您想要和不想要的部分,例如您可以单独使用 Html 而不使用 Css

稳定性

这个库应被视为实验性的。如果您觉得它的内容有用,请考虑维护一个分支。

安装

import PackageDescription

let package = Package(
  dependencies: [
    .package(url: "https://github.com/pointfreeco/swift-web.git", .branch("master")),
  ]
)

入门

这个库包含一个广泛的测试套件和一组可以探索的 playground。要使其运行

包含的模块

主要模块

支持模块

Css

一个用于 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;
}

HttpPipeline

一些类型和函数,用于将服务器中间件建模为一个将请求转换为响应的简单函数。它使用 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>

ApplicativeRouter

一个基于“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

HttpPipelineHtmlSupport

添加用于渲染 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>

HtmlCssSupport

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>

CssReset

包含一个类型为 Stylesheet 的单值 reset,它重置网页的所有默认值。它可以与另一个样式表通过 reset <> otherStyles 组合,或者可以通过 render(reset) 直接渲染为样式表字符串。

许可证

所有模块均在 MIT 许可证下发布。有关详细信息,请参阅 LICENSE