diff --git a/src/language-js/print/class.js b/src/language-js/print/class.js index 2a45b017b..70f9f06b7 100644 --- a/src/language-js/print/class.js +++ b/src/language-js/print/class.js @@ -21,11 +21,20 @@ import { printPropertyKey } from "./property.js"; import { printAssignment } from "./assignment.js"; import { printClassMemberDecorators } from "./decorators.js"; +/* +- `ClassDeclaration` +- `ClassExpression` +- `DeclareClass`(flow) +*/ function printClass(path, options, print) { const { node } = path; const parts = []; - if (node.declare) { + if ( + node.declare || + (node.type === "DeclareClass" && + path.parent.type !== "DeclareExportDeclaration") + ) { parts.push("declare "); } diff --git a/src/language-js/print/flow.js b/src/language-js/print/flow.js index 71f281702..93ccf3a50 100644 --- a/src/language-js/print/flow.js +++ b/src/language-js/print/flow.js @@ -5,7 +5,6 @@ import { printString, printNumber } from "../../common/util.js"; import { replaceEndOfLine } from "../../document/utils.js"; import UnexpectedNodeError from "../../utils/unexpected-node-error.js"; import { - getParentExportDeclaration, isFunctionNotation, isGetterOrSetter, rawText, @@ -36,61 +35,42 @@ import { function printFlow(path, options, print) { const { node } = path; const semi = options.semi ? ";" : ""; - /** @type{Doc[]} */ - const parts = []; + switch (node.type) { case "DeclareClass": - return printFlowDeclaration(path, printClass(path, options, print)); + return printClass(path, options, print); case "DeclareFunction": - return printFlowDeclaration(path, [ + return [ + path.parent.type !== "DeclareExportDeclaration" ? "declare " : "", "function ", print("id"), node.predicate ? " " : "", print("predicate"), semi, - ]); + ]; case "DeclareModule": - return printFlowDeclaration(path, [ - "module ", - print("id"), - " ", - print("body"), - ]); + return ["declare module ", print("id"), " ", print("body")]; case "DeclareModuleExports": - return printFlowDeclaration(path, [ - "module.exports", - ": ", - print("typeAnnotation"), - semi, - ]); + return ["declare module.exports", ": ", print("typeAnnotation"), semi]; case "DeclareVariable": - return printFlowDeclaration(path, ["var ", print("id"), semi]); - case "DeclareInterface": - return printFlowDeclaration(path, printInterface(path, options, print)); + return [ + path.parent.type !== "DeclareExportDeclaration" ? "declare " : "", + "var ", + print("id"), + semi, + ]; case "DeclareExportDeclaration": - return printFlowDeclaration( - path, - printExportDeclaration(path, options, print) - ); + return printExportDeclaration(path, options, print); case "DeclareExportAllDeclaration": - return printFlowDeclaration( - path, - printExportAllDeclaration(path, options, print) - ); + return printExportAllDeclaration(path, options, print); case "DeclareOpaqueType": - case "OpaqueType": { - const doc = printOpaqueType(path, options, print); - return node.type === "DeclareOpaqueType" - ? printFlowDeclaration(path, doc) - : doc; - } + case "OpaqueType": + return printOpaqueType(path, options, print); + case "DeclareTypeAlias": - case "TypeAlias": { - const doc = printTypeAlias(path, options, print); - return node.type === "DeclareTypeAlias" - ? printFlowDeclaration(path, doc) - : doc; - } + case "TypeAlias": + return printTypeAlias(path, options, print); + case "IntersectionTypeAnnotation": return printIntersectionType(path, options, print); case "UnionTypeAnnotation": @@ -126,18 +106,24 @@ function printFlow(path, options, print) { case "BooleanLiteralTypeAnnotation": return String(node.value); case "DeclareEnum": - case "EnumDeclaration": { - const doc = ["enum ", print("id"), " ", print("body")]; - return node.type === "DeclareEnum" - ? printFlowDeclaration(path, doc) - : doc; - } + case "EnumDeclaration": + return [ + node.type === "DeclareEnum" && + path.parent.type !== "DeclareExportDeclaration" + ? "declare " + : "", + "enum ", + print("id"), + " ", + print("body"), + ]; + case "EnumBooleanBody": case "EnumNumberBody": case "EnumStringBody": - case "EnumSymbolBody": + case "EnumSymbolBody": { + let type; if (node.type === "EnumSymbolBody" || node.explicitType) { - let type = null; switch (node.type) { case "EnumBooleanBody": type = "boolean"; @@ -152,10 +138,12 @@ function printFlow(path, options, print) { type = "symbol"; break; } - parts.push("of ", type, " "); } - parts.push(printEnumMembers(path, print, options)); - return parts; + return [ + type ? `of ${type} ` : "", + printEnumMembers(path, print, options), + ]; + } case "EnumBooleanMember": case "EnumNumberMember": @@ -177,6 +165,7 @@ function printFlow(path, options, print) { ]; } + case "DeclareInterface": case "InterfaceDeclaration": case "InterfaceTypeAnnotation": return printInterface(path, options, print); @@ -191,13 +180,8 @@ function printFlow(path, options, print) { return kind === "plus" ? "+" : "-"; } case "ObjectTypeCallProperty": - if (node.static) { - parts.push("static "); - } + return [node.static ? "static " : "", print("value")]; - parts.push(print("value")); - - return parts; case "ObjectTypeIndexer": return [ node.static ? "static " : "", @@ -308,18 +292,4 @@ function printFlow(path, options, print) { } } -function printFlowDeclaration(path, printed) { - const parentExportDecl = getParentExportDeclaration(path); - - if (parentExportDecl) { - assert.strictEqual(parentExportDecl.type, "DeclareExportDeclaration"); - return printed; - } - - // If the parent node has type DeclareExportDeclaration, then it - // will be responsible for printing the "declare" token. Otherwise - // it needs to be printed with this non-exported declaration node. - return ["declare ", printed]; -} - export { printFlow }; diff --git a/src/language-js/print/interface.js b/src/language-js/print/interface.js index 12af9c2df..72e43d37b 100644 --- a/src/language-js/print/interface.js +++ b/src/language-js/print/interface.js @@ -4,10 +4,16 @@ import { hasComment, identity, CommentCheckFlags } from "../utils/index.js"; import { getTypeParametersGroupId } from "./type-parameters.js"; import { printTypeScriptModifiers } from "./misc.js"; +/* +- `TSInterfaceDeclaration`(TypeScript) +- `DeclareInterface`(flow) +- `InterfaceDeclaration`(flow) +- `InterfaceTypeAnnotation`(flow) +*/ function printInterface(path, options, print) { const { node } = path; const parts = []; - if (node.declare) { + if (node.declare || node.type === "DeclareInterface") { parts.push("declare "); } diff --git a/src/language-js/print/module.js b/src/language-js/print/module.js index de417b7ed..8beccfdf4 100644 --- a/src/language-js/print/module.js +++ b/src/language-js/print/module.js @@ -57,6 +57,11 @@ function printImportDeclaration(path, options, print) { return parts; } +/* +- `ExportDefaultDeclaration` +- `ExportNamedDeclaration` +- `DeclareExportDeclaration`(flow) +*/ function printExportDeclaration(path, options, print) { const { node } = path; /** @type{Doc[]} */ @@ -70,6 +75,10 @@ function printExportDeclaration(path, options, print) { const { type, exportKind, declaration } = node; + if (type === "DeclareExportDeclaration") { + parts.push("declare "); + } + parts.push("export"); const isDefaultExport = node.default || type === "ExportDefaultDeclaration"; @@ -106,13 +115,21 @@ function printExportDeclaration(path, options, print) { return parts; } +/* +- `ExportAllDeclaration` +- `DeclareExportAllDeclaration`(flow) +*/ function printExportAllDeclaration(path, options, print) { const { node } = path; const semi = options.semi ? ";" : ""; /** @type{Doc[]} */ const parts = []; - const { exportKind, exported } = node; + const { exportKind, exported, type } = node; + + if (type === "DeclareExportAllDeclaration") { + parts.push("declare "); + } parts.push("export"); diff --git a/src/language-js/print/type-annotation.js b/src/language-js/print/type-annotation.js index 88e60d38b..52be9f87d 100644 --- a/src/language-js/print/type-annotation.js +++ b/src/language-js/print/type-annotation.js @@ -56,10 +56,22 @@ function shouldHugType(node) { return false; } +/* +- `DeclareOpaqueType`(flow) +- `OpaqueType`(flow) +*/ function printOpaqueType(path, options, print) { const semi = options.semi ? ";" : ""; const { node } = path; const parts = []; + + if ( + node.type === "DeclareOpaqueType" && + path.parent.type !== "DeclareExportDeclaration" + ) { + parts.push("declare "); + } + parts.push("opaque type ", print("id"), print("typeParameters")); if (node.supertype) { @@ -75,11 +87,21 @@ function printOpaqueType(path, options, print) { return parts; } +/* +- `DeclareTypeAlias`(flow) +- `TypeAlias`(flow) +- `TSTypeAliasDeclaration`(TypeScript) +*/ function printTypeAlias(path, options, print) { const semi = options.semi ? ";" : ""; const { node } = path; const parts = []; - if (node.declare) { + + if ( + node.declare || + (node.type === "DeclareTypeAlias" && + path.parent.type !== "DeclareExportDeclaration") + ) { parts.push("declare "); } parts.push("type ", print("id"), print("typeParameters")); @@ -284,7 +306,11 @@ function printFunctionType(path, options, print) { return group(parts); } -// `TSIndexedAccessType`, `IndexedAccessType`, and `OptionalIndexedAccessType` +/* +- `TSIndexedAccessType`(TypeScript) +- `IndexedAccessType`(flow) +- `OptionalIndexedAccessType`(flow) +*/ function printIndexedAccessType(path, options, print) { const { node } = path; const leftDelimiter = diff --git a/tests/format/misc/errors/flow/declare-export-interface/__snapshots__/jsfmt.spec.js.snap b/tests/format/misc/errors/flow/declare-export-interface/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..d7366192f --- /dev/null +++ b/tests/format/misc/errors/flow/declare-export-interface/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`snippet: #0 [babel-flow] format 1`] = ` +"\`declare export interface\` is not supported. Use \`export interface\` instead. (1:16) +> 1 | declare export interface A {} + | ^" +`; + +exports[`snippet: #0 [flow] format 1`] = ` +"\`declare export interface\` is not supported. Use \`export interface\` instead. (1:16) +> 1 | declare export interface A {} + | ^^^^^^^^^" +`; diff --git a/tests/format/misc/errors/flow/declare-export-interface/jsfmt.spec.js b/tests/format/misc/errors/flow/declare-export-interface/jsfmt.spec.js new file mode 100644 index 000000000..be0643c1b --- /dev/null +++ b/tests/format/misc/errors/flow/declare-export-interface/jsfmt.spec.js @@ -0,0 +1,7 @@ +run_spec( + { + importMeta: import.meta, + snippets: ["declare export interface A {}"], + }, + ["flow", "babel-flow"] +);