Multiline strings that don't break indentation

According to this esdiscuss discussion, it is possible in ECMAScript 6 to define multiline strings without having to place subsequent lines of the string at the very beginning of the line.

Allen Wirfs-Brock’s post contains a code example:

var a = dontIndent
        `This is a template string.
         Even though each line is indented to keep the
         code neat and tidy, the white space used to indent
         is not in the resulting string`;

Could someone explain how this can be achieved? How to define this dontIndent thing in order to remove the whitespace used for indentation?


2020 answer: there is still nothing built into the JS stdlib to handle de-denting long lines, although TC39 has discussed adding a new template literal that handles indentation. You have 2 options presently:

  1. The endent and dedent-js packages will handle this. Note the dedent-js package actually works with both tabs and spaces, dedent is a separate package and fails on tabs:
    var dedent = require('dedent-js');
    var text = dedent(`
      <div>
        <span>OK</span>
        <div>
          <div></div>
        </div>
      </div>
    `);

Will strip out the proceeding whitespace on each line and the leading carriage return. It also has more users, an issue tracker, and is more easily updated than copypasting from Stack Overflow!

  1. Don't indent long lines, but use an editor that shows long lines as indented. Eg, vsCode - you can simply use long lines, not indent anything, and include carriage returns in a long string. vsCode will show them indented. The string below has no indentation - the second line The empty export... is immediately after the carriage return, but shows up as indented.

enter image description here


This feature is implemented by defining a custom function and then using it as a tag (dontIndent above). The code blow is from Zenparsing's gist:

function dedent(callSite, ...args) {

    function format(str) {

        let size = -1;

        return str.replace(/\n(\s+)/g, (m, m1) => {

            if (size < 0)
                size = m1.replace(/\t/g, "    ").length;

            return "\n" + m1.slice(Math.min(m1.length, size));
        });
    }

    if (typeof callSite === "string")
        return format(callSite);

    if (typeof callSite === "function")
        return (...args) => format(callSite(...args));

    let output = callSite
        .slice(0, args.length + 1)
        .map((text, i) => (i === 0 ? "" : args[i - 1]) + text)
        .join("");

    return format(output);
}

I've successfully tested it in Firefox Nightly:

enter image description here


You can also just do a string replace for double spaces (assuming your indentation uses spaces, not tabs). Obviously any double spaces in your actual string would be removed, but for most cases this should be ok.

const MSG = (`Line 1
          line 2
          line 3`).replace(/  +/g, '');
// outputs
/*
Line 1
line 2
line 3
*/

How to define this dontIndent thing in order to remove the whitespace used for indentation?

I suppose something like this should suffice for many cases (including the OP):

function dontIndent(str){
  return ('' + str).replace(/(\n)\s+/g, '$1');
}

Demo code in this snippet:

var a = dontIndent
        `This is a template string.
         Even though each line is indented to keep the
         code neat and tidy, the white space used to indent
         is not in the resulting string`;

console.log(a);
         
function dontIndent(str){
  return ('' + str).replace(/(\n)\s+/g, '$1');
}

Explanation

JavaScript template literals can be called with a tag, which in this example is dontIndent. Tags are defined as functions, and are called with the template literal as an argument, so we define a dontIndent() function. The template literal is passed as an argument in an array, so we use the expression ('' + str) to cast the array content to a string. Then, we can use a regular expression like /(\n)\s+/g to .replace() all occurrences of line breaks followed by white space with only the line break to achieve the aim of the OP.


As Šime Vidas states, functions can be used as a tag, and invoked by just placing it in front of the template string.

A number of NPM modules exist to do this, and will cover many edge cases that would be hard to cover yourself. The main two are:

dedent, 4 million weekly downloads, last updated 4 years ago
dedent npm badge, with the above statistics

endent, 2.5 thousand weekly downloads, updated 4 months ago
endent npm badge, with the above statistics