prettierx
Version:
prettierX - a less opinionated fork of the Prettier code formatter
384 lines (366 loc) • 11.4 kB
JavaScript
"use strict";
/** @typedef {import("../../document").Doc} Doc */
const assert = require("assert");
const { printDanglingComments } = require("../../main/comments");
const { printString, printNumber } = require("../../common/util");
const {
builders: { hardline, softline, group, indent },
} = require("../../document");
const {
getParentExportDeclaration,
isFunctionNotation,
isGetterOrSetter,
rawText,
shouldPrintComma,
} = require("../utils");
const { locStart, locEnd } = require("../loc");
const { printClass } = require("./class");
const {
printOpaqueType,
printTypeAlias,
printIntersectionType,
printUnionType,
printFunctionType,
printTupleType,
printIndexedAccessType,
} = require("./type-annotation");
const { printInterface } = require("./interface");
const {
printTypeParameter,
printTypeParameters,
} = require("./type-parameters");
const {
printExportDeclaration,
printExportAllDeclaration,
} = require("./module");
const { printArrayItems } = require("./array");
const { printObject } = require("./object");
const { printPropertyKey } = require("./property");
const {
printOptionalToken,
printTypeAnnotation,
printRestSpread,
} = require("./misc");
function printFlow(path, options, print) {
const node = path.getValue();
const semi = options.semi ? ";" : "";
/** @type{Doc[]} */
const parts = [];
switch (node.type) {
case "DeclareClass":
return printFlowDeclaration(path, printClass(path, options, print));
case "DeclareFunction":
return printFlowDeclaration(path, [
"function ",
print("id"),
node.predicate ? " " : "",
print("predicate"),
semi,
]);
case "DeclareModule":
return printFlowDeclaration(path, [
"module ",
print("id"),
" ",
print("body"),
]);
case "DeclareModuleExports":
return printFlowDeclaration(path, [
"module.exports",
": ",
print("typeAnnotation"),
semi,
]);
case "DeclareVariable":
return printFlowDeclaration(path, ["var ", print("id"), semi]);
case "DeclareOpaqueType":
return printFlowDeclaration(path, printOpaqueType(path, options, print));
case "DeclareInterface":
return printFlowDeclaration(path, printInterface(path, options, print));
case "DeclareTypeAlias":
return printFlowDeclaration(path, printTypeAlias(path, options, print));
case "DeclareExportDeclaration":
return printFlowDeclaration(
path,
printExportDeclaration(path, options, print)
);
case "DeclareExportAllDeclaration":
return printFlowDeclaration(
path,
printExportAllDeclaration(path, options, print)
);
case "OpaqueType":
return printOpaqueType(path, options, print);
case "TypeAlias":
return printTypeAlias(path, options, print);
case "IntersectionTypeAnnotation":
return printIntersectionType(path, options, print);
case "UnionTypeAnnotation":
return printUnionType(path, options, print);
case "FunctionTypeAnnotation":
return printFunctionType(path, options, print);
case "TupleTypeAnnotation":
return printTupleType(path, options, print);
case "GenericTypeAnnotation":
return [
print("id"),
printTypeParameters(path, options, print, "typeParameters"),
];
case "IndexedAccessType":
case "OptionalIndexedAccessType":
return printIndexedAccessType(path, options, print);
// Type Annotations for Facebook Flow, typically stripped out or
// transformed away before printing.
case "TypeAnnotation":
return print("typeAnnotation");
case "TypeParameter":
return printTypeParameter(path, options, print);
case "TypeofTypeAnnotation":
return ["typeof ", print("argument")];
case "ExistsTypeAnnotation":
return "*";
case "EmptyTypeAnnotation":
return "empty";
case "MixedTypeAnnotation":
return "mixed";
case "ArrayTypeAnnotation":
return [print("elementType"), "[]"];
case "BooleanLiteralTypeAnnotation":
return String(node.value);
case "EnumDeclaration":
return ["enum ", print("id"), " ", print("body")];
case "EnumBooleanBody":
case "EnumNumberBody":
case "EnumStringBody":
case "EnumSymbolBody": {
if (node.type === "EnumSymbolBody" || node.explicitType) {
let type = null;
switch (node.type) {
case "EnumBooleanBody":
type = "boolean";
break;
case "EnumNumberBody":
type = "number";
break;
case "EnumStringBody":
type = "string";
break;
case "EnumSymbolBody":
type = "symbol";
break;
}
parts.push("of ", type, " ");
}
if (node.members.length === 0 && !node.hasUnknownMembers) {
parts.push(
group(["{", printDanglingComments(path, options), softline, "}"])
);
} else {
const members =
node.members.length > 0
? [
hardline,
printArrayItems(path, options, "members", print),
node.hasUnknownMembers || shouldPrintComma(options) ? "," : "",
]
: [];
parts.push(
group([
"{",
indent([
...members,
...(node.hasUnknownMembers ? [hardline, "..."] : []),
]),
printDanglingComments(path, options, /* sameIndent */ true),
hardline,
"}",
])
);
}
return parts;
}
case "EnumBooleanMember":
case "EnumNumberMember":
case "EnumStringMember":
return [
print("id"),
" = ",
typeof node.init === "object" ? print("init") : String(node.init),
];
case "EnumDefaultedMember":
return print("id");
case "FunctionTypeParam": {
const name = node.name
? print("name")
: path.getParentNode().this === node
? "this"
: "";
return [
name,
printOptionalToken(path),
name ? ": " : "",
print("typeAnnotation"),
];
}
case "InterfaceDeclaration":
case "InterfaceTypeAnnotation":
return printInterface(path, options, print);
case "ClassImplements":
case "InterfaceExtends":
return [print("id"), print("typeParameters")];
case "NullableTypeAnnotation":
return ["?", print("typeAnnotation")];
case "Variance": {
const { kind } = node;
assert.ok(kind === "plus" || kind === "minus");
return kind === "plus" ? "+" : "-";
}
case "ObjectTypeCallProperty":
if (node.static) {
parts.push("static ");
}
parts.push(print("value"));
return parts;
case "ObjectTypeIndexer": {
return [
node.variance ? print("variance") : "",
"[",
print("id"),
node.id ? ": " : "",
print("key"),
"]: ",
print("value"),
];
}
case "ObjectTypeProperty": {
let modifier = "";
if (node.proto) {
modifier = "proto ";
} else if (node.static) {
modifier = "static ";
}
return [
modifier,
isGetterOrSetter(node) ? node.kind + " " : "",
node.variance ? print("variance") : "",
printPropertyKey(path, options, print),
printOptionalToken(path),
isFunctionNotation(node) ? "" : ": ",
print("value"),
];
}
case "ObjectTypeAnnotation":
return printObject(path, options, print);
case "ObjectTypeInternalSlot":
return [
node.static ? "static " : "",
"[[",
print("id"),
"]]",
printOptionalToken(path),
node.method ? "" : ": ",
print("value"),
];
// Same as `RestElement`
case "ObjectTypeSpreadProperty":
return printRestSpread(path, options, print);
case "QualifiedTypeIdentifier":
return [print("qualification"), ".", print("id")];
case "StringLiteralTypeAnnotation":
return printString(rawText(node), options);
case "NumberLiteralTypeAnnotation":
assert.strictEqual(typeof node.value, "number");
// fall through
case "BigIntLiteralTypeAnnotation":
if (node.extra) {
return printNumber(node.extra.raw);
}
return printNumber(node.raw);
case "TypeCastExpression": {
return [
"(",
print("expression"),
printTypeAnnotation(path, options, print),
")",
];
}
case "TypeParameterDeclaration":
case "TypeParameterInstantiation": {
const printed = printTypeParameters(path, options, print, "params");
if (options.parser === "flow") {
const start = locStart(node);
const end = locEnd(node);
const commentStartIndex = options.originalText.lastIndexOf("/*", start);
const commentEndIndex = options.originalText.indexOf("*/", end);
if (commentStartIndex !== -1 && commentEndIndex !== -1) {
const comment = options.originalText
.slice(commentStartIndex + 2, commentEndIndex)
.trim();
if (
comment.startsWith("::") &&
!comment.includes("/*") &&
!comment.includes("*/")
) {
return ["/*:: ", printed, " */"];
}
}
}
return printed;
}
case "InferredPredicate":
return "%checks";
// Unhandled types below. If encountered, nodes of these types should
// be either left alone or desugared into AST types that are fully
// supported by the pretty-printer.
case "DeclaredPredicate":
return ["%checks(", print("value"), ")"];
case "AnyTypeAnnotation":
return "any";
case "BooleanTypeAnnotation":
return "boolean";
case "BigIntTypeAnnotation":
return "bigint";
case "NullLiteralTypeAnnotation":
return "null";
case "NumberTypeAnnotation":
return "number";
case "SymbolTypeAnnotation":
return "symbol";
case "StringTypeAnnotation":
return "string";
case "VoidTypeAnnotation":
return "void";
case "ThisTypeAnnotation":
return "this";
// These types are unprintable because they serve as abstract
// supertypes for other (printable) types.
case "Node":
case "Printable":
case "SourceLocation":
case "Position":
case "Statement":
case "Function":
case "Pattern":
case "Expression":
case "Declaration":
case "Specifier":
case "NamedSpecifier":
case "Comment":
case "MemberTypeAnnotation": // Flow
case "Type":
/* istanbul ignore next */
throw new Error("unprintable type: " + JSON.stringify(node.type));
}
}
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];
}
module.exports = { printFlow };