[ANN] HypertextLiteral.jl - generate tagged content with interpolation

With the assistance of many in the Pluto.jl community, I’ve created a performant implementation of @mbostock’s hypertext literal for server-side Julia applications. This package lets you use familiar string interpolation, such that HTML escaping is done for you. HypertextLiteral.jl is a new entrant in Julia template tools, joining HAML.jl, Mustache.jl, and Hyperscript.jl. It uses Julia string interpolation syntax, escaping content and providing context-sensitive translations of Vector, NamedTuple, and other types depending upon where the variable is located: in content, within an element tag, as an attribute value, etc. This package has an emphasis on performance (and correctness) so that it could be used in contexts where generating complex web pages must be done quickly, securely, and with minimal runtime costs. The support channel for this project is on Julia’s Zulip.

9 Likes

With the v0.3 release, the usage of @htl macros are now stabilizing. Notable changes from previous prototypes include:

  • With this release @htl is faster than naive string interpolation on Julia 1.5.3.
  • Content escaping is now done though a IO proxy design which streamlines code readability.
  • Integration with other approaches, such as the object-style Hyperscript.jl is tested.
  • A prototype API is developed for user extensions. This may change (slightly).

At this time, most of the design is settled. Feedback on particular usage is very welcome. I intend to use this library in conjunction with DataKnots.jl for server-side report generation and HTMX for client-server interaction. Hence, my need to have something that stays close to HTML yet is performant to support large, intricate, and interactive reports for medical research applications.

I wish to thank Michiel Dral and Kirill Simonov for their excellent guidance. I’m also continually impressed with Julia, this time with the deceptively simple and yet extremely effective string construction approach used by Docs.HTML together with Julia’s emphasis on using IO as a way to handle the printing and display of object serializations.

4 Likes

With the v0.4 release, HypertextLiteral is no longer in exploratory mode. The API for building handling with custom data has matured and is now documented. In particular there are a clean set of set of primitives that can be used to construct a hypertext stream to which extensions could use. This release was focused on updating documentation to ensure that it follows well. This release also produced a performance ticket with a small benchmark code that exercises these primitives.

The next phase in development will be responsive to user feedback and will also focus on non-breaking speed enhancements as many expressions can be converted during macro construction time. In particular, I intend to work on a TodoMVC that emphasizes server-side page interaction using Julia, ensuring the package speeds server-side hypertext generation. Please drop by to chat if you have any questions. I expect to tag @htl with 1.0 within a few months, after sufficient confirmation that the design decisions are workable. Here is the example on the homepage.

using HypertextLiteral

books = [
 (name="Who Gets What & Why", year=2012, authors=["Alvin Roth"]),
 (name="Switch", year=2010, authors=["Chip Heath", "Dan Heath"]),
 (name="Governing The Commons", year=1990, authors=["Elinor Ostrom"])]

render_row(book) = @htl("""
  <tr><td>$(book.name) ($(book.year))<td>$(join(book.authors, " & "))
""")

render_table(books) = @htl("""
  <table><caption><h3>Selected Books</h3></caption>
  <thead><tr><th>Book<th>Authors<tbody>
  $((render_row(b) for b in books))</tbody></table>""")

display("text/html", render_table(books))
#=>
<table><caption><h3>Selected Books</h3></caption>
<thead><tr><th>Book<th>Authors<tbody>
  <tr><td>Who Gets What &amp; Why (2012)<td>Alvin Roth
  <tr><td>Switch (2010)<td>Chip Heath &amp; Dan Heath
  <tr><td>Governing The Commons (1990)<td>Elinor Ostrom
</tbody></table>
=#

P.S. The additional set of parenthesis at $((render_row... seems unnecessary, and was reported as a Julia #38734. I do want to recognize again just how amazing Julia is – and how responsive/helpful its core development community have been. Thanks.

1 Like

With the v0.6 release, HypertextLiteral.jl has reached API stability. This release focused on test coverage. It also enables interpolated content within comments.

  • Inspired by techniques proven in Julia’s own documentation, HTL is quite fast. It’s at least 5x faster than using naive string interpolation and list comprehension.
  • It has a built-in HTML lexer which lets it distinguish between content, attribute values, comments, and raw text tags such as script and style.
  • It smartly handles all sorts of context-sensitive interpolations, including lists, generators, named tuples, dictionaries, etc.
  • It has a well designed extension API so hypertext serialization can be easily customized for user defined types.

At this time, we don’t expect further changes to the API or implementation. Later this summer, we’ll promote it to 1.0 once we’ve had community feedback and actual production usage. We are available for further discussion on Juilia’s zulip.

2 Likes

With the v0.7 & v0.8 releases, HypertextLiteral.jl adds support for JavaScript interpolation.

  • Within the <script> tag, variables go though a different translation path to convert them to JavaScript. This is extensible so that custom libraries could provide their own translation. Moreover, there is a hook where Pluto.jl provides its own marshalling that is speedier.
  • Within attributes starting with on, such as onclick, this same JavaScript translation occurs.
  • Within a <style> tag, variable interpolation better supports CSS constructs; this method of interpolaion is the same as in attribute values.
  • The documentation is now revisited to have one page per interpolation context, plus a starting page with an overview of key features to provide a taste of what’s possible.

At this time, I’m looking for any final feedback before we tag 1.0 – once tagged, I’d like to keep this library stable without further breaking changes.

4 Likes