pointfreeco / swift-html Goto Github PK
View Code? Open in Web Editor NEW🗺 A Swift DSL for type-safe, extensible, and transformable HTML documents.
Home Page: https://www.pointfree.co/episodes/ep28-an-html-dsl
License: MIT License
🗺 A Swift DSL for type-safe, extensible, and transformable HTML documents.
Home Page: https://www.pointfree.co/episodes/ep28-an-html-dsl
License: MIT License
When a 'srcset' attribute is converted from a dictionary to a string, the ordering should be fixed, e.g. sort by size.description ?
Without this:
I'm trying to construct an HTML document with an IE-specific directive in the <head>
tag. It looks like a comment:
<!--[if IE]><meta http-equiv='X-UA-Compatible' content='IE=edge'><![endif]-->
Problem is, I can't put this comment in the <head>
tag. I get this compile message:
It would be great to be able to put some kinds of nodes like .comment
, also probably .raw
nodes, at any place in a tree.
Other than Node.isEmpty()
and Node.append()
, we don't really have any way to manipulate the tree of Node
objects in the swift-html library.
I would like to suggest that we have a number of functions that are similar to DOM manipulation operations available in JavaScript and other languages.
The current structure of the Node
(especially with the copious use of the "fragment") makes it difficult to dig in and make manipulations to the nodes. So adding a familiar API layer to this will make it easier to build a web document out of Nodes.
For instance:
appendChild()
insertBefore(node)
removeChiild(child)
remove()
parentNode()
querySelector()
querySelectorAll()
getElementByID(id)
replaceChilden()
We do a good job of escaping the things that need to be escaped via what the HTML spec says, but right now we don't escape the name of attributes at all. This means you can do the following to inject an XSS:
let badAttribute = Attribute<Tag.A>("></a><script>alert('!');</script>", "foo")
a([badAttribute], ["XSS"])
and that renders as:
<a ></a><script>alert('!');</script>="foo">XSS</a>
which executes an alert.
However, this isn't really a big deal in the vast majority of uses of this library. There isn't really a big need for tag names specified by user input. After chatting today we came up with the following pros and cons for escaping this:
Pros:
Codable
with Node
, and that opens up a bigger vector of abuse. If you can deserialize HTML from a string stored anywhere you open yourself up to a lot of different ways bad attributes can sneak in.Cons:
Alternatives:
Node
.StaticString
, but then that doesn't allow for codable. We could at the very least make the Attribute
initializer take only static strings for the key
field.Hi everyone
I try to use swift-html in my project.
Currently I have two Issues:
On my Mac M1, when I try to build App I get following error:
"Could not find module 'Html' for target 'x86_64-apple-ios-simulator'; found: arm64, arm64-apple-ios-simulator, at:"
I try to use sample function unexclaim() from readme.
But I get this Issue:
Maybe someone has hint for me?
Bests from Swiss Mountains
Philippe
Output error:
error: the package PackageReference(identity: "swift-html", name: nil, path: "https://github.com/pointfreeco/swift-html.git", isLocal: false) @ 0.1.0 contains revisioned dependencies:
PackageReference(identity: "swift-snapshot-testing", name: nil, path: "https://github.com/pointfreeco/swift-snapshot-testing.git", isLocal: false) @ 69b48c8
I guess it needs to use a version number tag for the swift-snapshot-testing
dependency.
Mostly out of curiosity, I can work around this, but I'm not understanding why this works this way:
Here I can't pass an Array<Node>
...
...but I can pass an array literal
Node
is an Enum, and one of its cases is .fragment([Node])
so I see how the array literal works, but I don't understand why the Array<Node>
instance doesn't.
is there any plan to update for HTML 5 ?
Hello! Nice work with the library, I'm already working with it to create my personal website😊
From the playground:
Some simple CSS for our page. This can be written right inline to our DSL, we could also load it from a file on disk, or better yet, we could create another DSL for modeling it 😉
I agree on this. Once you get used to modeling html this way, working with that multiline string css stylesheet feels even worse than before. It would also help people with more experience with swift but not with css, since it would provide autocompletion, compile safety and documentation within Xcode.
Now say I wanted to contribute and start working on it, is this library the good place for it or would you prefer a separate module?
Whenever I use a variable for contents of Node
I get compile error that says:
Type of expression is ambiguous without more context
I'm unclear why this is the case and what I would need to do to fix it.
guard let age: String = test.studentAgeAtTimeOfTest() else { return "error" }
let htmlTree =
html([
head([
]),
body([
h1(["Student is \(age) years old"])
])
])
From quick help for dl
:
Each term-description group consists of one or more terms (represented by
<dt>
elements) possibly as children of a<div>
element child, and one or more descriptions (represented by<dd>
elements possibly as children of a<div>
element child), ignoring any nodes other than<dt>
and<dd>
element children, and<dt>
and<dd>
elements that are children of<div>
element children within a single<dl>
element.
But, trying the following causes compiler error Type 'ChildOf<Tag.Dl>' has no member 'div'
.dl(
.div(
.dt("term"),
.dd("description")
)
)
Hi guys! First of all, thank you for this great lib, you've done a great job with this and I love your work!
I was trying to kinda of rewrite your lib for fun in a playground and the compiler allowed me to declare the Node
enum without marking the .element
case as indirect. I wondered if that depends on the fact that children is actually an array of Node
s and not a single Node
... If that's the case you might want to drop the indirect
modifier, too!
I don't even know if that might have benefit performance / memory wise, maybe the Swift compiler is smart enough and has never introduced the "indirect reference / pointer" to the node in question ever.
mkdir Pot
cd Pot
swift package init --type executable
add swift-html
to Package.swift
// swift-tools-version:4.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Pot",
dependencies: [
.package(url: "https://github.com/pointfreeco/swift-html.git", from: "0.2.0")
],
targets: [
.target(
name: "Pot",
dependencies: ["Html"]),
.testTarget(
name: "PotTests",
dependencies: ["Pot"])
]
)
cat Sources/Pot/main.swift
import Html
let document: Node = .document(
.html(
.body(
.h1("Welcome!"),
.p("You’ve found our site!")
)
)
)
print(render(document))
$ swift build
Compile Swift Module 'Pot' (1 sources)
/Users/nix/Documents/Pot/Sources/Pot/main.swift:3:23: error: type 'Node' has no member 'document'; did you mean 'comment'?
let document: Node = .document(
^~~~~~~~
comment
Html.Node:2:10: note: 'comment' declared here
case comment(String)
^
error: terminated(1): /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-build-tool -f /Users/nix/Documents/Pot/.build/debug.yaml main output:
Hey! Really like the library. I was playing around with it and saw that it was possible to do something like
.document(
.html(
.body(
.html()
)
)
)
And tags like .div
and .script
can be used anywhere in .document.
.script
can also be used anywhere within .html
which are all examples of invalid html.
I was playing around a bit and it would be quite easy to improve on this behavior by
ChildOf
for doctype.ChildOf
for script
tags (and some others) and move these instead to specific extensions of ChildOf
(introducing a little bit more code)ChildOf where Element == Tag.Body
All these static functions would also return a ChildOf<Tag.Body> instead of the previous Node
type. Then we can use them nested as well!I've got this working locally and works quite nice! :) Let me know if you're interested in a PR for this behavior!
Until Carthage gets SPM support (Carthage/Carthage#1945), would you consider accepting a PR that would add an Xcode project to build the library? To improve Xcode support out of the box.
So far all of the examples I've seen are for building up a .document
from a complete set of sub-nodes all at once, but realistically a web page is going to be built up dynamically. I want to create a element and adjust the sub-tags based on logic in the program, then build up the piece by piece, and then finally assemble into .html
and into a final .document
for rendering.
After some exploration in the documentation and test methods I am for instance able to create an .html
node like this:
let htmlUsingHelper = Node.html(
.body(
.h1("Welcome!"),
.p("You’ve found our site!")
)
)
But then why can't I do something similar to create a .head
like this?
let headUsingHelper = Node.head(
.meta(attributes: [.charset(.utf8)]),
.title(self.title)
)
That has 3 compile errors!
Maybe there is a workaround by avoiding the "helper functions" but it would be so nice to be able to manipulate fragments with the helper functions and then assemble them into a full .document
(or fragment) as needed.
BTW is the dot-syntax you are using here in the Swift language documented somewhere? It doesn't seem to be like functional programming where "." is used to chain the result of one function into another function (e.g. in SwiftUI), but something different altogether. If you can point your users to a starting point here, it might be easier to figure out how to make use of the framework beyond the surface level. Thanks!
Moving this out of Trello as a potential future thing. We could upgrade our debug printer to use something like Doctor Pretty or something based on https://github.com/quchen/prettyprinter.
If I have a string like this:
let note = "I love tacos.\n\n\tI love every kind of taco."
and I use it for the text in a paragraph, the element preserves the new lines, but tab is lost.
Is there something I can do to fix this?
error: the library 'HtmlSnapshotTesting' requires macos 10.13, but depends on the product 'SnapshotTesting' which requires macos 10.15; consider changing the library 'HtmlSnapshotTesting' to require macos 10.15 or later, or the product 'SnapshotTesting' to require macos 10.13 or earlier.
swift run
/Users/dstevenson/src/swift/SongsToTheSiren/Sources/SongsToTheSiren/Page.swift:76:37: error: static member 'map' cannot be used on instance of type 'Node'
return .element(tag, attrs, children.map(unexclaim))
^~~~~~~~
Node
/Users/dstevenson/src/swift/SongsToTheSiren/Sources/SongsToTheSiren/Page.swift:76:50: error: missing argument for parameter 'name' in call
return .element(tag, attrs, children.map(unexclaim))
^
name: <#String#>,
/Users/dstevenson/src/swift/SongsToTheSiren/.build/checkouts/swift-html/Sources/Html/Elements.swift:489:22: note: 'map(name:attributes:_:)' declared here
public static func map(name: String, attributes: [Attribute<Tag.Map>] = [], _ content: ChildOf<Tag.Map>...)
^
/Users/dstevenson/src/swift/SongsToTheSiren/Sources/SongsToTheSiren/Page.swift:76:50: error: cannot convert value of type '(HtmlNode) -> HtmlNode' (aka '(Node) -> Node') to expected argument type 'ChildOf<Tag.Map>'
return .element(tag, attrs, children.map(unexclaim))
^
I've come across this error running the included playground:
error: Html.playground:30:10: error: type of expression is ambiguous without more context
head([
This is on swift-4.2 but the playground also fails in 4.1.2 for me (although I believe it was a different error message).
The issue is that in in the section
let doc = html([
head([
style(stylesheet)
]),
stylesheet
is a String
after assignment while style
has the signature
public func style(_ content: StaticString) -> ChildOf<Tag.Head> {
This is fixable by either declaring
let stylesheet = style("""
...
and using
let doc = html([
head([
stylesheet
]),
or changing style
to
public func style(_ content: String) -> ChildOf<Tag.Head> {
The latter seems to be the more lenient variant but I'm wondering if there was a deeper reason to constrain it to StaticString?
Hello, I have been able to make some examples work using the library but how would I add an element to the body that's not in the swift-html library? For example if a script is used and it gives access to some new element that's not a ".h1", ".p", ".div" etc. how would that be done? Would really appreciate any suggestions to solve this, thank you.
When I add the swift-html package as a dependency I get the following error:
product dependency 'Html' in package 'swift-html' not found
Not sure what to do here, have the feeling it has to do with tools versioning could that be?
package dependency looks as follows:
.package(url: "https://github.com/pointfreeco/swift-html.git", from: "0.3.1"),
target dependency: (needed for 5.2)
.product(name: "Html", package: "swift-html"),
Is there any particular reason why the style
attribute function only allows static strings? (see
swift-html/Sources/Html/Attributes.swift
Line 1117 in f4ac5ec
I used the old Html
module from swift-web
before and it didn't use to have this limitation.
I've been getting this error message as I build up HTML code, and I finally realized that what was wrong was a missing comma, for instance after the .header(…)
here. It might be good to document the error message phrase _"Cannot infer contextual base in reference to member _____" for people who encounter this error in the future.
(This is using Xcode 12 beta; don't know how it will appear in older or newer versions of the Swift compiler…)
Should these be of type Font?
swift-html/Sources/Html/MediaType.swift
Lines 90 to 95 in 05dcfbf
I found version "0.3.0" did not run on a Swift 4.2.1 system.
What is the plan relative to support/features for Swift 4 and Swift 5? Relative to the release numbers?
Is the 0.2.x Swift 4 version expected to have any new features and/or support updates?
Version differences? Or, maybe the version with Swift 5 so much better, that I should not prefer to spend too much time with the Swift 4 version??
Some roadmap-ish section would be generally helpful if the READMEmd. Thanks.
I'm probably missing out something, but is it possible to add .li
dynamically to .ul
?
The following does not seem to work:
var list = Node.ul()
list.append(.li("Item"))
Cheers
Maybe I'm doing this incorrectly, but it seems that if include an html entity such as ° (degrees), as follows
let div:Node = .div( .h3(.text("90°")) )
then the output in the rendered html document is 90&deg;
.
Not sure if this is a bug or just me missing the correct method to include an character entity?
First, thank you for the library, it’s a real joy to use! 🦄 The context-driven autocompletion feels like someone is reading my mind sometimes :) And the sheer amount of implemented HTML is impressive. (Did you use some kind of code generation or automation?)
The only thing I have trouble with is what all big DSLs in Swift seem to struggle with, and that’s namespace clashes. The compiler seems to get into a corner very often, ending up with hard-to-read error messages:
// error: expected expression in list of expressions
// error: consecutive statements on a line must be separated by ';'
// error: expected identifier in class declaration
let foo = small(class("sample"), "whatever")
Here the problem is easy to spot, but with more complex expressions one has to decompose them into pieces when the whole thing doesn’t compile at the first try. I usually end up prefixing the troublesome cases (Html.class
), but it’s slightly annoying. People who are just starting with DSLs might be pretty confused by error messages such as “being unable to infer T somewhere”, there’s not much to hang on. Do you also run into this problem often? Is there some better solution than prefixing a lot?
swift-html/.github/workflows/ci.yml
Line 28 in 789c267
Node.js 12 actions are deprecated. Please update the following actions to use Node.js 16: actions/checkout@v2. For more information see: https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/.
I was contemplating Bootstrap Vue to see if it fit with this library.
I noticed that the <template> element is not handled in the library helper functions. It is sort of a fragment "thing" but is distinct in that you may possibly exclude it from being modified as it is not actually displayed in the main DOM, (ex. the redact example)
There should at least be example documentation on how it should be coded as an element and allowed in the document / fragment.
elements rb,rtc,data also do not appear in the Tag enum?
I'm trying to set the colspan
attribute on a td
element, but I'm getting an error:
td([.colspan(2)], [.text(word.note)])
This should be valid but currently isn't:
ul([
li(["1"]),
li(["2"]),
ul([
li(["2a"]),
li(["2b"])
])
])
I'm currently working around this by using the un-typechecked element(...)
initializer.
Html Playground doesn't run, it can't find the Html module.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.