a-h / templ

A language for writing HTML user interfaces in Go.
https://templ.guide/
MIT License
8.26k stars 272 forks source link

generator: option to disable whitespace normalization #90

Closed diamondburned closed 5 months ago

diamondburned commented 1 year ago

Currently, this templ snippet:

templ x() {
    <pre>
        package main

        func main() &#123;
            println("Hello, world!")
        &#125;
    </pre>
}

generates this HTML:

<pre>package main func main() &#123; println("Hello, world!") &#125;</pre>

Note that all the proper indentation is gone, since templ turns all of the whitespaces into a single space. This makes writing code blocks in HTML impossible.

This issue also applies to <textarea> and <p> tags with style="white-space: pre*;".

Ideally, disabling or fixing whitespace normalization would be the best fix, but adding an extra --preserve-whitespace flag is fine too.

Potentially relevant comment: https://github.com/a-h/templ/issues/87#issuecomment-1548709257

diamondburned commented 1 year ago

I found a workaround in the meantime:

templ x() {
    <pre> {`
        package main

        func main() &#123;
            println("Hello, world!")
        &#125;
    `} </pre>
}
a-h commented 1 year ago

Just thinking about this some more, now that the auto formatting, and spaces between variables are dealt with.

Options as I see it from here:

What are you thoughts?

a-h commented 1 year ago

For option 2 above, the solution would probably be something like how the indentation for autoformatting now works. The element type has a field on it to set the behaviour: https://github.com/a-h/templ/blob/fa536dd2a2634247a7526ccd0de6ada7f1c98287/parser/v2/types.go#L319-L325

So in this case, adding a new PreserveWhitespace field to the element, and setting it to true if the element name is pre. Then, adding something to the generator that adds a context variable and passes that down to children. The generated code would always need to include the whitespace for elements, and optionally include it in the output based on the context value.

It could potentially slow down all rendered content, due to needing to check for the flag.

brlbil commented 1 year ago

I have just discovered the library, great job, thanks. I have to say I was a bit surprised when I realized the output did not have the same structure as the source. I think it would be much easier to reason about it if they were the same.

What happens in the case of putting an element inside another element like pre, I do not think a workaround works with that.

For example

<pre>
  for _, item := range items {
    <a href={ templ.URL(item.Name) }>{ item.Name }</a>   { item.ModifiedAt }    { item.Size }
  }
</pre>

Also, if this library will ever handle text formats other than HTML one day, auto line collapsing and single spacing would not be acceptable.

How about making single space, new line collapsing optional?

a-h commented 1 year ago

Thanks for everyone's thoughts on this.

One solution might be to drop built in minification as a goal, and suggest the use of something like github.com/tdewolff/Minify which provides HTTP middleware.

This may negatively affect the performance for users that require it.

However, it would simplify the job of templ since the rendering logic would be simpler.

On the other hand, maybe it's as simple as updating the rendering logic to not strip white space from pre elements. With regards to supporting changes to block/inline driven by CSS, I note that the minfication library above doesn't support that.

Supporting formats other than HTML isn't a goal for templ.

The goal is to make Go a viable language for commercial front end web development when used with tools like HTMX.

I'll digest this thread and come up with some concrete actions.

diamondburned commented 1 year ago

This may negatively affect the performance for users that require it.

I think this is a fair tradeoff. It should be reasonable for people to assume that templ is a templating library, not some sort of framework that tries to do everything at once.

Of course, we could always just introduce a config file, though I'm personally not a huge fan of this idea.

joerdav commented 9 months ago

This could do with some investigation to find out if the issue still stands, and make a decision on the outcome.

vblinden commented 8 months ago

I found a workaround in the meantime:

templ x() {
  <pre> {`
      package main

      func main() &#123;
          println("Hello, world!")
      &#125;
  `} </pre>
}

This will only work with <pre> not inside <pre><code>.

For example when I am using:

<pre>
  <code>
    { `
        sudo apt update
        sudo apt install nginx
    ` }
  </code>
</pre>

it doesn't work. But if I remove the <code>, then it will work.

<pre>
    { `
        sudo apt update
        sudo apt install nginx
    ` }
</pre>

Is there any other workaround for this?

a-h commented 8 months ago

@vblinden - what do you mean "works" vs "doesn't work"? Would be good to see the actual HTML output vs what you expected.

vblinden commented 8 months ago

@a-h Sorry, I was running an older version of the templ. I upgraded to the latest version and now the workaround works. Sorry again for the inconvenience.

joerdav commented 5 months ago

I can see that this now works for both <pre> and <code>