Prettier is an opinionated code formatter that enforces consistent style across multiple languages including JavaScript, TypeScript, Flow, JSX, JSON, CSS, SCSS, Less, HTML, Vue, Angular, GraphQL, Markdown, and YAML.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Prettier provides extensive utility functions for text processing, AST navigation, position tracking, and comment manipulation. These utilities are essential for plugin development and advanced formatting scenarios.
import { util } from 'prettier';
// Access all utilities
const { getStringWidth, skipWhitespace, addLeadingComment } = util;function getStringWidth(text: string): numberGet the display width of a string, accounting for Unicode characters, emoji, and ANSI escape codes.
Example:
const width1 = util.getStringWidth('hello'); // 5
const width2 = util.getStringWidth('你好'); // 4 (CJK characters are width 2)
const width3 = util.getStringWidth('🎉'); // 2 (emoji width)function getMaxContinuousCount(text: string, searchString: string): numberCount the maximum continuous occurrences of a substring in text.
Example:
const maxQuotes = util.getMaxContinuousCount('"""hello"""', '"'); // 3
const maxSpaces = util.getMaxContinuousCount('a b c', ' '); // 4function getAlignmentSize(text: string, tabWidth: number, startIndex?: number): numberCalculate the alignment size for a text string, considering tabs and spaces.
Parameters:
text (string): Text to measuretabWidth (number): Width of tab charactersstartIndex (number, optional): Starting position (default: 0)Example:
const size1 = util.getAlignmentSize(' text', 2); // 2
const size2 = util.getAlignmentSize('\ttext', 4); // 4
const size3 = util.getAlignmentSize(' \t text', 4); // 5 (1 space + 4 tab - 1 for alignment)function getIndentSize(value: string, tabWidth: number): numberCalculate the indentation size of a string.
Example:
const indent1 = util.getIndentSize(' code', 2); // 4
const indent2 = util.getIndentSize('\t\tcode', 4); // 8
const indent3 = util.getIndentSize(' \t code', 4); // 5function makeString(
rawText: string,
enclosingQuote: "'" | '"',
unescapeUnnecessaryEscapes?: boolean
): stringCreate a properly quoted and escaped string literal.
Parameters:
rawText (string): Raw text contentenclosingQuote (Quote): Quote character to useunescapeUnnecessaryEscapes (boolean, optional): Remove unnecessary escapesExample:
const quoted1 = util.makeString('hello "world"', "'"); // "'hello \"world\"'"
const quoted2 = util.makeString("it's nice", '"'); // '"it\'s nice"'
const quoted3 = util.makeString('simple', '"'); // '"simple"'function getPreferredQuote(
text: string,
preferredQuoteOrPreferSingleQuote: "'" | '"' | boolean
): "'" | '"'Determine the preferred quote character for a string to minimize escaping.
Parameters:
text (string): Text content to quotepreferredQuoteOrPreferSingleQuote (Quote | boolean): Preference settingExample:
const quote1 = util.getPreferredQuote('hello "world"', "'"); // "'" (no escaping needed)
const quote2 = util.getPreferredQuote("it's nice", '"'); // '"' (no escaping needed)
const quote3 = util.getPreferredQuote('simple', true); // "'" (prefer single)All skip functions follow this pattern:
text and startIndex parametersfalse if pattern not foundbackwards directioninterface SkipOptions {
backwards?: boolean; // Skip in reverse direction
}const skipWhitespace: (
text: string,
startIndex: number | false,
options?: SkipOptions
) => number | falseSkip whitespace characters (spaces, tabs, newlines).
Example:
const text = 'code \n more';
const nextPos = util.skipWhitespace(text, 4); // 10 (after whitespace)
const prevPos = util.skipWhitespace(text, 10, { backwards: true }); // 4const skipSpaces: (
text: string,
startIndex: number | false,
options?: SkipOptions
) => number | falseSkip space characters only (not tabs or newlines).
Example:
const text = 'code \tmore';
const nextPos = util.skipSpaces(text, 4); // 7 (stops at tab)function skipNewline(
text: string,
startIndex: number | false,
options?: SkipOptions
): number | falseSkip a single newline character (LF, CRLF, or CR).
Example:
const text = 'line1\nline2';
const nextPos = util.skipNewline(text, 5); // 6 (after newline)const skipToLineEnd: (
text: string,
startIndex: number | false,
options?: SkipOptions
) => number | falseSkip to the end of the current line.
const skipEverythingButNewLine: (
text: string,
startIndex: number | false,
options?: SkipOptions
) => number | falseSkip all characters except newlines.
function skip(characters: string | RegExp): (
text: string,
startIndex: number | false,
options?: SkipOptions
) => number | falseCreate a custom skip function for specific characters or patterns.
Example:
const skipDigits = util.skip(/\d/);
const skipPunctuation = util.skip('.,;:!?');
const text = '123abc';
const afterDigits = skipDigits(text, 0); // 3
const text2 = '...text';
const afterPuncts = skipPunctuation(text2, 0); // 3function skipInlineComment(text: string, startIndex: number | false): number | falseSkip JavaScript-style inline comments (// comment).
Example:
const text = 'code // comment\nmore';
const afterComment = util.skipInlineComment(text, 5); // 15 (after comment)function skipTrailingComment(text: string, startIndex: number | false): number | falseSkip JavaScript-style block comments (/* comment */).
Example:
const text = 'code /* comment */ more';
const afterComment = util.skipTrailingComment(text, 5); // 18 (after comment)function hasNewline(text: string, startIndex: number, options?: SkipOptions): booleanCheck if there's a newline at the specified position.
Example:
const text = 'line1\nline2';
const hasNL = util.hasNewline(text, 5); // truefunction hasNewlineInRange(text: string, startIndex: number, endIndex: number): booleanCheck if there's a newline within a character range.
Example:
const text = 'no newline here';
const hasNL = util.hasNewlineInRange(text, 0, 10); // falsefunction hasSpaces(text: string, startIndex: number, options?: SkipOptions): booleanCheck if there are spaces at the specified position.
function getNextNonSpaceNonCommentCharacterIndex(text: string, startIndex: number): number | falseFind the index of the next significant character, skipping whitespace and comments.
Example:
const text = 'code /* comment */ more';
const nextChar = util.getNextNonSpaceNonCommentCharacterIndex(text, 4); // 20 ('m' in 'more')function getNextNonSpaceNonCommentCharacter(text: string, startIndex: number): stringGet the next significant character, skipping whitespace and comments.
Example:
const text = 'code /* comment */ more';
const nextChar = util.getNextNonSpaceNonCommentCharacter(text, 4); // 'm'function isNextLineEmpty(text: string, startIndex: number): booleanCheck if the next line after the given position is empty.
Example:
const text = 'line1\n\nline3';
const isEmpty = util.isNextLineEmpty(text, 5); // true (line after line1 is empty)function isPreviousLineEmpty(text: string, startIndex: number): booleanCheck if the previous line before the given position is empty.
Example:
const text = 'line1\n\nline3';
const isEmpty = util.isPreviousLineEmpty(text, 8); // true (line before line3 is empty)function addLeadingComment(node: any, comment: any): voidAdd a comment before an AST node.
Example:
const node = { type: 'Identifier', name: 'x' };
const comment = { type: 'Line', value: ' This is x' };
util.addLeadingComment(node, comment);
// node.leadingComments = [comment]function addTrailingComment(node: any, comment: any): voidAdd a comment after an AST node.
Example:
const node = { type: 'Identifier', name: 'x' };
const comment = { type: 'Line', value: ' End of x' };
util.addTrailingComment(node, comment);
// node.trailingComments = [comment]function addDanglingComment(node: any, comment: any, marker: any): voidAdd a comment inside an AST node (not leading or trailing).
Parameters:
node (any): AST node to add comment tocomment (any): Comment object to addmarker (any): Marker for comment placementExample:
const node = { type: 'ObjectExpression', properties: [] };
const comment = { type: 'Block', value: ' empty object ' };
util.addDanglingComment(node, comment, 'inside');
// node.comments = [comment]function processText(text, startIndex) {
// Skip initial whitespace
let pos = util.skipWhitespace(text, startIndex);
if (pos === false) return null;
// Skip comments
pos = util.skipInlineComment(text, pos) || pos;
pos = util.skipTrailingComment(text, pos) || pos;
// Get next significant character
const nextChar = util.getNextNonSpaceNonCommentCharacter(text, pos);
return {
position: pos,
character: nextChar,
width: util.getStringWidth(text.slice(startIndex, pos))
};
}function smartQuote(text, preferSingle = false) {
const preferred = preferSingle ? "'" : '"';
const optimal = util.getPreferredQuote(text, preferred);
return util.makeString(text, optimal, true);
}function findStatementEnd(text, startIndex) {
let pos = startIndex;
while (pos < text.length) {
// Skip whitespace and comments
pos = util.skipWhitespace(text, pos) || pos;
pos = util.skipInlineComment(text, pos) || pos;
pos = util.skipTrailingComment(text, pos) || pos;
// Check for statement terminator
if (text[pos] === ';' || util.hasNewline(text, pos)) {
return pos;
}
pos++;
}
return pos;
}function attachComments(ast, comments) {
for (const comment of comments) {
const beforeNode = findNodeBefore(ast, comment.start);
const afterNode = findNodeAfter(ast, comment.end);
if (beforeNode && isOnSameLine(beforeNode.end, comment.start)) {
util.addTrailingComment(beforeNode, comment);
} else if (afterNode && isOnSameLine(comment.end, afterNode.start)) {
util.addLeadingComment(afterNode, comment);
} else {
// Dangling comment
const parentNode = findParentNode(ast, comment);
if (parentNode) {
util.addDanglingComment(parentNode, comment, 'inside');
}
}
}
}function calculateIndentation(line, tabWidth) {
const trimmed = line.trimStart();
const indentText = line.slice(0, line.length - trimmed.length);
return {
size: util.getIndentSize(indentText, tabWidth),
alignment: util.getAlignmentSize(indentText, tabWidth),
text: indentText,
content: trimmed
};
}function isNextLineEmptyAfterIndex(text: string, startIndex: number): boolean⚠️ Deprecated: This function will be removed in v4. Use isNextLineEmpty instead.
Legacy function that checks if the next line after the given index is empty.
Example:
// Deprecated usage
const isEmpty = util.isNextLineEmptyAfterIndex(text, 5);
// Modern equivalent
const isEmpty = util.isNextLineEmpty(text, 5);false if startIndex is falseInstall with Tessl CLI
npx tessl i tessl/npm-prettier