Plugins
Diese Seite wurde von PageTurner AI übersetzt (Beta). Nicht offiziell vom Projekt unterstützt. Fehler gefunden? Problem melden →
Plugins ermöglichen das Hinzufügen neuer Sprachen oder Formatierungsregeln zu Prettier. Prettiers eigene Implementierungen aller Sprachen nutzen die Plugin-API. Das Kernpaket prettier enthält JavaScript und andere webfokussierte Sprachen. Für zusätzliche Sprachen müssen Sie ein Plugin installieren.
Plugins verwenden
Plugins können geladen werden mit:
-
Der CLI über
--plugin:prettier --write main.foo --plugin=prettier-plugin-fooTippDie
--plugin-Option kann mehrfach gesetzt werden. -
Der API über die
plugins-Option:await prettier.format("code", {
parser: "foo",
plugins: ["prettier-plugin-foo"],
}); -
Der Konfigurationsdatei:
{
"plugins": ["prettier-plugin-foo"]
}
An plugins übergebene Zeichenketten werden letztendlich an die import()-Expression übergeben. Daher können Sie Modul-/Paketnamen, Pfade oder alles akzeptieren, was import() verarbeitet.
Offizielle Plugins
Community-Plugins
-
prettier-plugin-tomlvon @JounQin und @so1ve
Plugins entwickeln
Prettier-Plugins sind reguläre JavaScript-Module mit den folgenden fünf Exporten oder einem Standardexport mit folgenden Eigenschaften:
-
languages -
parsers -
printers -
options -
defaultOptions
languages
Languages ist ein Array von Sprachdefinitionen, die Ihr Plugin zu Prettier beiträgt. Es kann alle Felder enthalten, die in prettier.getSupportInfo() spezifiziert sind.
Es muss name und parsers enthalten.
export const languages = [
{
// The language name
name: "InterpretedDanceScript",
// Parsers that can parse this language.
// This can be built-in parsers, or parsers you have contributed via this plugin.
parsers: ["dance-parse"],
},
];
parsers
Parser wandeln Code als Zeichenkette in einen AST um.
Der Schlüssel muss mit dem Namen im parsers-Array aus languages übereinstimmen. Der Wert enthält eine Parse-Funktion, einen AST-Formatnamen und zwei Funktionen zur Positionsextraktion (locStart und locEnd).
export const parsers = {
"dance-parse": {
parse,
// The name of the AST that the parser produces.
astFormat: "dance-ast",
hasPragma,
hasIgnorePragma,
locStart,
locEnd,
preprocess,
},
};
Die Signatur der parse-Funktion lautet:
function parse(text: string, options: object): Promise<AST> | AST;
Die Funktionen zur Extraktion von Positionen (locStart und locEnd) geben die Start- und Endpositionen eines gegebenen AST-Knotens zurück:
function locStart(node: object): number;
(Optional) Die Pragma-Erkennungsfunktion (hasPragma) sollte zurückgeben, ob der Text den Pragma-Kommentar enthält.
function hasPragma(text: string): boolean;
(Optional) Die "Ignore-Pragma"-Erkennungsfunktion (hasIgnorePragma) sollte zurückgeben, ob der Text ein Pragma enthält, das anzeigt, dass der Text nicht formatiert werden soll.
function hasIgnorePragma(text: string): boolean;
(Optional) Die Preprocess-Funktion kann den Eingabetxt verarbeiten, bevor er an die parse-Funktion übergeben wird.
function preprocess(text: string, options: object): string | Promise<string>;
Unterstützung für asynchrones Preprocessing wurde erstmals in v3.7.0 hinzugefügt.
printers
Printer wandeln ASTs in eine Prettier-Zwischendarstellung um, auch bekannt als Doc.
Der Schlüssel muss mit dem astFormat übereinstimmen, den der Parser erzeugt. Der Wert enthält ein Objekt mit einer print-Funktion. Alle anderen Eigenschaften (embed, preprocess, etc.) sind optional.
export const printers = {
"dance-ast": {
print,
embed,
preprocess,
getVisitorKeys,
insertPragma,
canAttachComment,
isBlockComment,
printComment,
getCommentChildNodes,
hasPrettierIgnore,
printPrettierIgnored,
handleComments: {
ownLine,
endOfLine,
remaining,
},
},
};
Der Druckprozess
Prettier verwendet eine Zwischendarstellung namens Doc, die Prettier dann in einen String umwandelt (basierend auf Optionen wie printWidth). Die Aufgabe eines Printers ist es, den von parsers[<parser name>].parse generierten AST zu nehmen und ein Doc zurückzugeben. Ein Doc wird mit Builder-Befehlen konstruiert:
import * as prettier from "prettier";
const { join, line, ifBreak, group } = prettier.doc.builders;
Der Druckprozess besteht aus folgenden Schritten:
-
AST-Preprocessing (optional). Siehe
preprocess. -
Kommentar-Anbindung (optional). Siehe Kommentarverarbeitung in einem Printer.
-
Verarbeitung eingebetteter Sprachen (optional). Die
embed-Methode wird, falls definiert, für jeden Knoten Tiefe-zuerst aufgerufen. Während die Rekursion selbst aus Performancegründen synchron ist, kannembedasynchrone Funktionen zurückgeben, die andere Parser und Printer aufrufen können, um Docs für eingebettete Syntaxen wie CSS-in-JS zu komponieren. Diese zurückgegebenen Funktionen werden in eine Warteschlange gestellt und vor dem nächsten Schritt sequenziell ausgeführt. -
Rekursives Drucken. Ein Doc wird rekursiv aus dem AST konstruiert. Beginnend beim Wurzelknoten:
- Falls aus Schritt 3 ein eingebettetes Sprach-Doc für den aktuellen Knoten existiert, wird dieses verwendet.
- Andernfalls wird die
print(path, options, print): Doc-Methode aufgerufen. Diese komponiert ein Doc für den aktuellen Knoten, oft durch Drucken von Kindknoten mittels desprint-Callbacks.
print
Der Großteil der Arbeit eines Plugin-Printers findet in seiner print-Funktion statt, mit folgender Signatur:
function print(
// Path to the AST node to print
path: AstPath,
options: object,
// Recursively print a child node
print: (selector?: string | number | Array<string | number> | AstPath) => Doc,
): Doc;
Die print-Funktion erhält folgende Parameter:
-
path: Ein Objekt, das den Zugriff auf Knoten im AST ermöglicht. Es handelt sich um eine stapelartige Datenstruktur, die den aktuellen Rekursionszustand verwaltet. Es wird "path" genannt, weil es den Pfad zum aktuellen Knoten von der AST-Wurzel aus repräsentiert. Der aktuelle Knoten wird durchpath.nodezurückgegeben. -
options: Ein persistentes Objekt, das globale Optionen enthält und das ein Plugin zur Speicherung kontextbezogener Daten mutieren kann. -
print: Ein Callback zum Drucken von Unterknoten. Diese Funktion enthält die Kern-Drucklogik, deren Implementierung durch Plugins bereitgestellt wird. Insbesondere ruft sie dieprint-Funktion des Printers auf und übergibt sich selbst. Somit rufen sich die beidenprint-Funktionen – die aus dem Core und die aus dem Plugin – gegenseitig auf, während sie rekursiv durch den AST absteigen.
Hier ein vereinfachtes Beispiel, das eine typische print-Implementierung veranschaulicht:
import * as prettier from "prettier";
const { group, indent, join, line, softline } = prettier.doc.builders;
function print(path, options, print) {
const node = path.node;
switch (node.type) {
case "list":
return group([
"(",
indent([softline, join(line, path.map(print, "elements"))]),
softline,
")",
]);
case "pair":
return group([
"(",
indent([softline, print("left"), line, ". ", print("right")]),
softline,
")",
]);
case "symbol":
return node.name;
}
throw new Error(`Unknown node type: ${node.type}`);
}
Siehe prettier-pythons Printer für Beispiele möglicher Implementierungen.
(optional) embed
Ein Printer kann eine embed-Methode enthalten, um eine Sprache innerhalb einer anderen auszugeben. Beispiele hierfür sind CSS-in-JS oder eingebettete Codeblöcke in Markdown. Die Signatur lautet:
function embed(
// Path to the current AST node
path: AstPath,
// Current options
options: Options,
):
| ((
// Parses and prints the passed text using a different parser.
// You should set `options.parser` to specify which parser to use.
textToDoc: (text: string, options: Options) => Promise<Doc>,
// Prints the current node or its descendant node with the current printer
print: (
selector?: string | number | Array<string | number> | AstPath,
) => Doc,
// The following two arguments are passed for convenience.
// They're the same `path` and `options` that are passed to `embed`.
path: AstPath,
options: Options,
) => Promise<Doc | undefined> | Doc | undefined)
| Doc
| undefined;
Die embed-Methode ähnelt der print-Methode, da sie AST-Knoten auf Docs abbildet. Im Gegensatz zu print kann sie jedoch asynchrone Arbeit leisten, indem sie eine asynchrone Funktion zurückgibt. Deren erster Parameter, die asynchrone Funktion textToDoc, kann verwendet werden, um ein Doc mit einem anderen Plugin zu rendern.
Wenn eine von embed zurückgegebene Funktion ein Doc oder ein Promise zurückgibt, das zu einem Doc aufgelöst wird, wird dieses Doc für die Ausgabe verwendet, und die print-Methode wird für diesen Knoten nicht aufgerufen. Es ist auch möglich und in seltenen Situationen praktisch, synchron direkt aus embed ein Doc zurückzugeben. In diesem Fall sind jedoch textToDoc und der print-Callback nicht verfügbar. Geben Sie eine Funktion zurück, um sie zu erhalten.
Wenn embed undefined zurückgibt oder eine zurückgegebene Funktion undefined oder ein Promise zurückgibt, das zu undefined aufgelöst wird, wird der Knoten normal mit der print-Methode ausgegeben. Dasselbe passiert, wenn eine zurückgegebene Funktion einen Fehler auslöst oder ein Promise zurückgibt, das rejected (z. B. wenn ein Parsing-Fehler aufgetreten ist). Setzen Sie die Umgebungsvariable PRETTIER_DEBUG auf einen nicht-leeren Wert, wenn Sie möchten, dass Prettier diese Fehler erneut auslöst.
Ein Plugin mit Knoten, die eingebettetes JavaScript enthalten, könnte beispielsweise folgende embed-Methode haben:
function embed(path, options) {
const node = path.node;
if (node.type === "javascript") {
return async (textToDoc) => {
return [
"<script>",
hardline,
await textToDoc(node.javaScriptCode, { parser: "babel" }),
hardline,
"</script>",
];
};
}
}
Wenn die Option --embedded-language-formatting auf off gesetzt ist, wird der Einbettungsschritt vollständig übersprungen: embed wird nicht aufgerufen und alle Knoten werden mit der print-Methode ausgegeben.
(optional) preprocess
Die preprocess-Methode kann den AST vor der Übergabe an die print-Methode verarbeiten.
function preprocess(ast: AST, options: Options): AST | Promise<AST>;
(optional) getVisitorKeys
Diese Eigenschaft ist nützlich, wenn das Plugin Kommentaranbindung oder eingebettete Sprachen verwendet. Diese Features durchlaufen den AST, indem sie alle eigenen aufzählbaren Eigenschaften jedes Knotens ausgehend von der Wurzel iterieren. Wenn der AST Zyklen enthält, führt ein solcher Durchlauf zu einer Endlosschleife. Zudem können Knoten Nicht-Knoten-Objekte enthalten (z. B. Positionsdaten), deren Iteration Ressourcen verschwendet. Um diese Probleme zu lösen, kann der Printer eine Funktion definieren, die zu durchlaufende Eigenschaftsnamen zurückgibt.
Ihre Signatur lautet:
function getVisitorKeys(node, nonTraversableKeys: Set<string>): string[];
Die Standard-getVisitorKeys:
function getVisitorKeys(node, nonTraversableKeys) {
return Object.keys(node).filter((key) => !nonTraversableKeys.has(key));
}
Das zweite Argument nonTraversableKeys ist eine Menge gängiger Schlüssel und Schlüssel, die Prettier intern verwendet.
Falls Sie eine vollständige Liste der Visitor-Keys haben
const visitorKeys = {
Program: ["body"],
Identifier: [],
// ...
};
function getVisitorKeys(node /* , nonTraversableKeys*/) {
// Return `[]` for unknown node to prevent Prettier fallback to use `Object.keys()`
return visitorKeys[node.type] ?? [];
}
Falls Sie nur eine kleine Menge von Schlüsseln ausschließen müssen
const ignoredKeys = new Set(["prev", "next", "range"]);
function getVisitorKeys(node, nonTraversableKeys) {
return Object.keys(node).filter(
(key) => !nonTraversableKeys.has(key) && !ignoredKeys.has(key),
);
}
(optional) insertPragma
Ein Plugin kann in der Funktion insertPragma implementieren, wie ein Pragma-Kommentar in den resultierenden Code eingefügt wird, wenn die Option --insert-pragma verwendet wird. Ihre Signatur lautet:
function insertPragma(text: string): string;
Umgang mit Kommentaren in einem Printer
Kommentare sind oft nicht Teil des AST einer Sprache und stellen eine Herausforderung für Pretty Printer dar. Ein Prettier-Plugin kann Kommentare entweder selbst in seiner print-Funktion ausgeben oder sich auf Prettiers Kommentaralgorithmus verlassen.
Standardmäßig geht Prettier davon aus, dass die Eigenschaft comments im AST ein Array von Kommentarknoten enthält, wenn der AST eine Top-Level-comments-Eigenschaft besitzt. Prettier verwendet dann die bereitgestellten Funktionen parsers[<plugin>].locStart/locEnd, um den AST-Knoten zu suchen, zu dem jeder Kommentar "gehört". Kommentare werden dann an diese Knoten angehängt, wobei der AST dabei mutiert wird, und die comments-Eigenschaft wird aus dem AST-Stamm entfernt. Die *Comment-Funktionen werden verwendet, um Prettiers Algorithmus anzupassen. Sobald Kommentare am AST angehängt sind, ruft Prettier automatisch die Funktion printComment(path, options): Doc auf und fügt das zurückgegebene Doc (hoffentlich) an der richtigen Stelle ein.
(optional) getCommentChildNodes
Standardmäßig durchsucht Prettier rekursiv alle Objekteigenschaften (bis auf einige vordefinierte) jedes Knotens. Diese Funktion kann bereitgestellt werden, um dieses Verhalten zu überschreiben. Sie hat die Signatur:
function getCommentChildNodes(
// The node whose children should be returned.
node: AST,
// Current options
options: object,
): AST[] | undefined;
Gib [] zurück, wenn der Knoten keine Kinder hat, oder undefined, um auf das Standardverhalten zurückzufallen.
(optional) hasPrettierIgnore
function hasPrettierIgnore(path: AstPath): boolean;
Gibt an, ob der AST-Knoten mit prettier-ignore markiert ist.
(optional) printPrettierIgnored
Wenn ein AST-Knoten prettier-ignored ist, schneidet Prettier standardmäßig den Text für das Parsing aus, ohne die print-Funktion aufzurufen. Plugins können jedoch auch die Ausgabe von prettier-ignored-Knoten selbst übernehmen, indem sie diese Eigenschaft hinzufügen.
Diese Eigenschaft hat dieselbe Signatur wie die print-Eigenschaft.
Erstmals verfügbar in v3.7.0
(optional) printComment
Wird aufgerufen, wenn ein Kommentarknoten ausgegeben werden muss. Sie hat die Signatur:
function printComment(
// Path to the current comment node
commentPath: AstPath,
// Current options
options: object,
): Doc;
(optional) canAttachComment
function canAttachComment(node: AST, ancestors: T[]): boolean;
Diese Funktion entscheidet, ob ein Kommentar an einem bestimmten AST-Knoten angehängt werden kann. Standardmäßig werden alle AST-Eigenschaften durchlaufen, um nach Knoten zu suchen, an die Kommentare angehängt werden können. Diese Funktion verhindert, dass Kommentare an bestimmten Knoten angehängt werden. Eine typische Implementierung sieht wie folgt aus:
function canAttachComment(node, [parent]) {
return !(
node.type === "comment" ||
(parent?.type === "shorthand-property" &&
parent.key === node &&
parent.key !== parent.value)
);
}
Der zweite Parameter ancestors wurde erstmals in v3.7.0 hinzugefügt.
(optional) isBlockComment
function isBlockComment(node: AST): boolean;
Gibt zurück, ob der AST-Knoten ein Blockkommentar ist.
(optional) handleComments
Das handleComments-Objekt enthält drei optionale Funktionen, jede mit der Signatur:
(
// The AST node corresponding to the comment
comment: AST,
// The full source code text
text: string,
// The global options object
options: object,
// The AST
ast: AST,
// Whether this comment is the last comment
isLastComment: boolean,
) => boolean;
Diese Funktionen überschreiben Prettiers Standardalgorithmus zum Anhängen von Kommentaren. Es wird erwartet, dass ownLine/endOfLine/remaining entweder manuell einen Kommentar an einen Knoten anhängt und true zurückgibt, oder false zurückgibt und Prettier den Kommentar anhängen lässt.
Basierend auf dem Text um einen Kommentarknoten herum leitet Prettier zu:
-
ownLine, wenn ein Kommentar nur Leerzeichen davor und einen Zeilenumbruch danach hat, -
endOfLine, wenn ein Kommentar einen Zeilenumbruch danach hat, aber davor etwas anderes als Leerzeichen steht, -
remainingin allen anderen Fällen.
Zum Zeitpunkt der Zuordnung hat Prettier jeden AST-Kommentarknoten mit mindestens einer der Eigenschaften enclosingNode, precedingNode oder followingNode annotiert. Diese helfen beim Entscheidungsprozess des Plugins (der gesamte AST und Originaltext wird ebenfalls übergeben, um komplexere Entscheidungen zu ermöglichen).
Manuelles Anhängen eines Kommentars
Die Funktionen prettier.util.addTrailingComment/addLeadingComment/addDanglingComment können verwendet werden, um manuell einen Kommentar an einen AST-Knoten anzuhängen. Eine Beispiel-ownLine-Funktion, die sicherstellt, dass ein Kommentar nicht auf einen "Punktuations"-Knoten folgt (für Demonstrationszwecke erstellt), könnte so aussehen:
import * as prettier from "prettier";
function ownLine(comment, text, options, ast, isLastComment) {
const { precedingNode } = comment;
if (precedingNode && precedingNode.type === "punctuation") {
prettier.util.addTrailingComment(precedingNode, comment);
return true;
}
return false;
}
Knoten mit Kommentaren sollten eine comments-Eigenschaft mit einem Kommentararray enthalten. Jeder Kommentar sollte folgende Eigenschaften haben: leading, trailing, printed.
Das obige Beispiel verwendet prettier.util.addTrailingComment, das automatisch comment.leading/trailing/printed auf passende Werte setzt und den Kommentar zum comments-Array des AST-Knotens hinzufügt.
Die CLI-Option --debug-print-comments hilft bei der Fehlersuche von Kommentaranhängeproblemen. Sie gibt eine detaillierte Kommentarliste aus, die Klassifizierungsinformationen (ownLine/endOfLine/remaining, leading/trailing/dangling) und den angehängten Knoten enthält. Für integrierte Sprachen sind diese Informationen auch im Playground verfügbar (Kontrollkästchen 'Kommentare anzeigen' im Debug-Bereich).
options
options ist ein Objekt mit den benutzerdefinierten Optionen, die Ihr Plugin unterstützt.
Beispiel:
export default {
// ... plugin implementation
options: {
openingBraceNewLine: {
type: "boolean",
category: "Global",
default: true,
description: "Move open brace for code blocks onto new line.",
},
},
};
defaultOptions
Wenn Ihr Plugin abweichende Standardwerte für Prettier-Kernoptionen benötigt, können Sie diese in defaultOptions angeben:
export default {
// ... plugin implementation
defaultOptions: {
tabWidth: 4,
},
};
Hilfsfunktionen
prettier.util bietet den folgenden eingeschränkten Satz an Hilfsfunktionen für Plugins:
type Quote = '"' | "'";
type SkipOptions = { backwards?: boolean };
function getMaxContinuousCount(text: string, searchString: string): number;
function getStringWidth(text: string): number;
function getAlignmentSize(
text: string,
tabWidth: number,
startIndex?: number,
): number;
function getIndentSize(value: string, tabWidth: number): number;
function skip(
characters: string | RegExp,
): (
text: string,
startIndex: number | false,
options?: SkipOptions,
) => number | false;
function skipWhitespace(
text: string,
startIndex: number | false,
options?: SkipOptions,
): number | false;
function skipSpaces(
text: string,
startIndex: number | false,
options?: SkipOptions,
): number | false;
function skipToLineEnd(
text: string,
startIndex: number | false,
options?: SkipOptions,
): number | false;
function skipEverythingButNewLine(
text: string,
startIndex: number | false,
options?: SkipOptions,
): number | false;
function skipInlineComment(
text: string,
startIndex: number | false,
): number | false;
function skipTrailingComment(
text: string,
startIndex: number | false,
): number | false;
function skipNewline(
text: string,
startIndex: number | false,
options?: SkipOptions,
): number | false;
function hasNewline(
text: string,
startIndex: number,
options?: SkipOptions,
): boolean;
function hasNewlineInRange(
text: string,
startIndex: number,
startIndex: number,
): boolean;
function hasSpaces(
text: string,
startIndex: number,
options?: SkipOptions,
): boolean;
function getPreferredQuote(
text: string,
preferredQuoteOrPreferSingleQuote: Quote | boolean,
): Quote;
function makeString(
rawText: string,
enclosingQuote: Quote,
unescapeUnnecessaryEscapes?: boolean,
): string;
function getNextNonSpaceNonCommentCharacter(
text: string,
startIndex: number,
): string;
function getNextNonSpaceNonCommentCharacterIndex(
text: string,
startIndex: number,
): number | false;
function isNextLineEmpty(text: string, startIndex: number): boolean;
function isPreviousLineEmpty(text: string, startIndex: number): boolean;
Tutorials
- Wie man ein Plugin für Prettier schreibt: Zeigt, wie man ein sehr einfaches Prettier-Plugin für TOML erstellt.
Plugins testen
Da Plugins über relative Pfade aufgelöst werden können, können Sie während der Entwicklung folgendermaßen vorgehen:
import * as prettier from "prettier";
const code = "(add 1 2)";
await prettier.format(code, {
parser: "lisp",
plugins: ["./index.js"],
});
Dadurch wird ein Plugin relativ zum aktuellen Arbeitsverzeichnis aufgelöst.