Typescript: Emit no conserva las líneas vacías

Creado en 7 oct. 2014  ·  36Comentarios  ·  Fuente: microsoft/TypeScript

Hola,

Versión TS : 1.1

Dado

function foo() {

    var x = 10;

    var y = 11;
}

Solíamos conseguir

function foo() {
    var x = 10;

    var y = 11;
}

En el nuevo compilador falta el salto de línea

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

(Ambos compiladores eliminaron la primera línea vacía, pero el nuevo compilador ha ido un paso más allá).

Esto puede afectar la experiencia al depurar JavaScript en el navegador.

Bug help wanted

Comentario más útil

La multitud quiere preserveWhitespace: true/false
@ORESoftware ++

Todos 36 comentarios

Agregando información de fondo ... La razón por la que el nuevo compilador elimina todas las líneas en blanco es que esto es lo único que podemos hacer _consistentemente_. La preservación de líneas en blanco siempre va a ser algo impredecible cuando se trata de construcciones que están sujetas a reescritura, por ejemplo, declaraciones de clases y módulos. Enfrentamos exactamente los mismos problemas con la conservación de comentarios. Entonces, aunque comprendo que se resuelva esto, no es fácil de hacer.

@NoelAbrahams Tengo curiosidad por saber qué problemas ves al depurar?

@ahejlsberg @NoelAbrahams Creé un prototipo emitido en el proyecto CodePlex original que en realidad hizo un excelente trabajo con la preservación de comentarios y líneas . Incluso en construcciones que se reescribieron en gran medida (como funciones de flecha para expresiones de funciones), casi siempre haría lo que intuitivamente esperaba que hiciera.

La preservación de línea es principalmente una función de simplemente reutilizar los saltos de línea cuando se conserva el código antiguo y usar el espaciado relativo al hacer transformaciones. es decir, si tienes:

module M {
}
module N {
}

Luego, cuando emite el IIFE para 'N', dice "deberíamos mantener la trivia entre el módulo que estamos reescribiendo y el elemento sintáctico anterior".

Aquí está el antes / después de cómo funcionaba mi prototipo de emisor que conservaba los comentarios / nuevas líneas con altos niveles de fidelidad:

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

También puede ver muchos ejemplos aquí:
https://typescript.codeplex.com/SourceControl/latest#tests/Fidelity/emitter/ecmascript5/

La única característica que no implementé fue la 'alineación'. es decir, si tuvieras un código alineado de alguna manera con el original (muy común con las declaraciones de parámetros), querríamos que el código emitido también lo preservara.

Pero hubiera sido muy trivial hacerlo.

Dicho esto, la conversión de construcciones de TS a JS intentó preservar la sangría. Se puede ver que aquí:
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 cómo las declaraciones están correctamente integradas (incluso cuando se extienden en varias líneas) incluso después de convertir los módulos anidados y la clase en IIFE

@ahejlsberg , no hay problemas importantes al depurar en el navegador. Simplemente hace que sea más fácil navegar por el código JavaScript y localizar líneas para establecer puntos de interrupción cuando hay una correspondencia exacta con el código fuente de TypeScript real.

Yo personalmente podría vivir sin las líneas vacías, pero dado que TS ha hecho todo lo posible para preservar y emitir _beautiful _ JavaScript (: smile :) parece natural que esto se implemente.

@ahejlsberg @NoelAbrahams Existe un problema con la depuración en el navegador que está ligeramente relacionado con esta conversación. Cuando se usan cadenas setter / getter (como con jquery) o promesas de encadenamiento, los nuevos avances de línea se pierden durante la traducción. Dicho esto, es un gran problema cuando se trabaja con funciones de Arrow.

Como ejemplo:

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

Se convierte en:

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

Con Chrome y sourceMaps, los puntos de interrupción aún se omiten.

http://www.typescriptlang.org/Playground#src = (% 3Cany% 3E% 20x) .a ('% 23test')% 0A% 20% 20% 20% 20.b ('prueba')% 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 , en realidad creo que el problema que resaltas es un poco diferente.

En versiones anteriores, el cuerpo de una función en línea siempre se emitía en nuevas líneas:

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

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

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

Yo también he descubierto que esto es un problema: tener que volver ocasionalmente y crear un function para poder establecer un punto de interrupción al depurar en el navegador.

@NoelAbrahams Ahh sí, he estado usando exactamente esta misma solución temporal ... No estaba seguro de si este era un error apropiado para contribuir con este problema (borrado de los avances de línea), ¿o debería abrir otro?

Creé el # 2259 para el tema separado.

Como director de ingeniería que está explorando mover nuestra comunidad de desarrollo de javascript a mecanografiado, la capacidad de la nueva línea realmente ayudaría. Uno de los principales atractivos del mecanografiado era mantener la legibilidad y la estructura del código creado en JavaScript generado mecanografiado.

Es genial ver que los comentarios se mantienen en la actualización reciente y la adición de la directiva de línea de comandos "--removeComments".

También me gustaría esto por una razón similar a @timjmartel : cuando los desarrolladores ven que el JS emitido se ve _ agradable_, son menos resistentes a adoptar. Preservar el espacio en blanco (al menos vertical) hace que el código parezca menos generado por una máquina y más como código JS idomático escrito por un humano.

Si nuestro equipo alguna vez decidiera abandonar TS y en su lugar continuar con el JS transpilado, sería mucho más fácil adoptar las fuentes JS emitidas si tuvieran espacios en blanco amigables para los humanos.

Con respecto a las líneas vacías, ¿sería posible, por ahora, que se emitan, incluso si son impredecibles? Esta característica experimental podría solicitarse con una opción "--keepEmptyLines". No sería tanto por tener un JS agradable, sino por tener un JS más legible.

Con respecto a las llamadas a funciones encadenadas, ¿sería posible tener una llamada por línea para permitir que los usuarios establezcan puntos de interrupción? Una vez más, esta característica podría pedirse con una opción "--oneCallForLine", si es otra cosa "acertar o fallar".

Gracias por tu atención.

En realidad, las llamadas a funciones encadenadas podrían dividirse mediante un embellecedor de código fuente, por lo que no tiene sentido incrustar dicha función en TypeScript.

Esto no debería ser tan difícil, ¿no debería haber una opción en tsconfig.json?

preserveWhitespace: true/false

?

Pero no veo esto como una opción de compilador: https://www.typescriptlang.org/docs/handbook/compiler-options.html

Me acabo de dar cuenta. Una buena razón para no mantener espacios en blanco es que realmente te ayudará a evitar que edites accidentalmente .js en lugar de .ts. Supongo que una cosa sería escribir los archivos .js como de solo lectura / solo ejecución. Entonces, tal vez esto no sea un problema.

Dicho esto, no creo que tsc escriba archivos .js como de solo lectura / solo ejecución, ¿hay alguna manera de configurar tsc para hacer esto?

No en este momento. Sin embargo, siéntase libre de abrir un tema separado para eso.

@DanielRosenwasser, ¿estás diciendo que si queremos preservar los espacios en blanco deberíamos abrir una edición separada? ¿No es suficiente este problema? No importa LOL más de un mes después, me doy cuenta de que estaba diciendo que abriera un problema separado para los permisos de lectura / escritura / ejecución en archivos transpilados :)

Sería bueno tener esto.

+1

+1

+1

La multitud quiere preserveWhitespace: true/false
@ORESoftware ++

La razón por la que esto es importante es que se supone que TypeScript se "degrada con gracia" a JS. En este momento, no puede conservar nuevas líneas, lo que hace que JS sea un poco denso de leer, especialmente si escribe TypeScript, pero se supone que debe entregar JS en otro lugar.

+1 preserveWhitespace: true/false

Hack temporal

Utilice esformatter para agregar saltos de línea.

Con el siguiente archivo de configuración:

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

@mtraynham Tu ejemplo:

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

con el último compilador produce este JS:

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

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

Mucho ha cambiado desde TS Versión 1.1 (la versión de TypeScript para la que se creó este problema). ¿Quizás este tema se pueda solucionar? @ahejlsberg ?

@ valera-rozuvan En su lugar había abierto # 2259. Mi problema estaba relacionado con los avances de línea, pero no exactamente el mismo problema descrito por este error. # 2259 se cerró hace un tiempo (mayo de 2015).

Esta es la configuración del

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

No creo que el uso de esformatter resuelva todo el problema. Claro, puede insertar automáticamente líneas en blanco alrededor de funciones, etc. Pero para mí, las líneas en blanco

Esas líneas en blanco dentro de las funciones ayudan a comunicar la estructura de la función. Sin ellos, encuentro que la legibilidad se ve afectada.

@ahejlsberg Veo números de línea incorrectos en la salida de prueba de mi unidad cuando uso ts-jest, y este problema parece deberse a que las líneas vacías se eliminan en la salida js. Tengo curiosidad por saber por qué es tan difícil dejar estas líneas en los js finales. ¿Hay más información sobre esto? ¿Podemos ayudar de alguna manera a que esto suceda? :)

¿O ya se ha fusionado y aún no se ha lanzado? -> V4 Next Big Version # 3143

@JimTheMan Si usa mapas de origen, tal vez el paquete source-map-support pueda ayudarlo a obtener los seguimientos de pila correctos en la salida.

También me encuentro con este problema. Se me ocurrió una solución al crear un parche de diferencias y revertir los cambios de espacios en blanco en el parche. jsdiff le permite crear un objeto de parche estructurado y manipularlo como desee.

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

Con esta solución, puede conservar todos los saltos de línea del archivo original.

@zeroliu ¿Introduce un retraso de tiempo notable en el paso de compilación?

@ahejlsberg ¿Crees que vale la pena solucionar este problema?

@ valera-rozuvan dependiendo del tamaño de su proyecto. Para mis casos de uso en los que transpilo archivos 10-ish de 100-1000 LOC, no introduce ningún retraso notable.

¿Alguna solución aquí todavía? Yo también corro en este problema ...

Estaba a mitad de camino tratando de arreglar esto en el propio compilador cuando mi compañero de equipo @emadum me recordó que tsc puede conservar los comentarios. Aquí hay una pequeña tubería de trago que parece hacer un trabajo bastante decente al preservar las nuevas líneas:

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

Creo que un comentario más inteligente evitaría algunos falsos positivos, pero parece que hoy nos funciona bien. Solo probé esto en un archivo relativamente pequeño, pero la sobrecarga de rendimiento allí fue mínima.

hth

@mbroadst

Terminé usando tu idea como base y, finalmente, la amplié hasta que se convirtió en un módulo npm:
https://www.npmjs.com/package/gulp-preserve-typescript-whitespace

Acredité tu publicación en el archivo Léame, espero que no te importe :)

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

uber5001 picture uber5001  ·  3Comentarios

seanzer picture seanzer  ·  3Comentarios

fwanicka picture fwanicka  ·  3Comentarios

blendsdk picture blendsdk  ·  3Comentarios

Antony-Jones picture Antony-Jones  ·  3Comentarios