2017-01-10 20:18:22 +03:00
|
|
|
"use strict";
|
2016-12-23 21:38:10 +03:00
|
|
|
var assert = require("assert");
|
2017-01-19 22:46:37 +03:00
|
|
|
var comments = require("./comments");
|
2016-12-23 21:38:10 +03:00
|
|
|
var FastPath = require("./fast-path");
|
|
|
|
var util = require("./util");
|
2017-01-11 08:48:49 +03:00
|
|
|
var isIdentifierName = require("esutils").keyword.isIdentifierNameES6;
|
2017-01-16 19:03:59 +03:00
|
|
|
var jsesc = require("jsesc");
|
2016-12-23 21:38:10 +03:00
|
|
|
|
2017-01-19 23:46:57 +03:00
|
|
|
var docBuilders = require("./doc-builders");
|
|
|
|
var concat = docBuilders.concat;
|
|
|
|
var join = docBuilders.join;
|
|
|
|
var line = docBuilders.line;
|
|
|
|
var hardline = docBuilders.hardline;
|
|
|
|
var softline = docBuilders.softline;
|
|
|
|
var literalline = docBuilders.literalline;
|
|
|
|
var group = docBuilders.group;
|
|
|
|
var multilineGroup = docBuilders.multilineGroup;
|
|
|
|
var indent = docBuilders.indent;
|
|
|
|
var conditionalGroup = docBuilders.conditionalGroup;
|
|
|
|
var ifBreak = docBuilders.ifBreak;
|
|
|
|
|
|
|
|
var docUtils = require("./doc-utils");
|
|
|
|
var hasHardLine = docUtils.hasHardLine;
|
|
|
|
var getFirstString = docUtils.getFirstString;
|
|
|
|
var isEmpty = docUtils.isEmpty;
|
|
|
|
|
|
|
|
var types = require("ast-types");
|
|
|
|
var namedTypes = types.namedTypes;
|
|
|
|
var isString = types.builtInTypes.string;
|
|
|
|
var isObject = types.builtInTypes.object;
|
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
function maybeAddParens(path, lines) {
|
2017-01-09 20:09:04 +03:00
|
|
|
return path.needsParens() ? concat([ "(", lines, ")" ]) : lines;
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function genericPrint(path, options, printPath) {
|
|
|
|
assert.ok(path instanceof FastPath);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
var node = path.getValue();
|
|
|
|
var parts = [];
|
|
|
|
var needsParens = false;
|
2016-12-31 07:03:22 +03:00
|
|
|
var linesWithoutParens = genericPrintNoParens(path, options, printPath);
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-31 01:00:11 +03:00
|
|
|
if (!node || isEmpty(linesWithoutParens)) {
|
2016-12-23 21:38:10 +03:00
|
|
|
return linesWithoutParens;
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-31 07:03:22 +03:00
|
|
|
if (
|
2017-01-19 02:31:46 +03:00
|
|
|
node.decorators &&
|
|
|
|
node.decorators.length > 0 &&
|
2016-12-23 21:38:10 +03:00
|
|
|
// If the parent node is an export declaration, it will be
|
|
|
|
// responsible for printing node.decorators.
|
2016-12-31 07:03:22 +03:00
|
|
|
!util.getParentExportDeclaration(path)
|
|
|
|
) {
|
|
|
|
path.each(
|
|
|
|
function(decoratorPath) {
|
|
|
|
parts.push(printPath(decoratorPath), line);
|
|
|
|
},
|
|
|
|
"decorators"
|
|
|
|
);
|
2017-01-09 20:09:04 +03:00
|
|
|
} else if (
|
2017-01-19 02:31:46 +03:00
|
|
|
util.isExportDeclaration(node) &&
|
|
|
|
node.declaration &&
|
2017-01-09 20:09:04 +03:00
|
|
|
node.declaration.decorators
|
|
|
|
) {
|
|
|
|
// Export declarations are responsible for printing any decorators
|
|
|
|
// that logically apply to node.declaration.
|
|
|
|
path.each(
|
|
|
|
function(decoratorPath) {
|
|
|
|
parts.push(printPath(decoratorPath), line);
|
|
|
|
},
|
|
|
|
"declaration",
|
|
|
|
"decorators"
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
// Nodes with decorators can't have parentheses, so we can avoid
|
|
|
|
// computing path.needsParens() except in this case.
|
|
|
|
needsParens = path.needsParens();
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (needsParens) {
|
|
|
|
parts.unshift("(");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
parts.push(linesWithoutParens);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (needsParens) {
|
|
|
|
parts.push(")");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
return concat(parts);
|
|
|
|
}
|
|
|
|
|
|
|
|
function genericPrintNoParens(path, options, print) {
|
|
|
|
var n = path.getValue();
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (!n) {
|
2017-01-20 00:20:03 +03:00
|
|
|
return "";
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (typeof n === "string") {
|
2017-01-20 00:20:03 +03:00
|
|
|
return n;
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-27 21:28:04 +03:00
|
|
|
// TODO: For some reason NumericLiteralTypeAnnotation is not
|
|
|
|
// printable so this throws, but I think that's a bug in ast-types.
|
|
|
|
// This assert isn't very useful though.
|
|
|
|
// namedTypes.Printable.assert(n);
|
2016-12-23 21:38:10 +03:00
|
|
|
var parts = [];
|
|
|
|
switch (n.type) {
|
2017-01-13 23:03:53 +03:00
|
|
|
case "File":
|
|
|
|
return path.call(print, "program");
|
|
|
|
case "Program":
|
|
|
|
// Babel 6
|
|
|
|
if (n.directives) {
|
|
|
|
path.each(
|
|
|
|
function(childPath) {
|
|
|
|
parts.push(print(childPath), ";", hardline);
|
|
|
|
},
|
|
|
|
"directives"
|
|
|
|
);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(
|
|
|
|
path.call(
|
|
|
|
function(bodyPath) {
|
|
|
|
return printStatementSequence(bodyPath, options, print);
|
|
|
|
},
|
|
|
|
"body"
|
|
|
|
)
|
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(hardline);
|
2017-01-11 17:39:32 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
// Babel extension.
|
|
|
|
case "Noop":
|
|
|
|
case "EmptyStatement":
|
2017-01-20 00:20:03 +03:00
|
|
|
return "";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ExpressionStatement":
|
|
|
|
return concat([ path.call(print, "expression"), ";" ]);
|
|
|
|
case // Babel extension.
|
|
|
|
"ParenthesizedExpression":
|
|
|
|
return concat([ "(", path.call(print, "expression"), ")" ]);
|
|
|
|
case "AssignmentExpression":
|
|
|
|
return group(
|
|
|
|
concat([
|
|
|
|
path.call(print, "left"),
|
|
|
|
" ",
|
|
|
|
n.operator,
|
|
|
|
" ",
|
|
|
|
path.call(print, "right")
|
|
|
|
])
|
|
|
|
);
|
|
|
|
case "BinaryExpression":
|
2017-01-19 01:01:17 +03:00
|
|
|
case "LogicalExpression": {
|
|
|
|
const parts = [];
|
|
|
|
printBinaryishExpressions(path, parts, print, options);
|
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
return group(
|
|
|
|
concat([
|
|
|
|
// Don't include the initial expression in the indentation
|
|
|
|
// level. The first item is guaranteed to be the first
|
|
|
|
// left-most expression.
|
|
|
|
parts.length > 0 ? parts[0] : "",
|
|
|
|
indent(options.tabWidth, concat(parts.slice(1)))
|
|
|
|
])
|
|
|
|
);
|
2017-01-19 01:01:17 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "AssignmentPattern":
|
|
|
|
return concat([
|
2017-01-09 20:09:04 +03:00
|
|
|
path.call(print, "left"),
|
2017-01-13 23:03:53 +03:00
|
|
|
" = ",
|
2017-01-09 20:09:04 +03:00
|
|
|
path.call(print, "right")
|
2017-01-13 23:03:53 +03:00
|
|
|
]);
|
|
|
|
case "MemberExpression": {
|
2017-01-09 20:09:04 +03:00
|
|
|
return concat([
|
|
|
|
path.call(print, "object"),
|
|
|
|
printMemberLookup(path, print)
|
|
|
|
]);
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "MetaProperty":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "meta"),
|
|
|
|
".",
|
|
|
|
path.call(print, "property")
|
|
|
|
]);
|
|
|
|
case "BindExpression":
|
|
|
|
if (n.object) {
|
|
|
|
parts.push(path.call(print, "object"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push("::", path.call(print, "callee"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "Path":
|
|
|
|
return join(".", n.body);
|
|
|
|
case "Identifier":
|
|
|
|
return concat([
|
|
|
|
n.name,
|
|
|
|
n.optional ? "?" : "",
|
|
|
|
path.call(print, "typeAnnotation")
|
|
|
|
]);
|
|
|
|
case "SpreadElement":
|
|
|
|
case "SpreadElementPattern":
|
|
|
|
// Babel 6 for ObjectPattern
|
|
|
|
case "RestProperty":
|
|
|
|
case "SpreadProperty":
|
|
|
|
case "SpreadPropertyPattern":
|
|
|
|
case "RestElement":
|
|
|
|
return concat([
|
|
|
|
"...",
|
|
|
|
path.call(print, "argument"),
|
|
|
|
path.call(print, "typeAnnotation")
|
|
|
|
]);
|
|
|
|
case "FunctionDeclaration":
|
|
|
|
case "FunctionExpression":
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.async) parts.push("async ");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push("function");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.generator) parts.push("*");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.id) {
|
|
|
|
parts.push(" ", path.call(print, "id"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(
|
|
|
|
path.call(print, "typeParameters"),
|
|
|
|
multilineGroup(
|
|
|
|
concat([
|
|
|
|
printFunctionParams(path, print, options),
|
|
|
|
printReturnType(path, print)
|
|
|
|
])
|
|
|
|
),
|
|
|
|
" ",
|
|
|
|
path.call(print, "body")
|
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ArrowFunctionExpression":
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.async) parts.push("async ");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.typeParameters) {
|
|
|
|
parts.push(path.call(print, "typeParameters"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (
|
2017-01-19 02:31:46 +03:00
|
|
|
n.params.length === 1 &&
|
|
|
|
!n.rest &&
|
|
|
|
n.params[0].type === "Identifier" &&
|
|
|
|
!n.params[0].typeAnnotation &&
|
2017-01-13 23:03:53 +03:00
|
|
|
!n.predicate &&
|
|
|
|
!n.returnType
|
|
|
|
) {
|
|
|
|
parts.push(path.call(print, "params", 0));
|
|
|
|
} else {
|
|
|
|
parts.push(
|
|
|
|
multilineGroup(
|
|
|
|
concat([
|
|
|
|
printFunctionParams(path, print, options),
|
|
|
|
printReturnType(path, print)
|
|
|
|
])
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(" =>");
|
2017-01-11 00:46:33 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
const body = path.call(print, "body");
|
|
|
|
const collapsed = concat([ concat(parts), " ", body ]);
|
2017-01-12 02:15:12 +03:00
|
|
|
|
2017-01-15 08:04:50 +03:00
|
|
|
if (
|
2017-01-19 02:31:46 +03:00
|
|
|
n.body.type === "ArrayExpression" ||
|
|
|
|
n.body.type === "ObjectExpression" ||
|
|
|
|
n.body.type === "JSXElement"
|
2017-01-15 08:04:50 +03:00
|
|
|
) {
|
2017-01-13 23:03:53 +03:00
|
|
|
return group(collapsed);
|
|
|
|
}
|
2017-01-12 02:15:12 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return conditionalGroup([
|
|
|
|
collapsed,
|
|
|
|
concat([
|
|
|
|
concat(parts),
|
|
|
|
indent(options.tabWidth, concat([ line, body ]))
|
|
|
|
])
|
|
|
|
]);
|
|
|
|
case "MethodDefinition":
|
|
|
|
if (n.static) {
|
|
|
|
parts.push("static ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(printMethod(path, options, print));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "YieldExpression":
|
|
|
|
parts.push("yield");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.delegate) parts.push("*");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.argument) parts.push(" ", path.call(print, "argument"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "AwaitExpression":
|
|
|
|
parts.push("await");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.all) parts.push("*");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.argument) parts.push(" ", path.call(print, "argument"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ModuleDeclaration":
|
|
|
|
parts.push("module", path.call(print, "id"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.source) {
|
|
|
|
assert.ok(!n.body);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push("from", path.call(print, "source"));
|
|
|
|
} else {
|
|
|
|
parts.push(path.call(print, "body"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return join(" ", parts);
|
|
|
|
case "ImportSpecifier":
|
|
|
|
if (n.imported) {
|
|
|
|
parts.push(path.call(print, "imported"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.local && n.local.name !== n.imported.name) {
|
|
|
|
parts.push(" as ", path.call(print, "local"));
|
|
|
|
}
|
|
|
|
} else if (n.id) {
|
|
|
|
parts.push(path.call(print, "id"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.name) {
|
|
|
|
parts.push(" as ", path.call(print, "name"));
|
|
|
|
}
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ExportSpecifier":
|
|
|
|
if (n.local) {
|
|
|
|
parts.push(path.call(print, "local"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.exported && n.exported.name !== n.local.name) {
|
|
|
|
parts.push(" as ", path.call(print, "exported"));
|
|
|
|
}
|
|
|
|
} else if (n.id) {
|
|
|
|
parts.push(path.call(print, "id"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.name) {
|
|
|
|
parts.push(" as ", path.call(print, "name"));
|
|
|
|
}
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ExportBatchSpecifier":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "*";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ImportNamespaceSpecifier":
|
|
|
|
parts.push("* as ");
|
|
|
|
|
|
|
|
if (n.local) {
|
|
|
|
parts.push(path.call(print, "local"));
|
|
|
|
} else if (n.id) {
|
|
|
|
parts.push(path.call(print, "id"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ImportDefaultSpecifier":
|
|
|
|
if (n.local) {
|
|
|
|
return path.call(print, "local");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return path.call(print, "id");
|
|
|
|
case "ExportDeclaration":
|
|
|
|
case "ExportDefaultDeclaration":
|
|
|
|
case "ExportNamedDeclaration":
|
|
|
|
return printExportDeclaration(path, options, print);
|
|
|
|
case "ExportAllDeclaration":
|
|
|
|
parts.push("export *");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.exported) {
|
|
|
|
parts.push(" as ", path.call(print, "exported"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(" from ", path.call(print, "source"), ";");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ExportNamespaceSpecifier":
|
|
|
|
return concat([ "* as ", path.call(print, "exported") ]);
|
|
|
|
case "ExportDefaultSpecifier":
|
|
|
|
return path.call(print, "exported");
|
|
|
|
case "ImportDeclaration":
|
|
|
|
parts.push("import ");
|
|
|
|
|
|
|
|
if (n.importKind && n.importKind !== "value") {
|
|
|
|
parts.push(n.importKind + " ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.specifiers && n.specifiers.length > 0) {
|
|
|
|
var standalones = [];
|
|
|
|
var grouped = [];
|
|
|
|
|
|
|
|
path.each(
|
|
|
|
function(specifierPath) {
|
|
|
|
var value = specifierPath.getValue();
|
|
|
|
if (
|
|
|
|
namedTypes.ImportDefaultSpecifier.check(value) ||
|
|
|
|
namedTypes.ImportNamespaceSpecifier.check(value)
|
|
|
|
) {
|
|
|
|
standalones.push(print(specifierPath));
|
|
|
|
} else {
|
|
|
|
grouped.push(print(specifierPath));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"specifiers"
|
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
assert.ok(standalones.length <= 1);
|
Break multiline imports (#167)
Follows the same pattern as https://github.com/jlongster/prettier/pull/156/files
```js
echo "import { aaaaaaaa, bbbbbb, cccccc, dddddddd, eeeeeee, fffffffff, ggggggggg } from 'vjeux';" | ./bin/prettier.js --stdin
import {
aaaaaaaa,
bbbbbb,
cccccc,
dddddddd,
eeeeeee,
fffffffff,
ggggggggg
} from "vjeux";
```
2017-01-13 20:59:33 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (standalones.length > 0) {
|
2017-01-19 02:31:46 +03:00
|
|
|
parts.push(standalones[0]);
|
Break multiline imports (#167)
Follows the same pattern as https://github.com/jlongster/prettier/pull/156/files
```js
echo "import { aaaaaaaa, bbbbbb, cccccc, dddddddd, eeeeeee, fffffffff, ggggggggg } from 'vjeux';" | ./bin/prettier.js --stdin
import {
aaaaaaaa,
bbbbbb,
cccccc,
dddddddd,
eeeeeee,
fffffffff,
ggggggggg
} from "vjeux";
```
2017-01-13 20:59:33 +03:00
|
|
|
}
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (standalones.length > 0 && grouped.length > 0) {
|
|
|
|
parts.push(", ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (grouped.length > 0) {
|
|
|
|
parts.push(
|
2017-01-18 01:12:56 +03:00
|
|
|
multilineGroup(
|
Break multiline imports (#167)
Follows the same pattern as https://github.com/jlongster/prettier/pull/156/files
```js
echo "import { aaaaaaaa, bbbbbb, cccccc, dddddddd, eeeeeee, fffffffff, ggggggggg } from 'vjeux';" | ./bin/prettier.js --stdin
import {
aaaaaaaa,
bbbbbb,
cccccc,
dddddddd,
eeeeeee,
fffffffff,
ggggggggg
} from "vjeux";
```
2017-01-13 20:59:33 +03:00
|
|
|
concat([
|
2017-01-13 23:03:53 +03:00
|
|
|
"{",
|
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat([
|
|
|
|
options.bracketSpacing ? line : softline,
|
2017-01-19 02:31:46 +03:00
|
|
|
join(concat([ ",", line ]), grouped)
|
2017-01-13 23:03:53 +03:00
|
|
|
])
|
|
|
|
),
|
|
|
|
ifBreak(options.trailingComma ? "," : ""),
|
Break multiline imports (#167)
Follows the same pattern as https://github.com/jlongster/prettier/pull/156/files
```js
echo "import { aaaaaaaa, bbbbbb, cccccc, dddddddd, eeeeeee, fffffffff, ggggggggg } from 'vjeux';" | ./bin/prettier.js --stdin
import {
aaaaaaaa,
bbbbbb,
cccccc,
dddddddd,
eeeeeee,
fffffffff,
ggggggggg
} from "vjeux";
```
2017-01-13 20:59:33 +03:00
|
|
|
options.bracketSpacing ? line : softline,
|
2017-01-13 23:03:53 +03:00
|
|
|
"}"
|
Break multiline imports (#167)
Follows the same pattern as https://github.com/jlongster/prettier/pull/156/files
```js
echo "import { aaaaaaaa, bbbbbb, cccccc, dddddddd, eeeeeee, fffffffff, ggggggggg } from 'vjeux';" | ./bin/prettier.js --stdin
import {
aaaaaaaa,
bbbbbb,
cccccc,
dddddddd,
eeeeeee,
fffffffff,
ggggggggg
} from "vjeux";
```
2017-01-13 20:59:33 +03:00
|
|
|
])
|
2017-01-13 23:03:53 +03:00
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(" from ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(path.call(print, "source"), ";");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "BlockStatement":
|
|
|
|
var naked = path.call(
|
|
|
|
function(bodyPath) {
|
|
|
|
return printStatementSequence(bodyPath, options, print);
|
2016-12-31 07:03:22 +03:00
|
|
|
},
|
2017-01-13 23:03:53 +03:00
|
|
|
"body"
|
2016-12-31 07:03:22 +03:00
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-15 07:39:41 +03:00
|
|
|
const hasContent = getFirstString(naked);
|
|
|
|
const hasDirectives = n.directives && n.directives.length > 0;
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push("{");
|
|
|
|
|
|
|
|
// Babel 6
|
2017-01-15 07:39:41 +03:00
|
|
|
if (hasDirectives) {
|
2017-01-13 23:03:53 +03:00
|
|
|
path.each(
|
|
|
|
function(childPath) {
|
|
|
|
parts.push(
|
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
2017-01-15 07:39:41 +03:00
|
|
|
concat([ hardline, print(childPath), ";" ])
|
2017-01-13 23:03:53 +03:00
|
|
|
)
|
|
|
|
);
|
|
|
|
},
|
|
|
|
"directives"
|
|
|
|
);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-15 07:39:41 +03:00
|
|
|
if (hasContent) {
|
2017-01-19 02:31:46 +03:00
|
|
|
parts.push(indent(options.tabWidth, concat([ hardline, naked ])));
|
2017-01-20 21:12:37 +03:00
|
|
|
} else if (n.comments) {
|
2017-01-19 22:46:37 +03:00
|
|
|
parts.push(
|
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
comments.printDanglingComments(path, print, options)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(hardline, "}");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ReturnStatement":
|
|
|
|
parts.push("return");
|
|
|
|
|
|
|
|
if (n.argument) {
|
|
|
|
parts.push(" ", path.call(print, "argument"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(";");
|
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
case "CallExpression": {
|
2017-01-09 20:09:04 +03:00
|
|
|
const parent = path.getParentNode();
|
|
|
|
// We detect calls on member lookups and possibly print them in a
|
|
|
|
// special chain format. See `printMemberChain` for more info.
|
|
|
|
if (n.callee.type === "MemberExpression") {
|
|
|
|
const printed = printMemberChain(n, options, print);
|
|
|
|
if (printed) {
|
|
|
|
return printed;
|
|
|
|
}
|
2017-01-05 23:26:45 +03:00
|
|
|
}
|
|
|
|
|
2017-01-09 20:09:04 +03:00
|
|
|
return concat([
|
|
|
|
path.call(print, "callee"),
|
|
|
|
printArgumentsList(path, options, print)
|
|
|
|
]);
|
|
|
|
}
|
2017-01-05 23:26:45 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ObjectExpression":
|
|
|
|
case "ObjectPattern":
|
|
|
|
case "ObjectTypeAnnotation":
|
|
|
|
var allowBreak = false;
|
|
|
|
var isTypeAnnotation = n.type === "ObjectTypeAnnotation";
|
|
|
|
// Leave this here because we *might* want to make this
|
|
|
|
// configurable later -- flow accepts ";" for type separators
|
|
|
|
var separator = isTypeAnnotation ? "," : ",";
|
|
|
|
var fields = [];
|
|
|
|
var leftBrace = n.exact ? "{|" : "{";
|
|
|
|
var rightBrace = n.exact ? "|}" : "}";
|
|
|
|
|
|
|
|
if (isTypeAnnotation) {
|
|
|
|
fields.push("indexers", "callProperties");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
fields.push("properties");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
var i = 0;
|
|
|
|
var props = [];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
fields.forEach(function(field) {
|
|
|
|
path.each(
|
|
|
|
function(childPath) {
|
|
|
|
props.push(group(print(childPath)));
|
|
|
|
},
|
|
|
|
field
|
|
|
|
);
|
|
|
|
});
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (props.length === 0) {
|
|
|
|
return "{}";
|
|
|
|
} else {
|
|
|
|
return multilineGroup(
|
|
|
|
concat([
|
|
|
|
leftBrace,
|
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat([
|
|
|
|
options.bracketSpacing ? line : softline,
|
|
|
|
join(concat([ separator, line ]), props)
|
|
|
|
])
|
|
|
|
),
|
|
|
|
ifBreak(options.trailingComma ? "," : ""),
|
|
|
|
options.bracketSpacing ? line : softline,
|
|
|
|
rightBrace,
|
|
|
|
path.call(print, "typeAnnotation")
|
|
|
|
])
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
case "PropertyPattern":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "key"),
|
|
|
|
": ",
|
|
|
|
path.call(print, "pattern")
|
|
|
|
]);
|
|
|
|
// Babel 6
|
|
|
|
case "ObjectProperty":
|
|
|
|
case // Non-standard AST node type.
|
|
|
|
"Property":
|
|
|
|
if (n.method || n.kind === "get" || n.kind === "set") {
|
|
|
|
return printMethod(path, options, print);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n.shorthand) {
|
|
|
|
parts.push(path.call(print, "value"));
|
|
|
|
} else {
|
|
|
|
if (n.computed) {
|
|
|
|
parts.push("[", path.call(print, "key"), "]");
|
|
|
|
} else {
|
2017-01-19 20:37:24 +03:00
|
|
|
parts.push(printPropertyKey(path, options, print));
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
|
|
|
parts.push(": ", path.call(print, "value"));
|
|
|
|
}
|
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
case // Babel 6
|
|
|
|
"ClassMethod":
|
|
|
|
if (n.static) {
|
|
|
|
parts.push("static ");
|
|
|
|
}
|
|
|
|
|
|
|
|
parts = parts.concat(printObjectMethod(path, options, print));
|
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
case // Babel 6
|
|
|
|
"ObjectMethod":
|
|
|
|
return printObjectMethod(path, options, print);
|
|
|
|
case "Decorator":
|
|
|
|
return concat([ "@", path.call(print, "expression") ]);
|
|
|
|
case "ArrayExpression":
|
|
|
|
case "ArrayPattern":
|
|
|
|
if (n.elements.length === 0) {
|
|
|
|
parts.push("[]");
|
|
|
|
} else {
|
2017-01-16 20:53:39 +03:00
|
|
|
// JavaScript allows you to have empty elements in an array which
|
|
|
|
// changes its length based on the number of commas. The algorithm
|
|
|
|
// is that if the last argument is null, we need to force insert
|
|
|
|
// a comma to ensure JavaScript recognizes it.
|
|
|
|
// [,].length === 1
|
|
|
|
// [1,].length === 1
|
|
|
|
// [1,,].length === 2
|
|
|
|
//
|
|
|
|
// Note that util.getLast returns null if the array is empty, but
|
|
|
|
// we already check for an empty array just above so we are safe
|
|
|
|
const needsForcedTrailingComma = util.getLast(n.elements) === null;
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(
|
|
|
|
multilineGroup(
|
|
|
|
concat([
|
|
|
|
"[",
|
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat([
|
|
|
|
options.bracketSpacing ? line : softline,
|
|
|
|
join(concat([ ",", line ]), path.map(print, "elements"))
|
|
|
|
])
|
|
|
|
),
|
2017-01-16 20:53:39 +03:00
|
|
|
needsForcedTrailingComma ? "," : "",
|
2017-01-13 23:03:53 +03:00
|
|
|
ifBreak(options.trailingComma ? "," : ""),
|
|
|
|
options.bracketSpacing ? line : softline,
|
|
|
|
"]"
|
|
|
|
])
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.typeAnnotation) parts.push(path.call(print, "typeAnnotation"));
|
2017-01-13 23:03:53 +03:00
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
case "SequenceExpression":
|
|
|
|
return join(", ", path.map(print, "expressions"));
|
|
|
|
case "ThisExpression":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "this";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "Super":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "super";
|
2017-01-13 23:03:53 +03:00
|
|
|
case // Babel 6 Literal split
|
|
|
|
"NullLiteral":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "null";
|
2017-01-13 23:03:53 +03:00
|
|
|
case // Babel 6 Literal split
|
|
|
|
"RegExpLiteral":
|
2017-01-20 00:20:03 +03:00
|
|
|
return n.extra.raw;
|
2017-01-13 23:03:53 +03:00
|
|
|
// Babel 6 Literal split
|
|
|
|
case "NumericLiteral":
|
2017-01-17 07:01:51 +03:00
|
|
|
return n.extra.raw;
|
|
|
|
// Babel 6 Literal split
|
|
|
|
case "BooleanLiteral":
|
2017-01-13 23:03:53 +03:00
|
|
|
// Babel 6 Literal split
|
|
|
|
case "StringLiteral":
|
|
|
|
case "Literal":
|
2017-01-19 02:31:46 +03:00
|
|
|
if (typeof n.value === "number") return n.raw;
|
2017-01-20 00:20:03 +03:00
|
|
|
if (typeof n.value !== "string") return "" + n.value;
|
2017-01-13 23:03:53 +03:00
|
|
|
|
2017-01-18 00:47:20 +03:00
|
|
|
return nodeStr(n, options);
|
2017-01-13 23:03:53 +03:00
|
|
|
case // Babel 6
|
|
|
|
"Directive":
|
|
|
|
return path.call(print, "value");
|
|
|
|
case // Babel 6
|
|
|
|
"DirectiveLiteral":
|
2017-01-20 00:20:03 +03:00
|
|
|
return nodeStr(n, options);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ModuleSpecifier":
|
|
|
|
if (n.local) {
|
|
|
|
throw new Error("The ESTree ModuleSpecifier type should be abstract");
|
|
|
|
}
|
|
|
|
|
|
|
|
// The Esprima ModuleSpecifier type is just a string-valued
|
|
|
|
// Literal identifying the imported-from module.
|
2017-01-20 00:20:03 +03:00
|
|
|
return nodeStr(n, options);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "UnaryExpression":
|
|
|
|
parts.push(n.operator);
|
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (/[a-z]$/.test(n.operator)) parts.push(" ");
|
2017-01-13 23:03:53 +03:00
|
|
|
|
|
|
|
parts.push(path.call(print, "argument"));
|
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
case "UpdateExpression":
|
|
|
|
parts.push(path.call(print, "argument"), n.operator);
|
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.prefix) parts.reverse();
|
2017-01-13 23:03:53 +03:00
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
case "ConditionalExpression":
|
|
|
|
return group(
|
2017-01-09 20:09:04 +03:00
|
|
|
concat([
|
2017-01-13 23:03:53 +03:00
|
|
|
path.call(print, "test"),
|
2017-01-09 20:09:04 +03:00
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat([
|
2017-01-13 23:03:53 +03:00
|
|
|
line,
|
|
|
|
"? ",
|
|
|
|
path.call(print, "consequent"),
|
|
|
|
line,
|
|
|
|
": ",
|
|
|
|
path.call(print, "alternate")
|
2017-01-09 20:09:04 +03:00
|
|
|
])
|
2017-01-13 23:03:53 +03:00
|
|
|
)
|
2017-01-09 20:09:04 +03:00
|
|
|
])
|
|
|
|
);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "NewExpression":
|
|
|
|
parts.push("new ", path.call(print, "callee"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
var args = n.arguments;
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (args) {
|
|
|
|
parts.push(printArgumentsList(path, options, print));
|
2017-01-11 08:48:49 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "VariableDeclaration":
|
|
|
|
var printed = path.map(
|
|
|
|
function(childPath) {
|
|
|
|
return print(childPath);
|
|
|
|
},
|
|
|
|
"declarations"
|
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts = [
|
|
|
|
n.kind,
|
|
|
|
" ",
|
2017-01-19 02:31:46 +03:00
|
|
|
printed[0],
|
2017-01-13 23:03:53 +03:00
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat(printed.slice(1).map(p => concat([ ",", line, p ])))
|
|
|
|
)
|
|
|
|
];
|
|
|
|
|
|
|
|
// We generally want to terminate all variable declarations with a
|
|
|
|
// semicolon, except when they are children of for loops.
|
|
|
|
var parentNode = path.getParentNode();
|
|
|
|
|
|
|
|
if (
|
|
|
|
!namedTypes.ForStatement.check(parentNode) &&
|
|
|
|
!namedTypes.ForInStatement.check(parentNode) &&
|
|
|
|
!(namedTypes.ForOfStatement &&
|
|
|
|
namedTypes.ForOfStatement.check(parentNode)) &&
|
|
|
|
!(namedTypes.ForAwaitStatement &&
|
|
|
|
namedTypes.ForAwaitStatement.check(parentNode))
|
|
|
|
) {
|
|
|
|
parts.push(";");
|
|
|
|
}
|
|
|
|
|
|
|
|
return multilineGroup(concat(parts));
|
|
|
|
case "VariableDeclarator":
|
|
|
|
return n.init
|
|
|
|
? concat([ path.call(print, "id"), " = ", path.call(print, "init") ])
|
|
|
|
: path.call(print, "id");
|
|
|
|
case "WithStatement":
|
|
|
|
return concat([
|
|
|
|
"with (",
|
|
|
|
path.call(print, "object"),
|
2017-01-16 20:54:39 +03:00
|
|
|
")",
|
|
|
|
adjustClause(path.call(print, "body"), options)
|
2017-01-13 23:03:53 +03:00
|
|
|
]);
|
|
|
|
case "IfStatement":
|
|
|
|
const con = adjustClause(path.call(print, "consequent"), options);
|
|
|
|
|
|
|
|
parts = [
|
|
|
|
"if (",
|
|
|
|
group(
|
2017-01-09 20:09:04 +03:00
|
|
|
concat([
|
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
2017-01-13 23:03:53 +03:00
|
|
|
concat([ softline, path.call(print, "test") ])
|
2017-01-09 20:09:04 +03:00
|
|
|
),
|
2017-01-13 23:03:53 +03:00
|
|
|
softline
|
2017-01-09 20:09:04 +03:00
|
|
|
])
|
2017-01-13 23:03:53 +03:00
|
|
|
),
|
|
|
|
")",
|
|
|
|
con
|
|
|
|
];
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.alternate) {
|
|
|
|
const hasBraces = isCurlyBracket(con);
|
|
|
|
const isEmpty = isEmptyBlock(con);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (hasBraces && !isEmpty) {
|
|
|
|
parts.push(" else");
|
|
|
|
} else {
|
|
|
|
parts.push(concat([ hardline, "else" ]));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(
|
|
|
|
adjustClause(
|
|
|
|
path.call(print, "alternate"),
|
|
|
|
options,
|
|
|
|
n.alternate.type === "IfStatement"
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return group(concat(parts));
|
|
|
|
case "ForStatement": {
|
|
|
|
const body = adjustClause(path.call(print, "body"), options);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (!n.init && !n.test && !n.update) {
|
|
|
|
return concat([ "for (;;)", body ]);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat([
|
|
|
|
"for (",
|
|
|
|
group(
|
2017-01-09 20:09:04 +03:00
|
|
|
concat([
|
2017-01-13 23:03:53 +03:00
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat([
|
|
|
|
softline,
|
|
|
|
path.call(print, "init"),
|
|
|
|
";",
|
|
|
|
line,
|
|
|
|
path.call(print, "test"),
|
|
|
|
";",
|
|
|
|
line,
|
|
|
|
path.call(print, "update")
|
|
|
|
])
|
|
|
|
),
|
|
|
|
softline
|
2017-01-09 20:09:04 +03:00
|
|
|
])
|
2017-01-13 23:03:53 +03:00
|
|
|
),
|
|
|
|
")",
|
|
|
|
body
|
|
|
|
]);
|
2016-12-31 07:03:22 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "WhileStatement":
|
|
|
|
return concat([
|
|
|
|
"while (",
|
2017-01-20 20:42:16 +03:00
|
|
|
group(
|
|
|
|
concat([
|
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat([ softline, path.call(print, "test") ])
|
|
|
|
),
|
|
|
|
softline
|
|
|
|
])
|
|
|
|
),
|
2017-01-13 23:03:53 +03:00
|
|
|
")",
|
|
|
|
adjustClause(path.call(print, "body"), options)
|
|
|
|
]);
|
|
|
|
case "ForInStatement":
|
|
|
|
// Note: esprima can't actually parse "for each (".
|
|
|
|
return concat([
|
|
|
|
n.each ? "for each (" : "for (",
|
|
|
|
path.call(print, "left"),
|
|
|
|
" in ",
|
|
|
|
path.call(print, "right"),
|
|
|
|
")",
|
|
|
|
adjustClause(path.call(print, "body"), options)
|
|
|
|
]);
|
|
|
|
case "ForOfStatement":
|
|
|
|
return concat([
|
|
|
|
"for (",
|
|
|
|
path.call(print, "left"),
|
|
|
|
" of ",
|
|
|
|
path.call(print, "right"),
|
|
|
|
")",
|
|
|
|
adjustClause(path.call(print, "body"), options)
|
|
|
|
]);
|
|
|
|
case "ForAwaitStatement":
|
|
|
|
return concat([
|
|
|
|
"for await (",
|
|
|
|
path.call(print, "left"),
|
|
|
|
" of ",
|
|
|
|
path.call(print, "right"),
|
|
|
|
")",
|
|
|
|
adjustClause(path.call(print, "body"), options)
|
|
|
|
]);
|
|
|
|
case "DoWhileStatement":
|
|
|
|
var clause = adjustClause(path.call(print, "body"), options);
|
|
|
|
var doBody = concat([ "do", clause ]);
|
|
|
|
var parts = [ doBody ];
|
|
|
|
const hasBraces = isCurlyBracket(clause);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (hasBraces) parts.push(" while");
|
|
|
|
else parts.push(concat([ line, "while" ]));
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(" (", path.call(print, "test"), ");");
|
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
case "DoExpression":
|
|
|
|
var statements = path.call(
|
|
|
|
function(bodyPath) {
|
|
|
|
return printStatementSequence(bodyPath, options, print);
|
|
|
|
},
|
|
|
|
"body"
|
|
|
|
);
|
|
|
|
return concat([ "do {\n", statements.indent(options.tabWidth), "\n}" ]);
|
|
|
|
case "BreakStatement":
|
|
|
|
parts.push("break");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.label) parts.push(" ", path.call(print, "label"));
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-31 07:03:22 +03:00
|
|
|
parts.push(";");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ContinueStatement":
|
|
|
|
parts.push("continue");
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.label) parts.push(" ", path.call(print, "label"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(";");
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "LabeledStatement":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "label"),
|
|
|
|
":",
|
|
|
|
hardline,
|
|
|
|
path.call(print, "body")
|
|
|
|
]);
|
|
|
|
case "TryStatement":
|
|
|
|
parts.push("try ", path.call(print, "block"));
|
|
|
|
|
|
|
|
if (n.handler) {
|
|
|
|
parts.push(" ", path.call(print, "handler"));
|
|
|
|
} else if (n.handlers) {
|
|
|
|
path.each(
|
|
|
|
function(handlerPath) {
|
|
|
|
parts.push(" ", print(handlerPath));
|
|
|
|
},
|
|
|
|
"handlers"
|
|
|
|
);
|
2017-01-05 20:10:14 +03:00
|
|
|
}
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.finalizer) {
|
|
|
|
parts.push(" finally ", path.call(print, "finalizer"));
|
|
|
|
}
|
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
case "CatchClause":
|
|
|
|
parts.push("catch (", path.call(print, "param"));
|
|
|
|
|
|
|
|
if (n.guard)
|
|
|
|
// Note: esprima does not recognize conditional catch clauses.
|
|
|
|
parts.push(" if ", path.call(print, "guard"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(") ", path.call(print, "body"));
|
2017-01-13 21:00:32 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ThrowStatement":
|
|
|
|
return concat([ "throw ", path.call(print, "argument"), ";" ]);
|
|
|
|
// Note: ignoring n.lexical because it has no printing consequences.
|
|
|
|
case "SwitchStatement":
|
2017-01-13 21:00:32 +03:00
|
|
|
return concat([
|
2017-01-13 23:03:53 +03:00
|
|
|
"switch (",
|
|
|
|
path.call(print, "discriminant"),
|
|
|
|
") {",
|
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat([ hardline, join(hardline, path.map(print, "cases")) ])
|
|
|
|
),
|
|
|
|
hardline,
|
|
|
|
"}"
|
2017-01-13 21:00:32 +03:00
|
|
|
]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "SwitchCase":
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.test) parts.push("case ", path.call(print, "test"), ":");
|
|
|
|
else parts.push("default:");
|
2017-01-13 23:03:53 +03:00
|
|
|
|
|
|
|
if (n.consequent.length > 0) {
|
|
|
|
const cons = path.call(
|
|
|
|
function(consequentPath) {
|
|
|
|
return printStatementSequence(consequentPath, options, print);
|
|
|
|
},
|
|
|
|
"consequent"
|
|
|
|
);
|
|
|
|
|
|
|
|
parts.push(
|
|
|
|
isCurlyBracket(cons)
|
|
|
|
? concat([ " ", cons ])
|
|
|
|
: indent(options.tabWidth, concat([ hardline, cons ]))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
// JSX extensions below.
|
|
|
|
case "DebuggerStatement":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "debugger;";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "JSXAttribute":
|
|
|
|
parts.push(path.call(print, "name"));
|
|
|
|
|
|
|
|
if (n.value) {
|
|
|
|
let res;
|
|
|
|
if (
|
|
|
|
(n.value.type === "StringLiteral" || n.value.type === "Literal") &&
|
|
|
|
typeof n.value.value === "string"
|
|
|
|
) {
|
|
|
|
res = '"' + util.htmlEscapeInsideDoubleQuote(n.value.value) + '"';
|
|
|
|
} else {
|
|
|
|
res = path.call(print, "value");
|
|
|
|
}
|
|
|
|
parts.push("=", res);
|
|
|
|
}
|
2017-01-13 21:00:32 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "JSXIdentifier":
|
2017-01-20 00:20:03 +03:00
|
|
|
return "" + n.name;
|
2017-01-13 23:03:53 +03:00
|
|
|
case "JSXNamespacedName":
|
|
|
|
return join(":", [
|
|
|
|
path.call(print, "namespace"),
|
|
|
|
path.call(print, "name")
|
|
|
|
]);
|
|
|
|
case "JSXMemberExpression":
|
|
|
|
return join(".", [
|
|
|
|
path.call(print, "object"),
|
|
|
|
path.call(print, "property")
|
|
|
|
]);
|
|
|
|
case "JSXSpreadAttribute":
|
|
|
|
return concat([ "{...", path.call(print, "argument"), "}" ]);
|
|
|
|
case "JSXExpressionContainer":
|
2017-01-20 21:12:37 +03:00
|
|
|
const shouldIndent = n.expression.type !== "ArrayExpression" &&
|
|
|
|
n.expression.type !== "ObjectExpression" &&
|
|
|
|
n.expression.type !== "ArrowFunctionExpression" &&
|
|
|
|
n.expression.type !== "CallExpression" &&
|
|
|
|
n.expression.type !== "FunctionExpression";
|
2017-01-19 20:37:02 +03:00
|
|
|
|
|
|
|
if (!shouldIndent) {
|
2017-01-20 21:12:37 +03:00
|
|
|
return concat([ "{", path.call(print, "expression"), "}" ]);
|
2017-01-19 20:37:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return multilineGroup(
|
2017-01-13 23:03:53 +03:00
|
|
|
concat([
|
|
|
|
"{",
|
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat([ softline, path.call(print, "expression") ])
|
|
|
|
),
|
|
|
|
softline,
|
|
|
|
"}"
|
|
|
|
])
|
|
|
|
);
|
2017-01-19 00:25:20 +03:00
|
|
|
case "JSXElement": {
|
|
|
|
const elem = printJSXElement(path, options, print);
|
|
|
|
return maybeWrapJSXElementInParens(path, elem, options);
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "JSXOpeningElement":
|
|
|
|
return group(
|
2017-01-09 20:09:04 +03:00
|
|
|
concat([
|
2017-01-13 23:03:53 +03:00
|
|
|
"<",
|
|
|
|
path.call(print, "name"),
|
|
|
|
multilineGroup(
|
2017-01-09 20:09:04 +03:00
|
|
|
concat([
|
2017-01-13 23:03:53 +03:00
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat(
|
|
|
|
path.map(attr => concat([ line, print(attr) ]), "attributes")
|
|
|
|
)
|
|
|
|
),
|
|
|
|
n.selfClosing ? line : softline
|
2017-01-09 20:09:04 +03:00
|
|
|
])
|
|
|
|
),
|
2017-01-13 23:03:53 +03:00
|
|
|
n.selfClosing ? "/>" : ">"
|
2017-01-09 20:09:04 +03:00
|
|
|
])
|
|
|
|
);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "JSXClosingElement":
|
|
|
|
return concat([ "</", path.call(print, "name"), ">" ]);
|
|
|
|
case "JSXText":
|
|
|
|
throw new Error("JSXTest should be handled by JSXElement");
|
|
|
|
case "JSXEmptyExpression":
|
|
|
|
return "";
|
|
|
|
case "TypeAnnotatedIdentifier":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "annotation"),
|
|
|
|
" ",
|
|
|
|
path.call(print, "identifier")
|
|
|
|
]);
|
|
|
|
case "ClassBody":
|
|
|
|
if (n.body.length === 0) {
|
2017-01-20 21:12:37 +03:00
|
|
|
return "{}";
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat([
|
|
|
|
"{",
|
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat([
|
|
|
|
hardline,
|
|
|
|
path.call(
|
|
|
|
function(bodyPath) {
|
|
|
|
return printStatementSequence(bodyPath, options, print);
|
|
|
|
},
|
|
|
|
"body"
|
|
|
|
)
|
|
|
|
])
|
|
|
|
),
|
|
|
|
hardline,
|
|
|
|
"}"
|
|
|
|
]);
|
|
|
|
case "ClassPropertyDefinition":
|
|
|
|
parts.push("static ", path.call(print, "definition"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (!namedTypes.MethodDefinition.check(n.definition)) parts.push(";");
|
2017-01-09 20:07:39 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ClassProperty":
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.static) parts.push("static ");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
var key;
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.computed) {
|
|
|
|
key = concat([ "[", path.call(print, "key"), "]" ]);
|
2017-01-11 22:37:53 +03:00
|
|
|
} else {
|
2017-01-19 20:37:24 +03:00
|
|
|
key = printPropertyKey(path, options, print);
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.variance === "plus") {
|
|
|
|
key = concat([ "+", key ]);
|
|
|
|
} else if (n.variance === "minus") {
|
|
|
|
key = concat([ "-", key ]);
|
|
|
|
}
|
2017-01-11 22:37:53 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(key);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.typeAnnotation) parts.push(path.call(print, "typeAnnotation"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (n.value) parts.push(" = ", path.call(print, "value"));
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(";");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ClassDeclaration":
|
|
|
|
case "ClassExpression":
|
|
|
|
return concat(printClass(path, print));
|
|
|
|
case "TemplateElement":
|
|
|
|
return join(literalline, n.value.raw.split("\n"));
|
|
|
|
case "TemplateLiteral":
|
|
|
|
var expressions = path.map(print, "expressions");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push("`");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
path.each(
|
|
|
|
function(childPath) {
|
|
|
|
var i = childPath.getName();
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(print(childPath));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (i < expressions.length) {
|
|
|
|
parts.push("${", expressions[i], "}");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"quasis"
|
|
|
|
);
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push("`");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
// These types are unprintable because they serve as abstract
|
|
|
|
// supertypes for other (printable) types.
|
|
|
|
case "TaggedTemplateExpression":
|
|
|
|
return concat([ path.call(print, "tag"), path.call(print, "quasi") ]);
|
|
|
|
case "Node":
|
|
|
|
case "Printable":
|
|
|
|
case "SourceLocation":
|
|
|
|
case "Position":
|
|
|
|
case "Statement":
|
|
|
|
case "Function":
|
|
|
|
case "Pattern":
|
|
|
|
case "Expression":
|
|
|
|
case "Declaration":
|
|
|
|
case "Specifier":
|
|
|
|
case "NamedSpecifier":
|
|
|
|
// Supertype of Block and Line.
|
|
|
|
case "Comment":
|
|
|
|
// Flow
|
|
|
|
case "MemberTypeAnnotation":
|
|
|
|
case // Flow
|
|
|
|
"Type":
|
|
|
|
throw new Error("unprintable type: " + JSON.stringify(n.type));
|
|
|
|
// Babel block comment.
|
|
|
|
case "CommentBlock":
|
|
|
|
case // Esprima block comment.
|
|
|
|
"Block":
|
|
|
|
return concat([ "/*", n.value, "*/" ]);
|
|
|
|
// Babel line comment.
|
|
|
|
case "CommentLine":
|
|
|
|
case // Esprima line comment.
|
|
|
|
"Line":
|
|
|
|
return concat([ "//", n.value ]);
|
|
|
|
// Type Annotations for Facebook Flow, typically stripped out or
|
|
|
|
// transformed away before printing.
|
|
|
|
case "TypeAnnotation":
|
|
|
|
if (n.typeAnnotation) {
|
|
|
|
if (n.typeAnnotation.type !== "FunctionTypeAnnotation") {
|
|
|
|
parts.push(": ");
|
|
|
|
}
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(path.call(print, "typeAnnotation"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return "";
|
|
|
|
case "TupleTypeAnnotation":
|
|
|
|
return concat([ "[", join(", ", path.map(print, "types")), "]" ]);
|
|
|
|
case "ExistentialTypeParam":
|
|
|
|
case "ExistsTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "*";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "EmptyTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "empty";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "AnyTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "any";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "MixedTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "mixed";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ArrayTypeAnnotation":
|
|
|
|
return concat([ path.call(print, "elementType"), "[]" ]);
|
|
|
|
case "BooleanTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "boolean";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "NumericLiteralTypeAnnotation":
|
|
|
|
case "BooleanLiteralTypeAnnotation":
|
|
|
|
return "" + n.value;
|
|
|
|
case "DeclareClass":
|
|
|
|
return printFlowDeclaration(path, printClass(path, print));
|
|
|
|
case "DeclareFunction":
|
|
|
|
return printFlowDeclaration(path, [
|
|
|
|
"function ",
|
|
|
|
path.call(print, "id"),
|
|
|
|
n.predicate ? " " : "",
|
|
|
|
path.call(print, "predicate"),
|
|
|
|
";"
|
|
|
|
]);
|
|
|
|
case "DeclareModule":
|
|
|
|
return printFlowDeclaration(path, [
|
|
|
|
"module ",
|
|
|
|
path.call(print, "id"),
|
|
|
|
" ",
|
|
|
|
path.call(print, "body")
|
|
|
|
]);
|
|
|
|
case "DeclareModuleExports":
|
|
|
|
return printFlowDeclaration(path, [
|
|
|
|
"module.exports",
|
|
|
|
path.call(print, "typeAnnotation"),
|
|
|
|
";"
|
|
|
|
]);
|
|
|
|
case "DeclareVariable":
|
|
|
|
return printFlowDeclaration(path, [
|
|
|
|
"var ",
|
|
|
|
path.call(print, "id"),
|
|
|
|
";"
|
|
|
|
]);
|
|
|
|
case "DeclareExportAllDeclaration":
|
|
|
|
return concat([ "declare export * from ", path.call(print, "source") ]);
|
|
|
|
case "DeclareExportDeclaration":
|
|
|
|
return concat([
|
|
|
|
"declare ",
|
|
|
|
printExportDeclaration(path, options, print)
|
|
|
|
]);
|
|
|
|
case "FunctionTypeAnnotation":
|
|
|
|
// FunctionTypeAnnotation is ambiguous:
|
|
|
|
// declare function foo(a: B): void; OR
|
|
|
|
// var A: (a: B) => void;
|
|
|
|
var parent = path.getParentNode(0);
|
|
|
|
var isArrowFunctionTypeAnnotation = !(!parent.variance &&
|
|
|
|
!parent.optional &&
|
|
|
|
namedTypes.ObjectTypeProperty.check(parent) ||
|
|
|
|
namedTypes.ObjectTypeCallProperty.check(parent) ||
|
|
|
|
namedTypes.DeclareFunction.check(path.getParentNode(2)));
|
|
|
|
var needsColon = isArrowFunctionTypeAnnotation &&
|
|
|
|
namedTypes.TypeAnnotation.check(parent);
|
|
|
|
|
|
|
|
if (needsColon) {
|
2016-12-31 07:03:22 +03:00
|
|
|
parts.push(": ");
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(path.call(print, "typeParameters"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(multilineGroup(printFunctionParams(path, print, options)));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
// The returnType is not wrapped in a TypeAnnotation, so the colon
|
|
|
|
// needs to be added separately.
|
|
|
|
if (n.returnType || n.predicate) {
|
|
|
|
parts.push(
|
|
|
|
isArrowFunctionTypeAnnotation ? " => " : ": ",
|
|
|
|
path.call(print, "returnType"),
|
|
|
|
path.call(print, "predicate")
|
|
|
|
);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "FunctionTypeParam":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "name"),
|
|
|
|
n.optional ? "?" : "",
|
|
|
|
n.name ? ": " : "",
|
|
|
|
path.call(print, "typeAnnotation")
|
|
|
|
]);
|
|
|
|
case "GenericTypeAnnotation":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "id"),
|
|
|
|
path.call(print, "typeParameters")
|
|
|
|
]);
|
|
|
|
case "DeclareInterface":
|
2017-01-14 07:15:30 +03:00
|
|
|
case "InterfaceDeclaration": {
|
|
|
|
const parent = path.getParentNode(1);
|
|
|
|
if (parent && parent.type === "DeclareModule") {
|
|
|
|
parts.push("declare ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
parts.push(
|
2017-01-20 00:20:03 +03:00
|
|
|
"interface ",
|
2017-01-13 23:03:53 +03:00
|
|
|
path.call(print, "id"),
|
|
|
|
path.call(print, "typeParameters"),
|
|
|
|
" "
|
2016-12-23 21:38:10 +03:00
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n["extends"].length > 0) {
|
2017-01-19 02:31:46 +03:00
|
|
|
parts.push("extends ", join(", ", path.map(print, "extends")), " ");
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-14 07:15:30 +03:00
|
|
|
parts.push(path.call(print, "body"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
2017-01-14 07:15:30 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ClassImplements":
|
|
|
|
case "InterfaceExtends":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "id"),
|
|
|
|
path.call(print, "typeParameters")
|
|
|
|
]);
|
|
|
|
case "IntersectionTypeAnnotation":
|
|
|
|
case "UnionTypeAnnotation": {
|
2017-01-09 20:09:04 +03:00
|
|
|
const types = path.map(print, "types");
|
|
|
|
const op = n.type === "IntersectionTypeAnnotation" ? "&" : "|";
|
2017-01-11 00:28:23 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return conditionalGroup([
|
|
|
|
// single-line variation
|
|
|
|
// A | B | C
|
|
|
|
concat([
|
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat([
|
2017-01-19 02:31:46 +03:00
|
|
|
types[0],
|
2017-01-13 23:03:53 +03:00
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat(types.slice(1).map(t => concat([ " ", op, line, t ])))
|
|
|
|
)
|
|
|
|
])
|
|
|
|
)
|
|
|
|
]),
|
|
|
|
// multi-line variation
|
|
|
|
// | A
|
|
|
|
// | B
|
|
|
|
// | C
|
|
|
|
concat([
|
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat(types.map(t => concat([ line, op, " ", t ])))
|
|
|
|
)
|
|
|
|
])
|
|
|
|
]);
|
2016-12-31 07:03:22 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "NullableTypeAnnotation":
|
|
|
|
return concat([ "?", path.call(print, "typeAnnotation") ]);
|
|
|
|
case "NullLiteralTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "null";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ThisTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "this";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "NumberTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "number";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ObjectTypeCallProperty":
|
|
|
|
if (n.static) {
|
|
|
|
parts.push("static ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(path.call(print, "value"));
|
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
case "ObjectTypeIndexer":
|
|
|
|
var variance = n.variance === "plus"
|
|
|
|
? "+"
|
|
|
|
: n.variance === "minus" ? "-" : "";
|
|
|
|
return concat([
|
|
|
|
variance,
|
|
|
|
"[",
|
|
|
|
path.call(print, "id"),
|
|
|
|
n.id ? ": " : "",
|
|
|
|
path.call(print, "key"),
|
|
|
|
"]: ",
|
|
|
|
path.call(print, "value")
|
|
|
|
]);
|
|
|
|
case "ObjectTypeProperty":
|
|
|
|
var variance = n.variance === "plus"
|
|
|
|
? "+"
|
|
|
|
: n.variance === "minus" ? "-" : "";
|
|
|
|
// TODO: This is a bad hack and we need a better way to know
|
|
|
|
// when to emit an arrow function or not.
|
2017-01-19 02:31:46 +03:00
|
|
|
var isFunction = !n.variance &&
|
|
|
|
!n.optional &&
|
2017-01-13 23:03:53 +03:00
|
|
|
n.value.type === "FunctionTypeAnnotation";
|
|
|
|
return concat([
|
|
|
|
n.static ? "static " : "",
|
|
|
|
variance,
|
|
|
|
path.call(print, "key"),
|
|
|
|
n.optional ? "?" : "",
|
|
|
|
isFunction ? "" : ": ",
|
|
|
|
path.call(print, "value")
|
|
|
|
]);
|
|
|
|
case "QualifiedTypeIdentifier":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "qualification"),
|
|
|
|
".",
|
|
|
|
path.call(print, "id")
|
|
|
|
]);
|
|
|
|
case "StringLiteralTypeAnnotation":
|
2017-01-20 00:20:03 +03:00
|
|
|
return nodeStr(n, options);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "NumberLiteralTypeAnnotation":
|
|
|
|
assert.strictEqual(typeof n.value, "number");
|
|
|
|
|
2017-01-20 00:20:03 +03:00
|
|
|
return "" + n.value;
|
2017-01-13 23:03:53 +03:00
|
|
|
case "StringTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "string";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "DeclareTypeAlias":
|
|
|
|
case "TypeAlias": {
|
2016-12-30 08:01:44 +03:00
|
|
|
const parent = path.getParentNode(1);
|
2017-01-14 07:14:32 +03:00
|
|
|
if (parent && parent.type === "DeclareModule") {
|
2016-12-30 08:01:44 +03:00
|
|
|
parts.push("declare ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-30 08:01:44 +03:00
|
|
|
parts.push(
|
2016-12-23 21:38:10 +03:00
|
|
|
"type ",
|
|
|
|
path.call(print, "id"),
|
|
|
|
path.call(print, "typeParameters"),
|
|
|
|
" = ",
|
|
|
|
path.call(print, "right"),
|
|
|
|
";"
|
2016-12-30 08:01:44 +03:00
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-30 08:01:44 +03:00
|
|
|
return concat(parts);
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "TypeCastExpression":
|
|
|
|
return concat([
|
|
|
|
"(",
|
|
|
|
path.call(print, "expression"),
|
|
|
|
path.call(print, "typeAnnotation"),
|
|
|
|
")"
|
|
|
|
]);
|
|
|
|
case "TypeParameterDeclaration":
|
|
|
|
case "TypeParameterInstantiation":
|
|
|
|
return concat([ "<", join(", ", path.map(print, "params")), ">" ]);
|
|
|
|
case "TypeParameter":
|
|
|
|
switch (n.variance) {
|
|
|
|
case "plus":
|
|
|
|
parts.push("+");
|
|
|
|
|
|
|
|
break;
|
|
|
|
case "minus":
|
|
|
|
parts.push("-");
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
}
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(path.call(print, "name"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.bound) {
|
|
|
|
parts.push(path.call(print, "bound"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n["default"]) {
|
|
|
|
parts.push("=", path.call(print, "default"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "TypeofTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return concat([ "typeof ", path.call(print, "argument") ]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "VoidTypeAnnotation":
|
|
|
|
return "void";
|
|
|
|
case "NullTypeAnnotation":
|
|
|
|
return "null";
|
|
|
|
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"), ")" ]);
|
|
|
|
// TODO
|
|
|
|
case "ClassHeritage":
|
|
|
|
// TODO
|
|
|
|
case "ComprehensionBlock":
|
|
|
|
// TODO
|
|
|
|
case "ComprehensionExpression":
|
|
|
|
// TODO
|
|
|
|
case "Glob":
|
|
|
|
// TODO
|
|
|
|
case "GeneratorExpression":
|
|
|
|
// TODO
|
|
|
|
case "LetStatement":
|
|
|
|
// TODO
|
|
|
|
case "LetExpression":
|
|
|
|
// TODO
|
|
|
|
case "GraphExpression":
|
|
|
|
// TODO
|
|
|
|
// XML types that nobody cares about or needs to print.
|
|
|
|
case "GraphIndexExpression":
|
|
|
|
case "XMLDefaultDeclaration":
|
|
|
|
case "XMLAnyName":
|
|
|
|
case "XMLQualifiedIdentifier":
|
|
|
|
case "XMLFunctionQualifiedIdentifier":
|
|
|
|
case "XMLAttributeSelector":
|
|
|
|
case "XMLFilterExpression":
|
|
|
|
case "XML":
|
|
|
|
case "XMLElement":
|
|
|
|
case "XMLList":
|
|
|
|
case "XMLEscape":
|
|
|
|
case "XMLText":
|
|
|
|
case "XMLStartTag":
|
|
|
|
case "XMLEndTag":
|
|
|
|
case "XMLPointTag":
|
|
|
|
case "XMLName":
|
|
|
|
case "XMLAttribute":
|
|
|
|
case "XMLCdata":
|
|
|
|
case "XMLComment":
|
|
|
|
case "XMLProcessingInstruction":
|
|
|
|
default:
|
|
|
|
debugger;
|
|
|
|
throw new Error("unknown type: " + JSON.stringify(n.type));
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
function printStatementSequence(path, options, print) {
|
2016-12-31 07:10:22 +03:00
|
|
|
let printed = [];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-05 06:27:25 +03:00
|
|
|
path.map(function(stmtPath, i) {
|
|
|
|
var stmt = stmtPath.getValue();
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-05 06:27:25 +03:00
|
|
|
// Just in case the AST has been modified to contain falsy
|
|
|
|
// "statements," it's safer simply to skip them.
|
|
|
|
if (!stmt) {
|
|
|
|
return;
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-05 06:27:25 +03:00
|
|
|
// Skip printing EmptyStatement nodes to avoid leaving stray
|
|
|
|
// semicolons lying around.
|
|
|
|
if (stmt.type === "EmptyStatement") {
|
|
|
|
return;
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-05 06:27:25 +03:00
|
|
|
const stmtPrinted = print(stmtPath);
|
2017-01-10 05:49:06 +03:00
|
|
|
const text = options.originalText;
|
2017-01-05 06:27:25 +03:00
|
|
|
const parts = [];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-05 06:27:25 +03:00
|
|
|
parts.push(stmtPrinted);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (
|
|
|
|
util.newlineExistsAfter(text, util.locEnd(stmt)) &&
|
|
|
|
!isLastStatement(stmtPath)
|
|
|
|
) {
|
2017-01-05 06:27:25 +03:00
|
|
|
parts.push(hardline);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-05 06:27:25 +03:00
|
|
|
printed.push(concat(parts));
|
|
|
|
});
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-31 07:01:07 +03:00
|
|
|
return join(hardline, printed);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
2017-01-19 20:37:24 +03:00
|
|
|
function printPropertyKey(path, options, print) {
|
2017-01-11 08:48:49 +03:00
|
|
|
var node = path.getNode().key;
|
2017-01-15 07:40:26 +03:00
|
|
|
if (
|
|
|
|
(node.type === "StringLiteral" ||
|
|
|
|
node.type === "Literal" && typeof node.value === "string") &&
|
2017-01-19 20:37:24 +03:00
|
|
|
isIdentifierName(node.value) &&
|
|
|
|
// There's a bug in the flow parser where it throws if there are
|
|
|
|
// unquoted unicode literals as keys. Let's quote them for now.
|
2017-01-20 21:12:37 +03:00
|
|
|
(options.parser !== "flow" || node.value.match(/[a-zA-Z0-9$_]/))
|
2017-01-15 07:40:26 +03:00
|
|
|
) {
|
2017-01-11 20:31:34 +03:00
|
|
|
// 'a' -> a
|
|
|
|
return node.value;
|
2017-01-11 08:48:49 +03:00
|
|
|
}
|
|
|
|
return path.call(print, "key");
|
|
|
|
}
|
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
function printMethod(path, options, print) {
|
|
|
|
var node = path.getNode();
|
|
|
|
var kind = node.kind;
|
|
|
|
var parts = [];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (node.type === "ObjectMethod" || node.type === "ClassMethod") {
|
|
|
|
node.value = node;
|
|
|
|
} else {
|
|
|
|
namedTypes.FunctionExpression.assert(node.value);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (node.value.async) {
|
|
|
|
parts.push("async ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (!kind || kind === "init" || kind === "method" || kind === "constructor") {
|
|
|
|
if (node.value.generator) {
|
|
|
|
parts.push("*");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
assert.ok(kind === "get" || kind === "set");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
parts.push(kind, " ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-19 20:37:24 +03:00
|
|
|
var key = printPropertyKey(path, options, print);
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (node.computed) {
|
2016-12-31 07:03:22 +03:00
|
|
|
key = concat([ "[", key, "]" ]);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
parts.push(
|
|
|
|
key,
|
|
|
|
path.call(print, "value", "typeParameters"),
|
2017-01-13 23:03:53 +03:00
|
|
|
multilineGroup(
|
|
|
|
concat([
|
|
|
|
path.call(
|
|
|
|
function(valuePath) {
|
|
|
|
return printFunctionParams(valuePath, print, options);
|
|
|
|
},
|
|
|
|
"value"
|
|
|
|
),
|
|
|
|
path.call(p => printReturnType(p, print), "value")
|
|
|
|
])
|
|
|
|
),
|
2016-12-23 21:38:10 +03:00
|
|
|
" ",
|
|
|
|
path.call(print, "value", "body")
|
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-09 21:47:02 +03:00
|
|
|
return concat(parts);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function printArgumentsList(path, options, print) {
|
|
|
|
var printed = path.map(print, "arguments");
|
|
|
|
var args;
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-31 07:03:22 +03:00
|
|
|
if (printed.length === 0) {
|
2017-01-04 23:24:10 +03:00
|
|
|
return "()";
|
|
|
|
}
|
|
|
|
|
|
|
|
const lastArg = util.getLast(path.getValue().arguments);
|
|
|
|
// This is just an optimization; I think we could return the
|
|
|
|
// conditional group for all function calls, but it's more expensive
|
|
|
|
// so only do it for specific forms.
|
|
|
|
const groupLastArg = lastArg.type === "ObjectExpression" ||
|
2017-01-05 06:27:25 +03:00
|
|
|
lastArg.type === "ArrayExpression" ||
|
|
|
|
lastArg.type === "FunctionExpression" ||
|
2017-01-13 23:03:53 +03:00
|
|
|
lastArg.type === "ArrowFunctionExpression" &&
|
|
|
|
(lastArg.body.type === "BlockStatement" ||
|
2017-01-15 08:04:50 +03:00
|
|
|
lastArg.body.type === "ArrowFunctionExpression" ||
|
|
|
|
lastArg.body.type === "ObjectExpression" ||
|
|
|
|
lastArg.body.type === "ArrayExpression" ||
|
|
|
|
lastArg.body.type === "JSXElement") ||
|
2017-01-05 06:27:25 +03:00
|
|
|
lastArg.type === "NewExpression";
|
2017-01-04 23:24:10 +03:00
|
|
|
|
|
|
|
if (groupLastArg) {
|
2017-01-05 06:26:46 +03:00
|
|
|
const shouldBreak = printed.slice(0, -1).some(hasHardLine);
|
2017-01-04 23:24:10 +03:00
|
|
|
return conditionalGroup(
|
|
|
|
[
|
2017-01-05 06:27:25 +03:00
|
|
|
concat([ "(", join(concat([ ", " ]), printed), ")" ]),
|
2017-01-04 23:24:10 +03:00
|
|
|
concat([
|
|
|
|
"(",
|
|
|
|
join(concat([ ",", line ]), printed.slice(0, -1)),
|
2017-01-09 20:09:04 +03:00
|
|
|
printed.length > 1 ? ", " : "",
|
2017-01-13 23:03:53 +03:00
|
|
|
group(util.getLast(printed), { shouldBreak: true }),
|
2017-01-04 23:24:10 +03:00
|
|
|
")"
|
|
|
|
]),
|
|
|
|
group(
|
|
|
|
concat([
|
|
|
|
"(",
|
2017-01-09 20:09:04 +03:00
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat([ line, join(concat([ ",", line ]), printed) ])
|
|
|
|
),
|
2017-01-05 20:19:30 +03:00
|
|
|
options.trailingComma ? "," : "",
|
2017-01-04 23:24:10 +03:00
|
|
|
line,
|
|
|
|
")"
|
|
|
|
]),
|
2017-01-13 23:03:53 +03:00
|
|
|
{ shouldBreak: true }
|
2017-01-05 06:27:25 +03:00
|
|
|
)
|
2017-01-04 23:24:10 +03:00
|
|
|
],
|
2017-01-13 23:03:53 +03:00
|
|
|
{ shouldBreak }
|
2017-01-04 23:24:10 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-01-11 00:20:24 +03:00
|
|
|
return multilineGroup(
|
2017-01-04 23:24:10 +03:00
|
|
|
concat([
|
|
|
|
"(",
|
2017-01-09 20:09:04 +03:00
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat([ softline, join(concat([ ",", line ]), printed) ])
|
|
|
|
),
|
2017-01-05 20:19:30 +03:00
|
|
|
ifBreak(options.trailingComma ? "," : ""),
|
2017-01-04 23:24:10 +03:00
|
|
|
softline,
|
|
|
|
")"
|
2017-01-11 00:20:24 +03:00
|
|
|
])
|
2017-01-04 23:24:10 +03:00
|
|
|
);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
2017-01-05 00:52:49 +03:00
|
|
|
function printFunctionParams(path, print, options) {
|
2016-12-23 21:38:10 +03:00
|
|
|
var fun = path.getValue();
|
2016-12-30 08:01:44 +03:00
|
|
|
// namedTypes.Function.assert(fun);
|
2016-12-23 21:38:10 +03:00
|
|
|
var printed = path.map(print, "params");
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (fun.defaults) {
|
2016-12-31 07:03:22 +03:00
|
|
|
path.each(
|
|
|
|
function(defExprPath) {
|
|
|
|
var i = defExprPath.getName();
|
|
|
|
var p = printed[i];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-31 07:03:22 +03:00
|
|
|
if (p && defExprPath.getValue()) {
|
|
|
|
printed[i] = concat([ p, " = ", print(defExprPath) ]);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"defaults"
|
|
|
|
);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (fun.rest) {
|
2016-12-31 07:03:22 +03:00
|
|
|
printed.push(concat([ "...", path.call(print, "rest") ]));
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-15 07:55:38 +03:00
|
|
|
if (printed.length === 0) {
|
|
|
|
return "()";
|
|
|
|
}
|
|
|
|
|
2017-01-18 20:42:20 +03:00
|
|
|
const lastParam = util.getLast(path.getValue().params);
|
2017-01-20 21:12:37 +03:00
|
|
|
const canHaveTrailingComma = !(lastParam &&
|
|
|
|
lastParam.type === "RestElement") &&
|
|
|
|
!fun.rest;
|
2017-01-18 20:42:20 +03:00
|
|
|
|
2017-01-05 00:52:49 +03:00
|
|
|
return concat([
|
|
|
|
"(",
|
2017-01-09 20:09:04 +03:00
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat([ softline, join(concat([ ",", line ]), printed) ])
|
|
|
|
),
|
2017-01-18 20:42:20 +03:00
|
|
|
ifBreak(canHaveTrailingComma && options.trailingComma ? "," : ""),
|
2017-01-05 00:52:49 +03:00
|
|
|
softline,
|
|
|
|
")"
|
|
|
|
]);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function printObjectMethod(path, options, print) {
|
|
|
|
var objMethod = path.getValue();
|
|
|
|
var parts = [];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (objMethod.async) parts.push("async ");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (objMethod.generator) parts.push("*");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-31 07:03:22 +03:00
|
|
|
if (
|
|
|
|
objMethod.method || objMethod.kind === "get" || objMethod.kind === "set"
|
|
|
|
) {
|
2016-12-23 21:38:10 +03:00
|
|
|
return printMethod(path, options, print);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-19 20:37:24 +03:00
|
|
|
var key = printPropertyKey(path, options, print);
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (objMethod.computed) {
|
|
|
|
parts.push("[", key, "]");
|
|
|
|
} else {
|
|
|
|
parts.push(key);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-12 02:14:46 +03:00
|
|
|
if (objMethod.typeParameters) {
|
|
|
|
parts.push(path.call(print, "typeParameters"));
|
|
|
|
}
|
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
parts.push(
|
2017-01-13 23:03:53 +03:00
|
|
|
multilineGroup(
|
|
|
|
concat([
|
|
|
|
printFunctionParams(path, print, options),
|
|
|
|
printReturnType(path, print)
|
|
|
|
])
|
|
|
|
),
|
2016-12-23 21:38:10 +03:00
|
|
|
" ",
|
|
|
|
path.call(print, "body")
|
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-09 21:47:02 +03:00
|
|
|
return concat(parts);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
2016-12-30 21:32:43 +03:00
|
|
|
function printReturnType(path, print) {
|
|
|
|
const n = path.getValue();
|
2016-12-31 07:03:22 +03:00
|
|
|
const parts = [ path.call(print, "returnType") ];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-31 07:03:22 +03:00
|
|
|
if (n.predicate) {
|
2016-12-31 07:10:22 +03:00
|
|
|
// The return type will already add the colon, but otherwise we
|
|
|
|
// need to do it ourselves
|
2017-01-09 20:09:04 +03:00
|
|
|
parts.push(n.returnType ? " " : ": ", path.call(print, "predicate"));
|
2016-12-30 21:32:43 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-30 21:32:43 +03:00
|
|
|
return concat(parts);
|
|
|
|
}
|
|
|
|
|
2017-01-18 22:11:52 +03:00
|
|
|
function typeIsFunction(type) {
|
|
|
|
return type === "FunctionExpression" ||
|
|
|
|
type === "ArrowFunctionExpression" ||
|
|
|
|
type === "NewExpression";
|
|
|
|
}
|
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
function printExportDeclaration(path, options, print) {
|
|
|
|
var decl = path.getValue();
|
2016-12-31 07:03:22 +03:00
|
|
|
var parts = [ "export " ];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
namedTypes.Declaration.assert(decl);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-31 07:03:22 +03:00
|
|
|
if (decl["default"] || decl.type === "ExportDefaultDeclaration") {
|
2016-12-23 21:38:10 +03:00
|
|
|
parts.push("default ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (decl.declaration) {
|
|
|
|
parts.push(path.call(print, "declaration"));
|
2017-01-11 15:43:27 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (
|
|
|
|
decl.type === "ExportDefaultDeclaration" &&
|
2017-01-20 01:30:36 +03:00
|
|
|
(decl.declaration.type !== "ClassDeclaration" &&
|
|
|
|
decl.declaration.type !== "FunctionDeclaration" &&
|
|
|
|
decl.declaration.type !== "FunctionExpression")
|
2017-01-13 23:03:53 +03:00
|
|
|
) {
|
2017-01-11 15:43:27 +03:00
|
|
|
parts.push(";");
|
|
|
|
}
|
2017-01-16 19:58:23 +03:00
|
|
|
} else {
|
|
|
|
if (decl.specifiers && decl.specifiers.length > 0) {
|
|
|
|
if (
|
|
|
|
decl.specifiers.length === 1 &&
|
2017-01-19 02:31:46 +03:00
|
|
|
decl.specifiers[0].type === "ExportBatchSpecifier"
|
2017-01-16 19:58:23 +03:00
|
|
|
) {
|
|
|
|
parts.push("*");
|
|
|
|
} else {
|
|
|
|
parts.push(
|
|
|
|
decl.exportKind === "type" ? "type " : "",
|
2017-01-18 01:12:42 +03:00
|
|
|
multilineGroup(
|
2017-01-16 19:58:23 +03:00
|
|
|
concat([
|
|
|
|
"{",
|
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
concat([
|
|
|
|
options.bracketSpacing ? line : softline,
|
2017-01-19 02:31:46 +03:00
|
|
|
join(concat([ ",", line ]), path.map(print, "specifiers"))
|
2017-01-16 19:58:23 +03:00
|
|
|
])
|
|
|
|
),
|
|
|
|
ifBreak(options.trailingComma ? "," : ""),
|
|
|
|
options.bracketSpacing ? line : softline,
|
|
|
|
"}"
|
|
|
|
])
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2017-01-09 20:09:04 +03:00
|
|
|
} else {
|
2017-01-16 19:58:23 +03:00
|
|
|
parts.push("{}");
|
2017-01-09 20:09:04 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-09 20:09:04 +03:00
|
|
|
if (decl.source) {
|
|
|
|
parts.push(" from ", path.call(print, "source"));
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2017-01-11 15:43:27 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(";");
|
2017-01-09 20:09:04 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
return concat(parts);
|
|
|
|
}
|
|
|
|
|
|
|
|
function printFlowDeclaration(path, parts) {
|
|
|
|
var parentExportDecl = util.getParentExportDeclaration(path);
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (parentExportDecl) {
|
2016-12-31 07:03:22 +03:00
|
|
|
assert.strictEqual(parentExportDecl.type, "DeclareExportDeclaration");
|
2016-12-23 21:38:10 +03:00
|
|
|
} else {
|
|
|
|
// 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.
|
|
|
|
parts.unshift("declare ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
return concat(parts);
|
|
|
|
}
|
|
|
|
|
2016-12-30 08:01:44 +03:00
|
|
|
function printClass(path, print) {
|
|
|
|
const n = path.getValue();
|
2016-12-31 07:03:22 +03:00
|
|
|
const parts = [ "class" ];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-30 08:01:44 +03:00
|
|
|
if (n.id) {
|
2016-12-31 07:03:22 +03:00
|
|
|
parts.push(" ", path.call(print, "id"), path.call(print, "typeParameters"));
|
2016-12-30 08:01:44 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-30 08:01:44 +03:00
|
|
|
if (n.superClass) {
|
2017-01-09 20:09:04 +03:00
|
|
|
parts.push(
|
|
|
|
" extends ",
|
|
|
|
path.call(print, "superClass"),
|
|
|
|
path.call(print, "superTypeParameters")
|
|
|
|
);
|
|
|
|
} else if (n.extends && n.extends.length > 0) {
|
|
|
|
parts.push(" extends ", join(", ", path.map(print, "extends")));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-31 07:03:22 +03:00
|
|
|
if (n["implements"] && n["implements"].length > 0) {
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(" implements ", join(", ", path.map(print, "implements")));
|
2016-12-30 08:01:44 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-30 08:01:44 +03:00
|
|
|
parts.push(" ", path.call(print, "body"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-30 08:01:44 +03:00
|
|
|
return parts;
|
|
|
|
}
|
|
|
|
|
2017-01-05 23:26:45 +03:00
|
|
|
function printMemberLookup(path, print) {
|
|
|
|
const property = path.call(print, "property");
|
|
|
|
const n = path.getValue();
|
2017-01-09 20:09:04 +03:00
|
|
|
return concat(n.computed ? [ "[", property, "]" ] : [ ".", property ]);
|
2017-01-05 23:26:45 +03:00
|
|
|
}
|
|
|
|
|
2017-01-08 22:44:44 +03:00
|
|
|
// We detect calls on member expressions specially to format a
|
|
|
|
// comman pattern better. The pattern we are looking for is this:
|
|
|
|
//
|
|
|
|
// arr
|
|
|
|
// .map(x => x + 1)
|
|
|
|
// .filter(x => x > 10)
|
|
|
|
// .some(x => x % 2)
|
|
|
|
//
|
|
|
|
// where there is a "chain" of function calls on the result of
|
|
|
|
// previous expressions. We want to format them like above so they
|
|
|
|
// are consistently aligned.
|
|
|
|
//
|
|
|
|
// The way we do is by eagerly traversing the AST tree and
|
|
|
|
// re-shape it into a list of calls on member expressions. This
|
|
|
|
// lets us implement a heuristic easily for when we want to format
|
|
|
|
// it: if there are more than 1 calls on a member expression that
|
|
|
|
// pass in a function, we treat it like the above.
|
|
|
|
function printMemberChain(node, options, print) {
|
|
|
|
const nodes = [];
|
2017-01-18 06:39:47 +03:00
|
|
|
let leftmost = node;
|
|
|
|
let leftmostParent = null;
|
2017-01-08 22:44:44 +03:00
|
|
|
// Traverse down and gather up all of the calls on member
|
|
|
|
// expressions. This flattens it out into a list that we can
|
|
|
|
// easily analyze.
|
2017-01-20 21:12:37 +03:00
|
|
|
while (
|
|
|
|
leftmost.type === "CallExpression" &&
|
|
|
|
leftmost.callee.type === "MemberExpression"
|
|
|
|
) {
|
2017-01-18 06:39:47 +03:00
|
|
|
nodes.push({ member: leftmost.callee, call: leftmost });
|
|
|
|
leftmostParent = leftmost;
|
|
|
|
leftmost = leftmost.callee.object;
|
2017-01-08 22:44:44 +03:00
|
|
|
}
|
|
|
|
nodes.reverse();
|
|
|
|
|
|
|
|
// There are two kinds of formats we want to specialize: first,
|
|
|
|
// if there are multiple calls on lookups we want to group them
|
|
|
|
// together so they will all break at the same time. Second,
|
|
|
|
// it's a chain if there 2 or more calls pass in functions and
|
|
|
|
// we want to forcibly break all of the lookups onto new lines
|
|
|
|
// and indent them.
|
|
|
|
function argIsFunction(call) {
|
2017-01-09 20:09:04 +03:00
|
|
|
if (call.arguments.length > 0) {
|
2017-01-19 02:31:46 +03:00
|
|
|
const type = call.arguments[0].type;
|
2017-01-18 22:11:52 +03:00
|
|
|
return typeIsFunction(type);
|
2017-01-08 22:44:44 +03:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const hasMultipleLookups = nodes.length > 1;
|
2017-01-09 20:09:04 +03:00
|
|
|
const isChain = hasMultipleLookups &&
|
|
|
|
nodes.filter(n => argIsFunction(n.call)).length > 1;
|
2017-01-08 22:44:44 +03:00
|
|
|
|
2017-01-09 20:09:04 +03:00
|
|
|
if (hasMultipleLookups) {
|
2017-01-19 02:31:46 +03:00
|
|
|
const leftmostPrinted = FastPath
|
|
|
|
.from(leftmostParent)
|
|
|
|
.call(print, "callee", "object");
|
|
|
|
const nodesPrinted = nodes.map(node => ({
|
|
|
|
property: printMemberLookup(FastPath.from(node.member), print),
|
|
|
|
args: printArgumentsList(FastPath.from(node.call), options, print)
|
|
|
|
}));
|
2017-01-09 20:09:04 +03:00
|
|
|
const fullyExpanded = concat([
|
2017-01-18 06:39:47 +03:00
|
|
|
leftmostPrinted,
|
2017-01-09 20:09:04 +03:00
|
|
|
concat(
|
|
|
|
nodesPrinted.map(node => {
|
2017-01-08 22:44:44 +03:00
|
|
|
return indent(
|
|
|
|
options.tabWidth,
|
2017-01-12 23:06:44 +03:00
|
|
|
concat([ hardline, node.property, node.args ])
|
2017-01-08 22:44:44 +03:00
|
|
|
);
|
2017-01-09 20:09:04 +03:00
|
|
|
})
|
|
|
|
)
|
|
|
|
]);
|
2017-01-08 22:44:44 +03:00
|
|
|
|
|
|
|
// If it's a chain, force it to be fully expanded and print a
|
|
|
|
// newline before each lookup. If we're not sure if it's a
|
|
|
|
// chain (it *might* be printed on one line, but if gets too
|
|
|
|
// long it will be printed as a chain), we need to use
|
|
|
|
// `conditionalGroup` to describe both of these
|
|
|
|
// representations. We cannot describe both at the same time
|
|
|
|
// because the fully expanded form adds indentation, which
|
|
|
|
// messes up anything with hard lines.
|
2017-01-09 20:09:04 +03:00
|
|
|
if (isChain) {
|
2017-01-08 22:44:44 +03:00
|
|
|
return fullyExpanded;
|
2017-01-09 20:09:04 +03:00
|
|
|
} else {
|
2017-01-08 22:44:44 +03:00
|
|
|
return conditionalGroup([
|
|
|
|
concat([
|
2017-01-18 06:39:47 +03:00
|
|
|
leftmostPrinted,
|
2017-01-09 20:09:04 +03:00
|
|
|
concat(
|
|
|
|
nodesPrinted.map(node => {
|
2017-01-12 23:06:44 +03:00
|
|
|
return concat([ node.property, node.args ]);
|
2017-01-09 20:09:04 +03:00
|
|
|
})
|
|
|
|
)
|
2017-01-08 22:44:44 +03:00
|
|
|
]),
|
|
|
|
fullyExpanded
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-19 00:25:20 +03:00
|
|
|
function isEmptyJSXElement(node) {
|
|
|
|
if (node.children.length === 0) return true;
|
|
|
|
if (node.children.length > 1) return false;
|
|
|
|
|
|
|
|
// if there is one child but it's just a newline, treat as empty
|
|
|
|
const value = node.children[0].value;
|
|
|
|
if (!/\S/.test(value) && /\n/.test(value)) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// JSX Children are strange, mostly for two reasons:
|
|
|
|
// 1. JSX reads newlines into string values, instead of skipping them like JS
|
|
|
|
// 2. up to one whitespace between elements within a line is significant,
|
|
|
|
// but not between lines.
|
|
|
|
//
|
|
|
|
// So for one thing, '\n' needs to be parsed out of string literals
|
|
|
|
// and turned into hardlines (with string boundaries otherwise using softline)
|
|
|
|
//
|
|
|
|
// For another, leading, trailing, and lone whitespace all need to
|
|
|
|
// turn themselves into the rather ugly `{' '}` when breaking.
|
|
|
|
function printJSXChildren(path, options, print) {
|
|
|
|
const n = path.getValue();
|
|
|
|
const children = [];
|
|
|
|
const jsxWhitespace = options.singleQuote
|
|
|
|
? ifBreak("{' '}", " ")
|
2017-01-19 02:31:46 +03:00
|
|
|
: ifBreak('{" "}', " ");
|
2017-01-19 00:25:20 +03:00
|
|
|
|
|
|
|
// using `map` instead of `each` because it provides `i`
|
2017-01-19 02:31:46 +03:00
|
|
|
path.map(
|
|
|
|
function(childPath, i) {
|
|
|
|
const child = childPath.getValue();
|
|
|
|
const isLiteral = namedTypes.Literal.check(child);
|
|
|
|
|
|
|
|
if (isLiteral && typeof child.value === "string") {
|
2017-01-19 19:30:35 +03:00
|
|
|
// There's a bug in the flow parser where it doesn't unescape the
|
|
|
|
// value field. To workaround this, we can use rawValue which is
|
|
|
|
// correctly escaped (since it parsed).
|
|
|
|
// We really want to use value and re-escape it ourself when possible
|
|
|
|
// though.
|
2017-01-20 21:12:37 +03:00
|
|
|
const value = options.parser === "flow"
|
|
|
|
? child.raw
|
|
|
|
: util.htmlEscapeInsideAngleBracket(child.value);
|
2017-01-19 19:30:35 +03:00
|
|
|
|
|
|
|
if (/\S/.test(value)) {
|
|
|
|
const beginBreak = value.match(/^\s*\n/);
|
|
|
|
const endBreak = value.match(/\n\s*$/);
|
|
|
|
const beginSpace = value.match(/^\s+/);
|
|
|
|
const endSpace = value.match(/\s+$/);
|
2017-01-19 02:31:46 +03:00
|
|
|
|
|
|
|
if (beginBreak) {
|
|
|
|
children.push(hardline);
|
|
|
|
} else if (beginSpace) {
|
|
|
|
children.push(jsxWhitespace);
|
|
|
|
}
|
2017-01-19 00:25:20 +03:00
|
|
|
|
2017-01-19 19:30:35 +03:00
|
|
|
children.push(value.replace(/^\s+|\s+$/g, ""));
|
2017-01-19 00:25:20 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (endBreak) {
|
|
|
|
children.push(hardline);
|
|
|
|
} else {
|
|
|
|
if (endSpace) children.push(jsxWhitespace);
|
|
|
|
children.push(softline);
|
|
|
|
}
|
2017-01-19 19:30:35 +03:00
|
|
|
} else if (/\n/.test(value)) {
|
2017-01-19 02:31:46 +03:00
|
|
|
// TODO: add another hardline if >1 newline appeared. (also above)
|
2017-01-19 00:25:20 +03:00
|
|
|
children.push(hardline);
|
2017-01-19 19:30:35 +03:00
|
|
|
} else if (/\s/.test(value)) {
|
2017-01-19 02:31:46 +03:00
|
|
|
// whitespace-only without newlines,
|
|
|
|
// eg; a single space separating two elements
|
|
|
|
children.push(jsxWhitespace);
|
2017-01-19 00:25:20 +03:00
|
|
|
children.push(softline);
|
|
|
|
}
|
2017-01-19 02:31:46 +03:00
|
|
|
} else {
|
|
|
|
children.push(print(childPath));
|
2017-01-19 00:25:20 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
// add a line unless it's followed by a JSX newline
|
|
|
|
let next = n.children[i + 1];
|
|
|
|
if (!(next && /^\s*\n/.test(next.value))) {
|
|
|
|
children.push(softline);
|
|
|
|
}
|
2017-01-19 00:25:20 +03:00
|
|
|
}
|
2017-01-19 02:31:46 +03:00
|
|
|
},
|
|
|
|
"children"
|
|
|
|
);
|
2017-01-19 00:25:20 +03:00
|
|
|
|
|
|
|
return children;
|
|
|
|
}
|
|
|
|
|
|
|
|
// JSX expands children from the inside-out, instead of the outside-in.
|
|
|
|
// This is both to break children before attributes,
|
|
|
|
// and to ensure that when children break, their parents do as well.
|
|
|
|
//
|
|
|
|
// Any element that is written without any newlines and fits on a single line
|
|
|
|
// is left that way.
|
|
|
|
// Not only that, any user-written-line containing multiple JSX siblings
|
|
|
|
// should also be kept on one line if possible,
|
|
|
|
// so each user-written-line is wrapped in its own group.
|
|
|
|
//
|
|
|
|
// Elements that contain newlines or don't fit on a single line (recursively)
|
|
|
|
// are fully-split, using hardline and shouldBreak: true.
|
|
|
|
//
|
|
|
|
// To support that case properly, all leading and trailing spaces
|
|
|
|
// are stripped from the list of children, and replaced with a single hardline.
|
2017-01-12 02:15:12 +03:00
|
|
|
function printJSXElement(path, options, print) {
|
|
|
|
const n = path.getValue();
|
|
|
|
|
2017-01-19 00:25:20 +03:00
|
|
|
// turn <div></div> into <div />
|
|
|
|
if (isEmptyJSXElement(n)) {
|
|
|
|
n.openingElement.selfClosing = true;
|
|
|
|
delete n.closingElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if no children, just print the opening element
|
|
|
|
const openingLines = path.call(print, "openingElement");
|
2017-01-12 02:15:12 +03:00
|
|
|
if (n.openingElement.selfClosing) {
|
|
|
|
assert.ok(!n.closingElement);
|
2017-01-19 00:25:20 +03:00
|
|
|
return openingLines;
|
|
|
|
}
|
2017-01-12 02:15:12 +03:00
|
|
|
|
2017-01-19 00:25:20 +03:00
|
|
|
const children = printJSXChildren(path, options, print);
|
|
|
|
let hasAnyHardLine = false;
|
|
|
|
|
|
|
|
// trim trailing whitespace, recording if there was a hardline
|
|
|
|
while (children.length && util.getLast(children).type === "line") {
|
|
|
|
if (hasHardLine(util.getLast(children))) hasAnyHardLine = true;
|
|
|
|
children.pop();
|
|
|
|
}
|
2017-01-12 02:15:12 +03:00
|
|
|
|
2017-01-19 00:25:20 +03:00
|
|
|
// trim leading whitespace, recording if there was a hardline
|
|
|
|
while (children.length && children[0].type === "line") {
|
|
|
|
if (hasHardLine(children[0])) hasAnyHardLine = true;
|
|
|
|
children.shift();
|
|
|
|
}
|
2017-01-12 02:15:12 +03:00
|
|
|
|
2017-01-19 00:25:20 +03:00
|
|
|
// group by line, recording if there was a hardline.
|
|
|
|
let childrenGroupedByLine;
|
|
|
|
if (children.length === 1) {
|
|
|
|
if (!hasAnyHardLine && hasHardLine(children[0])) hasAnyHardLine = true;
|
2017-01-12 02:15:12 +03:00
|
|
|
|
2017-01-19 00:25:20 +03:00
|
|
|
// no need for groups, and a lone jsx whitespace doesn't work otherwise
|
|
|
|
childrenGroupedByLine = [ hardline, children[0] ];
|
|
|
|
} else {
|
|
|
|
// prefill leading newline, and initialize the first line's group
|
|
|
|
childrenGroupedByLine = [ hardline, group(concat([])) ];
|
|
|
|
children.forEach((child, i) => {
|
|
|
|
let prev = children[i - 1];
|
|
|
|
if (prev && hasHardLine(prev)) {
|
|
|
|
hasAnyHardLine = true;
|
|
|
|
|
|
|
|
// on a new line, so create a new group and put this element in it
|
|
|
|
const newLineGroup = group(concat([ child ]));
|
|
|
|
childrenGroupedByLine.push(newLineGroup);
|
|
|
|
} else {
|
|
|
|
// not on a newline, so add this element to the current group
|
|
|
|
const currentLineGroup = util.getLast(childrenGroupedByLine);
|
|
|
|
currentLineGroup.contents.parts.push(child);
|
|
|
|
}
|
2017-01-12 23:45:47 +03:00
|
|
|
|
2017-01-19 00:25:20 +03:00
|
|
|
// ensure we record hardline of last element
|
|
|
|
if (!hasAnyHardLine && i === children.length - 1) {
|
|
|
|
if (hasHardLine(child)) hasAnyHardLine = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2017-01-12 23:45:47 +03:00
|
|
|
|
2017-01-19 00:25:20 +03:00
|
|
|
const closingLines = path.call(print, "closingElement");
|
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
const multiLineElem = group(
|
|
|
|
concat([
|
|
|
|
openingLines,
|
|
|
|
indent(
|
|
|
|
options.tabWidth,
|
|
|
|
group(concat(childrenGroupedByLine), { shouldBreak: true })
|
|
|
|
),
|
|
|
|
hardline,
|
|
|
|
closingLines
|
|
|
|
])
|
|
|
|
);
|
2017-01-19 00:25:20 +03:00
|
|
|
|
|
|
|
let elem;
|
|
|
|
if (hasAnyHardLine) {
|
|
|
|
elem = multiLineElem;
|
|
|
|
} else {
|
|
|
|
elem = conditionalGroup([
|
2017-01-19 02:31:46 +03:00
|
|
|
group(concat([ openingLines, concat(children), closingLines ])),
|
|
|
|
multiLineElem
|
2017-01-19 00:25:20 +03:00
|
|
|
]);
|
2017-01-12 02:15:12 +03:00
|
|
|
}
|
|
|
|
|
2017-01-19 00:25:20 +03:00
|
|
|
return elem;
|
|
|
|
}
|
|
|
|
|
|
|
|
function maybeWrapJSXElementInParens(path, elem, options) {
|
2017-01-12 02:15:12 +03:00
|
|
|
const parent = path.getParentNode();
|
2017-01-19 00:25:20 +03:00
|
|
|
if (!parent) return elem;
|
|
|
|
|
|
|
|
const NO_WRAP_PARENTS = {
|
2017-01-19 02:31:46 +03:00
|
|
|
JSXElement: true,
|
|
|
|
ExpressionStatement: true,
|
|
|
|
CallExpression: true,
|
|
|
|
ConditionalExpression: true
|
2017-01-19 00:25:20 +03:00
|
|
|
};
|
|
|
|
if (NO_WRAP_PARENTS[parent.type]) {
|
2017-01-12 02:15:12 +03:00
|
|
|
return elem;
|
|
|
|
}
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return multilineGroup(
|
|
|
|
concat([
|
|
|
|
ifBreak("("),
|
|
|
|
indent(options.tabWidth, concat([ softline, elem ])),
|
|
|
|
softline,
|
|
|
|
ifBreak(")")
|
|
|
|
])
|
|
|
|
);
|
2017-01-12 02:15:12 +03:00
|
|
|
}
|
|
|
|
|
2017-01-19 01:01:17 +03:00
|
|
|
function isBinaryish(node) {
|
|
|
|
return node.type === "BinaryExpression" || node.type === "LogicalExpression";
|
|
|
|
}
|
|
|
|
|
|
|
|
// For binary expressions to be consistent, we need to group
|
|
|
|
// subsequent operators with the same precedence level under a single
|
|
|
|
// group. Otherwise they will be nested such that some of them break
|
|
|
|
// onto new lines but not all. Operators with the same precedence
|
|
|
|
// level should either all break or not. Because we group them by
|
|
|
|
// precedence level and the AST is structured based on precedence
|
|
|
|
// level, things are naturally broken up correctly, i.e. `&&` is
|
|
|
|
// broken before `+`.
|
|
|
|
function printBinaryishExpressions(path, parts, print) {
|
|
|
|
let node = path.getValue();
|
|
|
|
|
|
|
|
// We treat BinaryExpression and LogicalExpression nodes the same.
|
2017-01-19 02:31:46 +03:00
|
|
|
if (isBinaryish(node)) {
|
2017-01-19 01:01:17 +03:00
|
|
|
// Put all operators with the same precedence level in the same
|
|
|
|
// group. The reason we only need to do this with the `left`
|
|
|
|
// expression is because given an expression like `1 + 2 - 3`, it
|
|
|
|
// is always parsed like `((1 + 2) - 3)`, meaning the `left` side
|
|
|
|
// is where the rest of the expression will exist. Binary
|
|
|
|
// expressions on the right side mean they have a difference
|
|
|
|
// precedence level and should be treated as a separate group, so
|
|
|
|
// print them normally.
|
2017-01-19 02:31:46 +03:00
|
|
|
if (
|
|
|
|
util.getPrecedence(node.left.operator) ===
|
|
|
|
util.getPrecedence(node.operator)
|
|
|
|
) {
|
2017-01-19 01:01:17 +03:00
|
|
|
// Flatten them out by recursively calling this function. The
|
|
|
|
// printed values will all be appended to `parts`.
|
|
|
|
path.call(left => printBinaryishExpressions(left, parts, print), "left");
|
2017-01-19 02:31:46 +03:00
|
|
|
} else {
|
2017-01-19 01:01:17 +03:00
|
|
|
parts.push(path.call(print, "left"));
|
|
|
|
}
|
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
parts.push(" ", node.operator, line, path.call(print, "right"));
|
|
|
|
} else {
|
2017-01-19 01:01:17 +03:00
|
|
|
// Our stopping case. Simply print the node normally.
|
|
|
|
parts.push(path.call(print));
|
|
|
|
}
|
|
|
|
|
|
|
|
return parts;
|
|
|
|
}
|
|
|
|
|
2017-01-05 20:10:14 +03:00
|
|
|
function adjustClause(clause, options, forceSpace) {
|
2017-01-16 20:54:39 +03:00
|
|
|
if (clause === "") {
|
|
|
|
return ";";
|
|
|
|
}
|
|
|
|
|
2017-01-05 20:10:14 +03:00
|
|
|
if (isCurlyBracket(clause) || forceSpace) {
|
2016-12-31 07:03:22 +03:00
|
|
|
return concat([ " ", clause ]);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-15 07:37:13 +03:00
|
|
|
return indent(options.tabWidth, concat([ line, clause ]));
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
2017-01-05 20:10:14 +03:00
|
|
|
function isCurlyBracket(doc) {
|
|
|
|
const str = getFirstString(doc);
|
|
|
|
return str === "{" || str === "{}";
|
|
|
|
}
|
|
|
|
|
|
|
|
function isEmptyBlock(doc) {
|
|
|
|
const str = getFirstString(doc);
|
|
|
|
return str === "{}";
|
|
|
|
}
|
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
function lastNonSpaceCharacter(lines) {
|
|
|
|
var pos = lines.lastPos();
|
|
|
|
do {
|
|
|
|
var ch = lines.charAt(pos);
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
if (/\S/.test(ch)) return ch;
|
2016-12-23 21:38:10 +03:00
|
|
|
} while (lines.prevPos(pos));
|
|
|
|
}
|
|
|
|
|
2017-01-18 00:47:20 +03:00
|
|
|
function nodeStr(node, options) {
|
|
|
|
const str = node.value;
|
2016-12-23 21:38:10 +03:00
|
|
|
isString.assert(str);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-12 07:36:47 +03:00
|
|
|
const containsSingleQuote = str.indexOf("'") !== -1;
|
|
|
|
const containsDoubleQuote = str.indexOf('"') !== -1;
|
|
|
|
|
|
|
|
let shouldUseSingleQuote = options.singleQuote;
|
|
|
|
if (options.singleQuote && containsSingleQuote && !containsDoubleQuote) {
|
|
|
|
shouldUseSingleQuote = false;
|
|
|
|
}
|
|
|
|
if (!options.singleQuote && !containsSingleQuote && containsDoubleQuote) {
|
|
|
|
shouldUseSingleQuote = true;
|
|
|
|
}
|
|
|
|
|
2017-01-18 00:47:20 +03:00
|
|
|
const result = jsesc(str, {
|
2017-01-19 02:31:46 +03:00
|
|
|
quotes: shouldUseSingleQuote ? "single" : "double",
|
2017-01-16 19:03:59 +03:00
|
|
|
wrap: true
|
|
|
|
});
|
2017-01-18 00:47:20 +03:00
|
|
|
|
|
|
|
// Workaround a bug in the Javascript version of the flow parser where
|
|
|
|
// astral unicode characters like \uD801\uDC28 are incorrectly parsed as
|
|
|
|
// a sequence of \uFFFD.
|
2017-01-20 21:12:37 +03:00
|
|
|
if (options.parser === "flow" && result.indexOf("\\uFFFD") !== -1) {
|
2017-01-18 00:47:20 +03:00
|
|
|
return node.raw;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:01:07 +03:00
|
|
|
|
|
|
|
function isFirstStatement(path) {
|
|
|
|
const parent = path.getParentNode();
|
|
|
|
const node = path.getValue();
|
|
|
|
const body = parent.body;
|
2017-01-19 02:31:46 +03:00
|
|
|
return body && body[0] === node;
|
2016-12-31 07:01:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function isLastStatement(path) {
|
|
|
|
const parent = path.getParentNode();
|
|
|
|
const node = path.getValue();
|
|
|
|
const body = parent.body;
|
|
|
|
return body && body[body.length - 1] === node;
|
|
|
|
}
|
2017-01-19 23:43:10 +03:00
|
|
|
|
|
|
|
function printAstToDoc(ast, options) {
|
|
|
|
function printGenerically(path) {
|
|
|
|
return comments.printComments(
|
|
|
|
path,
|
|
|
|
p => genericPrint(p, options, printGenerically),
|
|
|
|
options
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return printGenerically(FastPath.from(ast));
|
|
|
|
}
|
|
|
|
|
2017-01-20 21:12:37 +03:00
|
|
|
module.exports = { printAstToDoc };
|