Многострочные строки, которые не нарушают отступ

Согласно этому обсуждению, в ECMAScript 6 возможно определить многострочные строки без необходимости размещать последующие строки строки в самом начале строки.

Пост Аллена Вирфса-Брока содержит пример кода:

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`;

Может ли кто-нибудь объяснить, как этого можно достичь? Как определить это dontIndent вещь для того, чтобы удалить пробел, используемый для отступа?

7 ответов

Ответ 2016: пакет dedent-js справится с этим. Обратите внимание, что пакет 'dedent-js' на самом деле работает как с вкладками, так и с пробелами, 'dedent' не работает на вкладках:

var dedent = require('dedent-js');

var text = dedent(`
  <div>
    <span>OK</span>
    <div>
      <div></div>
    </div>
  </div>
`);

Удалит исходящий пробел в каждой строке и возврат каретки. У него также есть больше пользователей, система отслеживания проблем, и ее легче обновлять, чем копирование из Stack Overflow!

Вы также можете просто заменить строку на двойные пробелы (при условии, что для отступа используются пробелы, а не символы табуляции). Очевидно, что любые двойные пробелы в вашей фактической строке будут удалены, но в большинстве случаев это должно быть в порядке.

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

Эта функция реализована путем определения пользовательской функции и последующего использования ее в качестве тега (dontIndent выше). Удар по коду от сути Zenparsing:

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);
}

Я успешно протестировал его в Firefox Nightly:

введите описание изображения здесь

Как определить это dontIndent вещь, чтобы удалить пробелы, используемые для отступа?

Я полагаю, что чего-то вроде этого должно хватить для многих случаев (включая OP):

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

Демо-код в этом фрагменте:

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');
}

Объяснение

Литералы шаблонов JavaScript можно вызывать с помощью тега, который в этом примереdontIndent. Теги определены как функции и вызываются с литералом шаблона в качестве аргумента, поэтому мы определяемdontIndent()функция. Литерал шаблона передается в качестве аргумента в массиве, поэтому мы используем выражение('' + str)чтобы преобразовать содержимое массива в строку. Затем мы можем использовать регулярное выражение, например/(\n)\s+/g к .replace() все вхождения разрывов строки, за которыми следует пробел, и только разрыв строки для достижения цели OP.

Проблема со всеми существующими ответами в том, что они являются решениями во время выполнения. То есть они берут литерал многострочного шаблона и пропускают его через функцию во время выполнения программы, чтобы избавиться от начальных пробелов. Это «неправильный способ», потому что эта операция должна выполняться во время компиляции. Причина в том, что в этой операции нет ничего, что требовало бы информации во время выполнения, вся необходимая для этого информация известна во время компиляции.

Чтобы сделать это во время компиляции, я написал плагин Babel под названием Dedent Template Literals. В основном это работает следующим образом:

      const httpRFC = `                Hypertext Transfer Protocol -- HTTP/1.1

                 Status of this Memo

                    This document specifies an Internet standards track protocol for the
                    Internet community, and requests discussion and suggestions for
                    improvements.  Please refer to the current edition of the "Internet
                    Official Protocol Standards" (STD 1) for the standardization state
                    and status of this protocol.  Distribution of this memo is unlimited.

                 Copyright Notice

                    Copyright (C) The Internet Society (1999).  All Rights Reserved.`;

console.log(httpRFC);

Будет печатать:

                      Hypertext Transfer Protocol -- HTTP/1.1

Status of this Memo

   This document specifies an Internet standards track protocol for the
   Internet community, and requests discussion and suggestions for
   improvements.  Please refer to the current edition of the "Internet
   Official Protocol Standards" (STD 1) for the standardization state
   and status of this protocol.  Distribution of this memo is unlimited.

Copyright Notice

   Copyright (C) The Internet Society (1999).  All Rights Reserved.

Интерполяции тоже работают без проблем. Также, если вы начнете строку перед первым столбцом после открывающей обратной кавычки литерала шаблона, плагин выдаст ошибку, показывающую место ошибки. Если следующий файл находится в src/httpRFC.jsпод ваш проект:

      const httpRFC = `                Hypertext Transfer Protocol -- HTTP/1.1

                 Status of this Memo

                    This document specifies an Internet standards track protocol for the
                    Internet community, and requests discussion and suggestions for
                    improvements.  Please refer to the current edition of the "Internet
                    Official Protocol Standards" (STD 1) for the standardization state
                    and status of this protocol.  Distribution of this memo is unlimited.

                Copyright Notice

                    Copyright (C) The Internet Society (1999).  All Rights Reserved.`;

console.log(httpRFC);

При транспиляции возникает следующая ошибка:

      Error: <path to your project>/src/httpRFC.js: LINE: 11, COLUMN: 17. Line must start at least at column 18.
    at PluginPass.dedentTemplateLiteral (<path to your project>/node_modules/babel-plugin-dedent-template-literals/index.js:39:15)
    at newFn (<path to your project>/node_modules/@babel/traverse/lib/visitors.js:175:21)
    at NodePath._call (<path to your project>/node_modules/@babel/traverse/lib/path/context.js:55:20)
    at NodePath.call (<path to your project>/node_modules/@babel/traverse/lib/path/context.js:42:17)
    at NodePath.visit (<path to your project>/node_modules/@babel/traverse/lib/path/context.js:92:31)
    at TraversalContext.visitQueue (<path to your project>/node_modules/@babel/traverse/lib/context.js:116:16)
    at TraversalContext.visitSingle (<path to your project>/node_modules/@babel/traverse/lib/context.js:85:19)
    at TraversalContext.visit (<path to your project>/node_modules/@babel/traverse/lib/context.js:144:19)
    at Function.traverse.node (<path to your project>/node_modules/@babel/traverse/lib/index.js:82:17)
    at NodePath.visit (<path to your project>/node_modules/@babel/traverse/lib/path/context.js:99:18) {
  code: 'BABEL_TRANSFORM_ERROR'
}

Это также работает с вкладками, если вы используете вкладки (и только вкладки) для отступов и используете пробелы (и только пробелы) для выравнивания.

Его можно установить, запустив npm install --save-dev babel-plugin-dedent-template-literalsи используется, помещая dedent-template-literalsв качестве первого элемента pluginsмассив в конфигурации Babel . Дополнительную информацию можно найти в README.

      function trim(segments, ...args) {
    const lines = segments
        .reduce((acc, segment, i) =>  acc + segment + (args[i] || ''), '') // get raw string
        .trimEnd().split('\n') // Split the raw string into lines
        .filter(line => line != "") // remove empty lines

    // Find the minimum number of leading spaces across all lines
    const minLeadingSpaces = lines.reduce((acc, line) => {
        // Find the number of leading spaces for this line
        const leadingSpaces = line.match(/^ */)[0].length
        // if it has less leading spaces than the previous minimum, set it as the new minimum
        return leadingSpaces < acc ? leadingSpaces :  acc
    }, Infinity)

    // Trim lines, join them and return the result
    return lines.map(line => line.substring(minLeadingSpaces)).join('\n')
}

Использование:

      console.log(trim`
  <div>
    <p>
      Lorem ipsum dolor sit amet.
    </p>
  </div>
`)

Как заявляет Шиме Видас, функции можно использовать как тег и вызывать, просто помещая его перед строкой шаблона.

Для этого существует ряд модулей NPM, которые охватывают многие крайние случаи, которые было бы трудно скрыть самостоятельно. Основными двумя являются:

dedent, 4 миллиона загрузок в неделю, последнее обновление 4 года назад
значок dedent npm с приведенной выше статистикой

endent, 2,5 тысячи загрузок еженедельно, обновлено 4 месяца назад
значок endent npm с приведенной выше статистикой

Другие вопросы по тегам