Typescript: Emit ne conserve pas les lignes vides

Créé le 7 oct. 2014  ·  36Commentaires  ·  Source: microsoft/TypeScript

Salut,

Version TS : 1.1

Donné

function foo() {

    var x = 10;

    var y = 11;
}

Nous avions l'habitude d'obtenir

function foo() {
    var x = 10;

    var y = 11;
}

Dans le nouveau compilateur, le saut de ligne est manquant

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

(Les deux compilateurs ont supprimé la première ligne vide, mais le nouveau compilateur est allé plus loin.)

Cela peut affecter l'expérience lors du débogage de JavaScript dans le navigateur.

Bug help wanted

Commentaire le plus utile

La foule veut preserveWhitespace: true/false
@ORESoftware ++

Tous les 36 commentaires

Ajout d'informations d'arrière-plan ... La raison pour laquelle le nouveau compilateur supprime toutes les lignes vides est que c'est la seule chose que nous pouvons faire de manière cohérente. La préservation des lignes vides sera toujours un jeu d'enfant quand il s'agit de constructions sujettes à réécriture, par exemple les déclarations de classes et de modules. Nous sommes confrontés exactement aux mêmes problèmes avec la préservation des commentaires. Donc, même si je suis favorable à la résolution de ce problème, ce n'est pas une chose facile à faire.

@NoelAbrahams Je suis curieux de savoir quels problèmes vous voyez lors du débogage?

@ahejlsberg @NoelAbrahams J'ai créé un prototype émis dans le projet CodePlex original qui a fait un travail formidable avec la préservation des commentaires et des nouvelles lignes. Même sur les constructions qui ont été fortement réécrites (comme les fonctions fléchées aux expressions de fonction), il ferait presque toujours ce que vous attendiez intuitivement qu'il fasse.

La préservation de ligne est principalement fonction de la simple réutilisation des sauts de ligne lors de la préservation de l'ancien code et de l'utilisation d'un espacement relatif lors des transformations. c'est à dire si vous avez:

module M {
}
module N {
}

Ensuite, lorsque vous émettez l'IIFE pour 'N', vous dites "nous devrions garder les anecdotes entre le module que nous réécrivons et l'élément syntaxique précédent".

Voici l'avant / après du fonctionnement de mon prototype d'émetteur qui a préservé les commentaires / nouvelles lignes avec des niveaux de fidélité élevés:

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

Vous pouvez également voir de nombreux exemples ici:
https://typescript.codeplex.com/SourceControl/latest#tests/Fidelity/emitter/ecmascript5/

La seule fonctionnalité que je n'ai pas implémentée était «l'alignement». c'est-à-dire que si vous aviez du code qui était aligné d'une certaine manière dans l'original (très courant avec les déclarations de paramètres), nous voudrions que le code émis le conserve également.

Mais cela aurait été très trivial à faire.

Cela dit, la conversion des produits de TS en JS a tenté de préserver l'indentation. Vous pouvez le voir ici:
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

Remarquez comment les instructions sont correctement indépendantes (même lorsqu'elles sont réparties sur plusieurs lignes) même après avoir converti les modules imbriqués et la classe en IIFE

@ahejlsberg , il n'y a pas de problèmes significatifs lors du débogage dans le navigateur. Cela facilite simplement la navigation dans le code JavaScript et la localisation des lignes pour définir les points d'arrêt lorsqu'il existe une correspondance exacte avec le code source TypeScript réel.

Personnellement, je pourrais vivre sans les lignes vides, mais depuis que TS s'est donné tant de mal pour préserver et émettre

@ahejlsberg @NoelAbrahams Il existe un problème de débogage dans le navigateur qui est légèrement lié à cette conversation. Lorsque vous utilisez des chaînes setter / getter (comme avec jquery) ou des promesses d'enchaînement, les nouveaux sauts de ligne sont perdus pendant la traduction. Cela étant dit, c'est un énorme problème lorsque vous travaillez avec les fonctions Arrow.

Par exemple:

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

Devient:

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

En utilisant Chrome et sourceMaps, les points d'arrêt sont toujours ignorés.

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 , en fait, je pense que le problème que vous mettez en évidence est légèrement différent.

Dans les versions précédentes, le corps d'une fonction en ligne était toujours émis sur de nouvelles lignes:

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

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

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

J'ai moi aussi trouvé que c'était un problème - devoir parfois revenir en arrière et créer un function afin de pouvoir définir un point d'arrêt lors du débogage dans le navigateur.

@NoelAbrahams Ahh oui, j'utilise exactement la même solution temporaire ... Je ne savais pas si c'était un bogue approprié pour contribuer à ce problème (effacement des sauts de ligne), ou devrais-je en ouvrir un autre?

J'ai créé le numéro 2259 pour le numéro séparé.

En tant que directeur de l'ingénierie explorant le passage de notre communauté de développement javascript à la dactylographie, la nouvelle capacité de ligne serait vraiment utile. L'un des principaux attraits du typecript était de maintenir la lisibilité et la structure du code créé dans le typeScript generatedJavascript.

C'est bien de voir que les commentaires sont conservés dans la mise à jour récente et l'ajout de la directive de ligne de commande «--removeComments».

J'aimerais aussi cela pour une raison similaire à @timjmartel - lorsque les développeurs voient que le JS émis semble _ agréable_, ils sont moins résistants à l'adoption. La préservation des espaces blancs (au moins verticaux) fait que le code ressemble moins à un code généré par une machine qu'à un code JS idomatique écrit par un humain.

Si notre équipe décidait d'abandonner TS et de continuer à la place avec le JS transpilé, il serait beaucoup plus facile d'adopter les sources JS émises si elles disposaient d'un espace blanc convivial.

En ce qui concerne les lignes vides, serait-il possible - pour l'instant - de les faire émettre, même si elles sont aléatoires? Une telle fonctionnalité expérimentale pourrait être demandée avec une option «--keepEmptyLines» Ce ne serait pas tant pour avoir un JS sympa, mais pour avoir un JS plus lisible.

En ce qui concerne les appels de fonction chaînés, serait-il possible d'avoir un appel pour la ligne, pour laisser les utilisateurs définir des points d'arrêt? Encore une fois, cette fonctionnalité pourrait être demandée avec une option "--oneCallForLine", s'il s'agit d'une autre chose "hit or miss".

Merci de votre attention.

En fait, les appels de fonction chaînés peuvent être séparés par un embellisseur de code source, il n'est donc pas logique d'incorporer une telle fonctionnalité dans TypeScript.

Cela ne devrait pas être si difficile, il ne devrait pas y avoir simplement une option dans tsconfig.json

preserveWhitespace: true/false

?

Mais je ne vois pas cela comme une option du compilateur: https://www.typescriptlang.org/docs/handbook/compiler-options.html

Je viens de me rendre compte. Une bonne raison de ne pas garder d'espaces blancs est que cela vous aidera vraiment à vous empêcher d'éditer accidentellement le .js au lieu du .ts. Je suppose qu'une chose à faire serait d'écrire les fichiers .js en lecture seule / exécuter uniquement. Alors peut-être que ce n'est pas un problème.

Cela étant dit, je ne pense pas que tsc écrit les fichiers .js en lecture seule / exécution uniquement, existe-t-il un moyen de configurer tsc pour ce faire?

Non, pas en ce moment. N'hésitez pas à ouvrir un autre problème pour cela.

@DanielRosenwasser vous dites que si nous voulons préserver les espaces blancs, nous devrions ouvrir un autre numéro? Ce problème ne suffit-il pas? Nevermind LOL plus d'un mois plus tard, je réalise que vous disiez d'ouvrir un problème séparé pour les autorisations de lecture / écriture / exécution sur les fichiers transpilés :)

Ce serait bien d'avoir ça.

+1

+1

+1

La foule veut preserveWhitespace: true/false
@ORESoftware ++

La raison pour laquelle c'est important est que TypeScript est censé "se dégrader gracieusement" en JS. Pour le moment, il ne peut pas conserver les nouvelles lignes, ce qui rend le JS un peu dense à lire, surtout si vous écrivez TypeScript, mais vous êtes censé livrer le JS à un autre endroit.

+1 preserveWhitespace: true/false

Piratage temporaire

Utilisez esformatter pour ajouter des sauts de ligne.

Avec le fichier de configuration suivant:

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

@mtraynham Votre exemple:

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

avec le dernier compilateur produit ce JS:

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

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

Beaucoup de choses ont changé depuis TS Version 1.1 (la version de TypeScript pour laquelle ce problème a été créé). Peut-être que ce problème peut être résolu? @ahejlsberg ?

@ valera-rozuvan J'avais plutôt ouvert # 2259. Mon problème était lié aux sauts de ligne, mais pas exactement le même problème décrit par ce bogue. Le n ° 2259 a été fermé il y a quelque temps (mai 2015).

Il s'agit de la configuration de bril-andrew esformatter mais avec un bug corrigé, (lorsque la déclaration de classe contenait le mot import, il n'y avait pas de saut de ligne):

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

Je ne pense pas que l'utilisation d'esformatter résout tout le problème. Bien sûr, il peut insérer automatiquement des lignes vides autour des fonctions, etc. Mais pour moi, les lignes vides dans les fonctions sont encore plus cruciales. Nous utilisons des lignes vierges comme des paragraphes en prose: pour regrouper des pensées individuelles.

Ces lignes vides dans les fonctions aident à communiquer la structure de la fonction. Sans eux, je trouve que la lisibilité en souffre.

@ahejlsberg Je vois des numéros de ligne incorrects dans ma sortie de test unitaire lors de l'utilisation de ts-jest, et ce problème semble être causé par la suppression des lignes vides dans la sortie js. Je suis curieux de savoir pourquoi il est si difficile de laisser ces lignes dans les js finaux. Y a-t-il plus d'informations à ce sujet? Pouvons-nous aider d'une manière ou d'une autre à y parvenir? :)

Ou a-t-il déjà été fusionné et n'a pas encore été publié? -> V4 Next Big Version # 3143

@JimTheMan Si vous utilisez des cartes source, le package source-map-support peut vous aider à obtenir les traces de pile correctes dans la sortie.

Je rencontre également ce problème. J'ai trouvé une solution de contournement en créant un patch de diff et en inversant les changements d'espaces blancs dans le patch. jsdiff vous permet de créer un objet patch structuré et de le manipuler comme vous le souhaitez.

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

Avec cette solution de contournement, vous pouvez conserver tous les sauts de ligne du fichier d'origine.

@zeroliu Introduit -il un retard notable dans l'étape de compilation?

@ahejlsberg Pensez-vous que cela vaut la peine de résoudre ce problème?

@ valera-rozuvan en fonction de la taille de votre projet. Pour mes cas d'utilisation où je transpile des fichiers 10-ish de 100-1000 LOC, cela n'introduit aucun retard notable.

Des solutions ici encore? Je cours aussi dans ce problème ...

J'essayais en partie de résoudre ce problème dans le compilateur lui-même lorsque mon coéquipier @emadum m'a rappelé que tsc pouvait conserver les commentaires. Voici un petit pipeline gulp qui semble faire un travail assez décent pour préserver les nouvelles lignes:

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

Je pense qu'un commentaire plus intelligent éviterait certains faux positifs, mais cela semble bien fonctionner pour nous aujourd'hui. Je n'ai testé cela que sur un fichier relativement petit, mais la surcharge de performances y était minime.

hth

@mbroadst

J'ai fini par utiliser votre idée comme base et je l'ai finalement développée jusqu'à ce qu'elle devienne un module npm:
https://www.npmjs.com/package/gulp-preserve-typescript-whitespace

J'ai crédité votre message dans le Readme, j'espère que cela ne vous dérange pas :)

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

blendsdk picture blendsdk  ·  3Commentaires

uber5001 picture uber5001  ·  3Commentaires

wmaurer picture wmaurer  ·  3Commentaires

jbondc picture jbondc  ·  3Commentaires

DanielRosenwasser picture DanielRosenwasser  ·  3Commentaires