sliemeobn / elementary

A modern and efficient HTML rendering library - inspired by SwiftUI, built for the web.
https://swiftpackageindex.com/sliemeobn/elementary
Apache License 2.0
146 stars 6 forks source link

Add typed erased HTMLDocument #47

Closed mapedd closed 1 month ago

mapedd commented 1 month ago

Hey, what do you think about creating a type erased HTMLDocument type?

It's seems somewhat limiting to always carry the generic type and making code that renders HTML conditionally quite hard, as it not always easy to find a place to put in the dynamic logic.

Screenshot 2024-10-02 at 22 32 44

I'd much prefer do logic and massaging context data for HTML earlier and make HTML generating code as simple as possible

sliemeobn commented 1 month ago

hey, thanks for engaging!

I wanted to stay away from an AnyHTML as long as possible, because I find it to be a bit of an anti-pattern.

However, for both the hummingbird and vapor binding this very question was posted, and I was never quite please with how to best do this. I think you are right that there should be a simple option to have a type-erased top-level type.

I am not convinced HTMLDocument is the right place, because if you think about HTMX use cases, you'd respond more with fragments than with full pages.

My gut-feel preference would be to have the type-erasure happen on the HTMLResponse type, maybe by adding an AnyHTMLResponse in the vapor and hummingbird packages? (but then again, there already is the Response type as a type-erased version, just a bit clumsy to spell...)

Or, we could just pull the finger out and add an AnyHTML type to elementary and have people use it however they see fit. I guess if we add docs to the type "do not use unless you have to" it's fine, and hope that SwiftUI's teachings have already settled in the community?

wdyt?

mapedd commented 1 month ago

I am not convinced HTMLDocument is the right place, because if you think about HTMX use cases, you'd respond more with fragments than with full pages.

For my use case that's what would be most useful right now (AnyHTMLDocument), as I do prefer have logic execute at higher level, and then inject results into the HTML templates at root level. But then I'm very new to web development so possibly it's not a good approach.

From mobile dev perspective, having your views access database is a code smell :)

Wouldn't HTMX response be usually smaller (it will be used to replace some small bit of HTML), thus having smaller chance of needing dynamically typed response?

What are the drawbacks of having AnyHTML erased type?

sliemeobn commented 1 month ago

> From mobile dev perspective, having your views access database is a code smell :)

I hear you, but so is adding unnecessary abstractions between where data comes from and where it should go ; ) IMHO it all comes down to meaningful testability. I am not advocating for anything specific, but for servers that wrap data in HTML to display it nicely, my experience is: keeping it simple is best.

> What are the drawbacks of having AnyHTML erased type?

For a top-level eraser, it's really nothing. But over-using them kind of defeats the purpose and benefits of structs/value types. You'll likely add more allocations and double-dispatches, just being a bit wasteful in general. In the grand scheme of things, it is probably very negligible...

I think I'll just add an AnyHTML type to have that covered.

sliemeobn commented 1 month ago

...oh no, as I write this, I realize we'll run into a sendability issue I think.

I cannot conditionally conform the AnyHTML to Sendable (because it erases its wrapped type), and the response generators (like AsyncResponseEncodable) must be sendable... hm... not ideal...

I think the best viable option right now is to return Response for now. The real "fix" would be that the return value is sending and not sendable, but that is a Swift 6 only feature 🤔

sliemeobn commented 1 month ago

@mapedd just FYI, I thought about this some more, and I think I will actually just make the HTMLResponse a type-erased wrapper. it is just more ergonomic to hold with very few downsides. I hope I will be forgive the potential source break (it is a 0. version still anyway) thanks for bringing this up!

mapedd commented 1 month ago

I like that. make sense, since it will from that point on just stream the bytes down the pipe 👍

sliemeobn commented 1 month ago

@mapedd wdyt? https://github.com/vapor-community/vapor-elementary/pull/4

sliemeobn commented 1 month ago

et viola: https://github.com/vapor-community/vapor-elementary/releases/tag/0.2.0

mapedd commented 1 month ago

👏 thanks amigo