Hot-reload support
Hi,
Thanks for creating this library. I really appreciate the compile-time checked templates, lazy rendering, and the flexibility of supporting two different syntaxes.
One challenge with compile-time templates, however, is that they can slow down iteration speed, especially as a project grows. Recently, Dioxus introduced hot-reload support for its RSX markup, and it would be fantastic to see something similar for hypertext.
I also came across an open PR for maud (https://github.com/lambda-fairy/maud/pull/451), which adds hot-reload functionality. Unfortunately, it hasn’t received much attention from the maintainer, and there’s some discussion about a potential fork. Since hypertext supports the maud syntax, perhaps there’s an opportunity to collaborate or build on that effort?
Thanks again for your work on this library!
After I opened this issue I learned about Dioxus' subsecond library. It's trivially easy to set up and provides hot-patching of rust applications when combined with their CLI tool (make sure you use the same alpha version of the library and the CLI tool). I did some experiments and was able to get some form of hot-reloading working in the htmx example by wrapping various functions in subsecond::call(|| ... ). This makes it possible to update the dynamic parts of the template without the recompilation. For example, adding items to the list function template or changing their values. It's quite impressive.
Unfortunately, this can't be used to update the static parts of the templates because proc macros expand at compile time, so there is nothing to hot-patch after compilation. I think hot reloading the templates would require big changes to the macros themselves to parse and expand them at runtime, probably something similar to the linked maud PR.
Hi there @avsaase, thank you for this idea! Is it possible you could send a simple example of use with hypertext + subsecond? I want to take a look at how the dev experience is, and make a decision afterwards.
I pushed a version of the htmx example with subsecond support here: https://github.com/avsaase/hypertext/tree/subsecond.
To run it, install the Dioxus CLI with cargo install [email protected] and then run dx serve --hotpatch from the examples/htmx/ directory. I also had to revert to using the default linker on linux, otherwise I would get error during the build. I found some related issues and PR's that supposedly solve this but I couldn't get it it work and didn't dig very deep.
Once you have it running edit the list_items in the list component. They will get updated immediately when you refresh the page.
As I mentioned, this doesn't work with changed made within the rsx macro because it it not expanded again by subsecond (I don't think that would even be possible without changes to the compiler).
At this point I don't think there's anything for this library to do to support subsecond as the library consumer can do this themselves like I did in the example. Without some way of hot reloading the templates you still need a full recompile to tweak the CSS for example.
It would be really cool if this library supported hot reloading but I think it would be a pretty big effort. Perhaps subsecond can help in some places but it's no silver bullet.
The way that maud does it in that pr seems pretty janky. I think using subsecond is currently the best method for this, even though it does not support changing the static parts.
I have implemented a minimal hypertext + hot-reload example using the subsecond library from dioxus and tower-livereload to refresh the page. This gives almost as good a developer experience as javascript/typescript, but with the obvious benefits of using rust!
@vidhanio would that be interesting to contribute as an example to hypertext?
While it works well for rust code, for some reason it currently doesn't pick up changes within the rsx! or maud! macros. I am not sure, but I think this might be because the macros are creating structs internally and subsecond doesn't currently support structs.
It seems to work fine when using askama or maud directly, so for sure there is a way to make this work, it's more a question of how. 🤔
Edit: to better explain the current limitation:
let title = format!("User ID: {user_id}"); // changing this is hot-patched
maud! {
h1 { (title) }
p { "Welcome to the user profile page!" } // changing this is not hot-patched
}
When using maud::html! from maud both lines are hot-patched, so this appears to be down to how subsecond limitations interact with the implementation of the macro.
While it works well for rust code, for some reason it currently doesn't pick up changes within the
rsx!ormaud!macros.
This is what I mentioned above. subsecond hot patches function calls at runtime but proc macros are expanded at compile time. If you expand the rsx or maud macros using cargo-expand or rust-analyzer, you'll see that the HTML generation boils down to a bunch of Renderable::render_to calls for dynamic parts and push_str of &'static str's for static parts of the template. Changing these static strings in the template requires expanding the proc macro again and AFAIK subsecond cannot hook into this process at all.
hi @biruburu, thank you for the investigation :)
yeah, as @avsaase mentioned this seems to be a limitation with how hypertext does static string pushes. the interesting thing you mentioned is that it works with maud without issue? iirc maud does something very similiar in terms of pushing strs, i don't see why maud would work and hypertext doesnt.
edit: forgot to address that part of the comment, but yes an example would be appreciated as a contribution. :)