Handling whitespace in ES6 template literals

[2016-05-15] dev, javascript, esnext, template literals
(Ad, please don’t block)

In this blog post, we look at problems that arise when template literals contain whitespace:

  • Breaking up long lines
  • Dedenting content
  • Joining Arrays
  • Indenting inserted content

I’m using the library common-tags by Declan de Wet (with “useful template literal tags for dealing with strings in ES6”) to demonstrate solutions for some of these problems.

Breaking up long lines  

Occasionally, you have long lines that you want to break up. common-tag’s tag function oneLine lets you do that:

console.log(oneLine`
a
single
line
with
many
words
`);
// a single line with many words

Dedenting content  

Template literals let you embed multi-line text content inside JavaScript. The main challenge is that the text must both have proper indentation and fit nicely into its JavaScript surroundings:

function foo() {
    console.log(`<ul>
    <li>first</li>
    <li>second</li>
</ul>`);
}

This does not look good: The initial <ul> is out of place at the end of the line and subsequent lines don’t respect JavaScript’s indentation. The output of foo() looks like this:

<ul>
    <li>first</li>
    <li>second</li>
</ul>

As a work-around one can use trim() to gain more freedom w.r.t. the first and the last line:

function foo() {
    console.log(`
<ul>
    <li>first</li>
    <li>second</li>
</ul>
`.trim());
}

The output is the same and the embedded text looks nicer, but the indentation problem remains.

A tag function like common-tag’s stripIndent can help:

function foo() {
    const ul = stripIndent`
        <ul>
            <li>first</li>
            <li>second</li>
        </ul>
        `;
}

The idea is to determine which line has the smallest indent and to remove that indent from all lines. Additionally, leading and trailing whitespace is trimmed.

Joining Arrays  

If you use template literals for templating, you often write code like this:

console.log(stripIndent`
    <ul>
        ${['foo', 'bar'].map(x => `<li>${x}</li>`).join('\n')}
    </ul>
`);

The problem is that the output is not properly indented:

<ul>
        <li>foo</li>
<li>bar</li>
    </ul>

That’s because the line breaks inserted via join() are not followed by the correct indentation.

common-tags has the tag function html which detects Arrays returned from substitutions (${}) and inserts them correctly. No need for join(), anymore:

console.log(html`
    <ul>
        ${['foo', 'bar'].map(x => `<li>${x}</li>`)}
    </ul>
`);

Now the output looks like this:

<ul>
    <li>foo</li>
    <li>bar</li>
</ul>

Indenting inserted content  

Alas, common-tags does not indent inserted strings correctly:

console.log(`
    <ul>
        ${'<li>foo</li>\n<li>bar</li>'}
    </ul>
    `);

Here, the output is:

<ul>
        <li>foo</li>
<li>bar</li>
    </ul>

The line break between the </li> and the <li> is not followed by the correct indentation.

Further reading