Typescript: Emit não preserva linhas vazias

Criado em 7 out. 2014  ·  36Comentários  ·  Fonte: microsoft/TypeScript

Oi,

Versão TS : 1.1

Dado

function foo() {

    var x = 10;

    var y = 11;
}

Costumávamos obter

function foo() {
    var x = 10;

    var y = 11;
}

No novo compilador falta a quebra de linha

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

(Ambos os compiladores removeram a primeira linha vazia, mas o novo compilador deu um passo adiante.)

Isso pode afetar a experiência ao depurar JavaScript no navegador.

Bug help wanted

Comentários muito úteis

A multidão quer preserveWhitespace: true/false
@ORESoftware ++

Todos 36 comentários

Adicionando algumas informações básicas ... A razão pela qual o novo compilador remove todas as linhas em branco é que esta é a única coisa que podemos fazer _consistentemente_. A preservação da linha em branco sempre será um acerto ou erro quando se trata de construções que estão sujeitas a reescrita, por exemplo, declarações de classe e módulo. Enfrentamos exatamente os mesmos problemas com a preservação de comentários. Portanto, embora eu tenha simpatia por resolver isso, não é uma coisa fácil de fazer.

@NoelAbrahams Estou curioso para saber quais problemas você vê durante a depuração?

@ahejlsberg @NoelAbrahams Eu criei um protótipo emitido no projeto CodePlex original que realmente fez um trabalho excelente com comentários e preservação de nova linha. Mesmo em construções que foram fortemente reescritas (como funções de seta para expressões de função), quase sempre faria o que você intuitivamente esperava que fizesse.

A preservação de linha é principalmente uma função de apenas reutilizar as quebras de linha ao preservar o código antigo e usar espaçamento relativo ao fazer transformações. ou seja, se você tiver:

module M {
}
module N {
}

Então, quando você está emitindo o IIFE para 'N', você diz "devemos manter as curiosidades entre o módulo que estamos reescrevendo e o elemento sintático anterior".

Aqui está o antes / depois de como meu protótipo de emissor funcionou, que preservou comentários / novas linhas com altos níveis de fidelidade:

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

Você pode ver muitos exemplos aqui também:
https://typescript.codeplex.com/SourceControl/latest#tests/Fidelity/emitter/ecmascript5/

O único recurso que não implementei foi o 'alinhamento'. ou seja, se você tinha um código que estava alinhado de alguma forma no original (muito comum com declarações de parâmetros), gostaríamos que o código emitido preservasse isso também.

Mas teria sido muito trivial fazer.

Dito isso, a conversão de contrutos de TS em JS tentou preservar a indentação. Você pode ver isso aqui:
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

Observe como as instruções são devidamente recuadas (mesmo quando se espalham por várias linhas), mesmo depois de convertermos os módulos aninhados e a classe em IIFEs

@ahejlsberg , não há problemas significativos ao depurar no navegador. Isso apenas torna mais fácil navegar pelo código JavaScript e localizar linhas para definir pontos de interrupção quando há uma correspondência exata com o código-fonte TypeScript real.

Eu pessoalmente poderia viver sem as linhas vazias, mas como o TS fez de tudo para preservar e emitir _belo _ JavaScript (: smile :), parece natural que isso seja implementado.

@ahejlsberg @NoelAbrahams Existe um problema com a depuração no navegador que está ligeiramente relacionado a esta conversa. Ao usar cadeias setter / getter (como com jquery) ou promessas de encadeamento, as novas alimentações de linha são perdidas durante a tradução. Dito isso, é um grande ponto de dor ao trabalhar com as funções Arrow.

Como um exemplo:

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

Torna-se:

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

Usando o Chrome e sourceMaps, os pontos de interrupção ainda são ignorados.

http://www.typescriptlang.org/Playground#src = (% 3Cany% 3E% 20x) .a ('% 23test')% 0A% 20% 20% 20% 20.b ('teste')% 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 , na verdade acho que o problema que você destacou é um pouco diferente.

Nas versões anteriores, o corpo de uma função embutida sempre era emitido em novas linhas:

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

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

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

Eu também descobri que isso é um problema - ter que ocasionalmente voltar e criar um function para que eu possa definir um ponto de interrupção ao depurar no navegador.

@NoelAbrahams Ahh sim, tenho usado essa mesma solução temporária ... Não tinha certeza se esse era um bug apropriado para contribuir com esse problema (apagamento de feeds de linha), ou devo abrir outro?

Criei o nº 2259 para a edição separada.

Como um diretor de engenharia, explorando a mudança de nossa comunidade de desenvolvimento javascript para a digitação, a capacidade da nova linha realmente ajudaria. Um dos principais atrativos do typescript era manter a legibilidade e a estrutura do código criado no typescript gerado pelo JavaScript.

É ótimo ver que os comentários são mantidos na atualização recente e na adição da diretiva de linha de comando “--removeComments".

Eu também gostaria disso por um motivo semelhante ao de @timjmartel - quando os desenvolvedores veem o JS emitido parece _agradável_, eles são menos resistentes à adoção. Preservar o espaço em branco (pelo menos vertical) faz com que o código pareça menos como se tivesse sido gerado por uma máquina e mais como um código JS idomatic escrito por um ser humano.

Se nossa equipe decidisse abandonar o TS e, em vez disso, continuar com o JS transpilado, seria muito mais fácil adotar as fontes JS emitidas se eles tivessem um espaço em branco amigável.

Em relação às linhas vazias, seria possível - por enquanto - tê-las emitidas, mesmo que sejam acertadas ou erradas? Tal recurso experimental poderia ser solicitado com a opção “--keepEmptyLines". Não seria tanto por ter um bom JS, mas por ter um JS mais legível.

Em relação às chamadas de função encadeadas, seria possível ter uma chamada por linha, para permitir que os usuários definissem pontos de interrupção? Novamente, esse recurso pode ser solicitado com uma opção "--oneCallForLine", se for outra coisa de "acertar ou errar".

Agradecimentos para sua atenção.

Na verdade, as chamadas de função encadeadas podem ser divididas por um embelezador de código-fonte, portanto, não faz sentido incorporar esse recurso no TypeScript.

Isso não deveria ser tão difícil, não deveria haver apenas uma opção em tsconfig.json

preserveWhitespace: true/false

?

Mas não estou vendo isso como uma opção de compilador: https://www.typescriptlang.org/docs/handbook/compiler-options.html

Eu apenas percebi. Um bom motivo para não manter os espaços em branco é que isso realmente ajudará a evitar que você edite acidentalmente o .js em vez do .ts. Eu acho que uma coisa a fazer seria gravar os arquivos .js como somente leitura / execução somente. Então, talvez isso não seja um problema.

Dito isso, não acho que o tsc grava arquivos .js como somente leitura / execução apenas. Existe uma maneira de configurar o tsc para fazer isso?

Não, não no momento. Sinta-se à vontade para abrir uma edição separada para isso.

@DanielRosenwasser, você está dizendo que se quisermos preservar os espaços em branco, devemos abrir uma edição separada? Este problema não é suficiente? Esquece, LOL, mais de um mês depois, percebi que você estava dizendo para abrir um problema separado para permissões de leitura / gravação / execução em arquivos transpilados :)

Seria bom ter isso.

+1

+1

+1

A multidão quer preserveWhitespace: true/false
@ORESoftware ++

A razão pela qual isso é importante é que o TypeScript deve "degradar-se normalmente" para JS. No momento, ele não pode preservar novas linhas tornando o JS um pouco denso para ler, especialmente se você escrever TypeScript, mas deve entregar JS em algum outro lugar.

+1 preserveWhitespace: true/false

Hack temporário

Use esformatter para adicionar quebras de linha.

Com o seguinte arquivo de configuração:

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

@mtraynham Seu exemplo:

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

com o compilador mais recente produz este JS:

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

(consulte TS PlayGround https://goo.gl/JViurr)

Muita coisa mudou desde a versão 1.1 do TS (a versão do TypeScript para a qual esse problema foi criado). Talvez este problema possa ser resolvido? @ahejlsberg ?

@ valera-rozuvan Eu abri # 2259 em vez disso. Meu problema estava relacionado a alimentações de linha, mas não exatamente o mesmo problema descrito por este bug. # 2259 foi fechado há algum tempo (maio de 2015).

Esta é a configuração do bril-andrew esformatter, mas com um bug corrigido (quando a declaração da classe continha a palavra import, não havia quebra de linha):

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

Eu não acho que usar o esformatter resolve todo o problema. Claro, ele pode inserir linhas em branco automaticamente em torno de funções, etc. Mas para mim, as linhas em branco

Essas linhas em branco nas funções ajudam a comunicar a estrutura da função. Sem eles, acho que a legibilidade sofre.

@ahejlsberg Estou vendo números de linha incorretos em minha saída de teste de unidade ao usar ts-jest, e esse problema parece ser causado pelas linhas vazias sendo removidas na saída js. Estou curioso para saber por que é tão difícil deixar essas linhas no js final. Há mais informações sobre isso? Podemos ajudar de alguma forma a fazer isso acontecer? :)

Ou já foi fundido e ainda não foi lançado? -> Próxima grande versão V4 # 3143

@JimTheMan Se você usar mapas de origem, talvez o pacote source-map-support possa ajudá-lo a obter os rastreamentos de pilha corretos na saída.

Eu também me deparo com esse problema. Eu vim com uma solução alternativa criando um patch diff e reverter as alterações dos espaços em branco no patch. jsdiff permite criar um objeto de patch estruturado e manipulá-lo como desejar.

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

Com essa solução alternativa, você pode preservar todas as quebras de linha do arquivo original.

@zeroliu Isso introduz um atraso perceptível na etapa de compilação?

@ahejlsberg Você acha que vale a pena corrigir esse problema?

@ valera-rozuvan dependendo do tamanho do seu projeto. Para meus casos de uso em que transpilo arquivos 10-ish de 100-1000 LOC, isso não apresenta nenhum atraso perceptível.

Alguma solução aqui ainda? Eu também corro nessa encrenca ...

Eu estava tentando consertar isso no próprio compilador quando meu colega de equipe @emadum me lembrou que tsc pode preservar comentários. Aqui está um pequeno canal gulp que parece fazer um trabalho bastante decente de preservação de novas linhas:

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

Acho que um comentário mais inteligente evitaria alguns falsos positivos, mas parece funcionar bem para nós hoje. Eu só testei isso em um arquivo relativamente pequeno, mas a sobrecarga de desempenho foi mínima.

hth

@mbroadst

Acabei usando sua ideia como base e, eventualmente, a expandi até que se tornasse um módulo NPM:
https://www.npmjs.com/package/gulp-preserve-typescript-whitespace

Eu creditei sua postagem no Leiame, espero que você não se importe :)

Esta página foi útil?
0 / 5 - 0 avaliações