Typescript: Bei der Ausgabe werden keine Leerzeilen beibehalten

Erstellt am 7. Okt. 2014  ·  36Kommentare  ·  Quelle: microsoft/TypeScript

Hallo,

TS Version : 1.1

Gegeben

function foo() {

    var x = 10;

    var y = 11;
}

Früher haben wir bekommen

function foo() {
    var x = 10;

    var y = 11;
}

Im neuen Compiler fehlt der Zeilenumbruch

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

(Beide Compiler haben die erste leere Zeile entfernt, aber der neue Compiler ist noch einen Schritt weiter gegangen.)

Dies kann sich auf die Erfahrung beim Debuggen von JavaScript im Browser auswirken.

Bug help wanted

Hilfreichster Kommentar

Die Menge will preserveWhitespace: true/false
@ORESoftware ++

Alle 36 Kommentare

Hintergrundinformationen hinzufügen ... Der Grund, warum der neue Compiler alle Leerzeilen entfernt, ist, dass dies das einzige ist, was wir _konsistent_ tun können. Die Beibehaltung leerer Zeilen wird immer ein Hit oder Miss sein, wenn es um Konstrukte geht, die neu geschrieben werden müssen, z. B. Klassen- und Moduldeklarationen. Bei der Aufbewahrung von Kommentaren stehen wir genau den gleichen Problemen gegenüber. Obwohl ich mit der Lösung dieses Problems einverstanden bin, ist dies keine einfache Sache.

@NoelAbrahams Ich bin gespannt, welche Probleme beim Debuggen auftreten.

@ahejlsberg @NoelAbrahams Ich habe einen Prototyp erstellt, der im ursprünglichen CodePlex-Projekt emittiert wurde und mit Kommentaren und der Beibehaltung von Zeilenumbrüchen hervorragende Arbeit geleistet hat. Selbst bei Konstrukten, die stark umgeschrieben wurden (wie Pfeilfunktionen zu Funktionsausdrücken), würde es fast immer das tun, was Sie intuitiv erwartet hatten.

Die Zeilenerhaltung besteht hauptsächlich darin, die Zeilenumbrüche nur wiederzuverwenden, wenn alter Code beibehalten wird, und bei Transformationen den relativen Abstand zu verwenden. dh wenn Sie haben:

module M {
}
module N {
}

Wenn Sie dann das IIFE für 'N' ausgeben, sagen Sie "Wir sollten die Trivia zwischen dem Modul, das wir neu schreiben, und dem vorherigen syntaktischen Element beibehalten".

Hier ist das Vorher / Nachher für die Funktionsweise meines Emitter-Prototyps, bei dem Kommentare / Zeilenumbrüche mit hoher Wiedergabetreue beibehalten wurden:

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

Sie können auch hier viele Beispiele sehen:
https://typescript.codeplex.com/SourceControl/latest#tests/Fidelity/emitter/ecmascript5/

Die einzige Funktion, die ich nicht implementiert habe, war "Ausrichtung". Wenn Sie also Code hatten, der im Original in irgendeiner Weise ausgerichtet war (sehr häufig bei Parameterdeklarationen), möchten wir, dass der ausgegebene Code dies auch beibehält.

Aber es wäre sehr trivial gewesen.

Die Konvertierung von Konstruktionen von TS zu JS versuchte jedoch, die Einrückung zu erhalten. Das können Sie hier sehen:
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

Beachten Sie, wie die Anweisungen ordnungsgemäß unabhängig sind (auch wenn sie sich über mehrere Zeilen erstrecken), selbst nachdem wir die verschachtelten Module und Klassen in IIFEs konvertiert haben

@ahejlsberg , es gibt keine wesentlichen Probleme beim Debuggen im Browser. Es macht es einfach einfacher, im JavaScript-Code zu navigieren und Zeilen zum Festlegen von Haltepunkten zu finden, wenn eine genaue Übereinstimmung mit dem tatsächlichen TypeScript-Quellcode besteht.

Ich könnte persönlich ohne die leeren Zeilen leben, aber da TS sich so viel Mühe gegeben hat, um

@ahejlsberg @NoelAbrahams Beim Debuggen im Browser gibt es ein Problem, das in geringem Zusammenhang mit dieser Konversation steht. Bei Verwendung von Setter / Getter-Ketten (wie bei jquery) oder Verkettungsversprechen gehen die neuen Zeilenvorschübe während der Übersetzung verloren. Davon abgesehen ist es ein großer Schmerzpunkt bei der Arbeit mit Pfeilfunktionen.

Als Beispiel:

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

Wird:

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

Bei Verwendung von Chrome und sourceMaps werden die Haltepunkte weiterhin übersprungen.

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 , eigentlich denke ich, dass das Problem, das Sie hervorheben, ein etwas anderes ist.

In früheren Versionen wurde der Hauptteil einer Inline-Funktion immer in neuen Zeilen ausgegeben:

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

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

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

Auch ich habe festgestellt, dass dies ein Problem ist - ich muss gelegentlich zurückgehen und ein function erstellen, damit ich beim Debuggen im Browser einen Haltepunkt setzen kann.

@NoelAbrahams Ahh ja, ich habe genau dieselbe temporäre Lösung verwendet ... Ich war mir nicht sicher, ob dies ein geeigneter Fehler war, um dieses Problem zu beheben (Löschen von Zeilenvorschüben), oder sollte ich eine andere öffnen?

Ich habe # 2259 für die separate Ausgabe erstellt.

Als technischer Direktor, der untersucht, wie wir unsere Javascript-Entwicklergemeinschaft auf Typoskript umstellen können, würde die neue Zeilenfunktion wirklich helfen. Eine der Hauptattraktionen von Typoskript war die Aufrechterhaltung der Lesbarkeit und Struktur des in Typoskript generierten JavaScript erstellten Codes.

Schön zu sehen, dass Kommentare in der letzten Aktualisierung und Hinzufügung der Befehlszeilenanweisung "--removeComments" beibehalten werden.

Ich würde dies auch aus einem ähnlichen Grund wie @timjmartel mögen - wenn Entwickler sehen, dass das emittierte JS _nice_ aussieht, sind sie weniger resistent gegen Adoption. Durch das Beibehalten von (zumindest vertikalen) Leerzeichen sieht der Code weniger wie von einer Maschine generiert aus, sondern eher wie von einem Menschen geschriebener idomatischer JS-Code.

Wenn unser Team jemals beschließen würde, TS aufzugeben und stattdessen mit dem transpilierten JS fortzufahren, wäre es viel einfacher, die emittierten JS-Quellen zu übernehmen, wenn sie menschenfreundliche Leerzeichen hätten.

Wäre es in Bezug auf leere Zeilen vorerst möglich, sie auszusenden, selbst wenn sie getroffen oder verfehlt werden? Eine solche experimentelle Funktion könnte mit der Option "--keepEmptyLines" abgefragt werden. Es wäre nicht so sehr für eine nette JS, sondern für eine besser lesbare JS.

Wäre es bei verketteten Funktionsaufrufen möglich, einen Anruf für die Leitung zu haben, damit Benutzer Haltepunkte setzen können? Auch diese Funktion könnte mit der Option "--oneCallForLine" abgefragt werden, wenn es sich um ein anderes "Hit or Miss" -Ding handelt.

Danke für Ihre Aufmerksamkeit.

Tatsächlich könnten verkettete Funktionsaufrufe durch einen Quellcode-Verschönerer aufgeteilt werden. Daher ist es nicht sinnvoll, eine solche Funktion in TypeScript einzubetten.

Dies sollte nicht so schwer sein, sollte es nicht nur eine Option in tsconfig.json geben

preserveWhitespace: true/false

?

Ich sehe dies jedoch nicht als Compileroption: https://www.typescriptlang.org/docs/handbook/compiler-options.html

Ich habe gerade festgestellt. Ein guter Grund, Leerzeichen nicht beizubehalten, besteht darin, dass Sie nicht versehentlich die .js anstelle der .ts bearbeiten müssen. Ich denke, eine Sache zu tun wäre, die .js-Dateien als schreibgeschützt / nur ausführbar zu schreiben. Vielleicht ist dies kein Problem.

Abgesehen davon denke ich nicht, dass tsc .js-Dateien als schreibgeschützt / nur ausführend schreibt. Gibt es eine Möglichkeit, tsc dafür zu konfigurieren?

Nein, nicht in diesem Moment. Sie können jedoch gerne ein separates Thema dafür eröffnen.

@ DanielRosenwasser Sie sagen, wenn wir Leerzeichen erhalten wollen, sollten wir eine separate Ausgabe eröffnen? Ist dieses Problem nicht genug? Nevermind LOL mehr als einen Monat später wurde mir klar, dass Sie ein separates Problem für Lese- / Schreib- / Ausführungsberechtigungen für transpilierte Dateien öffnen wollten :)

Es wäre schön, das zu haben.

+1

+1

+1

Die Menge will preserveWhitespace: true/false
@ORESoftware ++

Grund, warum dies wichtig ist, ist, dass TypeScript zu JS "elegant abgebaut" werden soll. Im Moment können keine neuen Zeilen beibehalten werden, wodurch das Lesen des JS etwas dichter wird, insbesondere wenn Sie TypeScript schreiben, aber Sie sollten JS an einen anderen Ort liefern.

+1 preserveWhitespace: true/false

Temporärer Hack

Verwenden Sie esformatter , um Zeilenumbrüche hinzuzufügen.

Mit folgender Konfigurationsdatei:

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

@mtraynham Dein Beispiel:

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

mit dem neuesten Compiler produziert diese JS:

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

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

Seit TS Version 1.1 (der Version von TypeScript, für die dieses Problem erstellt wurde) hat sich viel geändert. Vielleicht kann dieses Problem geschlossen werden? @ahejlsberg ?

@ valera-rozuvan Ich hatte stattdessen # 2259 geöffnet. Mein Problem betraf Zeilenvorschübe, aber nicht genau das gleiche Problem, das durch diesen Fehler beschrieben wurde. # 2259 wurde vor einiger Zeit geschlossen (Mai 2015).

Dies ist die Bril-Andrew- Esformatter-Konfiguration, aber mit einem behobenen Fehler (als die Klassendeklaration das Wort Import enthielt, gab es keinen Zeilenumbruch):

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

Ich denke nicht, dass die Verwendung von Esformatter das ganze Problem löst. Sicher, es kann automatisch Leerzeilen um Funktionen usw. einfügen. Für mich sind Leerzeilen innerhalb von Funktionen jedoch noch wichtiger. Wir verwenden leere Zeilen wie Absätze in der Prosa: um einzelne Gedanken zu gruppieren.

Diese Leerzeilen innerhalb von Funktionen helfen dabei, die Struktur der Funktion zu kommunizieren. Ohne sie leidet die Lesbarkeit.

@ahejlsberg Bei Verwendung von ts-jest werden in meiner Unit-Testing-Ausgabe falsche Zeilennummern

Oder wurde es bereits zusammengeführt und noch nicht veröffentlicht? -> V4 Next Big Version # 3143

@JimTheMan Wenn Sie , kann Ihnen das source-map-support möglicherweise dabei helfen, die richtigen Stapelspuren in der Ausgabe zu erhalten.

Ich stoße auch auf dieses Problem. Ich habe eine Problemumgehung gefunden, indem ich einen Diff-Patch erstellt und Whitespace-Änderungen im Patch rückgängig gemacht habe. Mit jsdiff können Sie ein strukturiertes Patch-Objekt erstellen und nach

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

Mit dieser Problemumgehung können Sie alle Zeilenumbrüche aus der Originaldatei beibehalten.

@zeroliu Führt es beim Kompilieren zu einer spürbaren Zeitverzögerung?

@ahejlsberg Glaubst du, es lohnt sich, dieses Problem zu beheben?

@ valera-rozuvan abhängig von der Größe Ihres Projekts. Für meine Anwendungsfälle, in denen ich 10-ish-Dateien mit 100-1000 LOC transpiliere, führt dies zu keiner merklichen Verzögerung.

Irgendwelche Lösungen hier schon? Ich renne auch in diese Schwierigkeiten ...

Ich war teilweise dabei, dies im Compiler selbst zu beheben, als mein Teamkollege @emadum mich

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

Ich denke, ein klügerer Kommentar würde einige Fehlalarme verhindern, aber er scheint heute für uns gut zu funktionieren. Ich habe dies nur an einer relativ kleinen Datei getestet, aber der Leistungsaufwand dort war minimal.

hth

@mbroadst

Am Ende habe ich Ihre Idee als Basis verwendet und sie schließlich erweitert, bis sie zu einem npm-Modul wurde:
https://www.npmjs.com/package/gulp-preserve-typescript-whitespace

Ich habe deinen Beitrag in der Readme-Datei gutgeschrieben, hoffentlich macht es dir nichts aus :)

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen