Merge pull request #3492 from loganfsmyth/codegen-cleanup

Refactor space insertion and remove some unneeded function options
This commit is contained in:
Logan Smyth 2016-06-16 22:15:56 -07:00 committed by GitHub
commit 4e1b4aecfd
23 changed files with 519 additions and 388 deletions

View File

@ -26,6 +26,7 @@ export default class Buffer {
column: null,
filename: null,
};
this._endsWithWord = false;
}
printedCommentStarts: Object;
@ -44,7 +45,7 @@ export default class Buffer {
// catch up to this nodes newline if we're behind
if (node.loc && this.format.retainLines && this.buf) {
while (this.position.line < node.loc.start.line) {
this._push("\n");
this.push("\n");
}
}
}
@ -98,15 +99,7 @@ export default class Buffer {
*/
semicolon() {
this.push(";");
}
/**
* Ensure last character is a semicolon.
*/
ensureSemicolon() {
if (!this.isLast(";")) this.semicolon();
this.token(";");
}
/**
@ -118,7 +111,7 @@ export default class Buffer {
if (this.format.minified && !this._lastPrintedIsEmptyStatement) {
this._removeLast(";");
}
this.push("}");
this.token("}");
}
/**
@ -126,22 +119,52 @@ export default class Buffer {
*/
keyword(name: string) {
this.push(name);
this.word(name);
this.space();
}
/**
* Add a space to the buffer unless it is compact (override with force).
* Add a space to the buffer unless it is compact.
*/
space(force?: boolean) {
if (!force && this.format.compact) return;
space() {
if (this.format.compact) return;
if (force || this.buf && !this.isLast(" ") && !this.isLast("\n")) {
if (this.buf && !this.endsWith(" ") && !this.endsWith("\n")) {
this.push(" ");
}
}
/**
* Writes a token that can't be safely parsed without taking whitespace into account.
*/
word(str: string) {
if (this._endsWithWord) this.push(" ");
this.push(str);
this._endsWithWord = true;
}
/**
* Writes a simple token.
*/
token(str: string) {
// space is mandatory to avoid outputting <!--
// http://javascript.spec.whatwg.org/#comment-syntax
if ((str === "--" && this.last === "!") ||
// Need spaces for operators of the same kind to avoid: `a+++b`
(str[0] === "+" && this.last === "+") ||
(str[0] === "-" && this.last === "-")) {
this.push(" ");
}
this.push(str);
}
/**
* Remove the last character.
*/
@ -152,7 +175,7 @@ export default class Buffer {
}
_removeLast(cha: string) {
if (!this._isLast(cha)) return;
if (!this.endsWith(cha)) return;
this.buf = this.buf.slice(0, -1);
this.last = this.buf[this.buf.length - 1];
this.position.unshift(cha);
@ -188,7 +211,7 @@ export default class Buffer {
if (state.printed) {
this.dedent();
this.newline();
this.push(")");
this.token(")");
}
}
@ -222,7 +245,9 @@ export default class Buffer {
this.removeLast(" ");
this._removeSpacesAfterLastNewline();
this._push(repeat("\n", i));
for (let j = 0; j < i; j++) {
this.push("\n");
}
}
/**
@ -232,8 +257,10 @@ export default class Buffer {
_removeSpacesAfterLastNewline() {
let lastNewlineIndex = this.buf.lastIndexOf("\n");
if (lastNewlineIndex >= 0 && this.get().length <= lastNewlineIndex) {
let toRemove = this.buf.slice(lastNewlineIndex + 1);
this.buf = this.buf.substring(0, lastNewlineIndex + 1);
this.last = "\n";
this.position.unshift(toRemove);
}
}
@ -257,6 +284,8 @@ export default class Buffer {
*/
withSource(prop: string, loc: Location, cb: () => void) {
if (!this.opts.sourceMaps) return cb();
// Use the call stack to manage a stack of "source location" data.
let originalLine = this._sourcePosition.line;
let originalColumn = this._sourcePosition.column;
@ -275,26 +304,12 @@ export default class Buffer {
* Push a string to the buffer, maintaining indentation and newlines.
*/
push(str: string, noIndent?: boolean) {
if (!this.format.compact && this._indent && !noIndent && str !== "\n") {
// we have an indent level and we aren't pushing a newline
let indent = this.getIndent();
// replace all newlines with newlines with the indentation
str = str.replace(/\n/g, `\n${indent}`);
push(str: string) {
if (!this.format.compact && this._indent && str[0] !== "\n") {
// we've got a newline before us so prepend on the indentation
if (this.isLast("\n")) this._push(indent);
if (this.endsWith("\n")) str = this.getIndent() + str;
}
this._push(str);
}
/**
* Push a string to the buffer.
*/
_push(str: string): void {
// see startTerminatorless() instance method
let parenPushNewlineState = this.parenPushNewlineState;
if (parenPushNewlineState) {
@ -308,7 +323,7 @@ export default class Buffer {
if (cha === "\n" || cha === "/") {
// we're going to break this terminator expression so we need to add a parentheses
this._push("(");
str = "(" + str;
this.indent();
parenPushNewlineState.printed = true;
}
@ -318,12 +333,15 @@ export default class Buffer {
}
// If there the line is ending, adding a new mapping marker is redundant
if (str[0] !== "\n") this.map.mark(this._sourcePosition);
if (this.opts.sourceMaps && str[0] !== "\n") this.map.mark(this._sourcePosition);
//
this.position.push(str);
this.buf += str;
this.last = str[str.length - 1];
// Clear any state-tracking flags that may have been set.
this._endsWithWord = false;
}
/**
@ -331,6 +349,8 @@ export default class Buffer {
*/
endsWith(str: string): boolean {
if (Array.isArray(str)) return str.some((s) => this.endsWith(s));
if (str.length === 1) {
return this.last === str;
} else {
@ -338,22 +358,4 @@ export default class Buffer {
}
}
/**
* Test if a character is last in the buffer.
*/
isLast(cha: string): boolean {
if (this.format.compact) return false;
return this._isLast(cha);
}
_isLast(cha: string): boolean {
let last = this.last;
if (Array.isArray(cha)) {
return cha.indexOf(last) >= 0;
} else {
return cha === last;
}
}
}

View File

@ -12,7 +12,7 @@ export function Program(node: Object) {
}
export function BlockStatement(node: Object) {
this.push("{");
this.token("{");
this.printInnerComments(node);
if (node.body.length) {
this.newline();
@ -27,7 +27,7 @@ export function BlockStatement(node: Object) {
this.rightBrace();
} else {
this.source("end", node.loc);
this.push("}");
this.token("}");
}
}
@ -38,6 +38,4 @@ export function Directive(node: Object) {
this.semicolon();
}
export function DirectiveLiteral(node: Object) {
this.push(this._stringLiteral(node.value));
}
export { StringLiteral as DirectiveLiteral } from "./types";

View File

@ -1,23 +1,27 @@
export function ClassDeclaration(node: Object) {
this.printJoin(node.decorators, node, { separator: "" });
this.push("class");
this.printJoin(node.decorators, node);
this.word("class");
if (node.id) {
this.push(" ");
this.space();
this.print(node.id, node);
}
this.print(node.typeParameters, node);
if (node.superClass) {
this.push(" extends ");
this.space();
this.word("extends");
this.space();
this.print(node.superClass, node);
this.print(node.superTypeParameters, node);
}
if (node.implements) {
this.push(" implements ");
this.printJoin(node.implements, node, { separator: ", " });
this.space();
this.word("implements");
this.space();
this.printList(node.implements, node);
}
this.space();
@ -27,10 +31,10 @@ export function ClassDeclaration(node: Object) {
export { ClassDeclaration as ClassExpression };
export function ClassBody(node: Object) {
this.push("{");
this.token("{");
this.printInnerComments(node);
if (node.body.length === 0) {
this.push("}");
this.token("}");
} else {
this.newline();
@ -43,14 +47,17 @@ export function ClassBody(node: Object) {
}
export function ClassProperty(node: Object) {
this.printJoin(node.decorators, node, { separator: "" });
this.printJoin(node.decorators, node);
if (node.static) this.push("static ");
if (node.static) {
this.word("static");
this.space();
}
this.print(node.key, node);
this.print(node.typeAnnotation, node);
if (node.value) {
this.space();
this.push("=");
this.token("=");
this.space();
this.print(node.value, node);
}
@ -58,14 +65,16 @@ export function ClassProperty(node: Object) {
}
export function ClassMethod(node: Object) {
this.printJoin(node.decorators, node, { separator: "" });
this.printJoin(node.decorators, node);
if (node.static) {
this.push("static ");
this.word("static");
this.space();
}
if (node.kind === "constructorCall") {
this.push("call ");
this.word("call");
this.space();
}
this._method(node);

View File

@ -10,67 +10,62 @@ const ZERO_DECIMAL_INTEGER = /\.0+$/;
const NON_DECIMAL_LITERAL = /^0[box]/;
export function UnaryExpression(node: Object) {
let needsSpace = /[a-z]$/.test(node.operator);
let arg = node.argument;
if (t.isUpdateExpression(arg) || t.isUnaryExpression(arg)) {
needsSpace = true;
if (node.operator === "void" || node.operator === "delete" || node.operator === "typeof") {
this.word(node.operator);
this.space();
} else {
this.token(node.operator);
}
if (t.isUnaryExpression(arg) && arg.operator === "!") {
needsSpace = false;
}
this.push(node.operator);
if (needsSpace) this.push(" ");
this.print(node.argument, node);
}
export function DoExpression(node: Object) {
this.push("do");
this.word("do");
this.space();
this.print(node.body, node);
}
export function ParenthesizedExpression(node: Object) {
this.push("(");
this.token("(");
this.print(node.expression, node);
this.push(")");
this.token(")");
}
export function UpdateExpression(node: Object) {
if (node.prefix) {
this.push(node.operator);
this.token(node.operator);
this.print(node.argument, node);
} else {
this.print(node.argument, node);
this.push(node.operator);
this.token(node.operator);
}
}
export function ConditionalExpression(node: Object) {
this.print(node.test, node);
this.space();
this.push("?");
this.token("?");
this.space();
this.print(node.consequent, node);
this.space();
this.push(":");
this.token(":");
this.space();
this.print(node.alternate, node);
}
export function NewExpression(node: Object, parent: Object) {
this.push("new ");
this.word("new");
this.space();
this.print(node.callee, node);
if (node.arguments.length === 0 && this.format.minified &&
!t.isCallExpression(parent, { callee: node }) &&
!t.isMemberExpression(parent) &&
!t.isNewExpression(parent)) return;
this.push("(");
this.token("(");
this.printList(node.arguments, node);
this.push(")");
this.token(")");
}
export function SequenceExpression(node: Object) {
@ -78,30 +73,35 @@ export function SequenceExpression(node: Object) {
}
export function ThisExpression() {
this.push("this");
this.word("this");
}
export function Super() {
this.push("super");
this.word("super");
}
export function Decorator(node: Object) {
this.push("@");
this.token("@");
this.print(node.expression, node);
this.newline();
}
function commaSeparatorNewline() {
this.token(",");
this.push("\n");
}
export function CallExpression(node: Object) {
this.print(node.callee, node);
if (node.loc) this.printAuxAfterComment();
this.push("(");
this.token("(");
let isPrettyCall = node._prettyCall && !this.format.retainLines && !this.format.compact;
let separator;
if (isPrettyCall) {
separator = ",\n";
separator = commaSeparatorNewline;
this.newline();
this.indent();
}
@ -113,19 +113,19 @@ export function CallExpression(node: Object) {
this.dedent();
}
this.push(")");
this.token(")");
}
function buildYieldAwait(keyword: string) {
return function (node: Object) {
this.push(keyword);
this.word(keyword);
if (node.delegate) {
this.push("*");
this.token("*");
}
if (node.argument) {
this.push(" ");
this.space();
let terminatorState = this.startTerminatorless();
this.print(node.argument, node);
this.endTerminatorless(terminatorState);
@ -149,7 +149,7 @@ export function ExpressionStatement(node: Object) {
export function AssignmentPattern(node: Object) {
this.print(node.left, node);
this.space();
this.push("=");
this.token("=");
this.space();
this.print(node.right, node);
}
@ -161,43 +161,29 @@ export function AssignmentExpression(node: Object, parent: Object) {
!n.needsParens(node, parent);
if (parens) {
this.push("(");
this.token("(");
}
this.print(node.left, node);
let spaces = !this.format.compact || node.operator === "in" || node.operator === "instanceof";
if (spaces) this.push(" ");
this.push(node.operator);
if (!spaces) {
// space is mandatory to avoid outputting <!--
// http://javascript.spec.whatwg.org/#comment-syntax
spaces = node.operator === "<" &&
t.isUnaryExpression(node.right, { prefix: true, operator: "!" }) &&
t.isUnaryExpression(node.right.argument, { prefix: true, operator: "--" });
// Need spaces for operators of the same kind to avoid: `a+++b`
if (!spaces) {
let right = getLeftMost(node.right);
spaces = t.isUnaryExpression(right, { prefix: true, operator: node.operator }) ||
t.isUpdateExpression(right, { prefix: true, operator: node.operator + node.operator });
}
this.space();
if (node.operator === "in" || node.operator === "instanceof") {
this.word(node.operator);
} else {
this.token(node.operator);
}
if (spaces) this.push(" ");
this.space();
this.print(node.right, node);
if (parens) {
this.push(")");
this.token(")");
}
}
export function BindExpression(node: Object) {
this.print(node.object, node);
this.push("::");
this.token("::");
this.print(node.callee, node);
}
@ -219,9 +205,9 @@ export function MemberExpression(node: Object) {
}
if (computed) {
this.push("[");
this.token("[");
this.print(node.property, node);
this.push("]");
this.token("]");
} else {
if (t.isNumericLiteral(node.object)) {
let val = this.getPossibleRaw(node.object) || node.object.value;
@ -230,24 +216,17 @@ export function MemberExpression(node: Object) {
!SCIENTIFIC_NOTATION.test(val) &&
!ZERO_DECIMAL_INTEGER.test(val) &&
!this.endsWith(".")) {
this.push(".");
this.token(".");
}
}
this.push(".");
this.token(".");
this.print(node.property, node);
}
}
export function MetaProperty(node: Object) {
this.print(node.meta, node);
this.push(".");
this.token(".");
this.print(node.property, node);
}
function getLeftMost(binaryExpr) {
if (!t.isBinaryExpression(binaryExpr)) {
return binaryExpr;
}
return getLeftMost(binaryExpr.left);
}

View File

@ -3,89 +3,103 @@
import * as t from "babel-types";
export function AnyTypeAnnotation() {
this.push("any");
this.word("any");
}
export function ArrayTypeAnnotation(node: Object) {
this.print(node.elementType, node);
this.push("[");
this.push("]");
this.token("[");
this.token("]");
}
export function BooleanTypeAnnotation() {
this.push("bool");
this.word("bool");
}
export function BooleanLiteralTypeAnnotation(node: Object) {
this.push(node.value ? "true" : "false");
this.word(node.value ? "true" : "false");
}
export function NullLiteralTypeAnnotation() {
this.push("null");
this.word("null");
}
export function DeclareClass(node: Object) {
this.push("declare class ");
this.word("declare");
this.space();
this.word("class");
this.space();
this._interfaceish(node);
}
export function DeclareFunction(node: Object) {
this.push("declare function ");
this.word("declare");
this.space();
this.word("function");
this.space();
this.print(node.id, node);
this.print(node.id.typeAnnotation.typeAnnotation, node);
this.semicolon();
}
export function DeclareInterface(node: Object) {
this.push("declare ");
this.word("declare");
this.space();
this.InterfaceDeclaration(node);
}
export function DeclareModule(node: Object) {
this.push("declare module ");
this.word("declare");
this.space();
this.word("module");
this.space();
this.print(node.id, node);
this.space();
this.print(node.body, node);
}
export function DeclareTypeAlias(node: Object) {
this.push("declare ");
this.word("declare");
this.space();
this.TypeAlias(node);
}
export function DeclareVariable(node: Object) {
this.push("declare var ");
this.word("declare");
this.space();
this.word("var");
this.space();
this.print(node.id, node);
this.print(node.id.typeAnnotation, node);
this.semicolon();
}
export function ExistentialTypeParam() {
this.push("*");
this.token("*");
}
export function FunctionTypeAnnotation(node: Object, parent: Object) {
this.print(node.typeParameters, node);
this.push("(");
this.token("(");
this.printList(node.params, node);
if (node.rest) {
if (node.params.length) {
this.push(",");
this.token(",");
this.space();
}
this.push("...");
this.token("...");
this.print(node.rest, node);
}
this.push(")");
this.token(")");
// this node type is overloaded, not sure why but it makes it EXTREMELY annoying
if (parent.type === "ObjectTypeProperty" || parent.type === "ObjectTypeCallProperty" || parent.type === "DeclareFunction") {
this.push(":");
this.token(":");
} else {
this.space();
this.push("=>");
this.token("=>");
}
this.space();
@ -94,8 +108,8 @@ export function FunctionTypeAnnotation(node: Object, parent: Object) {
export function FunctionTypeParam(node: Object) {
this.print(node.name, node);
if (node.optional) this.push("?");
this.push(":");
if (node.optional) this.token("?");
this.token(":");
this.space();
this.print(node.typeAnnotation, node);
}
@ -111,90 +125,102 @@ export function _interfaceish(node: Object) {
this.print(node.id, node);
this.print(node.typeParameters, node);
if (node.extends.length) {
this.push(" extends ");
this.printJoin(node.extends, node, { separator: ", " });
this.space();
this.word("extends");
this.space();
this.printList(node.extends, node);
}
if (node.mixins && node.mixins.length) {
this.push(" mixins ");
this.printJoin(node.mixins, node, { separator: ", " });
this.space();
this.word("mixins");
this.space();
this.printList(node.mixins, node);
}
this.space();
this.print(node.body, node);
}
export function InterfaceDeclaration(node: Object) {
this.push("interface ");
this.word("interface");
this.space();
this._interfaceish(node);
}
function andSeparator() {
this.space();
this.token("&");
this.space();
}
export function IntersectionTypeAnnotation(node: Object) {
this.printJoin(node.types, node, { separator: " & " });
this.printJoin(node.types, node, { separator: andSeparator });
}
export function MixedTypeAnnotation() {
this.push("mixed");
this.word("mixed");
}
export function NullableTypeAnnotation(node: Object) {
this.push("?");
this.token("?");
this.print(node.typeAnnotation, node);
}
export { NumericLiteral as NumericLiteralTypeAnnotation } from "./types";
export {
NumericLiteral as NumericLiteralTypeAnnotation,
StringLiteral as StringLiteralTypeAnnotation,
} from "./types";
export function NumberTypeAnnotation() {
this.push("number");
}
export function StringLiteralTypeAnnotation(node: Object) {
this.push(this._stringLiteral(node.value));
this.word("number");
}
export function StringTypeAnnotation() {
this.push("string");
this.word("string");
}
export function ThisTypeAnnotation() {
this.push("this");
this.word("this");
}
export function TupleTypeAnnotation(node: Object) {
this.push("[");
this.printJoin(node.types, node, { separator: ", " });
this.push("]");
this.token("[");
this.printList(node.types, node);
this.token("]");
}
export function TypeofTypeAnnotation(node: Object) {
this.push("typeof ");
this.word("typeof");
this.space();
this.print(node.argument, node);
}
export function TypeAlias(node: Object) {
this.push("type ");
this.word("type");
this.space();
this.print(node.id, node);
this.print(node.typeParameters, node);
this.space();
this.push("=");
this.token("=");
this.space();
this.print(node.right, node);
this.semicolon();
}
export function TypeAnnotation(node: Object) {
this.push(":");
this.token(":");
this.space();
if (node.optional) this.push("?");
if (node.optional) this.token("?");
this.print(node.typeAnnotation, node);
}
export function TypeParameter(node: Object) {
if (node.variance === "plus") {
this.push("+");
this.token("+");
} else if (node.variance === "minus") {
this.push("-");
this.token("-");
}
this.push(node.name);
this.word(node.name);
if (node.bound) {
this.print(node.bound, node);
@ -202,34 +228,32 @@ export function TypeParameter(node: Object) {
if (node.default) {
this.space();
this.push("=");
this.token("=");
this.space();
this.print(node.default, node);
}
}
export function TypeParameterInstantiation(node: Object) {
this.push("<");
this.printJoin(node.params, node, {
separator: ", ",
this.token("<");
this.printList(node.params, node, {
iterator: (node: Object) => {
this.print(node.typeAnnotation, node);
}
});
this.push(">");
this.token(">");
}
export { TypeParameterInstantiation as TypeParameterDeclaration };
export function ObjectTypeAnnotation(node: Object) {
this.push("{");
this.token("{");
let props = node.properties.concat(node.callProperties, node.indexers);
if (props.length) {
this.space();
this.printJoin(props, node, {
separator: false,
indent: true,
iterator: () => {
if (props.length !== 1) {
@ -242,33 +266,42 @@ export function ObjectTypeAnnotation(node: Object) {
this.space();
}
this.push("}");
this.token("}");
}
export function ObjectTypeCallProperty(node: Object) {
if (node.static) this.push("static ");
if (node.static) {
this.word("static");
this.space();
}
this.print(node.value, node);
}
export function ObjectTypeIndexer(node: Object) {
if (node.static) this.push("static ");
this.push("[");
if (node.static) {
this.word("static");
this.space();
}
this.token("[");
this.print(node.id, node);
this.push(":");
this.token(":");
this.space();
this.print(node.key, node);
this.push("]");
this.push(":");
this.token("]");
this.token(":");
this.space();
this.print(node.value, node);
}
export function ObjectTypeProperty(node: Object) {
if (node.static) this.push("static ");
if (node.static) {
this.word("static");
this.space();
}
this.print(node.key, node);
if (node.optional) this.push("?");
if (node.optional) this.token("?");
if (!t.isFunctionTypeAnnotation(node.value)) {
this.push(":");
this.token(":");
this.space();
}
this.print(node.value, node);
@ -276,21 +309,27 @@ export function ObjectTypeProperty(node: Object) {
export function QualifiedTypeIdentifier(node: Object) {
this.print(node.qualification, node);
this.push(".");
this.token(".");
this.print(node.id, node);
}
function orSeparator() {
this.space();
this.token("|");
this.space();
}
export function UnionTypeAnnotation(node: Object) {
this.printJoin(node.types, node, { separator: " | " });
this.printJoin(node.types, node, { separator: orSeparator });
}
export function TypeCastExpression(node: Object) {
this.push("(");
this.token("(");
this.print(node.expression, node);
this.print(node.typeAnnotation, node);
this.push(")");
this.token(")");
}
export function VoidTypeAnnotation() {
this.push("void");
this.word("void");
}

View File

@ -1,41 +1,42 @@
export function JSXAttribute(node: Object) {
this.print(node.name, node);
if (node.value) {
this.push("=");
this.token("=");
this.print(node.value, node);
}
}
export function JSXIdentifier(node: Object) {
this.push(node.name);
this.word(node.name);
}
export function JSXNamespacedName(node: Object) {
this.print(node.namespace, node);
this.push(":");
this.token(":");
this.print(node.name, node);
}
export function JSXMemberExpression(node: Object) {
this.print(node.object, node);
this.push(".");
this.token(".");
this.print(node.property, node);
}
export function JSXSpreadAttribute(node: Object) {
this.push("{...");
this.token("{");
this.token("...");
this.print(node.argument, node);
this.push("}");
this.token("}");
}
export function JSXExpressionContainer(node: Object) {
this.push("{");
this.token("{");
this.print(node.expression, node);
this.push("}");
this.token("}");
}
export function JSXText(node: Object) {
this.push(node.value, true);
this.token(node.value);
}
export function JSXElement(node: Object) {
@ -52,20 +53,29 @@ export function JSXElement(node: Object) {
this.print(node.closingElement, node);
}
function spaceSeparator() {
this.space();
}
export function JSXOpeningElement(node: Object) {
this.push("<");
this.token("<");
this.print(node.name, node);
if (node.attributes.length > 0) {
this.push(" ");
this.printJoin(node.attributes, node, { separator: " " });
this.space();
this.printJoin(node.attributes, node, { separator: spaceSeparator });
}
if (node.selfClosing) {
this.space();
this.token("/>");
} else {
this.token(">");
}
this.push(node.selfClosing ? " />" : ">");
}
export function JSXClosingElement(node: Object) {
this.push("</");
this.token("</");
this.print(node.name, node);
this.push(">");
this.token(">");
}
export function JSXEmptyExpression() {}

View File

@ -2,14 +2,14 @@ import * as t from "babel-types";
export function _params(node: Object) {
this.print(node.typeParameters, node);
this.push("(");
this.token("(");
this.printList(node.params, node, {
iterator: (node) => {
if (node.optional) this.push("?");
if (node.optional) this.token("?");
this.print(node.typeAnnotation, node);
}
});
this.push(")");
this.token(")");
if (node.returnType) {
this.print(node.returnType, node);
@ -22,20 +22,24 @@ export function _method(node: Object) {
if (kind === "method" || kind === "init") {
if (node.generator) {
this.push("*");
this.token("*");
}
}
if (kind === "get" || kind === "set") {
this.push(kind + " ");
this.word(kind);
this.space();
}
if (node.async) this.push("async ");
if (node.async) {
this.word("async");
this.space();
}
if (node.computed) {
this.push("[");
this.token("[");
this.print(key, node);
this.push("]");
this.token("]");
} else {
this.print(key, node);
}
@ -46,12 +50,15 @@ export function _method(node: Object) {
}
export function FunctionExpression(node: Object) {
if (node.async) this.push("async ");
this.push("function");
if (node.generator) this.push("*");
if (node.async) {
this.word("async");
this.space();
}
this.word("function");
if (node.generator) this.token("*");
if (node.id) {
this.push(" ");
this.space();
this.print(node.id, node);
} else {
this.space();
@ -65,7 +72,10 @@ export function FunctionExpression(node: Object) {
export { FunctionExpression as FunctionDeclaration };
export function ArrowFunctionExpression(node: Object) {
if (node.async) this.push("async ");
if (node.async) {
this.word("async");
this.space();
}
if (node.params.length === 1 && t.isIdentifier(node.params[0])) {
this.print(node.params[0], node);
@ -73,7 +83,9 @@ export function ArrowFunctionExpression(node: Object) {
this._params(node);
}
this.push(" => ");
this.space();
this.token("=>");
this.space();
this.print(node.body, node);
}

View File

@ -3,7 +3,9 @@ import * as t from "babel-types";
export function ImportSpecifier(node: Object) {
this.print(node.imported, node);
if (node.local && node.local.name !== node.imported.name) {
this.push(" as ");
this.space();
this.word("as");
this.space();
this.print(node.local, node);
}
}
@ -19,34 +21,49 @@ export function ExportDefaultSpecifier(node: Object) {
export function ExportSpecifier(node: Object) {
this.print(node.local, node);
if (node.exported && node.local.name !== node.exported.name) {
this.push(" as ");
this.space();
this.word("as");
this.space();
this.print(node.exported, node);
}
}
export function ExportNamespaceSpecifier(node: Object) {
this.push("* as ");
this.token("*");
this.space();
this.word("as");
this.space();
this.print(node.exported, node);
}
export function ExportAllDeclaration(node: Object) {
this.push("export *");
this.word("export");
this.space();
this.token("*");
if (node.exported) {
this.push(" as ");
this.space();
this.word("as");
this.space();
this.print(node.exported, node);
}
this.push(" from ");
this.space();
this.word("from");
this.space();
this.print(node.source, node);
this.semicolon();
}
export function ExportNamedDeclaration() {
this.push("export ");
this.word("export");
this.space();
ExportDeclaration.apply(this, arguments);
}
export function ExportDefaultDeclaration() {
this.push("export default ");
this.word("export");
this.space();
this.word("default");
this.space();
ExportDeclaration.apply(this, arguments);
}
@ -54,10 +71,11 @@ function ExportDeclaration(node: Object) {
if (node.declaration) {
let declar = node.declaration;
this.print(declar, node);
if (t.isStatement(declar) || t.isFunction(declar) || t.isClass(declar)) return;
if (!t.isStatement(declar)) this.semicolon();
} else {
if (node.exportKind === "type") {
this.push("type ");
this.word("type");
this.space();
}
let specifiers = node.specifiers.slice(0);
@ -70,7 +88,8 @@ function ExportDeclaration(node: Object) {
hasSpecial = true;
this.print(specifiers.shift(), node);
if (specifiers.length) {
this.push(", ");
this.token(",");
this.space();
}
} else {
break;
@ -78,29 +97,33 @@ function ExportDeclaration(node: Object) {
}
if (specifiers.length || (!specifiers.length && !hasSpecial)) {
this.push("{");
this.token("{");
if (specifiers.length) {
this.space();
this.printJoin(specifiers, node, { separator: ", " });
this.printList(specifiers, node);
this.space();
}
this.push("}");
this.token("}");
}
if (node.source) {
this.push(" from ");
this.space();
this.word("from");
this.space();
this.print(node.source, node);
}
}
this.ensureSemicolon();
this.semicolon();
}
}
export function ImportDeclaration(node: Object) {
this.push("import ");
this.word("import");
this.space();
if (node.importKind === "type" || node.importKind === "typeof") {
this.push(node.importKind + " ");
this.word(node.importKind);
this.space();
}
let specifiers = node.specifiers.slice(0);
@ -111,7 +134,8 @@ export function ImportDeclaration(node: Object) {
if (t.isImportDefaultSpecifier(first) || t.isImportNamespaceSpecifier(first)) {
this.print(specifiers.shift(), node);
if (specifiers.length) {
this.push(", ");
this.token(",");
this.space();
}
} else {
break;
@ -119,14 +143,16 @@ export function ImportDeclaration(node: Object) {
}
if (specifiers.length) {
this.push("{");
this.token("{");
this.space();
this.printJoin(specifiers, node, { separator: ", " });
this.printList(specifiers, node);
this.space();
this.push("}");
this.token("}");
}
this.push(" from ");
this.space();
this.word("from");
this.space();
}
this.print(node.source, node);
@ -134,6 +160,9 @@ export function ImportDeclaration(node: Object) {
}
export function ImportNamespaceSpecifier(node: Object) {
this.push("* as ");
this.token("*");
this.space();
this.word("as");
this.space();
this.print(node.local, node);
}

View File

@ -1,26 +1,23 @@
import repeat from "lodash/repeat";
import * as t from "babel-types";
const NON_ALPHABETIC_UNARY_OPERATORS = t.UPDATE_OPERATORS.concat(t.NUMBER_UNARY_OPERATORS).concat(["!"]);
export function WithStatement(node: Object) {
this.keyword("with");
this.push("(");
this.token("(");
this.print(node.object, node);
this.push(")");
this.token(")");
this.printBlock(node);
}
export function IfStatement(node: Object) {
this.keyword("if");
this.push("(");
this.token("(");
this.print(node.test, node);
this.push(")");
this.token(")");
this.space();
let needsBlock = node.alternate && t.isIfStatement(getLastStatement(node.consequent));
if (needsBlock) {
this.push("{");
this.token("{");
this.newline();
this.indent();
}
@ -30,12 +27,13 @@ export function IfStatement(node: Object) {
if (needsBlock) {
this.dedent();
this.newline();
this.push("}");
this.token("}");
}
if (node.alternate) {
if (this.isLast("}")) this.space();
this.push("else ");
if (this.endsWith("}")) this.space();
this.word("else");
this.space();
this.printAndIndentOnComments(node.alternate, node);
}
}
@ -48,44 +46,46 @@ function getLastStatement(statement) {
export function ForStatement(node: Object) {
this.keyword("for");
this.push("(");
this.token("(");
this._inForStatementInitCounter++;
this.print(node.init, node);
this._inForStatementInitCounter--;
this.push(";");
this.token(";");
if (node.test) {
this.space();
this.print(node.test, node);
}
this.push(";");
this.token(";");
if (node.update) {
this.space();
this.print(node.update, node);
}
this.push(")");
this.token(")");
this.printBlock(node);
}
export function WhileStatement(node: Object) {
this.keyword("while");
this.push("(");
this.token("(");
this.print(node.test, node);
this.push(")");
this.token(")");
this.printBlock(node);
}
let buildForXStatement = function (op) {
return function (node: Object) {
this.keyword("for");
this.push("(");
this.token("(");
this.print(node.left, node);
this.push(` ${op} `);
this.space();
this.word(op);
this.space();
this.print(node.right, node);
this.push(")");
this.token(")");
this.printBlock(node);
};
};
@ -94,27 +94,24 @@ export let ForInStatement = buildForXStatement("in");
export let ForOfStatement = buildForXStatement("of");
export function DoWhileStatement(node: Object) {
this.push("do ");
this.word("do");
this.space();
this.print(node.body, node);
this.space();
this.keyword("while");
this.push("(");
this.token("(");
this.print(node.test, node);
this.push(");");
this.token(")");
this.semicolon();
}
function buildLabelStatement(prefix, key = "label") {
return function (node: Object) {
this.push(prefix);
this.word(prefix);
let label = node[key];
if (label) {
if (!(this.format.minified && ((t.isUnaryExpression(label, { prefix: true }) ||
t.isUpdateExpression(label, { prefix: true })) &&
NON_ALPHABETIC_UNARY_OPERATORS.indexOf(label.operator) > -1))) {
this.push(" ");
}
this.space();
let terminatorState = this.startTerminatorless();
this.print(label, node);
@ -132,7 +129,8 @@ export let ThrowStatement = buildLabelStatement("throw", "argument");
export function LabeledStatement(node: Object) {
this.print(node.label, node);
this.push(": ");
this.token(":");
this.space();
this.print(node.body, node);
}
@ -152,27 +150,28 @@ export function TryStatement(node: Object) {
if (node.finalizer) {
this.space();
this.push("finally ");
this.word("finally");
this.space();
this.print(node.finalizer, node);
}
}
export function CatchClause(node: Object) {
this.keyword("catch");
this.push("(");
this.token("(");
this.print(node.param, node);
this.push(")");
this.token(")");
this.space();
this.print(node.body, node);
}
export function SwitchStatement(node: Object) {
this.keyword("switch");
this.push("(");
this.token("(");
this.print(node.discriminant, node);
this.push(")");
this.token(")");
this.space();
this.push("{");
this.token("{");
this.printSequence(node.cases, node, {
indent: true,
@ -181,16 +180,18 @@ export function SwitchStatement(node: Object) {
}
});
this.push("}");
this.token("}");
}
export function SwitchCase(node: Object) {
if (node.test) {
this.push("case ");
this.word("case");
this.space();
this.print(node.test, node);
this.push(":");
this.token(":");
} else {
this.push("default:");
this.word("default");
this.token(":");
}
if (node.consequent.length) {
@ -200,11 +201,27 @@ export function SwitchCase(node: Object) {
}
export function DebuggerStatement() {
this.push("debugger;");
this.word("debugger");
this.semicolon();
}
function variableDeclarationIdent() {
// "let " or "var " indentation.
this.token(",");
this.push("\n");
for (let i = 0; i < 4; i++) this.push(" ");
}
function constDeclarationIdent() {
// "const " indentation.
this.token(",");
this.push("\n");
for (let i = 0; i < 6; i++) this.push(" ");
}
export function VariableDeclaration(node: Object, parent: Object) {
this.push(node.kind + " ");
this.word(node.kind);
this.space();
let hasInits = false;
// don't add whitespace to loop heads
@ -229,14 +246,14 @@ export function VariableDeclaration(node: Object, parent: Object) {
// bar = "foo";
//
let sep;
let separator;
if (!this.format.compact && !this.format.concise && hasInits && !this.format.retainLines) {
sep = `,\n${repeat(" ", node.kind.length + 1)}`;
separator = node.kind === "const" ? constDeclarationIdent : variableDeclarationIdent;
}
//
this.printList(node.declarations, node, { separator: sep });
this.printList(node.declarations, node, { separator });
if (t.isFor(parent)) {
// don't give semicolons to these nodes since they'll be inserted in the parent generator
@ -251,7 +268,7 @@ export function VariableDeclarator(node: Object) {
this.print(node.id.typeAnnotation, node);
if (node.init) {
this.space();
this.push("=");
this.token("=");
this.space();
this.print(node.init, node);
}

View File

@ -3,24 +3,25 @@ export function TaggedTemplateExpression(node: Object) {
this.print(node.quasi, node);
}
export function TemplateElement(node: Object) {
this._push(node.value.raw);
export function TemplateElement(node: Object, parent: Object) {
const isFirst = parent.quasis[0] === node;
const isLast = parent.quasis[parent.quasis.length - 1] === node;
let value = (isFirst ? "`" : "}") + node.value.raw + (isLast ? "`" : "${");
if (!isFirst) this.space();
this.token(value);
if (!isLast) this.space();
}
export function TemplateLiteral(node: Object) {
this.push("`");
let quasis = node.quasis;
for (let i = 0; i < quasis.length; i++) {
this.print(quasis[i], node);
if (i + 1 < quasis.length) {
this._push("${ ");
this.print(node.expressions[i], node);
this.push(" }");
}
}
this._push("`");
}

View File

@ -10,17 +10,17 @@ export function Identifier(node: Object) {
// the next major.
if (node.variance) {
if (node.variance === "plus") {
this.push("+");
this.token("+");
} else if (node.variance === "minus") {
this.push("-");
this.token("-");
}
}
this.push(node.name);
this.word(node.name);
}
export function RestElement(node: Object) {
this.push("...");
this.token("...");
this.print(node.argument, node);
}
@ -33,7 +33,7 @@ export {
export function ObjectExpression(node: Object) {
let props = node.properties;
this.push("{");
this.token("{");
this.printInnerComments(node);
if (props.length) {
@ -42,23 +42,23 @@ export function ObjectExpression(node: Object) {
this.space();
}
this.push("}");
this.token("}");
}
export { ObjectExpression as ObjectPattern };
export function ObjectMethod(node: Object) {
this.printJoin(node.decorators, node, { separator: "" });
this.printJoin(node.decorators, node);
this._method(node);
}
export function ObjectProperty(node: Object) {
this.printJoin(node.decorators, node, { separator: "" });
this.printJoin(node.decorators, node);
if (node.computed) {
this.push("[");
this.token("[");
this.print(node.key, node);
this.push("]");
this.token("]");
} else {
// print `({ foo: foo = 5 } = {})` as `({ foo = 5 } = {});`
if (t.isAssignmentPattern(node.value) && t.isIdentifier(node.key) && node.key.name === node.value.left.name) {
@ -77,7 +77,7 @@ export function ObjectProperty(node: Object) {
}
}
this.push(":");
this.token(":");
this.space();
this.print(node.value, node);
}
@ -86,7 +86,7 @@ export function ArrayExpression(node: Object) {
let elems = node.elements;
let len = elems.length;
this.push("[");
this.token("[");
this.printInnerComments(node);
for (let i = 0; i < elems.length; i++) {
@ -94,44 +94,52 @@ export function ArrayExpression(node: Object) {
if (elem) {
if (i > 0) this.space();
this.print(elem, node);
if (i < len - 1) this.push(",");
if (i < len - 1) this.token(",");
} else {
// If the array expression ends with a hole, that hole
// will be ignored by the interpreter, but if it ends with
// two (or more) holes, we need to write out two (or more)
// commas so that the resulting code is interpreted with
// both (all) of the holes.
this.push(",");
this.token(",");
}
}
this.push("]");
this.token("]");
}
export { ArrayExpression as ArrayPattern };
export function RegExpLiteral(node: Object) {
this.push(`/${node.pattern}/${node.flags}`);
this.word(`/${node.pattern}/${node.flags}`);
}
export function BooleanLiteral(node: Object) {
this.push(node.value ? "true" : "false");
this.word(node.value ? "true" : "false");
}
export function NullLiteral() {
this.push("null");
this.word("null");
}
export function NumericLiteral(node: Object) {
this.push(node.value + "");
let raw = this.getPossibleRaw(node);
if (raw != null) {
this.word(raw);
return;
}
this.word(node.value + "");
}
export function StringLiteral(node: Object, parent: Object) {
this.push(this._stringLiteral(node.value, parent));
}
let raw = this.getPossibleRaw(node);
if (raw != null) {
this.token(raw);
return;
}
export function _stringLiteral(val: string, parent: Object): string {
val = JSON.stringify(val);
let val = JSON.stringify(node.value);
// escape illegal js but valid json unicode characters
val = val.replace(/[\u000A\u000D\u2028\u2029]/g, function (c) {
@ -152,5 +160,5 @@ export function _stringLiteral(val: string, parent: Object): string {
val = `'${val}'`;
}
return val;
return this.token(val);
}

View File

@ -10,13 +10,13 @@ import Printer from "./printer";
* user preferences, and valid output.
*/
export class CodeGenerator extends Printer {
class Generator extends Printer {
constructor(ast, opts, code) {
opts = opts || {};
let comments = ast.comments || [];
let tokens = ast.tokens || [];
let format = CodeGenerator.normalizeOptions(code, opts, tokens);
let format = Generator.normalizeOptions(code, opts, tokens);
let position = new Position;
@ -66,7 +66,6 @@ export class CodeGenerator extends Printer {
*
* - Detects code indentation.
* - If `opts.compact = "auto"` and the code is over 100KB, `compact` will be set to `true`.
*/
static normalizeOptions(code, opts, tokens) {
@ -85,7 +84,7 @@ export class CodeGenerator extends Printer {
compact: opts.compact,
minified: opts.minified,
concise: opts.concise,
quotes: opts.quotes || CodeGenerator.findCommonStringDelimiter(code, tokens),
quotes: opts.quotes || Generator.findCommonStringDelimiter(code, tokens),
indent: {
adjustMultilineComment: true,
style: style,
@ -161,7 +160,23 @@ export class CodeGenerator extends Printer {
}
}
/**
* We originally exported the Generator class above, but to make it extra clear that it is a private API,
* we have moved that to an internal class instance and simplified the interface to the two public methods
* that we wish to support.
*/
export class CodeGenerator {
constructor(ast, opts, code) {
this._generator = new Generator(ast, opts, code);
}
generate() {
return this._generator.generate();
}
}
export default function (ast: Object, opts: Object, code: string): Object {
let gen = new CodeGenerator(ast, opts, code);
let gen = new Generator(ast, opts, code);
return gen.generate();
}

View File

@ -41,7 +41,7 @@ export default class Printer extends Buffer {
this.printAuxBeforeComment(oldInAux);
let needsParens = n.needsParens(node, parent, this._printStack);
if (needsParens) this.push("(");
if (needsParens) this.token("(");
this.printLeadingComments(node, parent);
@ -53,7 +53,7 @@ export default class Printer extends Buffer {
let loc = (t.isProgram(node) || t.isFile(node)) ? null : node.loc;
this.withSource("start", loc, () => {
this._print(node, parent);
this[node.type](node, parent);
});
// Check again if any of our children may have left an aux comment on the stack
@ -61,7 +61,7 @@ export default class Printer extends Buffer {
this.printTrailingComments(node, parent);
if (needsParens) this.push(")");
if (needsParens) this.token(")");
// end
this._printStack.pop();
@ -96,30 +96,14 @@ export default class Printer extends Buffer {
}
getPossibleRaw(node) {
if (this.format.minified) return;
let extra = node.extra;
if (extra && extra.raw != null && extra.rawValue != null && node.value === extra.rawValue) {
return extra.raw;
}
}
_print(node, parent) {
// In minified mode we need to produce as little bytes as needed
// and need to make sure that string quoting is consistent.
// That means we have to always reprint as opposed to getting
// the raw value.
if (!this.format.minified) {
let extra = this.getPossibleRaw(node);
if (extra) {
this.push("");
this._push(extra);
return;
}
}
let printMethod = this[node.type];
printMethod.call(this, node, parent);
}
printJoin(nodes: ?Array, parent: Object, opts = {}) {
if (!nodes || !nodes.length) return;
@ -141,7 +125,7 @@ export default class Printer extends Buffer {
}
if (opts.separator && i < len - 1) {
this.push(opts.separator);
opts.separator.call(this);
}
}
};
@ -203,8 +187,7 @@ export default class Printer extends Buffer {
printList(items, parent, opts = {}) {
if (opts.separator == null) {
opts.separator = ",";
if (!this.format.compact) opts.separator += " ";
opts.separator = commaSeparator;
}
return this.printJoin(items, parent, opts);
@ -287,14 +270,11 @@ export default class Printer extends Buffer {
// whitespace before
this.newline(this.whitespace.getNewlinesBefore(comment));
if (!this.endsWith(["[", "{"])) this.space();
let column = this.position.column;
let val = this.generateComment(comment);
if (column && !this.isLast(["\n", " ", "[", "{"])) {
this._push(" ");
column++;
}
//
if (comment.type === "CommentBlock" && this.format.indent.adjustMultilineComment) {
let offset = comment.loc && comment.loc.start.column;
@ -307,10 +287,6 @@ export default class Printer extends Buffer {
val = val.replace(/\n/g, `\n${repeat(" ", indent)}`);
}
if (column === 0) {
val = this.getIndent() + val;
}
// force a newline for line comments when retainLines is set in case the next printed node
// doesn't catch up
if ((this.format.compact || this.format.concise || this.format.retainLines) &&
@ -319,7 +295,7 @@ export default class Printer extends Buffer {
}
//
this._push(val);
this.push(val);
// whitespace after
this.newline(this.whitespace.getNewlinesAfter(comment));
@ -335,6 +311,11 @@ export default class Printer extends Buffer {
}
}
function commaSeparator() {
this.token(",");
this.space();
}
for (let generator of [
require("./generators/template-literals"),
require("./generators/expressions"),

View File

@ -1 +1 @@
x=1;var {y=1}=obj;
x=1;var{y=1}=obj;

View File

@ -1,4 +1,4 @@
function foo(l){
return (
return(
l);}

View File

@ -0,0 +1,8 @@
var foo = (arg1, arg2) => {
arg1;
arg2;
};
var foo2 = (arg1, arg2) => {
arg1;
};
var foo3 = arg1 => arg1;

View File

@ -0,0 +1 @@
var foo=(arg1,arg2)=>{arg1;arg2};var foo2=(arg1,arg2)=>{arg1};var foo3=arg1=>arg1;

View File

@ -0,0 +1,12 @@
if (true) {
foo;
bar2;
} else {
foo;
bar2;
}
function fn () {
foo;
bar2;
}

View File

@ -0,0 +1 @@
if(true){foo;bar2}else{foo;bar2}function fn(){foo;bar2}

View File

@ -0,0 +1,7 @@
import * as foo from "foo";
import {foo as bar, foo2 as bar2} from "foo";
import {foo2} from "foo";
export * from "foo";
export {foo as bar} from "foo";
export {foo} from "foo";

View File

@ -0,0 +1 @@
import*as foo from"foo";import{foo as bar,foo2 as bar2}from"foo";import{foo2}from"foo";export*from"foo";export{foo as bar}from"foo";export{foo}from"foo";

View File

@ -1,4 +1,5 @@
var Whitespace = require("../lib/whitespace");
var Printer = require("../lib/printer");
var generate = require("../lib");
var assert = require("assert");
var parse = require("babylon").parse;
@ -9,10 +10,10 @@ var _ = require("lodash");
suite("generation", function () {
test("completeness", function () {
_.each(t.VISITOR_KEYS, function (keys, type) {
assert.ok(!!generate.CodeGenerator.prototype[type], type + " should exist");
assert.ok(!!Printer.prototype[type], type + " should exist");
});
_.each(generate.CodeGenerator.prototype, function (fn, type) {
_.each(Printer.prototype, function (fn, type) {
if (!/[A-Z]/.test(type[0])) return;
assert.ok(t.VISITOR_KEYS[type], type + " should not exist");
});

View File

@ -17,4 +17,4 @@ export default (param => {
prop1: 'prop1',
prop2: 'prop2'
}, _temp;
})
});