Freddy Bonda
Freddy Bonda

Reputation: 1259

Javascript remove whitespaces in `strings`

I JavaScript the ` is very useful when writing big strings. My problem is that it takes into account the amount of white space before it in the JavaScript. So if your string is indented in the JavaScript, then it will be indented in the string as well. Is there any way to get rid of this?

Because in an example like this I would like the html tag to be flush against the left side in the string, but I do not want to make it flush against the left side in the JavaScript. Check how the console.log turns out to understand it better.

MyFunction = function() {
  console.log(`
    <html>
      <head>
      </head>
      <body>
      </body>
    </html>
  `);
}

MyFunction();

console.log:

enter image description here

Upvotes: 2

Views: 3660

Answers (8)

Astro Bear
Astro Bear

Reputation: 71

TypeScript solution, removes common indentation from the string:

/**
 * Remove common indentation from a string
 * @param str The string to deindent
 * @param tabSize The size of the tab
 * @returns The deindented string
 */
export function deindent(strings: TemplateStringsArray, { tabSize = 2 } = {}) {
  let str = strings.join('');
  str = str.replace(/\t/g, ' '.repeat(tabSize));

  const maxCommonIndent = str
    .split('\n')
    .filter((line) => line.trim() !== '')
    .map((line) => line.match(/^ */)![0].length)
    .reduce((max, indent) => Math.max(max, indent), 0);

  return str
    .split('\n')
    .map((line) => line.slice(maxCommonIndent))
    .join('\n');
}

Example:

function deindent(strings, { tabSize = 2 } = {}) {
  let str = strings.join('');
  str = str.replace(/\t/g, ' '.repeat(tabSize));

  const minCommonIndent = str
    .split('\n')
    .filter((line) => line.trim() !== '')
    .map((line) => line.match(/^ */)[0].length)
    .reduce((min, indent) => Math.min(min, indent), Infinity);

  return str
    .split('\n')
    .map((line) => line.slice(minCommonIndent))
    .join('\n');
}


console.log(deindent`
    <html>
      <head>
      </head>
      <body>
        <div>
          Hello World!
        </div>
      </body>
    </html>
`);

Upvotes: 0

Kevin B.
Kevin B.

Reputation: 1363

There are, of course, a thousand ways to get to your destination. To make only the indent of the content disappear you can use the following method with your template strings. This ensures that only the indent created by the template string disappears, but the basic formatting remains intact.

function trimIndent(strings, ...values) {
    const result = new Array(strings.length);
    result[0] = strings[0].replace(/^\n/, '');
    const endIndex = strings.length - 1;
    result[endIndex] = strings[endIndex].replace(/\n$/, '');

    var indent = result[0].match(/^\s+/g);
    result[0] = result[0].replace(/^\s+/, '');

    for (let i = 0, m = result.length; i < m; i++) {
        var input = result[i] ?? strings[i];
        result[i] = input.replace(new RegExp('\n' + indent, "gm"), '\n');
    }

    return result.flatMap((value, index) => {
        return value + (index < values.length ? values[index] : '')
    }).join("");
}

// example call:
var user = {
  id: 10,
  name: "Kevin",
  
}
if (true) {
  console.log(trimIndent`
      <div>
        <span>ID: ${user.id}</span>
        <span>
          Username: ${user.name}
        </span>
      </div>
  `);
}

Upvotes: 0

J.Ko
J.Ko

Reputation: 1052

I would do the following:

MyFunction = function() {
  /*however many spaces you want to remove from the front*/
  const spaces = '    '; // four spaces
  /* or */
  const spacesRegex = /\s+/g;
  let str = '';
  const html = `
    <html>
      <head>
      </head>
      <body>
      </body>
    </html>
  `.split('\n')
  .forEach(line=>{
    str += line.replace(spaces, '') + '\n';
  });
  console.log(str);

}

Upvotes: 1

wiesion
wiesion

Reputation: 2435

Using a simple regex to find the initial indentation of the string can be done with /^[\r\n]?(\s+)/ - the result can be used to again do a line-for line replacement of the initial indent. Also this takes advantage of the performance of JS RegExp.

If you also want the first and last line breakes removed, just add .trim() after the string replacement.

function trimLeadingWS(str) {
  /*
    Get the initial indentation
    But ignore new line characters
  */
  var matcher = /^[\r\n]?(\s+)/;
  if(matcher.test(str)) {
    /*
      Replace the initial whitespace 
      globally and over multiple lines
    */
    return str.replace(new RegExp("^" + str.match(matcher)[1], "gm"), "");
  } else {
    // Regex doesn't match so return the original string
    return str;
  }
};

MyFunction = function() {
  console.log(trimLeadingWS('nothing to trim'));
  console.log(trimLeadingWS(`
                    really 
                      quite
                        some
                          of                    (amount of spaces after line break should be ignored)
                        indent
                      to 
                    remove
  `));
  console.log(trimLeadingWS(`
    <html>
      <head>
      </head>
      <body>
      </body>
    </html>
  `));
}

MyFunction();

Upvotes: 1

rmn
rmn

Reputation: 1169

Since you want them html tag to be flush against the left side, but still want head and body indented relatively, what you're looking for is to replace set of 4 spaces in the beginning of each line with an empty string.

string.replace(/^ {4}/gm, '')

Here's the live code ..

MyFunction = function() {
  console.log(`
    <html>
      <head>
      </head>
      <body>
      </body>
    </html>
  `.replace(/^ {4}/gm, ''));
}

MyFunction();

Upvotes: 6

ponury-kostek
ponury-kostek

Reputation: 8060

You can split text by \n, find indentation in first revelant line and remove it from the rest of line, then join it again.

MyFunction = function () {
	let indent = "";
	console.log(`
    <html>
      <head>
      </head>
      <body>
      </body>
    </html>
  `.split("\n").reduce((lines, line) => {
		if (!indent.length && line.trim()) {
			indent = line.match(/^(\s+)/)[0];
		}
		lines.push(line.replace(indent, ""));
		return lines;
	}, []).filter(line => line.length).join("\n"));
};
MyFunction();

Upvotes: 0

Sagiv b.g
Sagiv b.g

Reputation: 31014

You mean trim the white spaces?

MyFunction = function() {
  console.log(`
    <html>
      <head>
      </head>
      <body>
      </body>
    </html>
  `.trim());
}

MyFunction();

If you want auto indentations for all of your tags i would go for a library, maybe something like pretty.

No need to invent the wheel here.

Upvotes: 2

Mark
Mark

Reputation: 92440

You can match the spaces after new lines and get the length of the first one. From that you can build a regex that can replace them:

let MyFunction = function(str) {
    let matches = str.match(/ +/g)
    let initial = matches[0].length         // how big is the indent
    let re = RegExp(`\n {${initial}}`, 'g') // remove these at the beginning of lines
    return str.replace(re, '\n')
  }

let str = `
  <html>
    <head>
    </head>
    <body>
        <div>
           some text
        </div>
    </body>
  </html>
`  
console.log(MyFunction(str))

Upvotes: 0

Related Questions