Plugins
Plugins are ways of adding new languages to Prettier. Prettier's own implementations of all languages are expressed using the plugin API. The core prettier
package contains JavaScript and other web-focused languages built in. For additional languages you'll need to install a plugin.
Using Plugins
Plugins are automatically loaded if you have them installed in the same node_modules
directory where prettier
is located. Plugin package names must start with @prettier/plugin-
or prettier-plugin-
or @<scope>/prettier-plugin-
to be registered.
<scope>
should be replaced by a name, read more about NPM scope.
When plugins cannot be found automatically, you can load them with:
The CLI, via the
--plugin
and--plugin-search-dir
:prettier --write main.foo --plugin-search-dir=./dir-with-plugins --plugin=./foo-plugin
Tip: You can set
--plugin
or--plugin-search-dir
options multiple times.Or the API, via the
plugins
andpluginSearchDirs
options:prettier.format("code", { parser: "foo", pluginSearchDirs: ["./dir-with-plugins"], plugins: ["./foo-plugin"], });
Prettier expects each of pluginSearchDirs
to contain node_modules
subdirectory, where @prettier/plugin-*
, @*/prettier-plugin-*
and prettier-plugin-*
will be searched. For instance, this can be your project directory or the location of global npm modules.
Providing at least one path to --plugin-search-dir
/pluginSearchDirs
turns off plugin autoloading in the default directory (i.e. node_modules
above prettier
binary).
Official Plugins
@prettier/plugin-php
@prettier/plugin-pug
by @Shinigami92@prettier/plugin-ruby
@prettier/plugin-swift
@prettier/plugin-xml
Community Plugins
prettier-plugin-apex
by @dangmaiprettier-plugin-elm
by @giCentreprettier-plugin-java
by @JHipsterprettier-plugin-kotlin
by @Angry-Potatoprettier-plugin-package
by @shellscapeprettier-plugin-packagejson
by @matzkohprettier-plugin-pg
by @benjieprettier-plugin-properties
by @eemeliprettier-plugin-solidity
by @mattiaerreprettier-plugin-svelte
by @UnwrittenFunprettier-plugin-toml
by @bd82prettier-plugin-organize-imports
by @simonhaenischprettier-plugin-pkg
by @JounQinprettier-plugin-sh
by @JounQin
Developing Plugins
Prettier plugins are regular JavaScript modules with five exports:
languages
parsers
printers
options
defaultOptions
languages
Languages is an array of language definitions that your plugin will contribute to Prettier. It can include all of the fields specified in prettier.getSupportInfo()
.
It must include name
and parsers
.
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
Parsers convert code as a string into an AST.
The key must match the name in the parsers
array from languages
. The value contains a parse function, an AST format name, and two location extraction functions (locStart
and locEnd
).
export const parsers = {
"dance-parse": {
parse,
// The name of the AST that
astFormat: "dance-ast",
hasPragma,
locStart,
locEnd,
preprocess,
},
};
The signature of the parse
function is:
function parse(text: string, parsers: object, options: object): AST;
The location extraction functions (locStart
and locEnd
) return the starting and ending locations of a given AST node:
function locStart(node: object): number;
(Optional) The pragma detection function (hasPragma
) should return if the text contains the pragma comment.
function hasPragma(text: string): boolean;
(Optional) The preprocess function can process the input text before passing into parse
function.
function preprocess(text: string, options: object): string;
printers
Printers convert ASTs into a Prettier intermediate representation, also known as a Doc.
The key must match the astFormat
that the parser produces. The value contains an object with a print
function and (optionally) an embed
function.
export const printers = {
"dance-ast": {
print,
embed,
insertPragma,
},
};
Printing is a recursive process of converting an AST node (represented by a path to that node) into a doc. The doc is constructed using the builder commands:
const { concat, join, line, ifBreak, group } = require("prettier").doc.builders;
The signature of the print
function is:
function print(
// Path to the AST node to print
path: FastPath,
options: object,
// Recursively print a child node
print: (path: FastPath) => Doc
): Doc;
Check out prettier-python's printer as an example.
Embedding refers to printing one language inside another. Examples of this are CSS-in-JS and Markdown code blocks. Plugins can switch to alternate languages using the embed
function. Its signature is:
function embed(
// Path to the current AST node
path: FastPath,
// Print a node with the current printer
print: (path: FastPath) => Doc,
// Parse and print some text using a different parser.
// You should set `options.parser` to specify which parser to use.
textToDoc: (text: string, options: object) => Doc,
// Current options
options: object
): Doc | null;
If an embed
function is included, it will be called first on every node. If the embed
function returns a falsy value, the current plugin's print
function is called. Otherwise, the Doc
returned by the embed
function is used. The embed
function is passed textToDoc
which can be called with a suitable options
argument to obtain the Doc
produced by a different plugin (which can then be directly returned or used inside a larger Doc
construction). For example, to print embedded JavaScript, a plugin might return textToDoc(javaScriptText, { parser: "babel" })
from the embed
function.
A plugin can implement how a pragma comment is inserted in the resulting code when the --insert-pragma
option is used, in the insertPragma
function. Its signature is:
function insertPragma(text: string): string;
(Optional) The preprocess function can process the ast from parser before passing into print
function.
function preprocess(ast: AST, options: object): AST;
options
options
is an object containing the custom options your plugin supports.
Example:
options: {
openingBraceNewLine: {
type: "boolean",
category: "Global",
default: true,
description: "Move open brace for code blocks onto new line."
}
}
defaultOptions
If your plugin requires different default values for some of Prettier's core options, you can specify them in defaultOptions
:
defaultOptions: {
tabWidth: 4
}
Utility functions
A util
module from Prettier core is considered a private API and is not meant to be consumed by plugins. Instead, the util-shared
module provides the following limited set of utility functions for plugins:
type Quote = '"' | "'";
type SkipOptions = { backwards?: boolean };
function getMaxContinuousCount(str: string, target: string): number;
function getStringWidth(text: string): number;
function getAlignmentSize(value: string, tabWidth: number, startIndex?: number): number;
function getIndentSize(value: string, tabWidth: number): number;
function skip(chars: string | RegExp): (text: string, index: number | false, opts?: SkipOptions) => number | false;
function skipWhitespace(text: string, index: number | false, opts?: SkipOptions): number | false;
function skipSpaces(text: string, index: number | false, opts?: SkipOptions): number | false;
function skipToLineEnd(text: string, index: number | false, opts?: SkipOptions): number | false;
function skipEverythingButNewLine(text: string, index: number | false, opts?: SkipOptions): number | false;
function skipInlineComment(text: string, index: number | false): number | false;
function skipTrailingComment(text: string, index: number | false): number | false;
function skipNewline(text: string, index: number | false, opts?: SkipOptions): number | false;
function hasNewline(text: string, index: number, opts?: SkipOptions): boolean;
function hasNewlineInRange(text: string, start: number, end: number): boolean;
function hasSpaces(text: string, index: number, opts?: SkipOptions): boolean;
function makeString(rawContent: string, enclosingQuote: Quote, unescapeUnnecessaryEscapes?: boolean): string;
function getNextNonSpaceNonCommentCharacterIndex<N>(text: string, node: N, locEnd: (node: N) => number): number | false;
function isNextLineEmptyAfterIndex(text: string, index: number): boolean;
function isNextLineEmpty<N>(text: string, node: N, locEnd: (node: N) => number): boolean;
function isPreviousLineEmpty<N>(text: string, node: N, locStart: (node: N) => number): boolean;
Tutorials
- How to write a plugin for Prettier: Teaches you how to write a very basic Prettier plugin for TOML.
Testing Plugins
Since plugins can be resolved using relative paths, when working on one you can do:
const prettier = require("prettier");
const code = "(add 1 2)";
prettier.format(code, {
parser: "lisp",
plugins: ["."],
});
This will resolve a plugin relative to the current working directory.