diff --git a/.editorconfig b/.editorconfig index d18a28ff4..3e5834d00 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,7 +10,6 @@ insert_final_newline = true [cspell.json] indent_size = 4 -insert_final_newline = false [{,website/blog/,changelog_unreleased/**/}*.md] trim_trailing_whitespace = false diff --git a/.prettierignore b/.prettierignore index 0eb4190df..283fe8f06 100644 --- a/.prettierignore +++ b/.prettierignore @@ -10,5 +10,4 @@ coverage/ /website/build/ /website/static/lib/ /website/static/playground.js -cspell.json .nyc_output diff --git a/changelog_unreleased/javascript/12017.md b/changelog_unreleased/javascript/12017.md new file mode 100644 index 000000000..726ce45c5 --- /dev/null +++ b/changelog_unreleased/javascript/12017.md @@ -0,0 +1,22 @@ +#### Preserve end-of-line comment for if statements without blocks (#12017 by @sosukesuzuki) + + +```jsx +// Input +if (condition1) expression1; // comment A +else if (condition2) expression2; // comment B +else expression3; // comment C + +// Prettier stable +if (condition1) expression1; +// comment A +else if (condition2) expression2; +// comment B +else expression3; // comment C + +// Prettier main +if (condition1) expression1; // comment A +else if (condition2) expression2; // comment B +else expression3; // comment C + +``` diff --git a/cspell.json b/cspell.json index f974801ba..f3e220f94 100644 --- a/cspell.json +++ b/cspell.json @@ -390,4 +390,4 @@ "website/static/lib/**", "website/static/playground.js" ] -} \ No newline at end of file +} diff --git a/docs/precommit.md b/docs/precommit.md index 955acb272..bacd0d286 100644 --- a/docs/precommit.md +++ b/docs/precommit.md @@ -61,7 +61,27 @@ Copy the following config into your `.pre-commit-config.yaml` file: Read more at [mirror of prettier package for pre-commit](https://github.com/pre-commit/mirrors-prettier) and the [pre-commit](https://pre-commit.com) website. -## Option 4. [git-format-staged](https://github.com/hallettj/git-format-staged) +## Option 4. [Husky.Net](https://github.com/alirezanet/Husky.Net) + +**Use Case:** A dotnet solution to use Prettier along with other code quality tools (e.g. dotnet-format, ESLint, Stylelint, etc.). It supports multiple file states (staged - last-commit, git-files etc.) + +```bash +dotnet tool install husky +dotnet husky install +dotnet husky add .husky/pre-commit +``` + +after installation you can add prettier task to the `task-runner.json`. + +```json +{ + "command": "npx", + "args": ["prettier", "--ignore-unknown", "--write", "${staged}"], + "pathMode": "absolute" +} +``` + +## Option 5. [git-format-staged](https://github.com/hallettj/git-format-staged) **Use Case:** Great for when you want to format partially-staged files, and other options do not provide a good fit for your project. @@ -97,7 +117,7 @@ Add or remove file extensions to suit your project. Note that regardless of whic To read about how git-format-staged works see [Automatic Code Formatting for Partially-Staged Files](https://www.olioapps.com/blog/automatic-code-formatting/). -## Option 5. Shell script +## Option 6. Shell script Alternately you can save this script as `.git/hooks/pre-commit` and give it execute permission: diff --git a/package.json b/package.json index a19b62d05..ca4f32456 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "dependencies": { "@angular/compiler": "12.2.15", "@babel/code-frame": "7.16.7", - "@babel/parser": "7.16.6", + "@babel/parser": "7.16.7", "@glimmer/syntax": "0.83.1", "@iarna/toml": "2.2.5", "@typescript-eslint/typescript-estree": "5.8.1", diff --git a/scripts/clean-cspell.mjs b/scripts/clean-cspell.mjs index a82f4b0e7..df9ec5015 100644 --- a/scripts/clean-cspell.mjs +++ b/scripts/clean-cspell.mjs @@ -5,7 +5,7 @@ import { execa } from "execa"; const CSPELL_CONFIG_FILE = new URL("../cspell.json", import.meta.url); const updateConfig = (config) => - fs.writeFile(CSPELL_CONFIG_FILE, JSON.stringify(config, undefined, 4)); + fs.writeFile(CSPELL_CONFIG_FILE, JSON.stringify(config, undefined, 4) + "\n"); const runSpellcheck = (options) => { const { yarnArgs, args, execaOptions } = { yarnArgs: [], diff --git a/src/language-js/comments.js b/src/language-js/comments.js index d1eb48b03..f908647ac 100644 --- a/src/language-js/comments.js +++ b/src/language-js/comments.js @@ -27,6 +27,7 @@ const { isObjectProperty, getComments, CommentCheckFlags, + markerForIfWithoutBlockAndSameLineComment, } = require("./utils.js"); const { locStart, locEnd } = require("./loc.js"); @@ -206,7 +207,24 @@ function handleIfStatementComments({ if (precedingNode.type === "BlockStatement") { addTrailingComment(precedingNode, comment); } else { - addDanglingComment(enclosingNode, comment); + const isSingleLineComment = + comment.type === "SingleLine" || + comment.loc.start.line === comment.loc.end.line; + const isSameLineComment = + comment.loc.start.line === precedingNode.loc.start.line; + if (isSingleLineComment && isSameLineComment) { + // example: + // if (cond1) expr1; // comment A + // else if (cond2) expr2; // comment A + // else expr3; + addDanglingComment( + precedingNode, + comment, + markerForIfWithoutBlockAndSameLineComment + ); + } else { + addDanglingComment(enclosingNode, comment); + } } return true; } diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index faf2aa6af..db10d65ac 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -28,6 +28,7 @@ const { hasIgnoreComment, isCallExpression, isMemberExpression, + markerForIfWithoutBlockAndSameLineComment, } = require("./utils.js"); const { locStart, locEnd } = require("./loc.js"); @@ -180,7 +181,7 @@ function printPathNoParens(path, options, print, args) { // Babel extension. case "EmptyStatement": return ""; - case "ExpressionStatement": + case "ExpressionStatement": { // Detect Flow and TypeScript directives if (node.directive) { return [printDirective(node.expression, options), semi]; @@ -200,11 +201,20 @@ function printPathNoParens(path, options, print, args) { } } + const danglingComment = printDanglingComments( + path, + options, + /** sameIndent */ true, + ({ marker }) => marker === markerForIfWithoutBlockAndSameLineComment + ); + // Do not append semicolon after the only JSX element in a program return [ print("expression"), isTheOnlyJsxElementInMarkdown(options, path) ? "" : semi, + danglingComment ? [" ", danglingComment] : "", ]; + } // Babel non-standard node. Used for Closure-style type casts. See postprocess.js. case "ParenthesizedExpression": { const shouldHug = diff --git a/src/language-js/utils.js b/src/language-js/utils.js index 75cd89dd0..693e52b80 100644 --- a/src/language-js/utils.js +++ b/src/language-js/utils.js @@ -1315,6 +1315,13 @@ function isEnabledHackPipeline(options) { return Boolean(options.__isUsingHackPipeline); } +/** + * This is used as a marker for dangling comments. + */ +const markerForIfWithoutBlockAndSameLineComment = Symbol( + "ifWithoutBlockAndSameLineComment" +); + module.exports = { getFunctionParameters, iterateFunctionParametersPath, @@ -1379,4 +1386,5 @@ module.exports = { hasComment, getComments, CommentCheckFlags, + markerForIfWithoutBlockAndSameLineComment, }; diff --git a/tests/format/js/if/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/if/__snapshots__/jsfmt.spec.js.snap index 819abc509..fc49b1e98 100644 --- a/tests/format/js/if/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/if/__snapshots__/jsfmt.spec.js.snap @@ -87,6 +87,59 @@ function f() { ================================================================================ `; +exports[`expr_and_same_line_comments.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "flow", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +if (a === 0) doSomething(); // comment A1 +else if (a === 1) doSomethingElse(); // comment B1 +else if (a === 2) doSomethingElse(); // comment C1 + +if (a === 0) doSomething(); /* comment A2 */ +else if (a === 1) doSomethingElse(); /* comment B2 */ +else if (a === 2) doSomethingElse(); /* comment C2 */ + +if (a === 0) expr; // comment A3 +else if (a === 1) expr; // comment B3 +else if (a === 2) expr; // comment C3 + +if (a === 0) expr; /* comment A4 */ +else if (a === 1) expr; /* comment B4 */ +else if (a === 2) expr; /* comment C4 */ + +if (a === 0) looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment A5 +else if (a === 1) looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment B5 +else if (a === 2) looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment C5 + +=====================================output===================================== +if (a === 0) doSomething(); // comment A1 +else if (a === 1) doSomethingElse(); // comment B1 +else if (a === 2) doSomethingElse(); // comment C1 + +if (a === 0) doSomething(); /* comment A2 */ +else if (a === 1) doSomethingElse(); /* comment B2 */ +else if (a === 2) doSomethingElse(); /* comment C2 */ + +if (a === 0) expr; // comment A3 +else if (a === 1) expr; // comment B3 +else if (a === 2) expr; // comment C3 + +if (a === 0) expr; /* comment A4 */ +else if (a === 1) expr; /* comment B4 */ +else if (a === 2) expr; /* comment C4 */ + +if (a === 0) + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment A5 +else if (a === 1) + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment B5 +else if (a === 2) + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment C5 + +================================================================================ +`; + exports[`if_comments.js format 1`] = ` ====================================options===================================== parsers: ["babel", "flow", "typescript"] diff --git a/tests/format/js/if/expr_and_same_line_comments.js b/tests/format/js/if/expr_and_same_line_comments.js new file mode 100644 index 000000000..9611becde --- /dev/null +++ b/tests/format/js/if/expr_and_same_line_comments.js @@ -0,0 +1,19 @@ +if (a === 0) doSomething(); // comment A1 +else if (a === 1) doSomethingElse(); // comment B1 +else if (a === 2) doSomethingElse(); // comment C1 + +if (a === 0) doSomething(); /* comment A2 */ +else if (a === 1) doSomethingElse(); /* comment B2 */ +else if (a === 2) doSomethingElse(); /* comment C2 */ + +if (a === 0) expr; // comment A3 +else if (a === 1) expr; // comment B3 +else if (a === 2) expr; // comment C3 + +if (a === 0) expr; /* comment A4 */ +else if (a === 1) expr; /* comment B4 */ +else if (a === 2) expr; /* comment C4 */ + +if (a === 0) looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment A5 +else if (a === 1) looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment B5 +else if (a === 2) looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment C5 diff --git a/tests/format/typescript/definite/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/definite/__snapshots__/jsfmt.spec.js.snap index c5a8c7dc2..0bc639f0d 100644 --- a/tests/format/typescript/definite/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/typescript/definite/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,21 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`asi.ts format 1`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +let x +!function() {}; + +=====================================output===================================== +let x; +!function () {}; + +================================================================================ +`; + exports[`definite.ts format 1`] = ` ====================================options===================================== parsers: ["typescript"] diff --git a/tests/format/typescript/definite/asi.ts b/tests/format/typescript/definite/asi.ts new file mode 100644 index 000000000..d17851234 --- /dev/null +++ b/tests/format/typescript/definite/asi.ts @@ -0,0 +1,2 @@ +let x +!function() {}; diff --git a/website/blog/2022-01-06-prettier-begins-paying-maintainers.md b/website/blog/2022-01-06-prettier-begins-paying-maintainers.md new file mode 100644 index 000000000..26d496aa3 --- /dev/null +++ b/website/blog/2022-01-06-prettier-begins-paying-maintainers.md @@ -0,0 +1,22 @@ +--- +author: "Christopher Chedeau (@vjeux)" +authorURL: "https://github.com/vjeux" +title: "Prettier begins paying maintainers" +--- + +Prettier, an opinionated code formatter for JavaScript and many web languages, has been accepting donations at [OpenCollective](https://opencollective.com/prettier) since 2019 and thanks to many generous donations we now have \$50,000 in the bank! As a result, we are now able to pay the two active maintainers \$1,500/month each. This funding will ensure that they will be able to continue consistent maintenance of Prettier into the future. + + + +

📣 Prettier is now accepting donations! 🎉 If you 'd like to support our work, head to our OpenCollective! 🙏https://t.co/etDqkYLm4H

— Prettier (@PrettierCode) May 24, 2019
+ +Open Source and Money has always been a tricky topic. We've tried many things with Prettier. Right after the project was open sourced, Facebook contracted [James Long](https://twitter.com/jlongster) for 2 weeks into order to polish it. Facebook also paid Christopher Chedeau ([vjeux](https://twitter.com/Vjeux)) for 9 months to work on it full time. Once there was \$10,000 in contributions, we gave everyone that contributed significantly the opportunity to redeem \$1,000 but due to the complexities in collecting the money, few people did. + +We now have \$50,000 which is a sizable sum but not enough for someone with the skills to work on it to do it full time. But at the same time we shouldn't let it sit unused. So after a lot of thinking, we decided to provide a recurring \$1,500 to the two current maintainers of the project. They have been amazing stewards for the project for the past 2 years! + +- [Fisker Cheung](https://github.com/fisker) +- [Sosuke Suzuki](https://github.com/sosukesuzuki) + +As you can imagine, maintaining a project that is [used by 60% of all JavaScript developers](https://twitter.com/vjeux/status/1349383134010200068) is not a simple task. We are planning to release Prettier 3.0, including a migration to ECMAScript Modules, support for formatting new language features in, and fixing new edge cases as they are identified. + +If Prettier has been useful for you or your company, there has never been a better time to [donate to Prettier](https://opencollective.com/prettier). We will now be much more able to use that money to make Prettier better! diff --git a/website/versioned_docs/version-stable/precommit.md b/website/versioned_docs/version-stable/precommit.md index a7254dbaf..06ca39090 100644 --- a/website/versioned_docs/version-stable/precommit.md +++ b/website/versioned_docs/version-stable/precommit.md @@ -62,7 +62,27 @@ Copy the following config into your `.pre-commit-config.yaml` file: Read more at [mirror of prettier package for pre-commit](https://github.com/pre-commit/mirrors-prettier) and the [pre-commit](https://pre-commit.com) website. -## Option 4. [git-format-staged](https://github.com/hallettj/git-format-staged) +## Option 4. [Husky.Net](https://github.com/alirezanet/Husky.Net) + +**Use Case:** A dotnet solution to use Prettier along with other code quality tools (e.g. dotnet-format, ESLint, Stylelint, etc.). It supports multiple file states (staged - last-commit, git-files etc.) + +```bash +dotnet tool install husky +dotnet husky install +dotnet husky add .husky/pre-commit +``` + +after installation you can add prettier task to the `task-runner.json`. + +```json +{ + "command": "npx", + "args": ["prettier", "--ignore-unknown", "--write", "${staged}"], + "pathMode": "absolute" +} +``` + +## Option 5. [git-format-staged](https://github.com/hallettj/git-format-staged) **Use Case:** Great for when you want to format partially-staged files, and other options do not provide a good fit for your project. @@ -98,7 +118,7 @@ Add or remove file extensions to suit your project. Note that regardless of whic To read about how git-format-staged works see [Automatic Code Formatting for Partially-Staged Files](https://www.olioapps.com/blog/automatic-code-formatting/). -## Option 5. Shell script +## Option 6. Shell script Alternately you can save this script as `.git/hooks/pre-commit` and give it execute permission: diff --git a/yarn.lock b/yarn.lock index 3078a271a..7bf1e73fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -286,12 +286,7 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@7.16.6": - version "7.16.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.6.tgz#8f194828193e8fa79166f34a4b4e52f3e769a314" - integrity sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ== - -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.7.2": +"@babel/parser@7.16.7", "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.7.2": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.7.tgz#d372dda9c89fcec340a82630a9f533f2fe15877e" integrity sha512-sR4eaSrnM7BV7QPzGfEX5paG/6wrZM3I0HDzfIAK06ESvo9oy3xBuVBxE3MbQaKNhvg8g/ixjMWo2CGpzpHsDA==