Closed milochristiansen closed 3 years ago
This is actually "working as designed" - it's the same output JSX generates:
// this:
<>
Test line 1.
Test line 2.
<i>Test</i> line 3.
</>;
// compiles to this:
h(Fragment, null, "Test line 1. Test line 2.", h("i", null, "Test"), " line 3.");
// which renders:
"Test line 1. Test line 2.<i>Test</i> line 3."
I do agree that this behavior is unfortunate, though. Just HTM tries to match JSX as closely as possible, and this quirk is an important part of that.
FWIW, in both JSX and HTM, the "quick fix" is to use a string literal:
html`
Test line 1.
Test line 2.
${' '}
<i>Test</i> line 3.
`
In HTM you could also use an escaped newline:
html`
Test line 1.
Test line 2.\
<i>Test</i> line 3.
`
Well... That output is problematic. It certainly violates the law of least astonishment. Is it perhaps a JSX bug?
It was a grey area that the JSX spec/proposal did not describe, so the standard ended up being set by what the original Babel and Acorn implementations chose to do.
FWIW the logic for why this behavior was selected is because it prevents leading/trailing whitespace in JSXText, as well as whitespace between JSXElements.
One thing you might consider, if you're writing a lot of content directly in HTM tagged templates, would be to use a little intermediary function that patches this behavior before the strings get passed to HTM:
import htm from 'htm';
// your existing htm setup:
function h(type, props, ...children) { /* snip */ }
const htmlInternal = htm.bind(h);
// but instead of exporting htmlInternal, you export this wrapper:
const s = new WeakMap();
export function html(strings) {
let str = s.get(strings);
if (!str) s.set(strings, str = strings.map(s => s.replace(/\n(\s*<)/g, ' $1')));
arguments[0] = str;
return htmlInternal.apply(this, arguments);
}
If you have a large block of text that is broken up into lines in the source for readability, and you happen to get unlucky enough to have an inline element at the beginning of the line, the element will not be separated from the preceding content with a space in the rendered output.
Take the following fragment:
The rendered output that would be expected would be something like:
instead you get:
(Note the missing space)
This an issue because in page layouts with large paragraphs you are basically playing with fire. You can very easily run into a case where you have a "typo" despite the source being correct. It is possible to work around this issue, but that requires you to A) be aware of it and B) not be silently screwed by automated formatting tools.