Veröffentlichung von Prettier 1.0
Diese Seite wurde von PageTurner AI übersetzt (Beta). Nicht offiziell vom Projekt unterstützt. Fehler gefunden? Problem melden →
Dieser Beitrag wurde von vjeux verfasst und von jlongster bearbeitet. Ursprünglich veröffentlicht hier
Vor etwas mehr als zwei Monaten haben wir Prettier offiziell angekündigt, um das Problem des Zeitverschwendens durch Code-Formatierung zu lösen. Es begann als Experiment, fand aber offensichtlich großen Anklang, sammelte in nur zwei Monaten ~7000 GitHub-Sterne und über 100.000 monatliche npm downloads.
Falls Sie es noch nicht kennen: Prettier ist ein JavaScript-Formatierer, der Ihren Code in einen AST (Abstract Syntax Tree) kompiliert und diesen dann formatiert ausgibt. Ähnlich wie Browser Text umbrechen, bricht Prettier Code entsprechend einer vorgegebenen Zeilenlänge um. Das Ergebnis ist gut aussehender Code, der völlig konsistent ist, egal wer ihn geschrieben hat. Dies löst das Problem, dass Programmierer viel Zeit damit verbringen, Code manuell in ihrem Editor zu verschieben und über Stile zu diskutieren (siehe GIF).
Anfangs war nicht ganz klar, ob wir es schaffen würden, immer gültigen und gut aussehenden Code zu generieren, aber die gute Nachricht ist, dass dies keine offene Frage mehr ist. Viele Projekte (React, Jest, immutable-js, Haul und viele mehr) und Unternehmen (Oculus, Cloudflare) haben Prettier zur Formatierung ihrer JavaScript-Codebasen übernommen und die Vorteile automatisierter Formatierung erkannt (mehr dazu in diesem Tweet). Einige von ihnen haben Prettier auf ihrer gesamten Codebase (Zehntausende Codezeilen) ausgeführt.
In den letzten drei Wochen haben wir alle offenen Issues durchgearbeitet, so viele Entscheidungen wie möglich zur Codeformatierung getroffen und die meisten uns bekannten Fehler behoben. Es ist nicht perfekt und wir werden weiter daran arbeiten, aber es ist ein guter Zeitpunkt, Version 1.0 zu veröffentlichen!
Das bedeutet nicht, dass wir das Format nicht mehr ändern werden, aber Änderungen werden minimal sein. Zum Beispiel schauen wir uns an, ternäre Ausdrücke anzupassen, aber es wird von nun an nur sehr wenige Änderungen dieser Art geben, und wenn es sie gibt, werden wir eine neue Hauptversion veröffentlichen.
Was es bedeutet, ist, dass Prettier sicher für den Produktiveinsatz ist. Wir haben eine Menge Fehler behoben und sichergestellt, dass die Ausgabe stabil und semantisch korrekt ist. Prettier ist bereit für den Einsatz, von ein paar Dateien in Ihrem Nebenprojekt bis hin zur Konvertierung Ihrer gesamten Codebase. Es liegt an Ihnen, herauszufinden, wo Sie sich auf dem Weg befinden, sich von einer bestimmten Art der Codeformatierung zu verabschieden.
Werfen wir einen Blick darauf, was 1.0 für Prettier bringt!
Vielen Dank an alle folgenden Mitwirkenden, die zu dieser Version beigetragen haben, und ein besonderer Dank an Ian Storm Taylor für das Logo!
Adam Stankiewicz, Alex Rattray, Alex Stachowiak, Amjad Masad, Andrey Okonetchnikov, António Nuno Monteiro, Artem Sapegin, Benjamin Tan, Bill Mill, Brandon Mills, Brian Holt, Brian Ng, Charles Pick, ChristianHersevoort, Christopher Chedeau, Christopher Moeller, Damien Lebrun, Dan Harper, Dara Hak, David Ascher, David Hong, Davy Duperron, Eric Clemmons, Erick Romero, Esben Petersen, Filipe Fortes, Gregor Adams, Hampus,hlsson, Hawken Rives, Henry Zhu, Ilya Panasenko, James Henry, James Long, Jamie Webb, Jan Kassens, Jed Watson, Jeffrey Horn, Jeremy Morrell, Joe Fiorini, Jon LaBelle, Joseph Savona, Karl Horky, Karl O'Keeffe, Kent C. Dodds, Kevin Gibbons, Kim Joar Bekkelund, Lucas Bento, Lucas Duailibe, MarekMatejkaKCL, Mateusz Zatorski, Michael Ficarra, Michał Pierzchała, Mikael Brevik, Nathan Friedly, Patrick Camacho, Paul,arduner, Rafael Hengles, Raghuvir Kasturi, Rasmus Eneman, Riley Tomasek, Rogelio Guzman, Royce Townsend, Sean Grove, Shigeaki Okazaki, Simen,ekkhus, Simon Lydell, Sorin Muntean, Spencer Dixon, Thai Pangsakulyanont, Tom McKearney, Travis Jefferson, Umidbek Karimov, Vincent Voyer, Vu Tran, Walter Breakell, Yatharth Khatri, azu, daleroy, kalmanb, skratchdot
Optionen
Prettier ist ein opiniertes Code-Formatierungstool. Zu Projektbeginn dachten wir, dass dies bedeuten würde, keine Konfigurationsmöglichkeiten wie bei gofmt oder refmt anzubieten. Doch im Laufe der Zeit wurde uns klar, dass dies die Akzeptanz von Prettier beeinträchtigen würde - Nutzer, die davon profitieren könnten, würden es nicht verwenden, wenn es den Code nicht so formatiert, wie sie es wünschen.
Stattdessen verstehen wir ein opiniertes Code-Formatierungstool nun so, dass es Optionen für grundlegende Syntax-Aspekte bietet, bei denen gilt: "Wenn es X nicht macht, werde ich es niemals verwenden - egal wie gut es ist". Beispielsweise würde ich (@vjeux) niemals standard nutzen, weil es keine Semikolons verwendet. Das ist absolut unrational, aber viele Menschen verhalten sich so, weshalb wir "Style-Kriege" haben.
Wir werden weiterhin nicht für jeden Syntax-Typ Optionen einführen, sondern nur für besonders einflussreiche Dinge. Wir haben zwei Hauptoptionen identifiziert, die in diese Kategorie fallen: Tabs vs. Leerzeichen und Semikolon vs. kein Semikolon. Daher führen wir diese jetzt in Prettier ein!
--no-semi (#1129)
Danke an rattrayalex, der viel Arbeit investiert hat, um dies möglich zu machen!
// Before
console.log();
[1, 2, 3].map(x => x + 1);
// After
console.log()
;[1, 2, 3].map(x => x + 1)
--use-tabs (#1026)
Danke an rhengles für die Implementierung und die Erläuterung der dahinterstehenden Gründe!
// Before
if (1) {
··console.log(); // Two spaces
}
// After
if (1) {
» console.log(); // One Tab!
}
Formatierung
Der Rest dieses Beitrags dokumentiert alle kleineren Änderungen, die wir für Version 1.0 zusammengefasst haben. Normalerweise werden wir kein Changelog als Blogpost veröffentlichen, aber wir dachten, es zeigt, wie stabil Prettier wird, und gibt einen guten Überblick über die Art von Problemen, die wir derzeit beheben.
Zeilenumbruch für Pfeilfunktionen ohne Klammern, die Aufrufe zurückgeben (#927)
Wahrscheinlich das am häufigsten gemeldete Problem in der aktuellen Prettier-Version: Wir fügten nach Pfeilfunktionen innerhalb von Aufrufen keinen Zeilenumbruch ein. Jetzt tun wir das. Nebenbei bemerkt: Es ist ziemlich aufregend, dass es sich bei den Issues jetzt um Kleinigkeiten wie diese handelt und nicht mehr um Probleme vom Typ "das sieht total kaputt aus".
// Before
const testResults = results.testResults.map(testResult =>
formatResult(testResult, formatter, reporter));
// After
const testResults = results.testResults.map(testResult =>
formatResult(testResult, formatter, reporter)
);
Softline bei Zuweisungen und Klammern um Returns für binäre Ausdrücke hinzugefügt (#871 & #870)
Lange Ketten logischer Ausdrücke sehen merkwürdig aus, wenn die erste Zeile in derselben Zeile wie die Variablendeklaration oder das Return steht. Stattdessen ist es besser, sie in die nächste Zeile zu setzen und bei Returns Klammern hinzuzufügen, um sie gültig zu machen.
// Before
const computedDescriptionLines = (showConfirm &&
descriptionLinesConfirming) ||
(focused && !loading && descriptionLinesFocused) ||
descriptionLines;
// After
const computedDescriptionLines =
(showConfirm && descriptionLinesConfirming) ||
(focused && !loading && descriptionLinesFocused) ||
descriptionLines;
// Before
return !filePath.includes(coverageDirectory) &&
!filePath.endsWith(`.${SNAPSHOT_EXTENSION}`);
// After
return (
!filePath.includes(coverageDirectory) &&
!filePath.endsWith(`.${SNAPSHOT_EXTENSION}`)
);
Erstes Argument für Inline-Funktionen gruppieren (#947)
Das erste große benutzerdefinierte Muster, das wir in Prettier hinzugefügt haben, ist das Inline-Drucken von Funktionen als letzte Argumente. Ein weniger verbreitetes Muster ist das Gleiche für das erste Argument. Es ist bei modernen Bibliotheken unüblich, diesen Stil zu verwenden, aber einige Core-JavaScript-Funktionen wie setTimeout tun es, daher ist es sinnvoll, dies in Prettier zu unterstützen.
// Before
setTimeout(
function() {
thing();
},
500
);
// After
setTimeout(function() {
thing();
}, 500);
JSX-Ausgabe für Style-Komponenten verbessert (#1144)
Template-Literale sind wegen ihrer signifikanten Leerzeichen sehr knifflig zu formatieren. Mit Prettier wollen wir nicht die Semantik Ihres Programms durch Neuformatierung ändern, daher belassen wir sie unverändert. Die aktuelle Handhabung ist nicht optimal, und wir haben Ideen für eine allgemeine Lösung, aber bis dahin können wir gezielte Korrekturen wie diese für JSX vornehmen.
// Before
<style>
{
`
color: red;
`
}
</style>
// After
<style>{`
color: red;
`}</style>
Unterstützung für Punktnotation in derselben Zeile bei Dekoratoren hinzugefügt (#1029)
MobX 3 verwendet jetzt Member-Ausdrücke, die inline geschrieben werden sollten – die vorherige Heuristik war zu restriktiv. Dies ist jetzt behoben.
class X {
// Before
@action.bound
setPrice(price) {
this.price = price;
}
// After
@action.bound setPrice(price) {
this.price = price;
}
}
Kompakte Darstellung bei Destrukturierung einzelner Objekte in Funktionen (#1022)
Zustandslose funktionale React-Komponenten liegen derzeit voll im Trend, daher ist es sinnvoll, die Destrukturierung des ersten Arguments kompakter darzustellen.
// Before
export default function StatelessFunctionalComponent(
{
searchFilters,
title,
items
}
) {
return <div />;
}
// After
export default function StatelessFunctionalComponent({
searchFilters,
title,
items,
}) {
return <div />
}
Unterstützung für Currying hinzugefügt (#1066)
Redux fördert stark das Schreiben von Funktionen in Curry-Form, bei der jedes Argument eine verschachtelte Funktion ist. Statt sie einzurücken, stellen wir sie jetzt inline dar.
// Before
const mw = store =>
next =>
action => {
return next(action)
};
// After
const mw = store => next => action => {
return next(action)
};
Escaping für JSX-Strings berücksichtigen (#1056)
Bei Strings haben wir verschiedene Lösungen für Escaping ausprobiert: nichts escapen, alles escapen, eine Whitelist escapen... Aber wir fanden keine Heuristik, die alle Anwendungsfälle sinnvoll abdeckt. Daher entschieden wir uns, die Strings so belassen, wie sie eingegeben wurden. Dies wenden wir jetzt auch auf JSX-Strings an.
// Before
<a href="https://foo.bar?q1=foo&q2=bar" />
// After
<a href="https://foo.bar?q1=foo&q2=bar" />
Klammern
Ein Segen und Fluch beim Drucken aus dem AST ist, dass wir alle Klammern des Programms neu setzen müssen. Früher wollten wir nur die minimal notwendigen Klammern drucken, um das Programm gültig zu halten. Jetzt fügen wir auch nicht zwingend erforderliche Klammern hinzu, die das Verständnis des Codes erleichtern.
Klammern für binäre Operatoren hinzugefügt (#1153)
// Before
var sizeIndex = index - 1 >>> level & MASK;
// After
var sizeIndex = ((index - 1) >>> level) & MASK;
Klammern für die no-confusing-arrow-Regel hinzugefügt (#1182)
// Before
var x = a => 1 ? 2 : 3;
// After
var x = a => (1 ? 2 : 3);
Klammern bei verketteten Zuweisungen entfernt (#967)
// Before
this.size = (this._origin = (this._capacity = 0));
// After
this.size = this._origin = this._capacity = 0;
Flow
In den Anfängen von Prettier lag unser Fokus stark darauf, die Kernsprache JavaScript gut zu unterstützen. Jetzt, wo dies in guter Verfassung ist, haben wir mehr Zeit, Flow-Konstrukte ausgefeilt darzustellen.
Möglichkeit für Umbruch bei Flow-Generics hinzufügen (#1041)
// Before
type _ReactElement<DefaultProps, Props, Config: $Diff<Props, DefaultProps>, C: $React.Component<DefaultProps, Props, any>> = $React.Element<Config>;
// After
type _ReactElement<
DefaultProps,
Props,
Config: $Diff<Props, DefaultProps>,
C: $React.Component<DefaultProps, Props, any>
> = $React.Element<Config>;
Verbesserte Darstellung von Flow-Intersections (#1018 & #1155)
// Before
type Props =
& {
focusedChildren?: React.Children,
onClick: () => void,
overlayChildren?: React.Children,
}
& FooterProps;
// After
type Props = {
focusedChildren?: React.Children,
onClick: () => void,
overlayChildren?: React.Children,
} & FooterProps;
Unterstützung für Umbruch bei TupleTypeAnnotation hinzufügen (#1003)
// Before
export type FileMetaData = [/* id */ string, /* mtime */ number, /* visited */
| 0
| 1, /* dependencies */ Array<string>];
// After
export type FileMetaData = [
/* id */ string,
/* mtime */ number,
/* visited */ 0|1,
/* dependencies */ Array<string>,
];
Keine Klammern für Flow-Kurzform mit einem Argument (#972)
// Before
type T = { method: (a) => void };
// After
type T = { method: a => void };
Umbruchmöglichkeit für Interfaces hinzufügen (#1060)
// Before
export interface Environment1 extends GenericEnvironment<SomeType, AnotherType, YetAnotherType> {
m(): void
}
// After
export interface Environment1
extends GenericEnvironment<SomeType, AnotherType, YetAnotherType> {
m(): void
}
Korrektur der Darstellung von Flow-Zahlenliteralen (#1132)
// Before
type Hex = {n: 1};
// After
type Hex = {n: 0x01};
Umbruchstellen
Es gibt einige Fälle, in denen Prettier noch Code > 80 Zeichen ausgibt, obwohl eine kürzere Schreibweise möglich wäre. Die Lösung besteht darin, sorgfältig mögliche Umbruchstellen zu identifizieren und sicherzustellen, dass gängige Fälle nicht negativ beeinflusst werden.
Umbruch nach = für Strings und Member-Expressions ermöglichen (#1142 & #1188)
// Before
elements[0].innerHTML = '<div></div><div></div><div></div><div></div><div></div><div></div>';
var testExampleOrOrderOfGetterAndSetterReordered = obj.exampleOfOrderOfGetterAndSetterReordered;
// After
elements[0].innerHTML =
'<div></div><div></div><div></div><div></div><div></div><div></div>';
var testExampleOrOrderOfGetterAndSetterReordered =
obj.exampleOfOrderOfGetterAndSetterReordered;
Umbruchmöglichkeit für Top-Level-Member-Expressions hinzufügen (#1036)
// Before
expect(
findDOMNode(component.instance()).getElementsByClassName(styles.inner)[0].style.paddingRight
).toBe("1000px");
// After
expect(
findDOMNode(component.instance()).getElementsByClassName(styles.inner)[0]
.style.paddingRight
).toBe("1000px");
Verschiedenes
Inline-Klassendarstellung für Pfeilfunktionen ohne Klammern (#1023)
Kleinigkeit: Klassen, die von Pfeilfunktionen zurückgegeben werden, werden jetzt inline dargestellt.
// Before
jest.mock(
'../SearchSource',
() =>
class {
findMatchingTests(pattern) {
return {paths: []};
}
},
);
// After
jest.mock(
'../SearchSource',
() => class {
findMatchingTests(pattern) {
return {paths: []};
}
},
);
Zeilenumbrüche bei Destrukturierungsmustern nicht berücksichtigen (#981)
Bei Objekten gibt es eine Sonderbehandlung: Wenn der Originalquellcode einen \n enthält, bleibt die Darstellung erweitert. Dies war nur für Objekte gedacht, wurde aber versehentlich auch auf Destrukturierungen angewendet, da derselbe Code genutzt wird. Dies wurde korrigiert.
// Before
const Component2 = ({
props
}) => <Text>Test</Text>;
// After
const Component1 = ({ props }) => <Text>Test</Text>;
Sprachunterstützung
Damit Sie Prettier nutzen können, muss es Ihren Code darstellen können. Unser Ziel ist es, alles darzustellen, was unsere zugrunde liegenden Parser (babylon und flow) verarbeiten können.
Unterstützung für ForOfStatement mit await-Flag hinzufügen (#1073)
async function f() {
for await (const line of readLines(\\"/path/to/file\\")) {
(line: void); // error: string ~> void
}
}
Unterstützung für Flow-Type-Spread hinzufügen (#1064)
type TypeB = { ...TypeA };
Korrektheit
Die absolute Grundvoraussetzung für Prettier ist die Ausgabe von valide Code mit identischem Verhalten wie der Originalquelle. Sie sollten keine Bedenken haben, Prettier auf Ihrer gesamten Codebasis auszuführen. Um dies sicherzustellen, unternehmen wir mehrere Maßnahmen:
-
Automatisierte Sicherstellung, dass Prettier validen Code ausgibt
-
Einsatz an großen Codebasen wie Facebook, CDNjs und vielen Community-Nutzern
-
Umfangreiches Fuzzing mit eslump
-
Sofortige Behebung gemeldeter Probleme mit hoher Priorität
Falls Sie unglücklicherweise auf einen Bug stoßen sollten, melden Sie ihn bitte, damit wir ihn beheben können. In der Zwischenzeit können Sie // prettier-ignore verwenden, um die Formatierung für den betroffenen Codeabschnitt zu deaktivieren.
Korrekte Verarbeitung von \r\n in JSXText (#1170)
// Before
<div>
{" "}
Text{" "}
</div>;
// After
<div>
Text
</div>;
Klammerung bei Aufrufen innerhalb von new korrigieren (#1169)
// Before
new factory()();
// After
new (factory())();
Klammern um Rückgabetypen von Pfeilfunktionen hinzufügen (#1152)
// Before
const f = (): string => string => {};
// After
const f = (): (string => string) => {};
Druckdarstellung von exakten Objekttypen in Flow korrigieren (#1114)
// Before
type Props = {};
// After
type Props = {||};
Nachgestellte Kommentare bei return korrekt ausgeben (#1178)
// Before
function f() {
return;
}
// After
function f() {
return /* a */;
}
Kommentare nach Objektschlüsseln korrekt ausgeben (#1131)
// Before
let a = {
"a": () => 1
};
// After
let a = {
"a" /* comment */: () => 1
};
Führende Kommentare in zurückgegebenen Sequenzausdrücken korrigieren (#1133)
// Before
function sequenceExpressionInside() {
return;
// Reason for a
a, b;
}
// After
function sequenceExpressionInside() {
return ( // Reason for a
a, b
);
}
Druckdarstellung einzelner optionaler Pfeilfunktionsparameter korrigieren (#1002)
// Before
a = b? => c;
// After
a = (b?) => c;
Standardexporte korrekt behandeln (#998)
// Before
export { foo, bar } from "./baz";
// After
export foo, {bar} from './baz';
Zeilen-/Blockkommentare in neuen Zeilen innerhalb von JSXEmptyExpression ausgeben (#985)
// Before
<div>
{
// single line comment}
</div>;
// After
<div>
{
// single line comment
}
</div>;
Kommentare
prettier arbeitet durch Ausgabe des AST, was die Handhabung von Kommentaren herausfordernd macht, da sie beliebig zwischen Token platziert werden können. prettier löst dies, indem Kommentare an AST-Knoten angehängt werden - entweder davor oder danach. Wir verwenden eine generische Heuristik, die in den meisten Fällen funktioniert, aber es gibt zahlreiche Sonderfälle, die manuell behandelt werden müssen.
prettier wird niemals in der Lage sein, Kommentare in allen möglichen Positionen perfekt auszugeben, strebt aber an, in den meisten Fällen ein vernünftiges Ergebnis zu liefern. Falls Sie ungewöhnliches Verhalten bei Kommentaren beobachten, melden Sie bitte Issues - wir werden Lösungen erarbeiten.
BreakParent-Unterstützung für willBreak hinzufügen (#674)
// Before
runtimeAgent.getProperties(objectId, false, false, false, ( // ownProperties // accessorPropertiesOnly // generatePreview
error,
properties,
internalProperties
) => {
return 1;
});
// After
runtimeAgent.getProperties(
objectId,
false, // ownProperties
false, // accessorPropertiesOnly
false, // generatePreview
(error, properties, internalProperties) => {
return 1
},
);
Zusätzliche Leerzeilen bei Switch-Case-Kommentaren korrigieren (#936)
// Before
switch (foo) {
case "bar":
doThing()
// no default
}
// After
switch (foo) {
case "bar":
doThing()
// no default
}
Kommentare in Import-Deklarationen korrigieren (#1030)
// Before
import {
FN1,
FN2,
// FN3,
FN4
} from // FN4,
// FN5
"./module";
// After
import {
FN1,
FN2,
// FN3,
FN4,
// FN4,
// FN5
} from './module';
Kommentare beim letzten Funktionsargument korrigieren (#1176 & #905)
// Before
function f(
a: number
)// some comment here
: number {
return a + 1;
}
// After
function f(
a: number
// some comment here
): number {
return a + 1;
}
Kommentarprüfung bei Flow-Union-Typen lockern (#1040)
// Before
type UploadState<E, EM, D> =
// The upload hasn't begun yet
| A
| // The upload timed out
B
| // Failed somewhere on the line
C;
// After
type UploadState<E, EM, D> =
// The upload hasn't begun yet
| A
// The upload timed out
| B
// Failed somewhere on the line
| C;
Kommentare innerhalb von ObjectPattern korrigieren (#1045)
// Before
export default (
{
foo,
bar
// comment
// comment 2
}: {
foo?: Object,
bar?: Object
}
) => {};
// After
export default (
{
foo,
bar
}: {
// comment
foo?: Object,
// comment 2
bar?: Object,
},
) => {}
Positionierung beim Sortieren von Kommentaren korrigieren (#1038)
// Before
let {
// comment
a = b
} = c;
// After
let {
a = b // comment
} = c;
Kommentarposition bei binären Ausdrücken korrigieren (#1043)
// Before
a = Math.random() * (yRange * (1 - minVerticalFraction)) +
minVerticalFraction * yRange// Comment
-
offset;
// After
a =
// Comment
Math.random() * (yRange * (1 - minVerticalFraction)) +
minVerticalFraction * yRange -
offset;
Kommentare bei Klassenmethoden korrigieren (#1074)
// Before
class x {
focus() // do nothing
{
// do nothing
}
}
// After
class x {
focus() {
// do nothing
// do nothing
}
}
"// prettier-ignore" in Kommentarblöcken unterstützen (#1125)
// Before
module.exports = {
// Some comment
// prettier-ignore
m: matrix(1, 0, 0, 0, 1, 0, 0, 0, 1)
};
// After
module.exports = {
// Some comment
// prettier-ignore
m: matrix(
1, 0, 0,
0, 1, 0,
0, 0, 1
)
};
Konsistente Kommentarverarbeitung bei Variablendeklarationen (#1130)
// Before
let obj = [ // Comment
'val'
];
// After
let obj = [
// Comment
'val'
];
Korrektur der Kommentarerfassung vor Kommas (#1127)
// Before
const foo = {
a: 'a' /* comment for this line */,
/* Section B */
b: 'b',
};
// After
const foo = {
a: 'a' /* comment for this line */,
/* Section B */
b: 'b',
};
Korrektur des letzten Kommentars in einem if-Test (#1042)
// Before
if (isIdentifierStart(code) || code === 92) {
/* '\' */
}
// After
if (isIdentifierStart(code) || code === 92 /* '\' */) {}
