Files
test-repo/src/language-js/printer-estree.js
T

1259 lines
35 KiB
JavaScript

"use strict";
/** @typedef {import("../document").Doc} Doc */
/** @type {import("assert")} */
const assert = require("assert");
// TODO(azz): anything that imports from main shouldn't be in a `language-*` dir.
const { printDanglingComments } = require("../main/comments");
const {
hasNewline,
printString,
printNumber,
isNonEmptyArray,
} = require("../common/util");
const {
builders: {
concat,
join,
line,
hardline,
softline,
literalline,
group,
indent,
},
utils: { isEmpty },
} = require("../document");
const embed = require("./embed");
const clean = require("./clean");
const { insertPragma } = require("./pragma");
const handleComments = require("./comments");
const pathNeedsParens = require("./needs-parens");
const preprocess = require("./print-preprocess");
const {
getCallArguments,
getParentExportDeclaration,
hasFlowShorthandAnnotationComment,
hasNewlineBetweenOrAfterDecorators,
hasComment,
CommentCheckFlags,
isExportDeclaration,
isFunctionNotation,
isGetterOrSetter,
isTheOnlyJsxElementInMarkdown,
isBlockComment,
isLineComment,
isNextLineEmpty,
needsHardlineAfterDanglingComment,
rawText,
shouldPrintComma,
hasIgnoreComment,
} = require("./utils");
const { locStart, locEnd } = require("./loc");
const {
printHtmlBinding,
isVueEventBindingExpression,
} = require("./print/html-binding");
const { printAngular } = require("./print/angular");
const { printJsx, hasJsxIgnoreComment } = require("./print/jsx");
const { printFlow } = require("./print/flow");
const { printTypescript } = require("./print/typescript");
const {
printOptionalToken,
printBindExpressionCallee,
printTypeAnnotation,
adjustClause,
} = require("./print/misc");
const {
printImportDeclaration,
printExportDeclaration,
printExportAllDeclaration,
printModuleSpecifier,
} = require("./print/module");
const { printTernary } = require("./print/ternary");
const { printTemplateLiteral } = require("./print/template-literal");
const { printArray, printArrayItems } = require("./print/array");
const { printObject } = require("./print/object");
const {
printClass,
printClassMethod,
printClassProperty,
} = require("./print/class");
const { printTypeParameters } = require("./print/type-parameters");
const { printPropertyKey, printProperty } = require("./print/property");
const {
printFunctionDeclaration,
printArrowFunctionExpression,
printMethod,
printReturnStatement,
printThrowStatement,
} = require("./print/function");
const { printCallExpression } = require("./print/call-expression");
const { printInterface } = require("./print/interface");
const {
printVariableDeclarator,
printAssignmentExpression,
} = require("./print/assignment");
const { printBinaryishExpression } = require("./print/binaryish");
const { printSwitchCaseConsequent } = require("./print/statement");
const { printMemberExpression } = require("./print/member");
const { printBlock, printBlockBody } = require("./print/block");
const { printComment } = require("./print/comment");
function genericPrint(path, options, printPath, args) {
const node = path.getValue();
let needsParens = false;
const linesWithoutParens = printPathNoParens(path, options, printPath, args);
if (!node || isEmpty(linesWithoutParens)) {
return linesWithoutParens;
}
const parentExportDecl = getParentExportDeclaration(path);
const decorators = [];
if (
node.type === "ClassMethod" ||
node.type === "ClassPrivateMethod" ||
node.type === "ClassProperty" ||
node.type === "FieldDefinition" ||
node.type === "TSAbstractClassProperty" ||
node.type === "ClassPrivateProperty" ||
node.type === "MethodDefinition" ||
node.type === "TSAbstractMethodDefinition" ||
node.type === "TSDeclareMethod"
) {
// their decorators are handled themselves
} else if (
isNonEmptyArray(node.decorators) &&
// If the parent node is an export declaration and the decorator
// was written before the export, the export will be responsible
// for printing the decorators.
!(
parentExportDecl &&
locStart(parentExportDecl, { ignoreDecorators: true }) >
locStart(node.decorators[0])
)
) {
const shouldBreak =
node.type === "ClassExpression" ||
node.type === "ClassDeclaration" ||
hasNewlineBetweenOrAfterDecorators(node, options);
const separator = shouldBreak ? hardline : line;
path.each((decoratorPath) => {
let decorator = decoratorPath.getValue();
if (decorator.expression) {
decorator = decorator.expression;
} else {
decorator = decorator.callee;
}
decorators.push(printPath(decoratorPath), separator);
}, "decorators");
if (parentExportDecl) {
decorators.unshift(hardline);
}
} else if (
isExportDeclaration(node) &&
node.declaration &&
isNonEmptyArray(node.declaration.decorators) &&
// Only print decorators here if they were written before the export,
// otherwise they are printed by the node.declaration
locStart(node, { ignoreDecorators: true }) >
locStart(node.declaration.decorators[0])
) {
// Export declarations are responsible for printing any decorators
// that logically apply to node.declaration.
path.each(
(decoratorPath) => {
const decorator = decoratorPath.getValue();
const prefix = decorator.type === "Decorator" ? "" : "@";
decorators.push(prefix, printPath(decoratorPath), hardline);
},
"declaration",
"decorators"
);
} else {
// Nodes with decorators can't have parentheses, so we can avoid
// computing pathNeedsParens() except in this case.
needsParens = pathNeedsParens(path, options);
}
const parts = [];
if (needsParens) {
parts.unshift("(");
}
parts.push(linesWithoutParens);
if (needsParens) {
const node = path.getValue();
if (hasFlowShorthandAnnotationComment(node)) {
parts.push(" /*");
parts.push(node.trailingComments[0].value.trimStart());
parts.push("*/");
node.trailingComments[0].printed = true;
}
parts.push(")");
}
if (decorators.length > 0) {
return group(concat(decorators.concat(parts)));
}
return concat(parts);
}
function printPathNoParens(path, options, print, args) {
const n = path.getValue();
const semi = options.semi ? ";" : "";
if (!n) {
return "";
}
if (typeof n === "string") {
return n;
}
for (const printer of [
printHtmlBinding,
printAngular,
printJsx,
printFlow,
printTypescript,
]) {
const printed = printer(path, options, print);
if (typeof printed !== "undefined") {
return printed;
}
}
/** @type{Doc[]} */
let parts = [];
switch (n.type) {
case "JsExpressionRoot":
return path.call(print, "node");
case "JsonRoot":
return concat([path.call(print, "node"), hardline]);
case "File":
// Print @babel/parser's InterpreterDirective here so that
// leading comments on the `Program` node get printed after the hashbang.
if (n.program && n.program.interpreter) {
parts.push(
path.call(
(programPath) => programPath.call(print, "interpreter"),
"program"
)
);
}
parts.push(path.call(print, "program"));
return concat(parts);
case "Program":
return printBlockBody(path, options, print);
// Babel extension.
case "EmptyStatement":
return "";
case "ExpressionStatement":
// Detect Flow and TypeScript directives
if (n.directive) {
return concat([nodeStr(n.expression, options, true), semi]);
}
if (options.parser === "__vue_event_binding") {
const parent = path.getParentNode();
if (
parent.type === "Program" &&
parent.body.length === 1 &&
parent.body[0] === n
) {
return concat([
path.call(print, "expression"),
isVueEventBindingExpression(n.expression) ? ";" : "",
]);
}
}
// Do not append semicolon after the only JSX element in a program
return concat([
path.call(print, "expression"),
isTheOnlyJsxElementInMarkdown(options, path) ? "" : semi,
]);
// Babel non-standard node. Used for Closure-style type casts. See postprocess.js.
case "ParenthesizedExpression": {
const shouldHug = !hasComment(n.expression);
if (shouldHug) {
return concat(["(", path.call(print, "expression"), ")"]);
}
return group(
concat([
"(",
indent(concat([softline, path.call(print, "expression")])),
softline,
")",
])
);
}
case "AssignmentExpression":
return printAssignmentExpression(path, options, print);
case "VariableDeclarator":
return printVariableDeclarator(path, options, print);
case "BinaryExpression":
case "LogicalExpression":
return printBinaryishExpression(path, options, print);
case "AssignmentPattern":
return concat([
path.call(print, "left"),
" = ",
path.call(print, "right"),
]);
case "OptionalMemberExpression":
case "MemberExpression": {
return printMemberExpression(path, options, print);
}
case "MetaProperty":
return concat([
path.call(print, "meta"),
".",
path.call(print, "property"),
]);
case "BindExpression":
if (n.object) {
parts.push(path.call(print, "object"));
}
parts.push(
group(
indent(
concat([softline, printBindExpressionCallee(path, options, print)])
)
)
);
return concat(parts);
case "Identifier": {
return concat([
n.name,
printOptionalToken(path),
printTypeAnnotation(path, options, print),
]);
}
case "V8IntrinsicIdentifier":
return concat(["%", n.name]);
case "SpreadElement":
case "SpreadElementPattern":
case "SpreadProperty":
case "SpreadPropertyPattern":
case "RestElement":
case "ObjectTypeSpreadProperty":
return concat([
"...",
path.call(print, "argument"),
printTypeAnnotation(path, options, print),
]);
case "FunctionDeclaration":
case "FunctionExpression":
return printFunctionDeclaration(
path,
print,
options,
args &&
args.expandLastArg &&
getCallArguments(path.getParentNode()).length > 1
);
case "ArrowFunctionExpression":
return printArrowFunctionExpression(path, options, print, args);
case "YieldExpression":
parts.push("yield");
if (n.delegate) {
parts.push("*");
}
if (n.argument) {
parts.push(" ", path.call(print, "argument"));
}
return concat(parts);
case "AwaitExpression": {
parts.push("await");
if (n.argument) {
parts.push(" ", path.call(print, "argument"));
}
const parent = path.getParentNode();
if (
((parent.type === "CallExpression" ||
parent.type === "OptionalCallExpression") &&
parent.callee === n) ||
((parent.type === "MemberExpression" ||
parent.type === "OptionalMemberExpression") &&
parent.object === n)
) {
return group(
concat([indent(concat([softline, concat(parts)])), softline])
);
}
return concat(parts);
}
case "ExportDefaultDeclaration":
case "ExportNamedDeclaration":
return printExportDeclaration(path, options, print);
case "ExportAllDeclaration":
return printExportAllDeclaration(path, options, print);
case "ImportDeclaration":
return printImportDeclaration(path, options, print);
case "ImportSpecifier":
case "ExportSpecifier":
case "ImportNamespaceSpecifier":
case "ExportNamespaceSpecifier":
case "ImportDefaultSpecifier":
case "ExportDefaultSpecifier":
return printModuleSpecifier(path, options, print);
case "ImportAttribute":
return concat([path.call(print, "key"), ": ", path.call(print, "value")]);
case "Import":
return "import";
case "BlockStatement":
case "StaticBlock":
case "ClassBody":
return printBlock(path, options, print);
case "ThrowStatement":
return printThrowStatement(path, options, print);
case "ReturnStatement":
return printReturnStatement(path, options, print);
case "NewExpression":
case "ImportExpression":
case "OptionalCallExpression":
case "CallExpression":
return printCallExpression(path, options, print);
case "ObjectTypeInternalSlot":
return concat([
n.static ? "static " : "",
"[[",
path.call(print, "id"),
"]]",
printOptionalToken(path),
n.method ? "" : ": ",
path.call(print, "value"),
]);
case "ObjectExpression":
case "ObjectPattern":
case "ObjectTypeAnnotation":
case "RecordExpression":
return printObject(path, options, print);
// Babel 6
case "ObjectProperty": // Non-standard AST node type.
case "Property":
if (n.method || n.kind === "get" || n.kind === "set") {
return printMethod(path, options, print);
}
return printProperty(path, options, print);
case "ObjectMethod":
return printMethod(path, options, print);
case "Decorator":
return concat([
"@",
path.call(print, "expression"),
path.call(print, "callee"),
]);
case "ArrayExpression":
case "ArrayPattern":
case "TupleExpression":
return printArray(path, options, print);
case "SequenceExpression": {
const parent = path.getParentNode(0);
if (
parent.type === "ExpressionStatement" ||
parent.type === "ForStatement"
) {
// For ExpressionStatements and for-loop heads, which are among
// the few places a SequenceExpression appears unparenthesized, we want
// to indent expressions after the first.
const parts = [];
path.each((expressionPath, index) => {
if (index === 0) {
parts.push(print(expressionPath));
} else {
parts.push(",", indent(concat([line, print(expressionPath)])));
}
}, "expressions");
return group(concat(parts));
}
return group(
concat([join(concat([",", line]), path.map(print, "expressions"))])
);
}
case "ThisExpression":
return "this";
case "Super":
return "super";
case "NullLiteral": // Babel 6 Literal split
return "null";
case "RegExpLiteral": // Babel 6 Literal split
return printRegex(n);
case "NumericLiteral": // Babel 6 Literal split
return printNumber(n.extra.raw);
case "DecimalLiteral":
return printNumber(n.value) + "m";
case "BigIntLiteral":
// babel: n.extra.raw, flow: n.bigint
return (n.bigint || n.extra.raw).toLowerCase();
case "BooleanLiteral": // Babel 6 Literal split
case "StringLiteral": // Babel 6 Literal split
case "Literal":
if (n.regex) {
return printRegex(n.regex);
}
// typescript
if (n.bigint) {
return n.raw.toLowerCase();
}
if (typeof n.value === "number") {
return printNumber(n.raw);
}
if (typeof n.value !== "string") {
return "" + n.value;
}
return nodeStr(n, options);
case "Directive":
return concat([path.call(print, "value"), semi]); // Babel 6
case "DirectiveLiteral":
return nodeStr(n, options);
case "UnaryExpression":
parts.push(n.operator);
if (/[a-z]$/.test(n.operator)) {
parts.push(" ");
}
if (hasComment(n.argument)) {
parts.push(
group(
concat([
"(",
indent(concat([softline, path.call(print, "argument")])),
softline,
")",
])
)
);
} else {
parts.push(path.call(print, "argument"));
}
return concat(parts);
case "UpdateExpression":
parts.push(path.call(print, "argument"), n.operator);
if (n.prefix) {
parts.reverse();
}
return concat(parts);
case "ConditionalExpression":
return printTernary(path, options, print);
case "VariableDeclaration": {
const printed = path.map((childPath) => print(childPath), "declarations");
// We generally want to terminate all variable declarations with a
// semicolon, except when they in the () part of for loops.
const parentNode = path.getParentNode();
const isParentForLoop =
parentNode.type === "ForStatement" ||
parentNode.type === "ForInStatement" ||
parentNode.type === "ForOfStatement";
const hasValue = n.declarations.some((decl) => decl.init);
let firstVariable;
if (printed.length === 1 && !hasComment(n.declarations[0])) {
firstVariable = printed[0];
} else if (printed.length > 0) {
// Indent first var to comply with eslint one-var rule
firstVariable = indent(printed[0]);
}
parts = [
n.declare ? "declare " : "",
n.kind,
firstVariable ? concat([" ", firstVariable]) : "",
indent(
concat(
printed
.slice(1)
.map((p) =>
concat([",", hasValue && !isParentForLoop ? hardline : line, p])
)
)
),
];
if (!(isParentForLoop && parentNode.body !== n)) {
parts.push(semi);
}
return group(concat(parts));
}
case "WithStatement":
return group(
concat([
"with (",
path.call(print, "object"),
")",
adjustClause(n.body, path.call(print, "body")),
])
);
case "IfStatement": {
const con = adjustClause(n.consequent, path.call(print, "consequent"));
const opening = group(
concat([
"if (",
group(
concat([
indent(concat([softline, path.call(print, "test")])),
softline,
])
),
")",
con,
])
);
parts.push(opening);
if (n.alternate) {
const commentOnOwnLine =
hasComment(
n.consequent,
CommentCheckFlags.Trailing | CommentCheckFlags.Line
) || needsHardlineAfterDanglingComment(n);
const elseOnSameLine =
n.consequent.type === "BlockStatement" && !commentOnOwnLine;
parts.push(elseOnSameLine ? " " : hardline);
if (hasComment(n, CommentCheckFlags.Dangling)) {
parts.push(
printDanglingComments(path, options, true),
commentOnOwnLine ? hardline : " "
);
}
parts.push(
"else",
group(
adjustClause(
n.alternate,
path.call(print, "alternate"),
n.alternate.type === "IfStatement"
)
)
);
}
return concat(parts);
}
case "ForStatement": {
const body = adjustClause(n.body, path.call(print, "body"));
// We want to keep dangling comments above the loop to stay consistent.
// Any comment positioned between the for statement and the parentheses
// is going to be printed before the statement.
const dangling = printDanglingComments(
path,
options,
/* sameLine */ true
);
const printedComments = dangling ? concat([dangling, softline]) : "";
if (!n.init && !n.test && !n.update) {
return concat([printedComments, group(concat(["for (;;)", body]))]);
}
return concat([
printedComments,
group(
concat([
"for (",
group(
concat([
indent(
concat([
softline,
path.call(print, "init"),
";",
line,
path.call(print, "test"),
";",
line,
path.call(print, "update"),
])
),
softline,
])
),
")",
body,
])
),
]);
}
case "WhileStatement":
return group(
concat([
"while (",
group(
concat([
indent(concat([softline, path.call(print, "test")])),
softline,
])
),
")",
adjustClause(n.body, path.call(print, "body")),
])
);
case "ForInStatement":
return group(
concat([
"for (",
path.call(print, "left"),
" in ",
path.call(print, "right"),
")",
adjustClause(n.body, path.call(print, "body")),
])
);
case "ForOfStatement":
return group(
concat([
"for",
n.await ? " await" : "",
" (",
path.call(print, "left"),
" of ",
path.call(print, "right"),
")",
adjustClause(n.body, path.call(print, "body")),
])
);
case "DoWhileStatement": {
const clause = adjustClause(n.body, path.call(print, "body"));
const doBody = group(concat(["do", clause]));
parts = [doBody];
if (n.body.type === "BlockStatement") {
parts.push(" ");
} else {
parts.push(hardline);
}
parts.push("while (");
parts.push(
group(
concat([
indent(concat([softline, path.call(print, "test")])),
softline,
])
),
")",
semi
);
return concat(parts);
}
case "DoExpression":
return concat(["do ", path.call(print, "body")]);
case "BreakStatement":
parts.push("break");
if (n.label) {
parts.push(" ", path.call(print, "label"));
}
parts.push(semi);
return concat(parts);
case "ContinueStatement":
parts.push("continue");
if (n.label) {
parts.push(" ", path.call(print, "label"));
}
parts.push(semi);
return concat(parts);
case "LabeledStatement":
if (n.body.type === "EmptyStatement") {
return concat([path.call(print, "label"), ":;"]);
}
return concat([
path.call(print, "label"),
": ",
path.call(print, "body"),
]);
case "TryStatement":
return concat([
"try ",
path.call(print, "block"),
n.handler ? concat([" ", path.call(print, "handler")]) : "",
n.finalizer ? concat([" finally ", path.call(print, "finalizer")]) : "",
]);
case "CatchClause":
if (n.param) {
const parameterHasComments = hasComment(
n.param,
(comment) =>
!isBlockComment(comment) ||
(comment.leading &&
hasNewline(options.originalText, locEnd(comment))) ||
(comment.trailing &&
hasNewline(options.originalText, locStart(comment), {
backwards: true,
}))
);
const param = path.call(print, "param");
return concat([
"catch ",
parameterHasComments
? concat(["(", indent(concat([softline, param])), softline, ") "])
: concat(["(", param, ") "]),
path.call(print, "body"),
]);
}
return concat(["catch ", path.call(print, "body")]);
// Note: ignoring n.lexical because it has no printing consequences.
case "SwitchStatement":
return concat([
group(
concat([
"switch (",
indent(concat([softline, path.call(print, "discriminant")])),
softline,
")",
])
),
" {",
n.cases.length > 0
? indent(
concat([
hardline,
join(
hardline,
path.map((casePath, index, cases) => {
const caseNode = casePath.getValue();
return concat([
casePath.call(print),
index !== cases.length - 1 &&
isNextLineEmpty(caseNode, options)
? hardline
: "",
]);
}, "cases")
),
])
)
: "",
hardline,
"}",
]);
case "SwitchCase": {
if (n.test) {
parts.push("case ", path.call(print, "test"), ":");
} else {
parts.push("default:");
}
const consequent = n.consequent.filter(
(node) => node.type !== "EmptyStatement"
);
if (consequent.length > 0) {
const cons = printSwitchCaseConsequent(path, options, print);
parts.push(
consequent.length === 1 && consequent[0].type === "BlockStatement"
? concat([" ", cons])
: indent(concat([hardline, cons]))
);
}
return concat(parts);
}
// JSX extensions below.
case "DebuggerStatement":
return concat(["debugger", semi]);
case "ClassDeclaration":
case "ClassExpression":
return printClass(path, options, print);
case "ClassMethod":
case "ClassPrivateMethod":
case "MethodDefinition":
return printClassMethod(path, options, print);
case "ClassProperty":
case "FieldDefinition":
case "ClassPrivateProperty":
return printClassProperty(path, options, print);
case "TemplateElement":
return join(literalline, n.value.raw.split(/\r?\n/g));
case "TemplateLiteral":
return printTemplateLiteral(path, print, options);
case "TaggedTemplateExpression":
return concat([
path.call(print, "tag"),
path.call(print, "typeParameters"),
path.call(print, "quasi"),
]);
// 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(n.type));
case "ExistsTypeAnnotation":
return "*";
case "EmptyTypeAnnotation":
return "empty";
case "MixedTypeAnnotation":
return "mixed";
case "ArrayTypeAnnotation":
return concat([path.call(print, "elementType"), "[]"]);
case "BooleanLiteralTypeAnnotation":
return "" + n.value;
case "EnumDeclaration":
return concat([
"enum ",
path.call(print, "id"),
" ",
path.call(print, "body"),
]);
case "EnumBooleanBody":
case "EnumNumberBody":
case "EnumStringBody":
case "EnumSymbolBody": {
if (n.type === "EnumSymbolBody" || n.explicitType) {
let type = null;
switch (n.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 (n.members.length === 0 && !n.hasUnknownMembers) {
parts.push(
group(
concat(["{", printDanglingComments(path, options), softline, "}"])
)
);
} else {
const members = n.members.length
? [
hardline,
printArrayItems(path, options, "members", print),
n.hasUnknownMembers || shouldPrintComma(options) ? "," : "",
]
: [];
parts.push(
group(
concat([
"{",
indent(
concat([
...members,
...(n.hasUnknownMembers ? [hardline, "..."] : []),
])
),
printDanglingComments(path, options, /* sameIndent */ true),
hardline,
"}",
])
)
);
}
return concat(parts);
}
case "EnumBooleanMember":
case "EnumNumberMember":
case "EnumStringMember":
return concat([
path.call(print, "id"),
" = ",
typeof n.init === "object" ? path.call(print, "init") : String(n.init),
]);
case "EnumDefaultedMember":
return path.call(print, "id");
case "FunctionTypeParam": {
const name = n.name
? path.call(print, "name")
: path.getParentNode().this === n
? "this"
: "";
return concat([
name,
printOptionalToken(path),
name ? ": " : "",
path.call(print, "typeAnnotation"),
]);
}
case "InterfaceDeclaration":
case "InterfaceTypeAnnotation":
return printInterface(path, options, print);
case "ClassImplements":
case "InterfaceExtends":
return concat([
path.call(print, "id"),
path.call(print, "typeParameters"),
]);
case "NullableTypeAnnotation":
return concat(["?", path.call(print, "typeAnnotation")]);
case "Variance": {
const { kind } = n;
assert.ok(kind === "plus" || kind === "minus");
return kind === "plus" ? "+" : "-";
}
case "ObjectTypeCallProperty":
if (n.static) {
parts.push("static ");
}
parts.push(path.call(print, "value"));
return concat(parts);
case "ObjectTypeIndexer": {
return concat([
n.variance ? path.call(print, "variance") : "",
"[",
path.call(print, "id"),
n.id ? ": " : "",
path.call(print, "key"),
"]: ",
path.call(print, "value"),
]);
}
case "ObjectTypeProperty": {
let modifier = "";
if (n.proto) {
modifier = "proto ";
} else if (n.static) {
modifier = "static ";
}
return concat([
modifier,
isGetterOrSetter(n) ? n.kind + " " : "",
n.variance ? path.call(print, "variance") : "",
printPropertyKey(path, options, print),
printOptionalToken(path),
isFunctionNotation(n) ? "" : ": ",
path.call(print, "value"),
]);
}
case "QualifiedTypeIdentifier":
return concat([
path.call(print, "qualification"),
".",
path.call(print, "id"),
]);
case "StringLiteralTypeAnnotation":
return nodeStr(n, options);
case "NumberLiteralTypeAnnotation":
assert.strictEqual(typeof n.value, "number");
// fall through
case "BigIntLiteralTypeAnnotation":
if (n.extra != null) {
return printNumber(n.extra.raw);
}
return printNumber(n.raw);
case "TypeCastExpression": {
return concat([
"(",
path.call(print, "expression"),
printTypeAnnotation(path, options, print),
")",
]);
}
case "TypeParameterDeclaration":
case "TypeParameterInstantiation": {
const printed = printTypeParameters(path, options, print, "params");
if (options.parser === "flow") {
const start = locStart(n);
const end = locEnd(n);
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 concat(["/*:: ", 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 concat(["%checks(", path.call(print, "value"), ")"]);
case "AnyTypeAnnotation":
case "TSAnyKeyword":
return "any";
case "BooleanTypeAnnotation":
case "TSBooleanKeyword":
return "boolean";
case "BigIntTypeAnnotation":
case "TSBigIntKeyword":
return "bigint";
case "TSConstKeyword":
return "const";
case "NullLiteralTypeAnnotation":
case "TSNullKeyword":
return "null";
case "NumberTypeAnnotation":
case "TSNumberKeyword":
return "number";
case "SymbolTypeAnnotation":
case "TSSymbolKeyword":
return "symbol";
case "StringTypeAnnotation":
case "TSStringKeyword":
return "string";
case "VoidTypeAnnotation":
case "TSVoidKeyword":
return "void";
case "ThisTypeAnnotation":
case "TSThisType":
return "this";
case "PrivateName":
// babel use `id`, meriyah use `name`
return concat(["#", path.call(print, n.id ? "id" : "name")]);
case "InterpreterDirective":
parts.push("#!", n.value, hardline);
if (isNextLineEmpty(n, options)) {
parts.push(hardline);
}
return concat(parts);
case "PipelineBareFunction":
return path.call(print, "callee");
case "PipelineTopicExpression":
return path.call(print, "expression");
case "PipelinePrimaryTopicReference": {
parts.push("#");
return concat(parts);
}
case "ArgumentPlaceholder":
return "?";
default:
/* istanbul ignore next */
throw new Error("unknown type: " + JSON.stringify(n.type));
}
}
function nodeStr(node, options, isFlowOrTypeScriptDirectiveLiteral) {
const raw = rawText(node);
const isDirectiveLiteral =
isFlowOrTypeScriptDirectiveLiteral || node.type === "DirectiveLiteral";
return printString(raw, options, isDirectiveLiteral);
}
function printRegex(node) {
const flags = node.flags.split("").sort().join("");
return `/${node.pattern}/${flags}`;
}
function canAttachComment(node) {
return (
node.type &&
!isBlockComment(node) &&
!isLineComment(node) &&
node.type !== "EmptyStatement" &&
node.type !== "TemplateElement" &&
node.type !== "Import" &&
// `babel-ts` don't have similar node for `class Foo { bar() /* bat */; }`
node.type !== "TSEmptyBodyFunctionExpression"
);
}
module.exports = {
preprocess,
print: genericPrint,
embed,
insertPragma,
massageAstNode: clean,
hasPrettierIgnore(path) {
return hasIgnoreComment(path) || hasJsxIgnoreComment(path);
},
willPrintOwnComments: handleComments.willPrintOwnComments,
canAttachComment,
printComment,
isBlockComment,
handleComments: {
// TODO: Make this as default behavior
avoidAstMutation: true,
ownLine: handleComments.handleOwnLineComment,
endOfLine: handleComments.handleEndOfLineComment,
remaining: handleComments.handleRemainingComment,
},
getGapRegex: handleComments.getGapRegex,
getCommentChildNodes: handleComments.getCommentChildNodes,
};