Typescript: Emit не сохраняет пустые строки

Созданный на 7 окт. 2014  ·  36Комментарии  ·  Источник: microsoft/TypeScript

Привет,

Версия TS : 1.1

Дано

function foo() {

    var x = 10;

    var y = 11;
}

Раньше мы получали

function foo() {
    var x = 10;

    var y = 11;
}

В новом компиляторе отсутствует разрыв строки

function foo() {
    var x = 10;
    var y = 11;
}

(Оба компилятора удалили первую пустую строку, но новый компилятор пошел еще дальше.)

Это может повлиять на работу при отладке JavaScript в браузере.

Bug help wanted

Самый полезный комментарий

Толпа хочет preserveWhitespace: true/false
@ORESoftware ++

Все 36 Комментарий

Добавление некоторой справочной информации ... Причина, по которой новый компилятор удаляет все пустые строки, заключается в том, что это единственное, что мы можем делать _последовательно_. Сохранение пустых строк всегда будет проблемой, когда дело касается конструкций, которые подлежат переписыванию, например, объявлений классов и модулей. Мы сталкиваемся с точно такими же проблемами с сохранением комментариев. Итак, хотя я с пониманием отношусь к решению этой проблемы, это непросто.

@NoelAbrahams Мне любопытно, какие проблемы вы видите при отладке?

@ahejlsberg @NoelAbrahams Я создал прототип, выпущенный в исходном проекте CodePlex, который действительно проделал потрясающую работу с комментариями и сохранением новой строки. Даже в конструкциях, которые были сильно переписаны (например, стрелочные функции в функциональные выражения), он почти всегда делал то, что вы интуитивно ожидали от него.

Сохранение строк в основном является функцией простого повторного использования разрывов строк при сохранении старого кода и использования относительного интервала при выполнении преобразований. т.е. если у вас есть:

module M {
}
module N {
}

Затем, когда вы генерируете IIFE для 'N', вы говорите: «Мы должны сохранить мелочи между модулем, который мы переписываем, и предыдущим синтаксическим элементом».

Вот до и после того, как работал мой прототип эмиттера, который сохранял комментарии / новые строки с высоким уровнем точности:

https://typescript.codeplex.com/SourceControl/latest#tests/Fidelity/emitter2/ecmascript5/Parser.ts
https://typescript.codeplex.com/SourceControl/latest#tests/Fidelity/emitter2/ecmascript5/Parser.ts.expected

Здесь вы также можете увидеть множество примеров:
https://typescript.codeplex.com/SourceControl/latest#tests/Fidelity/emitter/ecmascript5/

Единственная функция, которую я не реализовал, - это «выравнивание». т.е. если у вас был код, который был каким-то образом выровнен в оригинале (очень часто с объявлениями параметров), мы бы хотели, чтобы излучаемый код также сохранил это.

Но сделать это было бы очень тривиально.

Тем не менее, преобразование контрактов из TS в JS действительно пыталось сохранить отступы. Вы можете увидеть это здесь:
https://typescript.codeplex.com/SourceControl/latest#tests/Fidelity/emitter/ecmascript5/ClassDeclaration/ClassDeclaration2.ts
https://typescript.codeplex.com/SourceControl/latest#tests/Fidelity/emitter/ecmascript5/ClassDeclaration/ClassDeclaration2.ts.expected

Обратите внимание на правильный отступ операторов (даже если они занимают несколько строк) даже после преобразования вложенных модулей и классов в IIFE.

@ahejlsberg , при отладке в браузере особых проблем нет. Это просто упрощает навигацию по коду JavaScript и поиск строк для установки точек останова, когда есть точное соответствие с фактическим исходным кодом TypeScript.

Я лично мог бы жить без пустых строк, но поскольку TS приложила все усилия, чтобы сохранить и испустить _beautiful _ JavaScript (: smile :), то, что это реализовано, кажется вполне естественным.

@ahejlsberg @NoelAbrahams Есть одна проблема, которая существует с отладкой в ​​браузере, которая немного связана с этим разговором. При использовании цепочек setter / getter (например, с jquery) или цепочки обещаний новые переводы строк теряются во время перевода. При этом это огромная проблема при работе с функциями стрелок.

Например:

(<any> x).a('#test')
    .b('test')
    .c(() => 'foo')
    .d(() => 'bar')
    .e(() => 5)
    .f(() => 6);

Становится:

x.a('#test').b('test').c(function () { return 'foo'; }).d(function () { return 'bar'; }).e(function () { return 5; }).f(function () { return 6; });

Используя Chrome и sourceMaps, точки останова по-прежнему пропускаются.

http://www.typescriptlang.org/Playground#src = (% 3Cany% 3E% 20x) .a ('% 23test')% 0A% 20% 20% 20% 20.b ('test')% 0A% 09.c (()% 20% 3D% 3E% 20'foo ')% 0A% 09.d (()% 20% 3D% 3E% 20'bar')% 0A% 09.e (()% 20 % 3D% 3E% 205)% 0A% 09.f (()% 20% 3D% 3E% 206)% 3B

@mtraynham , на самом деле я думаю, что проблема, которую вы выделили, немного другая.

В предыдущих версиях тело встроенной функции всегда выводилось на новых строках:

// TS
var x = () => 'foo';

// JS - old
var x = function () { 
             return 'foo'; 
       };

// JS - new
var x = function () { return 'foo'; };

Я тоже обнаружил, что это проблема - время от времени мне приходится возвращаться и создавать function чтобы я мог установить точку останова при отладке в браузере.

@NoelAbrahams Ах, да, я использовал то же самое временное решение ... Я не был уверен, была ли это подходящей ошибкой, чтобы способствовать этой проблеме (стирание перевода строки), или я должен открыть другой?

Я выделил №2259 в отдельный выпуск.

Как технический директор, изучающий возможность перевода нашего сообщества разработчиков javascript на машинописный текст, возможность новой строки действительно поможет. Одним из основных преимуществ машинописного текста было поддержание читабельности и структуры кода, созданного в машинописном тексте, сгенерированном JavaScript.

Замечательно, что комментарии сохранены в недавнем обновлении и добавлении директивы командной строки «--removeComments».

Я бы также хотел этого по той же причине, что и @timjmartel - когда разработчики видят, что сгенерированный JS выглядит красиво, они менее устойчивы к принятию. Сохранение (по крайней мере вертикальных) пробелов делает код менее похожим на то, что он был создан машиной, и больше похож на идоматический код JS, написанный человеком.

Если бы наша команда когда-либо решила отказаться от TS и вместо этого продолжить с транспилированным JS, было бы намного проще принять исходящие исходные файлы JS, если бы они имели удобные для человека пробелы.

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

Что касается связанных вызовов функций, можно ли иметь один вызов для линии, чтобы пользователи могли устанавливать точки останова? Опять же, эту функцию можно запросить с помощью опции «--oneCallForLine», если это еще одна вещь «ударил или пропустил».

Спасибо за внимание.

На самом деле, связанные вызовы функций могут быть разделены средством улучшения исходного кода, поэтому нет смысла встраивать такую ​​функцию в TypeScript.

Это не должно быть так сложно, разве не должна быть опция в tsconfig.json

preserveWhitespace: true/false

?

Но я не рассматриваю это как вариант компилятора: https://www.typescriptlang.org/docs/handbook/compiler-options.html

Я только что понял. Одна из веских причин не оставлять пробелы - это то, что они действительно помогут предотвратить случайное редактирование .js вместо .ts. Я предполагаю, что нужно было бы записывать файлы .js только для чтения / выполнения. Так что, возможно, это не проблема.

При этом я не думаю, что tsc записывает файлы .js только для чтения / выполнения, есть ли способ настроить tsc для этого?

Нет, не сейчас. Не стесняйтесь открыть для этого отдельную тему.

@DanielRosenwasser, вы говорите, если мы хотим сохранить пробелы, мы должны открыть отдельную проблему? Этого вопроса недостаточно? Nevermind LOL больше, чем через месяц, я понимаю, что вы говорили открыть отдельную проблему для разрешений на чтение / запись / выполнение для перенесенных файлов :)

Было бы неплохо иметь это.

+1

+1

+1

Толпа хочет preserveWhitespace: true/false
@ORESoftware ++

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

+1 preserveWhitespace: true/false

Временный взлом

Используйте esformatter для добавления

Со следующим файлом конфигурации:

{
  "lineBreak": {
    "before": {
      "FunctionDeclaration": ">=2",
      "FunctionDeclarationOpeningBrace": 0,
      "FunctionDeclarationClosingBrace": 1,
      "MethodDefinition": ">=2",
      "ClassDeclaration": ">=2"
    },
    "after": {
      "FunctionDeclaration": ">=2",
      "FunctionDeclarationOpeningBrace": 1,
      "MethodDefinitionClosingBrace": ">=2",
      "ClassClosingBrace": ">=2"
    }
  }
}

@mtraynham Ваш пример:

(<any> x).a('#test')
    .b('test')
    .c(() => 'foo')
    .d(() => 'bar')
    .e(() => 5)
    .f(() => 6);

с последним компилятором производит этот JS:

x.a('#test')
    .b('test')
    .c(function () { return 'foo'; })
    .d(function () { return 'bar'; })
    .e(function () { return 5; })
    .f(function () { return 6; });

(см. TS PlayGround https://goo.gl/JViurr)

Многое изменилось с TS версии 1.1 (версия TypeScript, для которой была создана эта проблема). Может, этот вопрос можно закрыть? @ahejlsberg ?

@ valera-rozuvan Вместо этого я открыл # 2259. Моя проблема была связана с переводом строки, но не с той же проблемой, что описана в этой ошибке. № 2259 был закрыт некоторое время назад (май 2015 г.).

Это конфигурация esformatter bril-andrew, но с исправленной ошибкой (когда объявление класса содержало слово import, разрыв строки отсутствовал):

{
    "lineBreak": {
        "before": {
            "FunctionDeclaration": ">=2",
            "FunctionDeclarationOpeningBrace": 0,
            "FunctionDeclarationClosingBrace": 1,
            "MethodDefinition": ">=2",
            "ClassDeclaration": ">=2",
            "ExportNamedDeclaration": 2
        },
        "after": {
            "FunctionDeclaration": ">=2",
            "FunctionDeclarationOpeningBrace": 1,
            "MethodDefinitionClosingBrace": ">=2",
            "ClassClosingBrace": ">=2"
        }
    }
}

Я не думаю, что использование esformatter решает всю проблему. Конечно, он может автоматически вставлять пустые строки вокруг функций и т.д. Но для меня пустые строки внутри функций еще более важны. Мы используем пустые строки как абзацы в прозе: чтобы сгруппировать отдельные мысли.

Эти пустые строки в функциях помогают передать структуру функции. Я считаю, что без них удобочитаемость страдает.

@ahejlsberg Я вижу неправильные номера строк в выходных данных модульного тестирования при использовании ts-jest, и эта проблема, похоже, вызвана удалением пустых строк в выходных данных js. Мне любопытно, почему так сложно оставить эти строчки в финальном js. Есть ли еще какая-нибудь информация по этому поводу? Можем ли мы как-то помочь, чтобы это произошло? :)

Или он уже был объединен и еще не выпущен? -> V4 Следующая большая версия # 3143

@JimTheMan Если вы используете исходные карты, возможно, пакет source-map-support может помочь вам получить правильные трассировки стека на выходе.

Я тоже сталкиваюсь с этой проблемой. Я придумал обходной путь, создав патч различий и отменив изменения пробелов в патче. jsdiff позволяет создавать структурированный объект патча и манипулировать им по

import * as diff from 'diff';

const patch =
      diff.parsePatch(diff.createPatch('file', oldText, newText, '', ''));
const hunks = patch[0].hunks;
for (let i = 0; i < hunks.length; ++i) {
  let lineOffset = 0;
  const hunk = hunks[i];
  hunk.lines = hunk.lines.map(line => {
    if (line === '-') {
      lineOffset++;
      return ' ';
    }
    return line;
  });
  hunk.newLines += lineOffset;
  for (let j = i + 1; j < hunks.length; ++j) {
    hunks[j].newStart += lineOffset;
  }
}
return diff.applyPatch(oldText, patch);

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

@zeroliu Вносит ли это заметную задержку по времени на этапе компиляции?

@ahejlsberg Как вы думаете, стоит ли исправить эту проблему?

@ valera-rozuvan в зависимости от размера вашего проекта. Для моих случаев использования, когда я транспилирую 10-иш файлы размером 100-1000 LOC, это не вызывает заметной задержки.

Есть еще какие-нибудь решения? Я тоже столкнулся с этой проблемой ...

Я частично пытался исправить это в самом компиляторе, когда мой товарищ по команде @emadum напомнил мне, что tsc может сохранять комментарии. Вот небольшой конвейер gulp, который, похоже, неплохо справляется с сохранением новых строк:

const gulp = require('gulp');
const ts = require('gulp-typescript');
const through = require('through2');

function preserveNewlines() {
  return through.obj(function(file, encoding, callback) {
    const data = file.contents.toString('utf8');
    const fixedUp = data.replace(/\n\n/g, '\n/** THIS_IS_A_NEWLINE **/');
    file.contents = Buffer.from(fixedUp, 'utf8');
    callback(null, file);
  });
}

function restoreNewlines() {
  return through.obj(function(file, encoding, callback) {
    const data = file.contents.toString('utf8');
    const fixedUp = data.replace(/\/\*\* THIS_IS_A_NEWLINE \*\*\//g, '\n');
    file.contents = Buffer.from(fixedUp, 'utf8');
    callback(null, file);
  });
}

gulp.task('default', function () {
  return gulp.src('src/**/*.ts')
    .pipe(preserveNewlines())
    .pipe(ts({
      removeComments: false
    }))
    .pipe(restoreNewlines())
    .pipe(gulp.dest('lib'));
});

Я думаю, что более умный комментарий предотвратит некоторые ложные срабатывания, но, похоже, сегодня он работает для нас. Я тестировал это только на относительно небольшом файле, но накладные расходы на производительность были минимальными.

hth

@mbroadst

В итоге я использовал вашу идею в качестве основы и в конечном итоге расширил ее, пока она не стала модулем npm:
https://www.npmjs.com/package/gulp-preserve-typescript-whitespace

Я процитировал ваш пост в Readme, надеюсь, вы не против :)

Была ли эта страница полезной?
0 / 5 - 0 рейтинги