pelme / htpy

Generate HTML in Python
http://htpy.dev
MIT License
246 stars 8 forks source link

Generating HTML comments #42

Closed it-is-wednesday closed 2 months ago

it-is-wednesday commented 2 months ago

Hello:) Thank you so much for this project – a breath of fresh air after wrangling Django templates!

I wanted to ask whether you'd consider adding support for generating HTML comments (and I would love to implement it if you're in).

My use case stems from these problems/wishes which cannot be solved via plain Python comments:

  1. During web development, when I see an element is behaving unexpectedly, I like inspecting it and using comments surrounding it in order to quickly locate it in the codebase.
  2. I like having my HTML+JS code available for inspection by visitors; that's how we used to learn cool stuff about web programming. I don't minify anything for the hopes that someone will found it interesting.

I propose adding to all elements a special keyword, comment, that will generate its value above the element. For example, this snippet:

div(comment="hello")

Will render this HTML:

<!-- hello -->
<div></div>

Perhaps comment is a bad choice because it will break backwards compatibility? Maybe we can instead pick something such as _comment?

Would love to hear your thoughts, and thanks again for this incredible piece of software:)

pelme commented 2 months ago

I agree, it would be nice to be able to put in HTML comments!

I think that a comment attribute on a custom element would not be too far fetched, someone might want to use something like <my-note comment="Hi"></my-note>. I also think it is not clear that it would be a child before the current element.

What about a special "comment" helper from htpy import comment? Can probably be implemented like this:

>>> from markupsafe import Markup, escape
>>> def comment(text: str) -> Markup:
...     return Markup(f'<!-- {escape(text)} -->')
...
>>> from htpy import div
>>> print(div[
...     comment("hello"),
...     div["element contents"],
... ])
<div><!-- hello --><div>element contents</div></div>
>>>

I you think this is a good approach, feel free to open a PR, ideally with docs and tests. :)

ggranger commented 2 months ago

I agree that comments should be managed like elements, especially if at some point we want to be able to handle conditional comments such as

<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.js" integrity="sha512-BWbLJlfp8hzXlxT6K5KLdxPVAj+4Zn2e4FVq5P7NSFH/mkAJ18UiZRQUD4anR3jyp0/WYkeZ0Zmq5EWWrDxneQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<![endif]-->

The comment itself should be the content of the comment tag, not an attribute (can be any valid Node, not just a str, even for regular comments).

comment(condition:str|None=None)[...]

pelme commented 2 months ago

That conditional "if .. IE" is special for IE and not something that is currently widely used, I think we should just aim for a simple <!-- X --> helper.

Markup() is always available as an escape hatch to craft any comment or text as needed. With Markup(), it is easy enough for anyone to implement a custom function (we could maybe document this as an example):

from markupsafe import Markup

import htpy as h

def ie_conditional(condition: str, *children: h.Node) -> h.Node:
    return [Markup(f"<!--[if {condition}]>"), children, Markup("<![endif]-->")]

print(h.render_node(ie_conditional("IE lt 9", h.p["Hello, IE!"])))

--> <!--[if IE lt 9]><p>Hello, IE!</p><![endif]-->

We should also consider how to escape the text properly. It needs to be safe to avoid breaking out of the comment. Someone may use comment(f"current user: {user.username}"). where user.username could contain user supplied data like --><script>evil()</script><!--.

After a looking into it a bit more, using escape() (as I suggested above) would not be useful. Maybe we can implement a check for https://developer.mozilla.org/en-US/docs/Web/HTML/Comments:

This text cannot start with the string > or ->, cannot contain the strings --> or --!>, nor end with the string <!-, though <! is allowed.". If it is not valid, we can just avoid rendering it altogether.

Or we could just always remove -- and add spaces.

pelme commented 2 months ago

This is part of the 24.8.1 release!