diff --git a/Html.playground/Contents.swift b/Html.playground/Contents.swift index cd025f4..03efaa6 100644 --- a/Html.playground/Contents.swift +++ b/Html.playground/Contents.swift @@ -32,30 +32,31 @@ code { """ /// A document built in the HTML DSL. -let doc = html([ - head([ - style(unsafe: stylesheet) - ]), - body([ - h1(["đŸ—ș HTML"]), - p([""" +let doc = document( + doctype, + html( + head( + style(unsafe: stylesheet) + ), + body( + h1("đŸ—ș HTML"), + p(""" A Swift DSL for type-safe, extensible, and transformable HTML documents. """ - ]), - - h2(["Motivation"]), - p([""" + ), + h2("Motivation"), + p(""" When building server-side application in Swift it is important to be able to render HTML documents. The current best practice in the community is to use templating languages like Stencil, Mustache, Handlebars, Leaf and others. However, templating languages are inherently unsafe due to the API being stringly typed. The vast majority of errors that can arise in creating a template happen only at runtime, including typos and type mismatches. -"""]), - p([""" +"""), + p(""" That’s unfortunate because we are used to working in Swift, which is strongly typed, and many potential bugs are discovered at compile time rather than runtime. Our approach is to instead embed HTML documents directly into Swift types so that we immediately get all of the features and safety Swift has to offer. -"""]), - - h2(["Examples"]), - p([""" +""" + ), + h2("Examples"), + p(""" HTML documents can be created with this library in a tree-like fashion, much like how you might create a nested JSON document: -"""]), - pre([""" +"""), + pre(""" import Html let document = html([ @@ -64,21 +65,20 @@ let document = html([ p(["You’ve found our site!"]) ]) ]) -"""]), - - p([ - "Underneath the hood these tag functions ", - code(["html"]), - ", ", code(["body"]), - ", ", code(["h1"]), - "etc. are just creating and nesting instances of a ", - code(["Node"]), - " type, which is a simple Swift enum. The cool part is that because ", - code(["Node"]), - " is just a simple Swift type, we can transform it in all types of intersting ways. For a silly example, what if we wanted to remove all instances of exclamation marks from our document?" - ]), - - pre([""" +"""), + + p( + "Underneath the hood these tag functions ", + code("html"), + ", ", code("body"), + ", ", code("h1"), + "etc. are just creating and nesting instances of a ", + code("Node"), + " type, which is a simple Swift enum. The cool part is that because ", + code("Node"), + " is just a simple Swift type, we can transform it in all types of intersting ways. For a silly example, what if we wanted to remove all instances of exclamation marks from our document?" + ), + pre(""" func unexclaim(_ node: Node) -> Node { switch node { case .comment: @@ -101,25 +101,25 @@ func unexclaim(_ node: Node) -> Node { unexclaim(document) // Node """ - ]), + ), - p([ - "And of course you can first run the document through the ", code(["unexlaim"]), " transformation, and then render it:" - ]), + p( + "And of course you can first run the document through the ", code("unexclaim"), " transformation, and then render it:" + ), - pre([""" + pre(""" render(unexclaim(document)) //

Welcome.

You’ve found our site.

""" - ]), - - ]) - ]) + ) + ) + ) +) /// A function that "redacts" an HTML document by transforming all text nodes /// into █-sequences of characters. func redacted(node: Node) -> Node { - func redacted(string: String) -> String { + func redact(string: String) -> String { return string .split(separator: " ") .map { String(repeating: "█", count: $0.count )} @@ -135,7 +135,7 @@ func redacted(node: Node) -> Node { return .comment("") // Raw strings will be redacted case let .raw(string): - return .raw(redacted(string: string)) + return .raw(redact(string: string)) // Style tags will not be redacted case .element("style", _, _): return node @@ -148,11 +148,14 @@ func redacted(node: Node) -> Node { children ) // All other elements will have their children redacted. - case let .element(tag, attrs, children): - return .element(tag, attrs, children.map(redacted(node:))) + case let .element(tag, attrs, child): + return .element(tag, attrs, redacted(node: child)) + // All fragments will have their children redacted. + case let .fragment(children): + return .fragment(children.map(redacted(node:))) // Text nodes will be redacted case let .text(string): - return .text(redacted(string: string)) + return .text(redact(string: string)) } } diff --git a/README.md b/README.md index cfe6c43..c739b1c 100644 --- a/README.md +++ b/README.md @@ -29,12 +29,12 @@ HTML documents can be created in a tree-like fashion, much like you might create ```swift import Html -let document = html([ - body([ - h1(["Welcome!"]), - p(["You’ve found our site!"]) - ]) - ]) +let document = html( + body( + h1("Welcome!"), + p("You’ve found our site!") + ) +) ``` Underneath the hood these tag functions `html`, `body`, `h1`, etc., are just creating and nesting instances of a `Node` type, which is a simple Swift enum. Because `Node` is just a simple Swift type, we can transform it in all kinds of interesting ways. For a silly example, what if we wanted to remove all instances of exclamation marks from our document? @@ -54,6 +54,10 @@ func unexclaim(_ node: Node) -> Node { // Recursively transform all of the children of an element return .element(tag, attrs, children.map(unexclaim)) + case let .fragment(children): + // Recursively transform all of the children of a fragment + return .fragment(children.map(unexclaim)) + case let .raw(string), .text(string): // Transform text nodes by replacing exclamation marks with periods. return string.replacingOccurrences(of: "!", with: ".") @@ -95,31 +99,32 @@ Here the `src` attribute takes a string, but `width` and `height` take integers, For a more advanced example, `
  • ` tags can only be placed inside `
      ` and `