-
-
Notifications
You must be signed in to change notification settings - Fork 405
Description
#779 does not specify the whitespace handling semantics for <template> tag, as a result the current implementation leaves all the whitespace intact as they appear in the source code. This causes several refactoring hazards:
- when migrating from
.hbsthe indentation level likely will change, since<template>is typically nested inside a class, and even for template-only component, the content is customarily indented after the opening<template>tag - when refactoring code, say, nesting things inside an if statement, it changes the indentation level thus the amount of whitespace inside the template tag
It appears that there are broad consensus that this is a problem, and at a minimum, we need to strip the leading indentation.
However, I would like to go a step further, and consider taking the opportunity and collapsing whitespaces by default.
The reason we care about preserving whitespace is for HTML compatibility. The guiding principle for Ember template is that "things that look like HTML should behave like HTML", however, I am not sure if in this particular case it is worth it. Generally, the browser collapses whitespace so it rarely matters, but in the cases where it does, I think the extra leading indentation from the <template> tag will make things too finicky/confusing to rely on whether we strip leading indention or not.
Since the <template> tag makes it easy to access the JavaScript scope, a more reliable and more readable thing to do, IMO, would be to do this in JavaScript:
import { action } from "@ember/object";
import Component from "@glimmer/component";
import { stripIndent } from "common-tags"
const STATIC_EXAMPLE = stripIndent`
function greeting() {
console.log(
"hello, world!"
);
}
`;
class ExampleCodeComponent {
@action dynamicExample(defaultName = "human") {
return stripIndent`
function personalizedGreeting(name = ${JSON.stringify(defaultName)}) {
console.log(
"hello, \${name}!"
);
}
`;
}
<template>
<p>
Static example:
<pre><code>{{STATIC_EXAMPLE}}</code></pre>
</p>
<p>
Dynamic example:
<pre><code>{{this.dynamicExample @user.name}}</code></pre>
</p>
</template>
}Since you can always extract any whitespace sensitive text into JavaScript, I think that is sufficient for the white-space: pre kind of use cases.
So I think we can specify that whitespace <template> has white-space: normal semantics by default – or more precisely template("...", { ... }) apply the equivalent of white-space: normal collapsing to its content by default.
In addition to the regular HTML/browser white space collapsing semantics, we have to specify:
- what happens to the leading and trailing whitespace
becomes
<template> [👈 these 👉] </templaet>
template("\n\x20\x20[👈 these 👉]\n\x20\x20") - what happens around handlebars constructs
becomes
<template> [these 👉] {{#if this.isAdmin}} [👈 and these 👉] {{/if}} [👈 and these] </templaet>
template("\n\x20\x20[these 👉]\n\x20\x20\x20\x20{{#if this.isAdmin}}\n\x20\x20\x20\x20\x20\x20[👈 and these 👉]\n\x20\x20\x20\x20{{/if}}\n\x20\x20[👈 and these]\n") - the browser algorithm is supposed to be language dependent
In languages that separate words with spaces, such as English:...is intended to read as (with applicable soft wrap)<p> Here is an English paragraph that is broken into multiple lines in the source code so that it can be more easily read and edited in a text editor. </p>
"Here is an English paragraph that is broken into multiple lines in the source code so that it can be more easily read and edited in a text editor."
Where the chunks joined by spaces.
On the other hand, in languages that do not separate words, such as Chinese:...is intended to read as<p> 這個段落是那麼長, 在一行寫不行。最好 用三行寫。 </p>
"這個段落是那麼長,在一行寫不行。最好用三行寫。"
...which is what the CSS spec recommends. However, "historically" browsers unconditionally treated\nthe same as spaces, which results in this well-known broken behavior:
"這個段落是那麼長,\x20在一行寫不行。最好\x20用三行寫。"
This behavior is ultimately left in the spec as browser defined. In my testing today, at least on Safari and Chrome they still have the "historical" broken behavior.
So, my proposal is:
- We
.trim()the template text for maximal composability, i.e. the template content begins with and end at the first/last none-whitespace character. Invoking a component should not introduce extra whitespace by default, the caller can easily add whitespace (and typically do) on the outside as needed. - We don't do anything special for handlebars but continue to respect handlebars whitespace control:
{{~,{{~#etc. - We normalize and collapse whitespaces within a line:
\tis replaced by\x20,- leading and trailing
\x20on the line are trimmed, - multiple successive whitespace characters are replace with a single
\x20.
- We normalize line breaks:
\r\nbecomes\n- lone
\rbecomes\n - multiple successive
\nare replaced with a single\n
- We leave the
\nalone for the browser to decide what to do
We should make it possible to overridable locally:
template("...", { whitespace: "collapse" | "preserve" = "collapse")- Something like:
..or if we are not ready to use the "attributes" space...
<template whitespace="collapse">...</template> <template whitespace="preserve">...</template>
// whitespace: collapse <template>...</template> // whitespace: preserve <template>...</template>
We could also consider having a way to change the global default, but it gets complicated:
- where do you put this config?
- is it global global, or is it scoped to the app/addon?
- does it get propagated to the runtime compiler if present?