diff --git a/changelog_unreleased/flow/18629.md b/changelog_unreleased/flow/18629.md new file mode 100644 index 000000000..8309b8951 --- /dev/null +++ b/changelog_unreleased/flow/18629.md @@ -0,0 +1,21 @@ +#### Fix dangling comment print in components (#18629 by @fisker) + + +```jsx +// Input +component A( + // comment +) {} +component B(/* comment */) {} + +// Prettier stable +component A() // comment +{} +component B /* comment */() {} + +// Prettier main +component A( + // comment +) {} +component B(/* comment */) {} +``` diff --git a/src/language-js/comments/handle-comments.js b/src/language-js/comments/handle-comments.js index d9ef50790..62a773631 100644 --- a/src/language-js/comments/handle-comments.js +++ b/src/language-js/comments/handle-comments.js @@ -14,6 +14,7 @@ import getTextWithoutComments from "../utilities/get-text-without-comments.js"; import { createTypeCheckFunction, getCallArguments, + getComponentParameters, getFunctionParameters, isBinaryCastExpression, isCallExpression, @@ -645,6 +646,12 @@ function isInArgumentOrParameterParentheses(node, comment, options) { ); } +const isFlowComponent = createTypeCheckFunction([ + "ComponentDeclaration", + "DeclareComponent", + "ComponentTypeAnnotation", +]); + function handleCommentInEmptyParens({ comment, enclosingNode, options }) { if (!enclosingNode) { return false; @@ -659,6 +666,15 @@ function handleCommentInEmptyParens({ comment, enclosingNode, options }) { return true; } + if ( + isFlowComponent(enclosingNode) && + getComponentParameters(enclosingNode).length === 0 && + isInArgumentOrParameterParentheses(enclosingNode, comment, options) + ) { + addDanglingComment(enclosingNode, comment); + return true; + } + // Only add dangling comments to fix the case when no params are present, // i.e. a function without any argument. diff --git a/src/language-js/print/component.js b/src/language-js/print/component.js index 0c9c7178d..78f2e6987 100644 --- a/src/language-js/print/component.js +++ b/src/language-js/print/component.js @@ -6,11 +6,16 @@ import { line, softline, } from "../../document/index.js"; -import { printDanglingComments } from "../../main/comments/print.js"; -import getNextNonSpaceNonCommentCharacter from "../../utilities/get-next-non-space-non-comment-character.js"; -import { locEnd } from "../loc.js"; -import { isNextLineEmpty, shouldPrintComma } from "../utilities/index.js"; -import { printDeclareToken } from "./miscellaneous.js"; +import { + getComponentParameters, + isNextLineEmpty, + iterateComponentParametersPath, + shouldPrintComma, +} from "../utilities/index.js"; +import { + printDanglingCommentsInList, + printDeclareToken, +} from "./miscellaneous.js"; /** * @import AstPath from "../../common/ast-path.js" @@ -52,23 +57,10 @@ function printComponent(path, options, print) { function printComponentParameters(path, options, print) { const { node: componentNode } = path; - let parameters = componentNode.params; - if (componentNode.rest) { - parameters = [...parameters, componentNode.rest]; - } + const parameters = getComponentParameters(componentNode); if (parameters.length === 0) { - return [ - "(", - printDanglingComments(path, options, { - filter: (comment) => - getNextNonSpaceNonCommentCharacter( - options.originalText, - locEnd(comment), - ) === ")", - }), - ")", - ]; + return ["(", printDanglingCommentsInList(path, options), ")"]; } const printed = []; @@ -107,16 +99,6 @@ function hasRestParameter(componentNode, parameters) { return componentNode.rest || parameters.at(-1)?.type === "RestElement"; } -function iterateComponentParametersPath(path, iteratee) { - const { node } = path; - let index = 0; - const callback = (childPath) => iteratee(childPath, index++); - path.each(callback, "params"); - if (node.rest) { - path.call(callback, "rest"); - } -} - /* - `ComponentParameter` (Flow) */ diff --git a/src/language-js/utilities/index.js b/src/language-js/utilities/index.js index 9d8323872..cd517f9bc 100644 --- a/src/language-js/utilities/index.js +++ b/src/language-js/utilities/index.js @@ -849,6 +849,31 @@ function iterateFunctionParametersPath(path, iteratee) { } } +const componentParametersCache = new WeakMap(); +function getComponentParameters(node) { + if (componentParametersCache.has(node)) { + return componentParametersCache.get(node); + } + + const parameters = [...node.params]; + if (node.rest) { + parameters.push(node.rest); + } + + componentParametersCache.set(node, parameters); + return parameters; +} + +function iterateComponentParametersPath(path, iteratee) { + const { node } = path; + let index = 0; + const callback = () => iteratee(path, index++); + path.each(callback, "params"); + if (node.rest) { + path.call(callback, "rest"); + } +} + const callArgumentsCache = new WeakMap(); function getCallArguments(node) { if (callArgumentsCache.has(node)) { @@ -1119,6 +1144,7 @@ export { getCallArguments, getCallArgumentSelector, getComments, + getComponentParameters, getFunctionParameters, getLeftSide, getLeftSidePathName, @@ -1174,6 +1200,7 @@ export { isTypeAnnotationAFunction, isUnionType, iterateCallArgumentsPath, + iterateComponentParametersPath, iterateFunctionParametersPath, needsHardlineAfterDanglingComment, shouldFlatten, diff --git a/tests/format/flow/component/__snapshots__/format.test.js.snap b/tests/format/flow/component/__snapshots__/format.test.js.snap index 72c6abecb..46d3e88e3 100644 --- a/tests/format/flow/component/__snapshots__/format.test.js.snap +++ b/tests/format/flow/component/__snapshots__/format.test.js.snap @@ -317,6 +317,83 @@ function A( ================================================================================ `; +exports[`dangling-comments.js format 1`] = ` +====================================options===================================== +parsers: ["flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +component C11(// 11 +) {} +component C12( +// 12 +) {} +declare component C13(// 13 +) +declare component C14( +// 14 +) +type T11 = component(//15 +); +type T12 = component( +//16 +); + +component C21(/* 21 */ +) {} +component C22( +/* 22*/ +) {} +component C23( +/* 23*/){} +declare component C24(/* 24 */ +) +declare component C25( +/* 25*/ +) +declare component C26( +/* 26*/) +type T21 = component(/* 27*/ +); +type T22 = component( +/* 28*/ +); +type T23 = component( +/* 29*/); + +=====================================output===================================== +component C11( + // 11 +) {} +component C12( + // 12 +) {} +declare component C13( + // 13 +); +declare component C14( + // 14 +); +type T11 = component( + //15 +); +type T12 = component( + //16 +); + +component C21(/* 21 */) {} +component C22(/* 22*/) {} +component C23(/* 23*/) {} +declare component C24(/* 24 */); +declare component C25(/* 25*/); +declare component C26(/* 26*/); +type T21 = component(/* 27*/); +type T22 = component(/* 28*/); +type T23 = component(/* 29*/); + +================================================================================ +`; + exports[`declare-component.js format 1`] = ` ====================================options===================================== parsers: ["flow"] diff --git a/tests/format/flow/component/dangling-comments.js b/tests/format/flow/component/dangling-comments.js new file mode 100644 index 000000000..45681331c --- /dev/null +++ b/tests/format/flow/component/dangling-comments.js @@ -0,0 +1,37 @@ +component C11(// 11 +) {} +component C12( +// 12 +) {} +declare component C13(// 13 +) +declare component C14( +// 14 +) +type T11 = component(//15 +); +type T12 = component( +//16 +); + +component C21(/* 21 */ +) {} +component C22( +/* 22*/ +) {} +component C23( +/* 23*/){} +declare component C24(/* 24 */ +) +declare component C25( +/* 25*/ +) +declare component C26( +/* 26*/) +type T21 = component(/* 27*/ +); +type T22 = component( +/* 28*/ +); +type T23 = component( +/* 29*/);