TextSorter

Was Das Formatieren Einer Abfrage Wirklich Verändert

Kurz gesagt: Der Formatierer rückt jedes Selection Set auf zwei Leerzeichen pro Verschachtelungsebene ein, setzt jedes Feld in eine eigene Zeile und vereinheitlicht die Abstände um Argumente, Aliase und Direktiven. Was die Abfrage tatsächlich anfordert, bleibt unangetastet.

Unordentlich rein
query{me:user(id:1){name email posts(limit:5){title comments{body author{name}}}}}
Formatiert raus
query {
  me: user(id: 1) {
    name
    email
    posts(limit: 5) {
      title
      comments {
        body
        author {
          name
        }
      }
    }
  }
}

Fügen Sie diese einzeilige Abfrage oben in das Tool ein und klicken Sie auf Code formatieren, um dasselbe Ergebnis zu sehen. Jede öffnende geschweifte Klammer erhält ihre eigene Einrückungsebene, jedes Feld, das zuvor in einer Zeile zusammengedrängt war, bekommt seine eigene Zeile, und der Alias vor user bleibt exakt an seiner Stelle, nur mit einheitlichem Abstand um den Doppelpunkt. Verschachtelte Selection Sets, also ein Feld innerhalb eines Feldes innerhalb eines weiteren Feldes, erhalten jeweils zwei Leerzeichen mehr Einrückung als ihr übergeordnetes Feld. Deshalb landet comments eine Ebene tiefer als posts und author noch eine Ebene tiefer.

Im Hintergrund läuft dafür der GraphQL-Parser von Prettier, der beim ersten Klick auf Code formatieren von einem öffentlichen CDN geladen wird. Prettier liest Ihren Text in einen Syntaxbaum ein, unterscheidet also zwischen einem Feld, einem Argument, einer Direktive und einem Fragment-Spread, und druckt diesen Baum anschließend anhand fester Regeln wieder aus, statt Muster in Ihrer ursprünglichen Formatierung zu suchen. Deshalb sieht das Ergebnis gleich aus, egal ob Sie eine einzelne 400 Zeichen lange Zeile eingefügt haben oder etwas, das bereits halb eingerückt war und Tabs mit Leerzeichen mischte: Die ursprüngliche Formatierung wird vollständig verworfen und aus der geparsten Struktur neu aufgebaut.

Was Sie geschrieben habenWas dabei herauskommt
Ein Feld, das direkt neben seinen Geschwistern stehtEine eigene Zeile, im selben Selection Set, in der Reihenfolge, in der Sie es geschrieben haben
Ein Argument wie id:1id: 1, ein Leerzeichen nach dem Doppelpunkt, keines davor
Ein Alias wie me:user(...)me: user(...), weiterhin vor dem Feld, das er umbenennt
Eine Direktive wie @include(if:$x)@include(if: $x), direkt nach dem gedruckt, was sie verändert
Ein verschachteltes { } Selection SetEine eigene Einrückungsebene, genau zwei Leerzeichen tiefer als das übergeordnete Feld

Ebenso wichtig ist, was sich nicht ändert. Der Formatierer ordnet Felder niemals neu an, entfernt niemals ein Feld, das er für überflüssig hält, und benennt niemals etwas um. Stand name bei Ihnen vor email, steht name auch in der Ausgabe vor email. Haben Sie ein Feld zweimal mit unterschiedlichen Aliasen versehen, um es unter zwei verschiedenen Schlüsseln abzurufen, bleiben beide Aliase unangetastet erhalten. Die Neuformatierung ist ein reiner Druckschritt über die geparste Struktur. Sie kann die Bedeutung der Abfrage nicht verändern, weil sie den geparsten Baum nie verändert, sondern nur den Text, mit dem dieser Baum wieder auf die Seite gedruckt wird.

Wie Einrückung Und Zeilenumbruch Entschieden Werden

Kurz gesagt: Jedes verschachtelte Selection Set fügt genau zwei Leerzeichen hinzu, unabhängig davon, wie die Quelle eingerückt war, und eine Argumentliste bricht erst dann auf mehrere Zeilen um, wenn die gesamte Zeile etwa 80 Zeichen überschreiten würde.

Eine lange Zeile rein
query { search(firstArgument: "some long value here", secondArgument: "another long value", thirdArgument: true) { id } }
Umgebrochene Ausgabe
query {
  search(
    firstArgument: "some long value here"
    secondArgument: "another long value"
    thirdArgument: true
  ) {
    id
  }
}

Eine kurze Argumentliste bleibt genau dort, wo sie ist: user(id: 1) wird in einer Zeile gedruckt, weil das gesamte Feld bequem innerhalb der Breitengrenze passt. Der dreiargumentige search-Aufruf oben passt nicht, also setzt der Formatierer jedes Argument in eine eigene Zeile, eine Einrückungsebene tiefer als das Feld selbst, und richtet die schließende Klammer wieder an search aus. Einen Zwischenzustand gibt es nicht. Die Argumente eines Feldes stehen entweder vollständig in einer Zeile oder vollständig gestapelt, niemals ist ein Teil auf der Zeile des Feldes und der Rest darunter.

Die Entscheidung über die Einrückung ignoriert Ihre Quelle vollständig. Eine mit Tabs eingefügte Abfrage, eine mit vier Leerzeichen eingerückte Abfrage und eine als einzelne durchgehende Zeile eingefügte Abfrage erzeugen nach der Formatierung alle das identische Ergebnis, denn der Parser entfernt beim Parsen sämtliche Leerzeichen, und der Printer baut sie anschließend nach seiner eigenen Zwei-Leerzeichen-Regel von Grund auf neu auf. Es gibt nichts zu konfigurieren und nichts, was Sie im Eingabefeld tun könnten, um die Einrückungsbreite zu ändern. Sie ist für jede Konstruktion, die der Formatierer verarbeitet, fest auf zwei Leerzeichen pro Ebene eingestellt.

SituationAngewendete Regel
Die Quelle verwendet Tabs, vier Leerzeichen oder gar keine EinrückungWird ignoriert. Die Ausgabe verwendet immer zwei Leerzeichen pro Verschachtelungsebene
Eine Argumentliste passt in die eigene Zeile des FeldesBleibt in der Zeile, z. B. posts(limit: 5)
Eine Argumentliste würde die Zeile über die Breitengrenze hinaus verlängernBricht auf ein Argument pro Zeile um, eingerückt unter dem Feld, ohne abschließendes Komma
Ein Objektwert wie {name:"bob",active:true}Leerzeichen innerhalb der Klammern werden ergänzt: { name: "bob", active: true }
Ein Listenwert wie ["a","b","c"]Leerzeichen werden nach jedem Komma ergänzt: ["a", "b", "c"]
Zwei oder mehr Leerzeilen zwischen Definitionen auf oberster EbeneWerden auf genau eine Leerzeile zusammengefasst

Sie müssen vor dem Einfügen niemals von Hand irgendetwas ausrichten. Da der Parser Ihre ursprünglichen Leerzeichen verwirft und die Einrückung aus der erkannten Struktur neu aufbaut, bringt es nichts, vor der Formatierung Zeit damit zu verbringen, Klammern in Ihrer Quelle von Hand auszurichten. Fügen Sie den Code genau so ein, wie er in Ihrem Terminal, im Netzwerk-Tab Ihres Browsers oder in einer Chat-Nachricht steht, und überlassen Sie die Ausrichtung dem Formatierer.

Aliase, Direktiven, Fragmente Und Variablen Werden Neu Formatiert, Nicht Neu Geschrieben

Kurz gesagt: Aliase, Direktiven, Fragment-Spreads, Inline-Fragmente und Operationsvariablen behalten alle ihre exakten Namen und Struktur. Formatierung ändert nur ihre Abstände und Einrückung, niemals, worauf sie verweisen oder wie sie miteinander verdrahtet sind.

Fragment plus Alias plus Direktive, rein
fragment PostFields on Post { title publishedAt }
query GetFeed($includeDrafts: Boolean!) { feed: posts(status: "published") { ...PostFields drafts: posts(status: "draft") @include(if: $includeDrafts) { title } } }
Formatiert raus
fragment PostFields on Post {
  title
  publishedAt
}
query GetFeed($includeDrafts: Boolean!) {
  feed: posts(status: "published") {
    ...PostFields
    drafts: posts(status: "draft") @include(if: $includeDrafts) {
      title
    }
  }
}

Eine Fragmentdefinition wird genauso gedruckt wie eine Operation: ein Feld pro Zeile innerhalb ihres eigenen Selection Sets, auf oberster Ebene des Dokuments statt in etwas anderes verschachtelt. Wo Sie die Fragmentdefinition relativ zur Abfrage platzieren, die sie verwendet, bleibt exakt so, wie Sie es geschrieben haben, davor oder danach, denn der Formatierer ordnet Definitionen auf oberster Ebene in einer Datei nicht neu an. Ein Fragment-Spread wie ...PostFields wird in seiner eigenen Zeile zwischen den anderen ausgewählten Feldern gedruckt, und ein Inline-Fragment wie ... on User { name } erhält sein eigenes eingerücktes Selection Set, genau wie ein normales verschachteltes Feld.

Auf einer Operation deklarierte Variablen, einschließlich solcher mit Standardwerten wie $limit: Int = 10, bleiben innerhalb der Klammern direkt nach dem Operationsnamen, jeweils durch Komma und Leerzeichen getrennt. Direktiven bleiben an das gebunden, worauf sie geschrieben wurden: Eine Direktive auf einem Feld, wie @include(if: $includeDrafts) oben, wird direkt nach den Argumenten dieses Feldes und vor seinem Selection Set gedruckt, und eine Direktive auf einem Argument bleibt an dieses Argument gebunden. Der Formatierer akzeptiert auch eine anonyme Kurzform ohne query-Schlüsselwort und ohne jeden Namen, eine öffnende { direkt vor dem Selection Set, und rückt sie genauso ein wie eine vollständig benannte Abfrage.

KonstruktWie es gedruckt wird
Feld-Aliasalias: field(...), Abstände wie bei jedem anderen Feld vereinheitlicht
Fragment-Spread...FragmentName, eigene Zeile innerhalb des Selection Sets
Inline-Fragment... on TypeName { }, eingerückt wie ein verschachteltes Feld
DirektiveBleibt direkt nach dem Feld oder Argument, das sie verändert
Variable mit Standardwert$name: Type = default, innerhalb der Klammern der Operation
Anonyme Kurzform-AbfrageKein query-Schlüsselwort nötig, wird wie eine benannte Abfrage eingerückt

Dies ist Druckformatierung, keine Abfrageauflösung. Der Formatierer bindet niemals die Felder eines Fragments an der Stelle ein, an der es verwendet wird, prüft niemals, ob $includeDrafts überhaupt deklariert wurde, bevor es verwendet wird, und bestätigt niemals, dass @include eine Direktive ist, die Ihr Schema kennt. Das sind Aufgaben für einen GraphQL-Client oder einen schemabewussten Linter, der gegen Ihre echte API läuft, nicht für einen Formatierer, der nur Syntax liest und neu ausdruckt.

Eine Schemadefinition Wird Nach Anderen Regeln Formatiert Als Eine Abfrage

Kurz gesagt: Fügen Sie statt einer Abfrage eine Typdefinition ein, wechselt derselbe Code-formatieren-Button von selbst das Verhalten: Typen, Interfaces, Enums, Unions und Inputs erhalten jeweils ein Feld oder einen Wert pro Zeile, und ein Beschreibungsstring wird immer auf drei eigene Zeilen aufgeteilt, selbst ein einzeiliger.

Typ mit einer eingefügten Beschreibung, rein
type User{"""The user's unique id"""id:ID! name:String! email:String posts:[Post!]!}
Formatiert raus
type User {
  """
  The user's unique id
  """
  id: ID!
  name: String!
  email: String
  posts: [Post!]!
}

Der Formatierer fragt nicht, ob Ihr eingefügter Text eine Operation oder ein Schema ist. Er parst, was auch immer Sie ihm geben, und druckt zurück, welche Art von Dokument er darin gefunden hat. Geben Sie ihm eine Abfrage, erhalten Sie eine abfrageförmige Ausgabe. Geben Sie ihm Typ-, Interface-, Enum-, Union- und Input-Definitionen, erhalten Sie eine schemaförmige Ausgabe, bei der jedes Feld eines Typs, jeder Wert eines Enums und jedes Argument einer Interface-Methode in seiner eigenen Zeile steht, genau wie die Felder einer Operation. Nicht-Null- und Listenmarkierungen, ID!, [Post!]! und so weiter, bleiben exakt so erhalten, wie sie geschrieben wurden, denn diese Zeichen sind Teil des Typs selbst und nicht etwas, das der Drucker erzeugt.

Beschreibungsstrings verhalten sich anders als die gewöhnliche Feldformatierung, und das sollten Sie wissen, bevor Sie sich darauf verlassen: Eine dreifach angeführte Beschreibung wird auf drei Zeilen aufgeteilt, ein öffnendes """, der Text in eigener Zeile, ein schließendes """, selbst wenn die gesamte Beschreibung problemlos in eine Zeile passen würde, wie das siebenwortige Beispiel oben zeigt. Das ist kein Fehler, den man umgehen müsste. Es ist einfach so, wie der Schema-Drucker Blockbeschreibungen immer darstellt, und das gilt gleichermaßen für einen Typ, ein Feld, ein Argument, einen Enum-Wert oder ein Input-Feld.

Die Feldreihenfolge in einem Schema wird niemals alphabetisch sortiert. Ein Typ mit id, name, email und posts in dieser Reihenfolge kommt in genau dieser Reihenfolge wieder heraus. Dasselbe gilt für Operationen: Fragen Sie zebra, apple, dann mango ab, werden sie in dieser Reihenfolge zurückgegeben, und ein Feld, das im selben Selection Set zweimal wiederholt wird, wird zweimal gedruckt statt entfernt zu werden. Wenn Sie eine bestimmte Feldreihenfolge möchten, müssen Sie sie so schreiben. Der Formatierer rückt nur ein, was bereits vorhanden ist.

Kommentare Überstehen Die Formatierung. Die Meisten Leerzeilen Auch.

Kurz gesagt: Ein #-Kommentar, ob in einer eigenen Zeile oder einem Feld nachgestellt, wird unverändert durchgereicht. Leerzeilen zwischen Definitionen auf oberster Ebene bleiben ebenfalls erhalten, aber nie mehr als eine hintereinander.

Kommentierte Abfrage, rein
# This is a comment
query {
  # inline comment
  user(id: 1) {
    name # trailing comment
  }
}
Formatiert raus
# This is a comment
query {
  # inline comment
  user(id: 1) {
    name # trailing comment
  }
}

Beachten Sie, dass die Ausgabe oben mit der Eingabe identisch ist, abgesehen von der Neueinrückung, die bei einer unkommentierten Version derselben Abfrage ohnehin angewendet würde. Ein alleinstehender Kommentar in eigener Zeile behält seine Position relativ zu dem Feld oder der Definition, die darauf folgt, und ein nachgestellter Kommentar bleibt am Ende der Zeile, in der er geschrieben wurde. Das ist in der Praxis wichtig, weil Kommentare einer der wenigen Orte sind, an denen Entwickler direkt in einer Abfrage Notizen für Kollegen hinterlassen, etwa um ein Feld zu markieren, das nur für einen alten Client noch gebraucht wird, und ein Formatierer, der sie stillschweigend entfernen würde, würde diesen Kontext klammheimlich löschen.

Leerzeilen werden nachsichtiger behandelt als Kommentare. Eine einzelne Leerzeile zwischen zwei Definitionen auf oberster Ebene, zwei getrennten Operationen im selben Dokument oder einer Operation und einer darunterliegenden Fragmentdefinition, bleibt als visuelle Trennung erhalten. Lassen Sie zwei, drei oder mehr Leerzeilen hintereinander stehen, fasst der Formatierer sie alle auf genau eine zusammen. Er entfernt die Trennung nie vollständig, und er behält niemals zusätzlichen Abstand über eine einzelne Zeile hinaus. Innerhalb eines Selection Sets, zwischen einzelnen Feldern, werden Leerzeilen überhaupt nicht erhalten, denn Felder innerhalb von Klammern werden unabhängig vom Abstand in der Quelle direkt hintereinander gedruckt.

Nachgestellte Leerzeichen und verirrte Leerzeilen im Eingabefeld haben keine Bedeutung. Da der Parser Struktur liest und keinen Rohtext, hat eine zusätzliche Leerzeile ganz oben in Ihrem eingefügten Code oder ein nachgestelltes Leerzeichen am Ende einer Zeile keinerlei Auswirkung auf das Ergebnis. Das einzige Leerzeichen, das der Formatierer als bedeutsam behandelt, ist eine einzelne Leerzeile zwischen zwei Definitionen auf oberster Ebene, alles andere wird beim Parsen verworfen.

Kaputtes GraphQL Wird Nicht Markiert. Es Wird Erraten.

Kurz gesagt: Dies ist ein Formatierer, kein Validator. Enthält Ihr eingefügter Code einen echten Syntaxfehler, sagt Ihnen nichts auf der Seite davon. Das Tool wechselt stillschweigend zu einem groben Suchen-und-Ersetzen-Durchgang, der GraphQL überhaupt nicht versteht.

Unabgeschlossene Abfrage rein
query { me: user(id: 1) { name email posts(limit: 5) { title 
Was Sie tatsächlich bekommen
query  {
  me: user(id: 1)  {
  name email posts(limit: 5)  {
  title 

Die Ausgabe oben ist kein Formatierungsfehler in diesem Artikel, sie ist genau das, was das Tool erzeugt, doppelte Leerzeichen vor Klammern inklusive. Hier ist der Grund. Hinter dem Code-formatieren-Button erkennt Prettiers echter GraphQL-Parser durchaus korrekt, dass diese Abfrage kaputt ist. Gibt man ihm denselben unabgeschlossenen Text, liefert er einen präzisen Fehler, Syntax Error: Expected Name, found <EOF>, der genau auf Zeile und Spalte zeigt, an der die Abfrage endet. Aber die Komponente umschließt diesen Aufruf mit einem try/catch-Block, und bei jedem Fehler protokolliert sie ihn in der Entwicklerkonsole des Browsers, wo Sie ihn nie sehen werden, es sei denn, Sie suchen gezielt danach, und gibt stattdessen die Ausgabe einer Fallback-Funktion zurück statt einer Fehlermeldung auf der Seite.

Dieser Fallback besteht aus vier verketteten Textersetzungen: Alle Leerzeichen auf ein einziges reduzieren, nach jeder { einen Zeilenumbruch und zwei Leerzeichen Einrückung einfügen, nach jeder } einen Zeilenumbruch einfügen, nach jedem Komma einen Zeilenumbruch und zwei Leerzeichen Einrückung einfügen. Er hat kein Konzept von Verschachtelungstiefe, weshalb jede öffnende Klammer oben dieselbe flache Zwei-Leerzeichen-Einrückung erhält, egal wie tief sie tatsächlich sitzt, und kein Konzept einer passenden schließenden Klammer, weshalb eine unabgeschlossene Abfrage wie diese einfach ausläuft, ohne dass die Struktur je geschlossen wird. Das doppelte Leerzeichen vor jeder { ist ein Nebeneffekt der Reihenfolge der Ersetzungen: Der Schritt, der Leerzeichen reduziert, lässt ein Leerzeichen vor der Klammer stehen, und der Schritt, der die Klammer ersetzt, fügt ein weiteres hinzu, ohne zu prüfen, was bereits da ist.

Derselbe stille Fallback greift auch dann, wenn die Formatierungsbibliothek selbst nie lädt, nicht nur bei einem Syntaxfehler in Ihrem Text. Der echte Parser ist nicht in die Seite eingebettet, er wird beim ersten Klick auf Code formatieren von einem öffentlichen CDN nachgeladen. Wird diese Anfrage blockiert, ist die Verbindung offline, oder schlägt sie aus einem anderen Netzwerkgrund fehl, behandelt der try/catch-Block das genauso wie einen Parsing-Fehler und liefert dieselbe grobe, unverschachtelte Ausgabe, selbst für eine vollkommen gültige Abfrage. Es gibt keine Anzeige auf der Seite, die unterscheidet, ob Ihr GraphQL einen Syntaxfehler hat, der echte Formatierer nicht laden konnte, oder der echte Formatierer gelaufen ist und dies wirklich das korrekte Ergebnis ist. Ein Ergebnis, das flach und seltsam gesetzt wirkt, mit doppelten Leerzeichen vor Klammern, ist das Erkennungszeichen dafür, dass Sie den Fallback sehen und keinen echten Formatierungsdurchgang.

Die praktische Konsequenz ist, dass sich dieses Tool nicht dazu eignet, zu prüfen, ob GraphQL gültig ist. Brauchen Sie eine echte Validierung, die Bestätigung, dass jedes Feld auf dem Typ existiert, gegen den es abgefragt wird, dass jede verwendete Variable deklariert ist, dass jede Direktive korrekt geschrieben ist, dann muss das von einem GraphQL-Client, einer IDE-Erweiterung mit Anbindung an Ihr echtes Schema oder einem dedizierten Linter kommen. Die Aufgabe dieses Formatierers endet bei der Neueinrückung von Syntax, die er erfolgreich parsen kann.

Frequently Asked Questions

Sagt mir der Formatierer, wenn mein GraphQL einen Syntaxfehler hat?
Nein. Es ist ein Formatierer, kein Validator. Kann die Eingabe nicht geparst werden, wird der Fehler nur in der Entwicklerkonsole des Browsers protokolliert und nie auf der Seite angezeigt. Stattdessen erhalten Sie die Ausgabe eines groben Fallbacks: Leerzeichen werden reduziert, dann werden nach jeder öffnenden Klammer ein Zeilenumbruch und zwei Leerzeichen eingefügt, ohne jedes Bewusstsein für die Verschachtelungstiefe oder dafür, ob überhaupt jede Klammer geschlossen ist.
Formatiert es auch Schemata, nicht nur Abfragen und Mutationen?
Ja. Es parst jedes GraphQL-Dokument, das Sie einfügen, und druckt es in der passenden Form zurück. Typ-, Interface-, Enum-, Union- und Input-Definitionen erhalten jeweils ein Feld oder einen Wert pro Zeile, genauso wie die Felder einer Operation jeweils ihre eigene Zeile bekommen, ohne dass Sie dem Tool sagen müssen, welche Art von Dokument Sie ihm geben.
Ordnet es meine Felder neu an oder alphabetisiert es sie?
Nein. Felder werden genau in der Reihenfolge gedruckt, in der Sie sie geschrieben haben, sowohl in Operationen als auch in Schema-Typen, und ein Feld, das im selben Selection Set zweimal vorkommt, wird zweimal gedruckt statt entfernt. Der Formatierer ändert nur Einrückung und Abstände, niemals die Reihenfolge von Feldern oder Werten.
Wie entscheidet es, wann Argumente auf separate Zeilen gesetzt werden?
Eine kurze Argumentliste bleibt in der Zeile des Feldes. Sobald die vollständige Zeile etwa 80 Zeichen überschreiten würde, rückt jedes Argument in seine eigene Zeile um, eine Ebene tiefer eingerückt als das Feld, wobei die schließende Klammer wieder unter dem Feldnamen ausgerichtet wird.
Spielt die Einrückung in meinem eingefügten Code eine Rolle?
Nein. Der Parser liest Struktur, keine Leerzeichen, daher erzeugen mit Tabs eingerückter Code, mit vier Leerzeichen eingerückter Code und eine einzige durchgehende Codezeile alle das identische Ergebnis. Jede Verschachtelungsebene wird unabhängig von der Einrückung der Eingabe von Grund auf neu mit zwei Leerzeichen pro Ebene aufgebaut.
Was passiert mit Kommentaren in meiner Abfrage?
Sie bleiben erhalten. Ein Kommentar in eigener Zeile behält seine Position relativ zu dem Feld oder der Definition, die darauf folgt, und ein Kommentar, der einem Feld nachgestellt ist, bleibt am Ende dieser Zeile, beides genau wie geschrieben.
Warum wurde aus meiner einzeiligen Schemabeschreibung drei Zeilen?
Das ist so vorgesehen, kein Fehler. Ein dreifach angeführter Beschreibungsstring wird immer auf drei Zeilen gedruckt, ein öffnendes Trennzeichen, der Text, und ein schließendes Trennzeichen, selbst wenn die gesamte Beschreibung kurz genug wäre, um in eine einzelne Zeile zu passen.
Ist mein Code sicher und privat?
Ja. Die Formatierung läuft in Ihrem Browser mithilfe einer Bibliothek, die beim ersten Klick auf Code formatieren von einem öffentlichen CDN geladen wird. Sobald diese Bibliothek geladen ist, wird Ihr Abfragetext niemals hochgeladen oder gespeichert; er wird lokal auf Ihrem Gerät geparst und gedruckt.