From ae8dcc48f5d71317724e6a695c08622ce10ae454 Mon Sep 17 00:00:00 2001 From: Sam Zhou Date: Thu, 4 Apr 2024 07:10:00 -0700 Subject: [PATCH] Component syntax and related language features printing support (#16191) * Component syntax and related language features printing support Relevant Flow docs: - https://flow.org/en/docs/react/component-syntax/ - https://flow.org/en/docs/react/render-types/ * Update issue number * declare new test as unstable * fix tests --- changelog_unreleased/flow/16191.md | 23 + src/language-js/comments/handle-comments.js | 33 ++ src/language-js/needs-parens.js | 29 +- src/language-js/parse/flow.js | 2 + src/language-js/print/block.js | 2 + src/language-js/print/component.js | 142 ++++++ src/language-js/print/flow.js | 27 + src/language-js/print/function-parameters.js | 5 +- src/language-js/print/hook.js | 114 +++++ src/language-js/print/misc.js | 2 + src/language-js/print/module.js | 4 + src/language-js/print/type-annotation.js | 12 + .../traverse/visitor-keys.evaluate.js | 16 - tests/config/run-format-test.js | 2 + .../__snapshots__/jsfmt.spec.js.snap | 468 ++++++++++++++++++ .../flow/component/component-declaration.js | 66 +++ .../component/component-type-annotation.js | 50 ++ .../flow/component/declare-component.js | 45 ++ tests/format/flow/component/jsfmt.spec.js | 9 + .../hook/__snapshots__/jsfmt.spec.js.snap | 421 ++++++++++++++++ tests/format/flow/hook/declare-hook.js | 47 ++ tests/format/flow/hook/hook-declaration.js | 29 ++ .../format/flow/hook/hook-type-annotation.js | 75 +++ tests/format/flow/hook/jsfmt.spec.js | 9 + tests/unit/__snapshots__/visitor-keys.js.snap | 44 ++ 25 files changed, 1657 insertions(+), 19 deletions(-) create mode 100644 changelog_unreleased/flow/16191.md create mode 100644 src/language-js/print/component.js create mode 100644 src/language-js/print/hook.js create mode 100644 tests/format/flow/component/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/format/flow/component/component-declaration.js create mode 100644 tests/format/flow/component/component-type-annotation.js create mode 100644 tests/format/flow/component/declare-component.js create mode 100644 tests/format/flow/component/jsfmt.spec.js create mode 100644 tests/format/flow/hook/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/format/flow/hook/declare-hook.js create mode 100644 tests/format/flow/hook/hook-declaration.js create mode 100644 tests/format/flow/hook/hook-type-annotation.js create mode 100644 tests/format/flow/hook/jsfmt.spec.js diff --git a/changelog_unreleased/flow/16191.md b/changelog_unreleased/flow/16191.md new file mode 100644 index 000000000..f1c4291f1 --- /dev/null +++ b/changelog_unreleased/flow/16191.md @@ -0,0 +1,23 @@ +#### Component syntax printing support for Flow (#16191 by @SamChou19815) + + +```jsx +// Input +component MyComponent(a: string, b: number) renders SomeComponent { + return ; +} +hook useMyHook(a: string) { + return useState(a); +} + +// Prettier stable +// does not parse + +// Prettier main +component MyComponent(a: string, b: number) renders SomeComponent { + return ; +} +hook useMyHook(a: string) { + return useState(a); +} +``` diff --git a/src/language-js/comments/handle-comments.js b/src/language-js/comments/handle-comments.js index f47483b86..407f80b86 100644 --- a/src/language-js/comments/handle-comments.js +++ b/src/language-js/comments/handle-comments.js @@ -49,6 +49,7 @@ function handleOwnLineComment(context) { handleIgnoreComments, handleConditionalExpressionComments, handleLastFunctionArgComments, + handleLastComponentArgComments, handleMemberExpressionComments, handleIfStatementComments, handleWhileComments, @@ -600,6 +601,38 @@ function handleCommentInEmptyParens({ comment, enclosingNode, text }) { return false; } +function handleLastComponentArgComments({ + comment, + precedingNode, + enclosingNode, + followingNode, + text, +}) { + // "DeclareComponent" and "ComponentTypeAnnotation" definitions + if ( + precedingNode?.type === "ComponentTypeParameter" && + (enclosingNode?.type === "DeclareComponent" || + enclosingNode?.type === "ComponentTypeAnnotation") && + followingNode?.type !== "ComponentTypeParameter" + ) { + addTrailingComment(precedingNode, comment); + return true; + } + + // "ComponentParameter" definitions + if ( + (precedingNode?.type === "ComponentParameter" || + precedingNode?.type === "RestElement") && + enclosingNode?.type === "ComponentDeclaration" && + getNextNonSpaceNonCommentCharacter(text, locEnd(comment)) === ")" + ) { + addTrailingComment(precedingNode, comment); + return true; + } + + return false; +} + function handleLastFunctionArgComments({ comment, precedingNode, diff --git a/src/language-js/needs-parens.js b/src/language-js/needs-parens.js index c0074632b..0d9c5b52d 100644 --- a/src/language-js/needs-parens.js +++ b/src/language-js/needs-parens.js @@ -119,6 +119,8 @@ function needsParens(path, options) { case "using": case "yield": case "let": + case "component": + case "hook": case "type": { const ancestorNeitherAsNorSatisfies = path.findAncestor( (node) => !isBinaryCastExpression(node), @@ -601,6 +603,16 @@ function needsParens(path, options) { (key === "objectType" && parent.type === "TSIndexedAccessType") || (key === "elementType" && parent.type === "TSArrayType") ); + // Same as `TSTypeOperator`, but for Flow syntax + case "TypeOperator": + return ( + parent.type === "ArrayTypeAnnotation" || + parent.type === "NullableTypeAnnotation" || + (key === "objectType" && + (parent.type === "IndexedAccessType" || + parent.type === "OptionalIndexedAccessType")) || + parent.type === "TypeOperator" + ); // Same as `TSTypeQuery`, but for Flow syntax case "TypeofTypeAnnotation": return ( @@ -615,6 +627,7 @@ function needsParens(path, options) { case "IntersectionTypeAnnotation": case "UnionTypeAnnotation": return ( + parent.type === "TypeOperator" || parent.type === "ArrayTypeAnnotation" || parent.type === "NullableTypeAnnotation" || parent.type === "IntersectionTypeAnnotation" || @@ -632,7 +645,15 @@ function needsParens(path, options) { parent.type === "OptionalIndexedAccessType")) ); + case "ComponentTypeAnnotation": case "FunctionTypeAnnotation": { + if ( + node.type === "ComponentTypeAnnotation" && + (node.rendersType === null || node.rendersType === undefined) + ) { + return false; + } + if ( path.match( undefined, @@ -679,8 +700,8 @@ function needsParens(path, options) { (key === "checkType" && parent.type === "ConditionalTypeAnnotation") || (key === "extendsType" && parent.type === "ConditionalTypeAnnotation" && - node.returnType.type === "InferTypeAnnotation" && - node.returnType.typeParameter.bound) || + node.returnType?.type === "InferTypeAnnotation" && + node.returnType?.typeParameter.bound) || // We should check ancestor's parent to know whether the parentheses // are really needed, but since ??T doesn't make sense this check // will almost never be true. @@ -992,6 +1013,7 @@ function needsParens(path, options) { const isStatement = createTypeCheckFunction([ "BlockStatement", "BreakStatement", + "ComponentDeclaration", "ClassBody", "ClassDeclaration", "ClassMethod", @@ -1000,10 +1022,12 @@ const isStatement = createTypeCheckFunction([ "ClassPrivateProperty", "ContinueStatement", "DebuggerStatement", + "DeclareComponent", "DeclareClass", "DeclareExportAllDeclaration", "DeclareExportDeclaration", "DeclareFunction", + "DeclareHook", "DeclareInterface", "DeclareModule", "DeclareModuleExports", @@ -1020,6 +1044,7 @@ const isStatement = createTypeCheckFunction([ "ForOfStatement", "ForStatement", "FunctionDeclaration", + "HookDeclaration", "IfStatement", "ImportDeclaration", "InterfaceDeclaration", diff --git a/src/language-js/parse/flow.js b/src/language-js/parse/flow.js index 9c57f3a23..0664a00cf 100644 --- a/src/language-js/parse/flow.js +++ b/src/language-js/parse/flow.js @@ -12,6 +12,8 @@ const parseOptions = { // all_comments: true, // `comments` (boolean, default `true`) - attach comments to AST nodes (`leadingComments` and `trailingComments`) comments: false, + // `components` (boolean, default `false`) - enable parsing of Flow component syntax + components: true, // `enums` (boolean, default `false`) - enable parsing of Flow enums enums: true, // `esproposal_decorators` (boolean, default `false`) - enable parsing of decorators diff --git a/src/language-js/print/block.js b/src/language-js/print/block.js index f659c06e1..1652a87cc 100644 --- a/src/language-js/print/block.js +++ b/src/language-js/print/block.js @@ -40,6 +40,8 @@ function printBlock(path, options, print) { parent.type === "ArrowFunctionExpression" || parent.type === "FunctionExpression" || parent.type === "FunctionDeclaration" || + parent.type === "ComponentDeclaration" || + parent.type === "HookDeclaration" || parent.type === "ObjectMethod" || parent.type === "ClassMethod" || parent.type === "ClassPrivateMethod" || diff --git a/src/language-js/print/component.js b/src/language-js/print/component.js new file mode 100644 index 000000000..93232f8ae --- /dev/null +++ b/src/language-js/print/component.js @@ -0,0 +1,142 @@ +import { + group, + hardline, + ifBreak, + indent, + line, + softline, +} from "../../document/builders.js"; +import { printDanglingComments } from "../../main/comments/print.js"; +import getNextNonSpaceNonCommentCharacter from "../../utils/get-next-non-space-non-comment-character.js"; +import { locEnd } from "../loc.js"; +import { isNextLineEmpty, shouldPrintComma } from "../utils/index.js"; +import { printDeclareToken } from "./misc.js"; + +/** + * @typedef {import("../../common/ast-path.js").default} AstPath + * @typedef {import("../../document/builders.js").Doc} Doc + */ + +/* +- "ComponentDeclaration" +- "DeclareComponent" +- "ComponentTypeAnnotation" +*/ +function printComponent(path, options, print) { + const { node } = path; + + const parts = [printDeclareToken(path), "component"]; + if (node.id) { + parts.push(" ", print("id")); + } + + parts.push(print("typeParameters")); + + const parametersDoc = printComponentParameters(path, print, options); + if (node.rendersType) { + parts.push(group([parametersDoc, " ", print("rendersType")])); + } else { + parts.push(group([parametersDoc])); + } + + if (node.body) { + parts.push(" ", print("body")); + } + + if (options.semi && node.type === "DeclareComponent") { + parts.push(";"); + } + + return parts; +} + +function printComponentParameters(path, print, options) { + const { node: componentNode } = path; + let parameters = componentNode.params; + if (componentNode.rest) { + parameters = [...parameters, componentNode.rest]; + } + + if (parameters.length === 0) { + return [ + "(", + printDanglingComments(path, options, { + filter: (comment) => + getNextNonSpaceNonCommentCharacter( + options.originalText, + locEnd(comment), + ) === ")", + }), + ")", + ]; + } + + const printed = []; + iterateComponentParametersPath(path, (parameterPath, index) => { + const isLastParameter = index === parameters.length - 1; + if (isLastParameter && componentNode.rest) { + printed.push("..."); + } + printed.push(print()); + if (isLastParameter) { + return; + } + printed.push(","); + if (isNextLineEmpty(parameters[index], options)) { + printed.push(hardline, hardline); + } else { + printed.push(line); + } + }); + + return [ + "(", + indent([softline, ...printed]), + ifBreak( + shouldPrintComma(options, "all") && + !hasRestParameter(componentNode, parameters) + ? "," + : "", + ), + softline, + ")", + ]; +} + +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"); + } +} + +function printComponentParameter(path, options, print) { + const { node } = path; + if (node.shorthand) { + return print("local"); + } + + return [print("name"), " as ", print("local")]; +} + +function printComponentTypeParameter(path, options, print) { + const { node } = path; + + const printed = []; + if (node.name) { + printed.push(print("name"), node.optional ? "?: " : ": "); + } + + printed.push(print("typeAnnotation")); + + return printed; +} + +export { printComponent, printComponentParameter, printComponentTypeParameter }; diff --git a/src/language-js/print/flow.js b/src/language-js/print/flow.js index 18ef2e714..043569138 100644 --- a/src/language-js/print/flow.js +++ b/src/language-js/print/flow.js @@ -10,11 +10,21 @@ import isFlowKeywordType from "../utils/is-flow-keyword-type.js"; import { printArray } from "./array.js"; import { printBinaryCastExpression } from "./cast-expression.js"; import { printClass } from "./class.js"; +import { + printComponent, + printComponentParameter, + printComponentTypeParameter, +} from "./component.js"; import { printEnumBody, printEnumDeclaration, printEnumMember, } from "./enum.js"; +import { + printDeclareHook, + printHook, + printHookTypeAnnotation, +} from "./hook.js"; import { printInterface } from "./interface.js"; import { printBigInt } from "./literal.js"; import { printFlowMappedTypeProperty } from "./mapped-type.js"; @@ -56,6 +66,20 @@ function printFlow(path, options, print) { const semi = options.semi ? ";" : ""; switch (node.type) { + case "ComponentDeclaration": + case "DeclareComponent": + case "ComponentTypeAnnotation": + return printComponent(path, options, print); + case "ComponentParameter": + return printComponentParameter(path, options, print); + case "ComponentTypeParameter": + return printComponentTypeParameter(path, options, print); + case "HookDeclaration": + return printHook(path, options, print); + case "DeclareHook": + return printDeclareHook(path, options, print); + case "HookTypeAnnotation": + return printHookTypeAnnotation(path, options, print); case "DeclareClass": return printClass(path, options, print); case "DeclareFunction": @@ -256,6 +280,9 @@ function printFlow(path, options, print) { case "TypePredicate": return printTypePredicate(path, print); + case "TypeOperator": + return [node.operator, " ", print("typeAnnotation")]; + case "TypeParameterDeclaration": case "TypeParameterInstantiation": return printTypeParameters(path, options, print, "params"); diff --git a/src/language-js/print/function-parameters.js b/src/language-js/print/function-parameters.js index fcdef5cde..420bac78a 100644 --- a/src/language-js/print/function-parameters.js +++ b/src/language-js/print/function-parameters.js @@ -137,7 +137,10 @@ function printFunctionParameters( !functionNode.rest; if (isFlowShorthandWithOneArg) { - if (options.arrowParens === "always") { + if ( + options.arrowParens === "always" || + functionNode.type === "HookTypeAnnotation" + ) { return ["(", ...printed, ")"]; } return printed; diff --git a/src/language-js/print/hook.js b/src/language-js/print/hook.js new file mode 100644 index 000000000..1e4f55d82 --- /dev/null +++ b/src/language-js/print/hook.js @@ -0,0 +1,114 @@ +import { group } from "../../document/builders.js"; +import { printReturnType } from "./function.js"; +import { + printFunctionParameters, + shouldGroupFunctionParameters, +} from "./function-parameters.js"; +import { printDeclareToken } from "./misc.js"; + +/** + * @typedef {import("../../common/ast-path.js").default} AstPath + * @typedef {import("../../document/builders.js").Doc} Doc + */ + +/* +- "HookDeclaration" +*/ +function printHook(path, options, print) { + const { node } = path; + + /** @type {Array} */ + const parts = ["hook"]; + if (node.id) { + parts.push(" ", print("id")); + } + + const parametersDoc = printFunctionParameters( + path, + print, + options, + false, + true, + ); + const returnTypeDoc = printReturnType(path, print); + const shouldGroupParameters = shouldGroupFunctionParameters( + node, + returnTypeDoc, + ); + + parts.push( + group([ + shouldGroupParameters ? group(parametersDoc) : parametersDoc, + returnTypeDoc, + ]), + node.body ? " " : "", + print("body"), + ); + + return parts; +} + +/* +- "DeclareHook" +*/ +function printDeclareHook(path, options, print) { + const { node } = path; + + const parts = [printDeclareToken(path), "hook"]; + if (node.id) { + parts.push(" ", print("id")); + } + + if (options.semi) { + parts.push(";"); + } + + return parts; +} + +/* +`HookTypeAnnotation` is ambiguous: +- `declare hook useFoo(a: B): void;` +- `var A: hook (a: B) => void;` +*/ +function isDeclareHookTypeAnnotation(path) { + const { node } = path; + return ( + node.type === "HookTypeAnnotation" && + path.getParentNode(2)?.type === "DeclareHook" + ); +} + +/* +- "HookTypeAnnotation" +*/ +function printHookTypeAnnotation(path, options, print) { + const { node } = path; + const parts = []; + + parts.push(isDeclareHookTypeAnnotation(path) ? "" : "hook "); + + let parametersDoc = printFunctionParameters( + path, + print, + options, + /* expandArg */ false, + /* printTypeParams */ true, + ); + + const returnTypeDoc = []; + returnTypeDoc.push( + isDeclareHookTypeAnnotation(path) ? ": " : " => ", + print("returnType"), + ); + + if (shouldGroupFunctionParameters(node, returnTypeDoc)) { + parametersDoc = group(parametersDoc); + } + + parts.push(parametersDoc, returnTypeDoc); + + return group(parts); +} + +export { printDeclareHook, printHook, printHookTypeAnnotation }; diff --git a/src/language-js/print/misc.js b/src/language-js/print/misc.js index e651233af..0ca691331 100644 --- a/src/language-js/print/misc.js +++ b/src/language-js/print/misc.js @@ -48,7 +48,9 @@ function printDefiniteToken(path) { const flowDeclareNodeTypes = new Set([ "DeclareClass", + "DeclareComponent", "DeclareFunction", + "DeclareHook", "DeclareVariable", "DeclareExportDeclaration", "DeclareExportAllDeclaration", diff --git a/src/language-js/print/module.js b/src/language-js/print/module.js index 5a0c57034..d5495106e 100644 --- a/src/language-js/print/module.js +++ b/src/language-js/print/module.js @@ -105,10 +105,14 @@ function printExportDeclaration(path, options, print) { const shouldOmitSemicolon = createTypeCheckFunction([ "ClassDeclaration", + "ComponentDeclaration", "FunctionDeclaration", "TSInterfaceDeclaration", "DeclareClass", + "DeclareComponent", "DeclareFunction", + "DeclareHook", + "HookDeclaration", "TSDeclareFunction", "EnumDeclaration", ]); diff --git a/src/language-js/print/type-annotation.js b/src/language-js/print/type-annotation.js index a80b3d8ae..e38530bf9 100644 --- a/src/language-js/print/type-annotation.js +++ b/src/language-js/print/type-annotation.js @@ -473,6 +473,18 @@ const getTypeAnnotationFirstToken = (path) => { ) || /* Flow + ```js + declare hook foo(): void; + ^^^^^^^^ `TypeAnnotation` + ``` + */ + path.match( + (node) => node.type === "TypeAnnotation", + (node, key) => key === "typeAnnotation" && node.type === "Identifier", + (node, key) => key === "id" && node.type === "DeclareHook", + ) || + /* + Flow ```js type A = () => infer R extends string; diff --git a/src/language-js/traverse/visitor-keys.evaluate.js b/src/language-js/traverse/visitor-keys.evaluate.js index cafbccf73..9e074605c 100644 --- a/src/language-js/traverse/visitor-keys.evaluate.js +++ b/src/language-js/traverse/visitor-keys.evaluate.js @@ -85,20 +85,4 @@ const visitorKeys = Object.fromEntries( ]), ); -for (const type of [ - // Flow, Not supported yet - "ComponentDeclaration", - "ComponentParameter", - "ComponentTypeAnnotation", - "ComponentTypeParameter", - "DeclareComponent", - - // Flow, Not supported yet - "HookDeclaration", - "HookTypeAnnotation", - "DeclareHook", -]) { - delete visitorKeys[type]; -} - export default visitorKeys; diff --git a/tests/config/run-format-test.js b/tests/config/run-format-test.js index dd38d24f1..7e2acf881 100644 --- a/tests/config/run-format-test.js +++ b/tests/config/run-format-test.js @@ -36,6 +36,8 @@ const unstableTests = new Map( ], ["js/no-semi/comments.js", (options) => options.semi === false], ["flow/no-semi/comments.js", (options) => options.semi === false], + "flow/hook/declare-hook.js", + "flow/hook/hook-type-annotation.js", "typescript/prettier-ignore/mapped-types.ts", "typescript/prettier-ignore/issue-14238.ts", "js/comments/html-like/comment.js", diff --git a/tests/format/flow/component/__snapshots__/jsfmt.spec.js.snap b/tests/format/flow/component/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..f8aa8720d --- /dev/null +++ b/tests/format/flow/component/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,468 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`component-declaration.js [babel-flow] format 1`] = ` +"Unexpected token, expected "{" (15:8) + 13 | } + 14 | +> 15 | export component MyComponent() {} + | ^ + 16 | + 17 | export default component MyComponent() {} + 18 | +Cause: Unexpected token, expected "{" (15:7)" +`; + +exports[`component-declaration.js format 1`] = ` +====================================options===================================== +parsers: ["flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +component MyComponent() {} + +component MyComponent() renders SomeComponent {} + +component MyComponent() renders React.Element {} + +component MyComponent() { + return ; +} + +component MyComponent(a: string, b: number) renders SomeComponent { + return ; +} + +export component MyComponent() {} + +export default component MyComponent() {} + +component MyComponent() {} + +component MyComponent() {} + +component MyComponent(bar: string) {} + +component MyComponent(bar?: string) {} + +component MyComponent(bar: string = '') {} + +component MyComponent(propBar as bar: string) {} + +component MyComponent(propBar as [bar]: $ReadOnlyArray) {} + +component MyComponent(propBar as {bar}: $ReadOnly<{bar: string}>) {} + +component MyComponent(propBar as {bar, reallllllllllllllllllllllllllllllllllllllyLong}: $ReadOnly<{bar: string, reallllllllllllllllllllllllllllllllllllllyLong: string}>) {} + +component MyComponent('data-bar' as bar: string) {} + +component MyComponent(...restProps: $ReadOnly<{k: string}>) {} + +component MyComponent(bar: string, baz: $ReadOnly<{k: string}>) {} + +component MyComponent(bar: string, baz: $ReadOnly<{k: string}>, realllllllllllllllllllyLong: string) {} + +component MyComponent(bar: string, baz: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}>) {} + +component MyComponent(bar: string, 'data-baz' as baz: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}>) {} + +// Attached comment +component MyComponent( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + 'data-baz' as baz: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +) {} + +component MyComponent( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +) {} + +component MyComponent() /* Trailing comment */ {} + +=====================================output===================================== +component MyComponent() {} + +component MyComponent() renders SomeComponent {} + +component MyComponent() renders React.Element< + typeof SomeComponentLonnnnnnnnnnnnnnnnnnnnnnnnnnnnng, +> {} + +component MyComponent() { + return ; +} + +component MyComponent(a: string, b: number) renders SomeComponent { + return ; +} + +export component MyComponent() {} + +export default component MyComponent() {} + +component MyComponent() {} + +component MyComponent< + T: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo, +>() {} + +component MyComponent(bar: string) {} + +component MyComponent(bar?: string) {} + +component MyComponent(bar: string = "") {} + +component MyComponent(propBar as bar: string) {} + +component MyComponent(propBar as [bar]: $ReadOnlyArray) {} + +component MyComponent(propBar as { bar }: $ReadOnly<{ bar: string }>) {} + +component MyComponent( + propBar as { + bar, + reallllllllllllllllllllllllllllllllllllllyLong, + }: $ReadOnly<{ + bar: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }>, +) {} + +component MyComponent("data-bar" as bar: string) {} + +component MyComponent(...restProps: $ReadOnly<{ k: string }>) {} + +component MyComponent(bar: string, baz: $ReadOnly<{ k: string }>) {} + +component MyComponent( + bar: string, + baz: $ReadOnly<{ k: string }>, + realllllllllllllllllllyLong: string, +) {} + +component MyComponent( + bar: string, + baz: $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }>, +) {} + +component MyComponent( + bar: string, + "data-baz" as baz: $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }>, +) {} + +// Attached comment +component MyComponent( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + "data-baz" as baz: $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }>, + // Trailing comment +) {} + +component MyComponent( + ...props: $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }> + // Trailing comment +) {} + +component MyComponent() /* Trailing comment */ {} + +================================================================================ +`; + +exports[`component-type-annotation.js [babel-flow] format 1`] = ` +"Unexpected token (1:20) +> 1 | type T = component(); + | ^ + 2 | + 3 | type T = component() renders SomeComponent; + 4 | +Cause: Unexpected token (1:19)" +`; + +exports[`component-type-annotation.js format 1`] = ` +====================================options===================================== +parsers: ["flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +type T = component(); + +type T = component() renders SomeComponent; + +type T = component() renders React.Element; + +type T = component(); + +type T = component(); + +type T = component(bar: string); + +type T = component(bar?: string); + +type T = component('data-bar': string); + +type T = component(...restProps: $ReadOnly<{k: string}>); + +type T = component(...$ReadOnly<{k: string}>); + +type T = component(bar: string, baz: $ReadOnly<{k: string}>); + +type T = component(bar: string, baz: $ReadOnly<{k: string}>, realllllllllllllllllllyLong: string); + +// Attached comment +type T = component( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + 'data-baz': $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +); + +type T = component( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +); + +type T = component( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +) renders SomeComponent; + +type T = component(bar: string) | component(baz: $ReadOnly<{k: string}>) | component(realllllllllllllllllllyLong: string, reallllllllllllllllllllllllllllllllllllllyLong: string); + + +function A(realllllllllllllllllllyLong: string, reallllllllllllllllllllllllllllllllllllllyLong: string): component(realllllllllllllllllllyLong: string, reallllllllllllllllllllllllllllllllllllllyLong: string) {} +=====================================output===================================== +type T = component(); + +type T = component() renders SomeComponent; + +type T = component() renders React.Element< + typeof SomeComponentLonnnnnnnnnnnnnnnnnnnnnnnnnnnnng, +>; + +type T = component(); + +type T = component< + T: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo, +>(); + +type T = component(bar: string); + +type T = component(bar?: string); + +type T = component("data-bar": string); + +type T = component(...restProps: $ReadOnly<{ k: string }>); + +type T = component(...$ReadOnly<{ k: string }>); + +type T = component(bar: string, baz: $ReadOnly<{ k: string }>); + +type T = component( + bar: string, + baz: $ReadOnly<{ k: string }>, + realllllllllllllllllllyLong: string, +); + +// Attached comment +type T = component( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + "data-baz": $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }>, + // Trailing comment +); + +type T = component( + ...props: $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }> + // Trailing comment +); + +type T = component( + ...props: $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }> + // Trailing comment +) renders SomeComponent; + +type T = + | component(bar: string) + | component(baz: $ReadOnly<{ k: string }>) + | component( + realllllllllllllllllllyLong: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + ); + +function A( + realllllllllllllllllllyLong: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, +): component( + realllllllllllllllllllyLong: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, +) {} + +================================================================================ +`; + +exports[`declare-component.js [babel-flow] format 1`] = ` +"Unexpected token (1:9) +> 1 | declare component MyComponent(); + | ^ + 2 | + 3 | declare component MyComponent() renders SomeComponent; + 4 | +Cause: Unexpected token (1:8)" +`; + +exports[`declare-component.js format 1`] = ` +====================================options===================================== +parsers: ["flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +declare component MyComponent(); + +declare component MyComponent() renders SomeComponent; + +declare component MyComponent() renders React.Element; + +declare component MyComponent(); + +declare component MyComponent(); + +declare component MyComponent(bar: string); + +declare component MyComponent(bar?: string); + +declare component MyComponent('data-bar': string); + +declare component MyComponent(...restProps: $ReadOnly<{k: string}>); + +declare component MyComponent(...$ReadOnly<{k: string}>); + +declare component MyComponent(bar: string, baz: $ReadOnly<{k: string}>); + +declare component MyComponent(bar: string, baz: $ReadOnly<{k: string}>, realllllllllllllllllllyLong: string); + +// Attached comment +declare component MyComponent( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + 'data-baz': $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +); + +declare component MyComponent( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +); + +declare component MyComponent( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +) renders SomeComponent; +=====================================output===================================== +declare component MyComponent(); + +declare component MyComponent() renders SomeComponent; + +declare component MyComponent() renders React.Element< + typeof SomeComponentLonnnnnnnnnnnnnnnnnnnnnnnnnnnnng, +>; + +declare component MyComponent(); + +declare component MyComponent< + T: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo, +>(); + +declare component MyComponent(bar: string); + +declare component MyComponent(bar?: string); + +declare component MyComponent("data-bar": string); + +declare component MyComponent(...restProps: $ReadOnly<{ k: string }>); + +declare component MyComponent(...$ReadOnly<{ k: string }>); + +declare component MyComponent(bar: string, baz: $ReadOnly<{ k: string }>); + +declare component MyComponent( + bar: string, + baz: $ReadOnly<{ k: string }>, + realllllllllllllllllllyLong: string, +); + +// Attached comment +declare component MyComponent( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + "data-baz": $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }>, + // Trailing comment +); + +declare component MyComponent( + ...props: $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }> + // Trailing comment +); + +declare component MyComponent( + ...props: $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }> + // Trailing comment +) renders SomeComponent; + +================================================================================ +`; diff --git a/tests/format/flow/component/component-declaration.js b/tests/format/flow/component/component-declaration.js new file mode 100644 index 000000000..29c45e02b --- /dev/null +++ b/tests/format/flow/component/component-declaration.js @@ -0,0 +1,66 @@ +component MyComponent() {} + +component MyComponent() renders SomeComponent {} + +component MyComponent() renders React.Element {} + +component MyComponent() { + return ; +} + +component MyComponent(a: string, b: number) renders SomeComponent { + return ; +} + +export component MyComponent() {} + +export default component MyComponent() {} + +component MyComponent() {} + +component MyComponent() {} + +component MyComponent(bar: string) {} + +component MyComponent(bar?: string) {} + +component MyComponent(bar: string = '') {} + +component MyComponent(propBar as bar: string) {} + +component MyComponent(propBar as [bar]: $ReadOnlyArray) {} + +component MyComponent(propBar as {bar}: $ReadOnly<{bar: string}>) {} + +component MyComponent(propBar as {bar, reallllllllllllllllllllllllllllllllllllllyLong}: $ReadOnly<{bar: string, reallllllllllllllllllllllllllllllllllllllyLong: string}>) {} + +component MyComponent('data-bar' as bar: string) {} + +component MyComponent(...restProps: $ReadOnly<{k: string}>) {} + +component MyComponent(bar: string, baz: $ReadOnly<{k: string}>) {} + +component MyComponent(bar: string, baz: $ReadOnly<{k: string}>, realllllllllllllllllllyLong: string) {} + +component MyComponent(bar: string, baz: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}>) {} + +component MyComponent(bar: string, 'data-baz' as baz: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}>) {} + +// Attached comment +component MyComponent( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + 'data-baz' as baz: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +) {} + +component MyComponent( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +) {} + +component MyComponent() /* Trailing comment */ {} diff --git a/tests/format/flow/component/component-type-annotation.js b/tests/format/flow/component/component-type-annotation.js new file mode 100644 index 000000000..6f0723b3b --- /dev/null +++ b/tests/format/flow/component/component-type-annotation.js @@ -0,0 +1,50 @@ +type T = component(); + +type T = component() renders SomeComponent; + +type T = component() renders React.Element; + +type T = component(); + +type T = component(); + +type T = component(bar: string); + +type T = component(bar?: string); + +type T = component('data-bar': string); + +type T = component(...restProps: $ReadOnly<{k: string}>); + +type T = component(...$ReadOnly<{k: string}>); + +type T = component(bar: string, baz: $ReadOnly<{k: string}>); + +type T = component(bar: string, baz: $ReadOnly<{k: string}>, realllllllllllllllllllyLong: string); + +// Attached comment +type T = component( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + 'data-baz': $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +); + +type T = component( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +); + +type T = component( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +) renders SomeComponent; + +type T = component(bar: string) | component(baz: $ReadOnly<{k: string}>) | component(realllllllllllllllllllyLong: string, reallllllllllllllllllllllllllllllllllllllyLong: string); + + +function A(realllllllllllllllllllyLong: string, reallllllllllllllllllllllllllllllllllllllyLong: string): component(realllllllllllllllllllyLong: string, reallllllllllllllllllllllllllllllllllllllyLong: string) {} \ No newline at end of file diff --git a/tests/format/flow/component/declare-component.js b/tests/format/flow/component/declare-component.js new file mode 100644 index 000000000..49d2bb3bb --- /dev/null +++ b/tests/format/flow/component/declare-component.js @@ -0,0 +1,45 @@ +declare component MyComponent(); + +declare component MyComponent() renders SomeComponent; + +declare component MyComponent() renders React.Element; + +declare component MyComponent(); + +declare component MyComponent(); + +declare component MyComponent(bar: string); + +declare component MyComponent(bar?: string); + +declare component MyComponent('data-bar': string); + +declare component MyComponent(...restProps: $ReadOnly<{k: string}>); + +declare component MyComponent(...$ReadOnly<{k: string}>); + +declare component MyComponent(bar: string, baz: $ReadOnly<{k: string}>); + +declare component MyComponent(bar: string, baz: $ReadOnly<{k: string}>, realllllllllllllllllllyLong: string); + +// Attached comment +declare component MyComponent( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + 'data-baz': $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +); + +declare component MyComponent( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +); + +declare component MyComponent( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +) renders SomeComponent; \ No newline at end of file diff --git a/tests/format/flow/component/jsfmt.spec.js b/tests/format/flow/component/jsfmt.spec.js new file mode 100644 index 000000000..5d99681f6 --- /dev/null +++ b/tests/format/flow/component/jsfmt.spec.js @@ -0,0 +1,9 @@ +runFormatTest(import.meta, ["flow"], { + errors: { + "babel-flow": [ + "component-declaration.js", + "component-type-annotation.js", + "declare-component.js", + ], + }, +}); diff --git a/tests/format/flow/hook/__snapshots__/jsfmt.spec.js.snap b/tests/format/flow/hook/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..92e905aba --- /dev/null +++ b/tests/format/flow/hook/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,421 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`declare-hook.js [babel-flow] format 1`] = ` +"Unexpected token (1:9) +> 1 | declare hook useFoo(): void; + | ^ + 2 | + 3 | declare hook useFoo(): SomeComponent; + 4 | +Cause: Unexpected token (1:8)" +`; + +exports[`declare-hook.js format 1`] = ` +====================================options===================================== +parsers: ["flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +declare hook useFoo(): void; + +declare hook useFoo(): SomeComponent; + +declare hook useFoo(): React.Element; + +declare hook useFoo(): void; + +declare hook useFoo(): Array; + +declare hook useFoo(): void; + +declare hook useFoo(bar: string): string; + +declare hook useFoo(bar?: string): string; + +declare hook useFoo(data: string): string; + +declare hook useFoo(...restProps: $ReadOnly<{k: string}>): {k: string}; + +declare hook useFoo(...$ReadOnly<{k: string}>): {k: string}; + +declare hook useFoo(bar: string, baz: $ReadOnly<{k: string}>): void; + +declare hook useFoo(bar: string, baz: $ReadOnly<{k: string}>, realllllllllllllllllllyLong: string): void; + +// Attached comment +declare hook useFoo( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + baz: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +): void; + +declare hook useFoo( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +): SomeComponent; + +declare hook useFoo( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +): SomeComponent; + +=====================================output===================================== +declare hook useFoo(): void; + +declare hook useFoo(): SomeComponent; + +declare hook useFoo(): React.Element< + typeof SomeComponentLonnnnnnnnnnnnnnnnnnnnnnnnnnnnng, +>; + +declare hook useFoo(): void; + +declare hook useFoo< + T, +>(): Array; + +declare hook useFoo< + T: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo, +>(): void; + +declare hook useFoo(bar: string): string; + +declare hook useFoo(bar?: string): string; + +declare hook useFoo(data: string): string; + +declare hook useFoo(...restProps: $ReadOnly<{ k: string }>): { k: string }; + +declare hook useFoo(...$ReadOnly<{ k: string }>): { k: string }; + +declare hook useFoo(bar: string, baz: $ReadOnly<{ k: string }>): void; + +declare hook useFoo( + bar: string, + baz: $ReadOnly<{ k: string }>, + realllllllllllllllllllyLong: string, +): void; + +// Attached comment +declare hook useFoo( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + baz: $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }>, +): // Trailing comment +void; + +declare hook useFoo( + ...props: $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }> +): // Trailing comment +SomeComponent; + +declare hook useFoo( + ...props: $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }> +): // Trailing comment +SomeComponent; + +================================================================================ +`; + +exports[`hook-declaration.js [babel-flow] format 1`] = ` +"Unexpected token, expected "{" (5:8) + 3 | export default hook useFoo2() {} + 4 | +> 5 | export hook useFoo3() {} + | ^ + 6 | + 7 | hook useFoo4(): string {} + 8 | +Cause: Unexpected token, expected "{" (5:7)" +`; + +exports[`hook-declaration.js format 1`] = ` +====================================options===================================== +parsers: ["flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +hook useFoo1() {} + +export default hook useFoo2() {} + +export hook useFoo3() {} + +hook useFoo4(): string {} + +hook useFoo5() {} + +hook useFoo6(...foo) {} + +hook useFoo7(...rest?: Foo) {} + +hook useFoo8(foo, ...bar) {} + +hook useFoo9(foo: Foo, ...bar: Bar) {} + +hook useFoo10(foo: () => void,): number { return; } + +hook useFoo11(o: { f(string): void }) {} + +hook useFoo12(foo, ...bar): React.Element {} + +hook useFoo13(foo: Array, ...bar): void {} + +hook useFoo14< + T: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo, +>(): any {}; + +=====================================output===================================== +hook useFoo1() {} + +export default hook useFoo2() {} + +export hook useFoo3() {} + +hook useFoo4(): string {} + +hook useFoo5() {} + +hook useFoo6(...foo) {} + +hook useFoo7(...rest?: Foo) {} + +hook useFoo8(foo, ...bar) {} + +hook useFoo9(foo: Foo, ...bar: Bar) {} + +hook useFoo10(foo: () => void): number { + return; +} + +hook useFoo11(o: { f(string): void }) {} + +hook useFoo12( + foo, + ...bar +): React.Element {} + +hook useFoo13( + foo: Array, + ...bar +): void {} + +hook useFoo14< + T: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo, +>(): any {} + +================================================================================ +`; + +exports[`hook-type-annotation.js [babel-flow] format 1`] = ` +"Unexpected token (1:25) +> 1 | type T = hook () => void; + | ^ + 2 | + 3 | type T = hook (bar) => SomeComponent; + 4 | +Cause: Unexpected token (1:24)" +`; + +exports[`hook-type-annotation.js format 1`] = ` +====================================options===================================== +parsers: ["flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +type T = hook () => void; + +type T = hook (bar) => SomeComponent; + +type T = hook () => React.Element; + +type T = hook () => void; + +type T = hook () => Array; + +type T = hook () => void; + +type T = hook (bar: string) => string; + +type T = hook (bar?: string) => string; + +type T = hook (data: string) => string; + +type T = hook (...restProps: $ReadOnly<{k: string}>) => {k: string}; + +type T = hook (...$ReadOnly<{k: string}>) => {k: string}; + +type T = hook (bar: string, baz: $ReadOnly<{k: string}>) => void; + +type T = hook (bar: string, baz: $ReadOnly<{k: string}>, realllllllllllllllllllyLong: string) => void; + +type Banana = { + useFoo: hook (string) => boolean, +}; + +interface F2 { m: hook (string) => number } + +type f3 = hook (...arg) => void; + +type f4 = hook (/* comment */ arg) => void; + +type f5 = hook (arg /* comment */) => void; + +type f6 = hook (?arg) => void; + +class Y { + constructor( + ideConnectionFactory: hook (child_process$ChildProcess) => FlowIDEConnection = + defaultIDEConnectionFactory, + ) { + } +} + +interface F { + ideConnectionFactoryLongLongLong: hook (child_process$ChildProcess) => FlowIDEConnection +} + +type ExtractType = hook (B) => D + +// Attached comment +type T = hook ( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + baz: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +) => void; + +type T = hook ( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +) => SomeComponent; + +type T = hook ( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +) => SomeComponent; + +=====================================output===================================== +type T = hook () => void; + +type T = hook (bar) => SomeComponent; + +type T = hook () => React.Element< + typeof SomeComponentLonnnnnnnnnnnnnnnnnnnnnnnnnnnnng, +>; + +type T = hook () => void; + +type T = hook < + T, +>() => Array; + +type T = hook < + T: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo, +>() => void; + +type T = hook (bar: string) => string; + +type T = hook (bar?: string) => string; + +type T = hook (data: string) => string; + +type T = hook (...restProps: $ReadOnly<{ k: string }>) => { k: string }; + +type T = hook (...$ReadOnly<{ k: string }>) => { k: string }; + +type T = hook (bar: string, baz: $ReadOnly<{ k: string }>) => void; + +type T = hook ( + bar: string, + baz: $ReadOnly<{ k: string }>, + realllllllllllllllllllyLong: string, +) => void; + +type Banana = { + useFoo: hook (string) => boolean, +}; + +interface F2 { + m: hook (string) => number; +} + +type f3 = hook (...arg) => void; + +type f4 = hook (/* comment */ arg) => void; + +type f5 = hook (arg /* comment */) => void; + +type f6 = hook (?arg) => void; + +class Y { + constructor( + ideConnectionFactory: hook ( + child_process$ChildProcess, + ) => FlowIDEConnection = defaultIDEConnectionFactory, + ) {} +} + +interface F { + ideConnectionFactoryLongLongLong: hook ( + child_process$ChildProcess, + ) => FlowIDEConnection; +} + +type ExtractType = hook (B) => D; + +// Attached comment +type T = hook ( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + baz: $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }>, +) => // Trailing comment +void; + +type T = hook ( + ...props: $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }> +) => // Trailing comment +SomeComponent; + +type T = hook ( + ...props: $ReadOnly<{ + k: string, + reallllllllllllllllllllllllllllllllllllllyLong: string, + }> +) => // Trailing comment +SomeComponent; + +================================================================================ +`; diff --git a/tests/format/flow/hook/declare-hook.js b/tests/format/flow/hook/declare-hook.js new file mode 100644 index 000000000..8c3dcedc4 --- /dev/null +++ b/tests/format/flow/hook/declare-hook.js @@ -0,0 +1,47 @@ +declare hook useFoo(): void; + +declare hook useFoo(): SomeComponent; + +declare hook useFoo(): React.Element; + +declare hook useFoo(): void; + +declare hook useFoo(): Array; + +declare hook useFoo(): void; + +declare hook useFoo(bar: string): string; + +declare hook useFoo(bar?: string): string; + +declare hook useFoo(data: string): string; + +declare hook useFoo(...restProps: $ReadOnly<{k: string}>): {k: string}; + +declare hook useFoo(...$ReadOnly<{k: string}>): {k: string}; + +declare hook useFoo(bar: string, baz: $ReadOnly<{k: string}>): void; + +declare hook useFoo(bar: string, baz: $ReadOnly<{k: string}>, realllllllllllllllllllyLong: string): void; + +// Attached comment +declare hook useFoo( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + baz: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +): void; + +declare hook useFoo( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +): SomeComponent; + +declare hook useFoo( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +): SomeComponent; diff --git a/tests/format/flow/hook/hook-declaration.js b/tests/format/flow/hook/hook-declaration.js new file mode 100644 index 000000000..be8b7361e --- /dev/null +++ b/tests/format/flow/hook/hook-declaration.js @@ -0,0 +1,29 @@ +hook useFoo1() {} + +export default hook useFoo2() {} + +export hook useFoo3() {} + +hook useFoo4(): string {} + +hook useFoo5() {} + +hook useFoo6(...foo) {} + +hook useFoo7(...rest?: Foo) {} + +hook useFoo8(foo, ...bar) {} + +hook useFoo9(foo: Foo, ...bar: Bar) {} + +hook useFoo10(foo: () => void,): number { return; } + +hook useFoo11(o: { f(string): void }) {} + +hook useFoo12(foo, ...bar): React.Element {} + +hook useFoo13(foo: Array, ...bar): void {} + +hook useFoo14< + T: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo, +>(): any {}; diff --git a/tests/format/flow/hook/hook-type-annotation.js b/tests/format/flow/hook/hook-type-annotation.js new file mode 100644 index 000000000..f672ac092 --- /dev/null +++ b/tests/format/flow/hook/hook-type-annotation.js @@ -0,0 +1,75 @@ +type T = hook () => void; + +type T = hook (bar) => SomeComponent; + +type T = hook () => React.Element; + +type T = hook () => void; + +type T = hook () => Array; + +type T = hook () => void; + +type T = hook (bar: string) => string; + +type T = hook (bar?: string) => string; + +type T = hook (data: string) => string; + +type T = hook (...restProps: $ReadOnly<{k: string}>) => {k: string}; + +type T = hook (...$ReadOnly<{k: string}>) => {k: string}; + +type T = hook (bar: string, baz: $ReadOnly<{k: string}>) => void; + +type T = hook (bar: string, baz: $ReadOnly<{k: string}>, realllllllllllllllllllyLong: string) => void; + +type Banana = { + useFoo: hook (string) => boolean, +}; + +interface F2 { m: hook (string) => number } + +type f3 = hook (...arg) => void; + +type f4 = hook (/* comment */ arg) => void; + +type f5 = hook (arg /* comment */) => void; + +type f6 = hook (?arg) => void; + +class Y { + constructor( + ideConnectionFactory: hook (child_process$ChildProcess) => FlowIDEConnection = + defaultIDEConnectionFactory, + ) { + } +} + +interface F { + ideConnectionFactoryLongLongLong: hook (child_process$ChildProcess) => FlowIDEConnection +} + +type ExtractType = hook (B) => D + +// Attached comment +type T = hook ( + /** + * Commet block + */ + bar: string, // Trailing comment + + // preceding comment + baz: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +) => void; + +type T = hook ( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +) => SomeComponent; + +type T = hook ( + ...props: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}> + // Trailing comment +) => SomeComponent; diff --git a/tests/format/flow/hook/jsfmt.spec.js b/tests/format/flow/hook/jsfmt.spec.js new file mode 100644 index 000000000..de0340097 --- /dev/null +++ b/tests/format/flow/hook/jsfmt.spec.js @@ -0,0 +1,9 @@ +runFormatTest(import.meta, ["flow"], { + errors: { + "babel-flow": [ + "declare-hook.js", + "hook-declaration.js", + "hook-type-annotation.js", + ], + }, +}); diff --git a/tests/unit/__snapshots__/visitor-keys.js.snap b/tests/unit/__snapshots__/visitor-keys.js.snap index c9cdd1616..781d0d210 100644 --- a/tests/unit/__snapshots__/visitor-keys.js.snap +++ b/tests/unit/__snapshots__/visitor-keys.js.snap @@ -147,6 +147,27 @@ exports[`visitor keys estree 1`] = ` "decorators", "variance", ], + "ComponentDeclaration": [ + "id", + "params", + "body", + "typeParameters", + "rendersType", + ], + "ComponentParameter": [ + "name", + "local", + ], + "ComponentTypeAnnotation": [ + "params", + "rest", + "typeParameters", + "rendersType", + ], + "ComponentTypeParameter": [ + "name", + "typeAnnotation", + ], "ConditionalExpression": [ "test", "consequent", @@ -171,6 +192,13 @@ exports[`visitor keys estree 1`] = ` "implements", "body", ], + "DeclareComponent": [ + "id", + "params", + "rest", + "typeParameters", + "rendersType", + ], "DeclareEnum": [ "id", "body", @@ -187,6 +215,9 @@ exports[`visitor keys estree 1`] = ` "id", "predicate", ], + "DeclareHook": [ + "id", + ], "DeclareInterface": [ "id", "typeParameters", @@ -350,6 +381,19 @@ exports[`visitor keys estree 1`] = ` "id", "typeParameters", ], + "HookDeclaration": [ + "id", + "params", + "body", + "typeParameters", + "returnType", + ], + "HookTypeAnnotation": [ + "params", + "returnType", + "rest", + "typeParameters", + ], "Identifier": [ "typeAnnotation", "decorators",