diff --git a/.github/ISSUE_TEMPLATE/formatting.md b/.github/ISSUE_TEMPLATE/formatting.md index c790749e9..b41b177fb 100644 --- a/.github/ISSUE_TEMPLATE/formatting.md +++ b/.github/ISSUE_TEMPLATE/formatting.md @@ -26,7 +26,7 @@ Don't fill the form below manually! Let a program create a report for you: --> -**Prettier 2.8.8** +**Prettier 3.0.0** [Playground link](https://prettier.io/playground/#.....) ```sh diff --git a/.github/ISSUE_TEMPLATE/integration.md b/.github/ISSUE_TEMPLATE/integration.md index 7a0204a67..017763ba5 100644 --- a/.github/ISSUE_TEMPLATE/integration.md +++ b/.github/ISSUE_TEMPLATE/integration.md @@ -20,7 +20,7 @@ BEFORE SUBMITTING AN ISSUE: **Environments:** -- Prettier Version: 2.8.8 +- Prettier Version: 3.0.0 - Running Prettier via: - Runtime: - Operating System: diff --git a/CHANGELOG.md b/CHANGELOG.md index afab1bd36..ec495f72d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 3.0.0 + +[diff](https://github.com/prettier/prettier/compare/3.0.0-alpha.6...3.0.0) + +🔗 [Release Notes](https://prettier.io/blog/2023/07/05/3.0.0.html) + # 2.8.8 This version is a republished version of v2.8.7. diff --git a/docs/browser.md b/docs/browser.md index bb47b5271..f24045b4c 100644 --- a/docs/browser.md +++ b/docs/browser.md @@ -18,7 +18,7 @@ Required options: - **[`parser`](options.md#parser) (or [`filepath`](options.md#file-path))**: One of these options has to be specified for Prettier to know which parser to use. -- **`plugins`**: Unlike the `format` function from the [Node.js-based API](api.md#prettierformatsource--options), this function doesn’t load plugins automatically. The `plugins` option is required because all the parsers included in the Prettier package come as plugins (for reasons of file size). These plugins are files in +- **`plugins`**: Unlike the `format` function from the [Node.js-based API](api.md#prettierformatsource--options), this function doesn’t load plugins automatically. The `plugins` option is required because all the parsers included in the Prettier package come as plugins (for reasons of file size). These plugins are files in You need to load the ones that you’re going to use and pass them to `prettier.format` using the `plugins` option. @@ -29,8 +29,8 @@ See below for examples. ### Global ```html - - + + - + + ``` @@ -49,12 +48,12 @@ Note that the [`unpkg` field](https://unpkg.com/#examples) in Prettier’s `pack ```html ``` @@ -63,10 +62,10 @@ Note that the [`unpkg` field](https://unpkg.com/#examples) in Prettier’s `pack ```js define([ - "https://unpkg.com/prettier@2.8.8/standalone.js", - "https://unpkg.com/prettier@2.8.8/parser-graphql.js", -], (prettier, ...plugins) => { - prettier.format("type Query { hello: String }", { + "https://unpkg.com/prettier@3.0.0/standalone.js", + "https://unpkg.com/prettier@3.0.0/plugins/graphql.js", +], async (prettier, ...plugins) => { + const formatted = await prettier.format("type Query { hello: String }", { parser: "graphql", plugins, }); @@ -77,11 +76,13 @@ define([ ```js const prettier = require("prettier/standalone"); -const plugins = [require("prettier/parser-graphql")]; -prettier.format("type Query { hello: String }", { - parser: "graphql", - plugins, -}); +const plugins = [require("prettier/plugins/graphql")]; +(async () => { + const formatted = await prettier.format("type Query { hello: String }", { + parser: "graphql", + plugins, + }); +})(); ``` This syntax doesn’t necessarily work in the browser, but it can be used when bundling the code with browserify, Rollup, webpack, or another bundler. @@ -89,12 +90,14 @@ This syntax doesn’t necessarily work in the browser, but it can be used when b ### Worker ```js -importScripts("https://unpkg.com/prettier@2.8.8/standalone.js"); -importScripts("https://unpkg.com/prettier@2.8.8/parser-graphql.js"); -prettier.format("type Query { hello: String }", { - parser: "graphql", - plugins: prettierPlugins, -}); +importScripts("https://unpkg.com/prettier@3.0.0/standalone.js"); +importScripts("https://unpkg.com/prettier@3.0.0/plugins/graphql.js"); +(async () => { + const formatted = await prettier.format("type Query { hello: String }", { + parser: "graphql", + plugins: prettierPlugins, + }); +})(); ``` ## Parser plugins for embedded code @@ -103,13 +106,13 @@ If you want to format [embedded code](options.md#embedded-language-formatting), ```html ", + ]; + }; } - return false; } ``` +If the [`--embedded-language-formatting`](options.md#embedded-language-formatting) option is set to `off`, the embedding step is entirely skipped, `embed` isn’t called, and all nodes are printed with the `print` method. + #### (optional) `preprocess` -The preprocess function can process the AST from parser before passing into `print` function. +The `preprocess` method can process the AST from the parser before passing it into the `print` method. ```ts -function preprocess(ast: AST, options: object): AST; +function preprocess(ast: AST, options: Options): AST | Promise; +``` + +#### (optional) `getVisitorKeys` + +This property might come in handy if the plugin uses comment attachment or embedded languages. These features traverse the AST iterating through all the own enumerable properties of each node starting from the root. If the AST has [cycles](), such a traverse ends up in an infinite loop. Also, nodes might contain non-node objects (e.g., location data), iterating through which is a waste of resources. To solve these issues, the printer can define a function to return property names that should be traversed. + +Its signature is: + +```ts +function getVisitorKeys(node, nonTraversableKeys: Set): string[]; +``` + +The default `getVisitorKeys`: + +```js +function getVisitorKeys(node, nonTraversableKeys) { + return Object.keys(node).filter((key) => !nonTraversableKeys.has(key)); +} +``` + +The second argument `nonTraversableKeys` is a set of common keys and keys that prettier used internal. + +If you have full list of visitor keys + +```js +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] ?? []; +} +``` + +If you only need exclude a small set of keys + +```js +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` @@ -357,18 +421,18 @@ Returns whether or not the AST node is a block comment. The `handleComments` object contains three optional functions, each with signature ```ts -function( - // 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 +( + // 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; ``` These functions are used to override Prettier's default comment attachment algorithm. `ownLine`/`endOfLine`/`remaining` is expected to either manually attach a comment to a node and return `true`, or return `false` and let Prettier attach the comment. @@ -386,7 +450,7 @@ At the time of dispatching, Prettier will have annotated each AST comment node ( The `util.addTrailingComment`/`addLeadingComment`/`addDanglingComment` functions can be used to manually attach a comment to an AST node. An example `ownLine` function that ensures a comment does not follow a "punctuation" node (made up for demonstration purposes) might look like: ```js -const { util } = require("prettier"); +import { util } from "prettier"; function ownLine(comment, text, options, ast, isLastComment) { const { precedingNode } = comment; @@ -413,54 +477,137 @@ The `--debug-print-comments` CLI flag can help with debugging comment attachment Example: ```js -options: { - openingBraceNewLine: { - type: "boolean", - category: "Global", - default: true, - description: "Move open brace for code blocks onto new line." - } -} +export default { + // ... plugin implementation + 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 -} +```js +export default { + // ... plugin implementation + 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: - ```ts type Quote = '"' | "'"; type SkipOptions = { backwards?: boolean }; -function getMaxContinuousCount(str: string, target: string): number; + +function getMaxContinuousCount(text: string, searchString: string): number; + function getStringWidth(text: string): number; -function getAlignmentSize(value: string, tabWidth: number, startIndex?: number): number; + +function getAlignmentSize( + text: 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(text: string, node: N, locEnd: (node: N) => number): number | false; -function isNextLineEmptyAfterIndex(text: string, index: number): boolean; -function isNextLineEmpty(text: string, node: N, locEnd: (node: N) => number): boolean; -function isPreviousLineEmpty(text: string, node: N, locStart: (node: N) => number): boolean; + +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 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 @@ -472,9 +619,9 @@ function isPreviousLineEmpty(text: string, node: N, locStart: (node: N) => nu Since plugins can be resolved using relative paths, when working on one you can do: ```js -const prettier = require("prettier"); +import * as prettier from "prettier"; const code = "(add 1 2)"; -prettier.format(code, { +await prettier.format(code, { parser: "lisp", plugins: ["."], }); diff --git a/website/versioned_docs/version-stable/precommit.md b/website/versioned_docs/version-stable/precommit.md index ef04f80c0..a7507c552 100644 --- a/website/versioned_docs/version-stable/precommit.md +++ b/website/versioned_docs/version-stable/precommit.md @@ -43,6 +43,14 @@ yarn add --dev pretty-quick yarn husky set .husky/pre-commit "npx pretty-quick --staged" ``` + + +```bash +pnpm exec husky-init +pnpm add --save-dev pretty-quick +pnpm exec husky set .husky/pre-commit "npx pretty-quick --staged" +``` + Read more at the [pretty-quick](https://github.com/azz/pretty-quick) repo. @@ -113,6 +121,14 @@ yarn add --dev git-format-staged yarn husky set .husky/pre-commit "git-format-staged -f 'prettier --ignore-unknown --stdin --stdin-filepath \"{}\"' ." ``` + + +```bash +pnpm exec husky-init +pnpm add --save-dev git-format-staged +pnpm husky set .husky/pre-commit "git-format-staged -f 'prettier --ignore-unknown --stdin --stdin-filepath \"{}\"' ." +``` + Add or remove file extensions to suit your project. Note that regardless of which extensions you list formatting will respect any `.prettierignore` files in your project.