Zum Hauptinhalt springen

Grundprinzipien

Inoffizielle Beta-Übersetzung

Diese Seite wurde von PageTurner AI übersetzt (Beta). Nicht offiziell vom Projekt unterstützt. Fehler gefunden? Problem melden →

Prettier ist ein Code-Formatter mit festen Regeln. Dieses Dokument erläutert einige seiner Designentscheidungen.

Was Prettier wichtig ist

Korrektheit

Die oberste Anforderung an Prettier ist die Ausgabe von valideem Code mit identischem Verhalten vor und nach der Formatierung. Bitte melden Sie jeden Code, bei dem Prettier diese Korrektheitsregeln verletzt – das ist ein Fehler, der behoben werden muss!

Zeichenketten

Doppelte oder einfache Anführungszeichen? Prettier wählt die Variante, die weniger Escape-Zeichen benötigt. "It's gettin' better!", nicht 'It\'s gettin\' better!'. Bei Gleichstand oder fehlenden Anführungszeichen im String verwendet Prettier standardmäßig doppelte Anführungszeichen (kann über singleQuote geändert werden).

JSX hat eine eigene Option für Anführungszeichen: jsxSingleQuote. JSX stammt von HTML ab, wo Attributwerte meist in doppelten Anführungszeichen stehen. Browser-Entwicklertools folgen dieser Konvention und zeigen HTML stets mit doppelten Anführungszeichen an, selbst bei einfachen im Quellcode. Eine separate Option ermöglicht einfache Anführungszeichen für JS und doppelte für "HTML" (JSX).

Prettier bewahrt die ursprüngliche Escape-Darstellung von Strings. Beispielsweise wird "🙂" nicht in "\uD83D\uDE42" umgewandelt und umgekehrt.

Leerzeilen

Leerzeilen sind automatisch schwer zu generieren. Prettier bewahrt daher Leerzeilen aus dem Originalcode. Zusätzlich gelten zwei Regeln:

  • Mehrere Leerzeilen werden zu einer einzigen reduziert.

  • Leerzeilen am Anfang/Ende von Blöcken (oder Dateien) werden entfernt. (Dateien enden stets mit einem Zeilenumbruch.)

Mehrzeilige Objekte

Standardmäßig gibt Prettier Ausdrücke einzeilig aus, wenn sie passen. Da Objekte in JavaScript vielfältig genutzt werden, kann Mehrzeiligkeit die Lesbarkeit verbessern (z.B. bei Objektlisten, [geschachtelten Konfigurationen], Stylesheets oder Methoden mit Schlüsseln). Für alle Fälle gibt es keine ideale Regel, daher bewahrt Prettier standardmäßig Mehrzeiligkeit, wenn im Original zwischen { und erstem Schlüssel ein Zeilenumbruch steht. Lange einzeilige Objekte werden automatisch erweitert, kurze mehrzeilige nie komprimiert.

Dieses Verhalten kann mit der objectWrap-Option deaktiviert werden.

Tipp: Um ein mehrzeiliges Objekt in eine Zeile zu komprimieren:

const user = {
name: "John Doe",
age: 30,
};

...entfernen Sie einfach den Zeilenumbruch nach {:

const user = {  name: "John Doe",
age: 30
};

...und führen Sie Prettier aus:

const user = { name: "John Doe", age: 30 };

Um wieder mehrzeilig zu formatieren, fügen Sie nach { einen Zeilenumbruch ein:

const user = {
name: "John Doe", age: 30 };

...und führen Sie Prettier aus:

const user = {
name: "John Doe",
age: 30,
};
Hinweis zur Umkehrbarkeit der Formatierung

Die halb-manuelle Formatierung von Objektliteralen ist tatsächlich eine Problemumgehung, kein Feature. Sie wurde nur implementiert, weil damals keine gute Heuristik gefunden wurde und eine dringende Lösung benötigt wurde. Als allgemeine Strategie vermeidet Prettier jedoch nicht umkehrbare Formatierungen wie diese. Daher sucht das Team weiter nach Heuristiken, die es ermöglichen, dieses Verhalten entweder vollständig zu entfernen oder zumindest die Anzahl der Situationen zu reduzieren, in denen es angewendet wird.

Was bedeutet umkehrbar? Sobald ein Objektliteral mehrzeilig wird, wird Prettier es nicht wieder zusammenführen. Wenn wir in Prettier-formatiertem Code eine Eigenschaft zu einem Objektliteral hinzufügen, Prettier ausführen, dann unsere Meinung ändern, die hinzugefügte Eigenschaft entfernen und erneut Prettier ausführen, könnten wir am Ende eine Formatierung erhalten, die nicht identisch mit der ursprünglichen ist. Diese unnötige Änderung könnte sogar in einen Commit aufgenommen werden – genau die Art von Situation, die Prettier eigentlich verhindern soll.

Decorators

Ähnlich wie bei Objekten werden Decorators für viele verschiedene Zwecke verwendet. Manchmal ist es sinnvoll, Decorators über der Zeile zu schreiben, die sie dekorieren, manchmal ist es schöner, wenn sie in derselben Zeile stehen. Wir konnten keine gute Regel dafür finden, daher behält Prettier die Position Ihrer Decorators bei, wie Sie sie geschrieben haben (wenn sie in die Zeile passen). Dies ist nicht ideal, aber eine pragmatische Lösung für ein schwieriges Problem.

@Component({
selector: "hero-button",
template: `<button>{{ label }}</button>`,
})
class HeroButtonComponent {
// These decorators were written inline and fit on the line so they stay
// inline.
@Output() change = new EventEmitter();
@Input() label: string;

// These were written multiline, so they stay multiline.
@readonly
@nonenumerable
NODE_TYPE: 2;
}

Es gibt eine Ausnahme: Klassen. Wir glauben nicht, dass es jemals sinnvoll ist, Decorators für Klassen inline zu setzen, daher werden sie immer in eine eigene Zeile verschoben.

// Before running Prettier:
@observer class OrderLine {
@observable price: number = 0;
}
// After running Prettier:
@observer
class OrderLine {
@observable price: number = 0;
}

Hinweis: Prettier 1.14.x und ältere Versionen versuchten, Ihre Decorators automatisch zu verschieben. Wenn Sie also eine ältere Prettier-Version auf Ihrem Code ausgeführt haben, müssen Sie möglicherweise manuell einige Decorators zusammenführen, um Inkonsistenzen zu vermeiden:

@observer
class OrderLine {
@observable price: number = 0;
@observable
amount: number = 0;
}

Template-Literals

Template-Literals können Interpolationen enthalten. Die Entscheidung, ob ein Zeilenumbruch innerhalb einer Interpolation eingefügt werden soll, hängt leider vom semantischen Inhalt des Templates ab – beispielsweise ist ein Zeilenumbruch mitten in einem natürlichsprachlichen Satz meist unerwünscht. Da Prettier nicht genügend Informationen hat, um diese Entscheidung selbst zu treffen, verwendet es eine Heuristik ähnlich der für Objekte: Es teilt einen Interpolationsausdruck nur dann über mehrere Zeilen auf, wenn bereits ein Zeilenumbruch innerhalb dieser Interpolation vorhanden war.

Das bedeutet, dass ein Literal wie das folgende nicht in mehrere Zeilen unterteilt wird, selbst wenn es die Druckbreite überschreitet:

`this is a long message which contains an interpolation: ${format(data)} <- like this`;

Wenn Sie möchten, dass Prettier eine Interpolation aufteilt, müssen Sie sicherstellen, dass irgendwo innerhalb des ${...} ein Zeilenumbruch vorhanden ist. Andernfalls bleibt alles in einer einzigen Zeile, egal wie lang es ist.

Das Team würde es vorziehen, nicht auf diese Weise von der ursprünglichen Formatierung abhängig zu sein, aber es ist die beste Heuristik, die wir derzeit haben.

Semikolons

Hier geht es um die Verwendung der Option noSemi.

Betrachten Sie diesen Code:

if (shouldAddLines) {
[-1, 1].forEach(delta => addLine(delta * 20))
}

Während der obige Code ohne Semikolons einwandfrei funktioniert, wandelt Prettier ihn tatsächlich in Folgendes um:

if (shouldAddLines) {
;[-1, 1].forEach(delta => addLine(delta * 20))
}

Dies dient dazu, Fehler zu vermeiden. Stellen Sie sich vor, Prettier würde dieses Semikolon nicht einfügen und diese Zeile hinzufügen:

 if (shouldAddLines) {
+ console.log('Do we even get here??')
[-1, 1].forEach(delta => addLine(delta * 20))
}

Hoppla! Das obige bedeutet tatsächlich:

if (shouldAddLines) {
console.log('Do we even get here??')[-1, 1].forEach(delta => addLine(delta * 20))
}

Mit einem Semikolon vor diesem [ treten solche Probleme nie auf. Es macht die Zeile unabhängig von anderen Zeilen, sodass Sie Zeilen verschieben und hinzufügen können, ohne über ASI-Regeln nachdenken zu müssen.

Diese Praxis ist auch in standard üblich, das einen semikolonfreien Stil verwendet.

Hinweis: Wenn Ihr Programm derzeit einen Semikolon-bezogenen Fehler enthält, wird Prettier diesen Fehler nicht automatisch beheben. Denken Sie daran: Prettier formatiert Code nur neu, es ändert nicht das Verhalten des Codes. Nehmen Sie diesen fehlerhaften Code als Beispiel, bei dem der Entwickler vergessen hat, ein Semikolon vor dem ( zu setzen:

console.log('Running a background task')
(async () => {
await doBackgroundWork()
})()

Wenn Sie diesen Code in Prettier verarbeiten, wird das Verhalten des Codes nicht verändert; stattdessen wird er so neu formatiert, dass deutlich wird, wie dieser Code tatsächlich bei der Ausführung agiert.

console.log("Running a background task")(async () => {
await doBackgroundWork();
})();

Druckbreite

Die Option printWidth ist eher eine Richtlinie für Prettier als eine starre Regel. Sie stellt keine absolute Obergrenze für die Zeilenlänge dar, sondern definiert vielmehr den ungefähren Zielwert. Prettier erzeugt sowohl kürzere als auch längere Zeilen, strebt aber generell an, die angegebene Druckbreite einzuhalten.

Es gibt Randfälle wie sehr lange String-Literale, Regex-Ausdrücke, Kommentare und Variablennamen, die nicht über mehrere Zeilen verteilt werden können (ohne Code-Transformationen, die Prettier nicht vornimmt). Oder wenn Ihr Code 50 Ebenen tief verschachtelt ist, bestehen Ihre Zeilen natürlich hauptsächlich aus Einrückungen :)

Abgesehen davon gibt es einige Fälle, in denen Prettier bewusst die Druckbreite überschreitet.

Imports

Prettier kann lange import-Anweisungen über mehrere Zeilen verteilen:

import {
CollectionDashboard,
DashboardPlaceholder,
} from "../components/collections/collection-dashboard/main";

Das folgende Beispiel überschreitet die Druckbreite, wird aber dennoch in einer einzigen Zeile ausgegeben:

import { CollectionDashboard } from "../components/collections/collection-dashboard/main";

Dies mag für manche unerwartet sein, geschieht jedoch aufgrund häufiger Anfragen, import-Anweisungen mit einzelnen Elementen in einer Zeile zu belassen. Gleiches gilt für require-Aufrufe.

Testfunktionen

Eine weitere häufige Anfrage war, umfangreiche Testbeschreibungen trotz Überlänge in einer Zeile zu belassen. In solchen Fällen hilft das Umbrechen der Argumente in neue Zeilen kaum.

describe("NodeRegistry", () => {
it("makes no request if there are no nodes to prefetch, even if the cache is stale", async () => {
// The above line exceeds the print width but stayed on one line anyway.
});
});

Prettier hat Sonderregeln für gängige Test-Framework-Funktionen wie describe, it und test.

JSX

Prettier formatiert Code mit JSX-Beteiligung etwas anders als regulären JavaScript-Code:

function greet(user) {
return user
? `Welcome back, ${user.name}!`
: "Greetings, traveler! Sign up today!";
}

function Greet({ user }) {
return (
<div>
{user ? (
<p>Welcome back, {user.name}!</p>
) : (
<p>Greetings, traveler! Sign up today!</p>
)}
</div>
);
}

Dafür gibt es zwei Gründe.

Erstens haben viele Entwickler ihren JSX-Code bereits in Klammern eingefasst, insbesondere in return-Anweisungen. Prettier folgt diesem verbreiteten Stil.

Zweitens erleichtert die alternative Formatierung die Bearbeitung von JSX. Es ist leicht, ein Semikolon zu übersehen. Im Gegensatz zu normalem JS kann ein vergessenes Semikolon in JSX als sichtbarer Text auf Ihrer Seite erscheinen.

<div>
<p>Greetings, traveler! Sign up today!</p>; {/* <-- Oops! */}
</div>

Kommentare

Bezüglich des Inhalts von Kommentaren kann Prettier wenig ausrichten. Kommentare können alles enthalten: von Fließtext über auskommentierten Code bis hin zu ASCII-Diagrammen. Da sie beliebigen Inhalt haben können, weiß Prettier nicht, wie sie formatiert oder umgebrochen werden sollen. Daher bleiben sie unverändert. Die einzige Ausnahme sind JSDoc-artige Kommentare (Blockkommentare, bei denen jede Zeile mit * beginnt), deren Einrückung Prettier korrigieren kann.

Hinzu kommt die Frage, wo Kommentare platziert werden sollen. Dies erweist sich als komplexes Problem. Prettier bemüht sich, Ihre Kommentare etwa an ihrer ursprünglichen Position zu belassen, was aufgrund der flexiblen Platzierungsmöglichkeiten jedoch herausfordernd ist.

Generell erzielen Sie die besten Ergebnisse, wenn Sie Kommentare in eigenständigen Zeilen platzieren, statt sie ans Zeilenende anzuhängen. Bevorzugen Sie // eslint-disable-next-line gegenüber // eslint-disable-line.

Hinweis: "Magische Kommentare" wie eslint-disable-next-line oder $FlowFixMe müssen manuell verschoben werden, wenn Prettier einen Ausdruck über mehrere Zeilen verteilt.

Betrachten Sie diesen Codeausschnitt:

// eslint-disable-next-line no-eval
const result = safeToEval ? eval(input) : fallback(input);

Dann fügen Sie eine weitere Bedingung hinzu:

// eslint-disable-next-line no-eval
const result = safeToEval && settings.allowNativeEval ? eval(input) : fallback(input);

Prettier wandelt dies um in:

// eslint-disable-next-line no-eval
const result =
safeToEval && settings.allowNativeEval ? eval(input) : fallback(input);

Dadurch verliert der eslint-disable-next-line-Kommentar seine Wirkung. In diesem Fall müssen Sie den Kommentar verschieben:

const result =
// eslint-disable-next-line no-eval
safeToEval && settings.allowNativeEval ? eval(input) : fallback(input);

Wenn möglich, bevorzugen Sie Kommentare, die auf Zeilenbereichen wirken (z.B. eslint-disable und eslint-enable) oder auf Anweisungsebene (z.B. /* istanbul ignore next */), da sie noch sicherer sind. Sie können die Verwendung von eslint-disable-line- und eslint-disable-next-line-Kommentaren mit eslint-plugin-eslint-comments untersagen lassen.

Hinweis zu nicht standardkonformer Syntax

Prettier erkennt und formatiert oft nicht standardkonforme Syntax wie frühe ECMAScript-Vorschläge und spezifikationslose Markdown-Syntaxerweiterungen. Diese Unterstützung erfolgt bestmöglich und experimentell. Inkompatibilitäten können in jeder Version auftreten und gelten nicht als Breaking Changes.

Hinweis zu maschinell generierten Dateien

Manche Dateien wie package.json oder composer.lock werden maschinell generiert und regelmäßig vom Paketmanager aktualisiert. Würde Prettier dieselben JSON-Regeln verwenden, käme es zu Konflikten mit diesen Tools. Daher nutzt Prettier stattdessen einen auf JSON.stringify basierenden Formatierer. Unterschiede wie entfernte Leerzeilen sind beabsichtigt.

Worauf Prettier keinen Wert legt

Prettier gibt Code nur aus, ohne ihn zu transformieren. Dies begrenzt den Umfang von Prettier. Konzentrieren wir uns auf die Ausgabe und machen wir sie exzellent!

Hier sind Beispiele für Prettiers Grenzen:

  • Umwandlung von einfachen/doppelten Anführungszeichen in Template-Literals oder umgekehrt

  • Verwendung von + zum Aufteilen langer String-Literale

  • Hinzufügen/Entfernen optionaler {} oder return

  • Umwandlung von ?: in if-else-Anweisungen

  • Sortieren/Verschieben von Imports, Objektschlüsseln, Klassenmitgliedern, JSX-Keys, CSS-Eigenschaften etc. Neben der Transformation wäre Sortieren durch Nebeneffekte (z.B. bei Imports) riskant und würde die Korrektheitsüberprüfung erschweren.