preact-render-to-string icon indicating copy to clipboard operation
preact-render-to-string copied to clipboard

Don't escape HTML &-entities in literal text

Open broofa opened this issue 3 years ago • 5 comments

See also https://github.com/developit/htm/issues/76

According to the JSX docs, "You can insert HTML entities within literal text in JSX", but this does not appear to work because of how &'s are escaped in the encodeEntities() method.

Could this method be made to ignore &'s that are part of valid html character entities? I could put a PR together, but want to get a sense of how receptive you are to this. (And I suspect I may be missing other, important requirements.)

broofa avatar Nov 28 '20 18:11 broofa

Moving this to https://github.com/preactjs/preact-render-to-string

JoviDeCroock avatar Nov 28 '20 19:11 JoviDeCroock

This actually is an HTM issue. JSX converts html-encoded entities to UTF8 (see example), it's not part of VDOM rendering.

Because of this, preact-render-to-string is never passed "a & b" and expected to render "a & b". That would be a mismatch for how JSXText works on the client, where html-encoded entities in text are rendered as-is. This is a little confusing, since obviously entities do get decoded, just as the link above shows this actually happens during JSX compilation.

I think developit/htm#76 describes this issue pretty well though. From a Preact standpoint, neither the client-side renderer nor the server-side renderer should be handling this, since both treat JSXText as string literals (to match all other VDOM libraries). It would be too strange/broken if <div>{"&"}</div> and <div>{"&amp;"}</div> rendered the same output.

~~

For context on the HTM issue: the reason this hasn't been done is because HTML entity decoding is disproportionately expensive for no material gain. Entity decoding can only be implemented using a character mapping that is larger than HTM and Preact combined (along with a decent bit of conversion logic). Here's the mapping Babel's JSX transform uses: https://github.com/babel/babel/blob/328ef420a45d63a12434a07f939ff592e9832727/packages/babel-parser/src/plugins/jsx/xhtml.js

When running in a browser context, HTM could technically use the DOM to perform this conversion, which is slow but cacheable. Personally though, I'm not a huge fan of any of the approaches. There are two use-cases for HTM decoding HTML entities:

  1. to be a 1:1 compile target for JSX - this is already handled by the jsx-to-htm Babel transform, does entity decoding.
  2. using HTM as a general-purposes HTML-to-VDOM converter - something it just isn't built for.

For the second use-case, at some point I would like to extract the old HTM 1 codebase, which was built on DOMParser and actually set out to cater to handling the full spectrum of HTML use-cases. In the meantime, preact-markup is a decent approximation of the same approach (albeit slower and larger).

developit avatar Feb 05 '21 02:02 developit

I'm not very good at English, so I don't really understand what's going on, but I'm having trouble escaping the following results.

Code:

const outRender = render(html`<link href="https://fonts.googleapis.com/css2?family=Lora&display=swap" rel="stylesheet" />`)
console.log(outRender)

Result:

<link href="https://fonts.googleapis.com/css2?family=Lora&amp;display=swap" rel="stylesheet" />

Lora&display => Lora&amp;display

Is there an option to cancel the escaping?

My workaround: https://stackoverflow.com/a/59609991/1979953

shingo-nakanishi avatar Sep 05 '21 10:09 shingo-nakanishi

Should this be occurring in props too? For example,

render(h('div', { class: 'flex(& col)' }));

// Results in

<div class="flex(&amp; col)"></div>

which is rather unexpected.

Edit: Seems React does it too. Interesting. I thought props shouldn't be an issue.

rschristian avatar Apr 10 '22 04:04 rschristian

any updates on this?

renhiyama avatar Jun 16 '22 11:06 renhiyama

bump

torchsmith avatar Mar 06 '23 01:03 torchsmith

[Closing out issues I authored that appear to be stagnant.]

broofa avatar Apr 03 '23 18:04 broofa

@broofa please re-open.

torchsmith avatar Apr 03 '23 19:04 torchsmith

@torchsmith: No. This issue is 2+ years old and conversation petered out at [in essence], "Won't fix".

If you want this reopened, make the case for that to the maintainers. I've moved on to other things and have unsubscribed from this issue.

broofa avatar Apr 04 '23 17:04 broofa

I fixed it with: <style dangerouslySetInnerHTML={{ __html: myString }} />

brandon-lb avatar Jul 24 '23 23:07 brandon-lb