Index: lib/src/js/printer.dart |
diff --git a/lib/src/js/printer.dart b/lib/src/js/printer.dart |
index 6ee3f3ecf4df70108506f7786ea3d789019b3c0c..bbb6dc466039ae7c88630e596585e0dbb0e98af8 100644 |
--- a/lib/src/js/printer.dart |
+++ b/lib/src/js/printer.dart |
@@ -9,11 +9,13 @@ class JavaScriptPrintingOptions { |
final bool shouldCompressOutput; |
final bool minifyLocalVariables; |
final bool preferSemicolonToNewlineInMinifiedOutput; |
+ final bool avoidKeywordsInIdentifiers; |
JavaScriptPrintingOptions( |
{this.shouldCompressOutput: false, |
this.minifyLocalVariables: false, |
- this.preferSemicolonToNewlineInMinifiedOutput: false}); |
+ this.preferSemicolonToNewlineInMinifiedOutput: false, |
+ this.avoidKeywordsInIdentifiers: false}); |
} |
@@ -344,6 +346,20 @@ class Printer implements NodeVisitor { |
blockBody(loop.body, needsSeparation: false, needsNewline: true); |
} |
+ visitForOf(ForOf loop) { |
+ outIndent("for"); |
+ spaceOut(); |
+ out("("); |
+ visitNestedExpression(loop.leftHandSide, EXPRESSION, |
+ newInForInit: true, newAtStatementBegin: false); |
+ out(" of"); |
+ pendingSpace = true; |
+ visitNestedExpression(loop.iterable, EXPRESSION, |
+ newInForInit: false, newAtStatementBegin: false); |
+ out(")"); |
+ blockBody(loop.body, needsSeparation: false, needsNewline: true); |
+ } |
+ |
visitWhile(While loop) { |
outIndent("while"); |
spaceOut(); |
@@ -540,7 +556,7 @@ class Printer implements NodeVisitor { |
// (function() { ... })(). |
// ({a: 2, b: 3}.toString()). |
(newAtStatementBegin && (node is NamedFunction || |
- node is Fun || |
+ node is FunctionExpression || |
node is ObjectInitializer)); |
if (needsParentheses) { |
inForInit = false; |
@@ -556,7 +572,8 @@ class Printer implements NodeVisitor { |
} |
visitVariableDeclarationList(VariableDeclarationList list) { |
- out("var "); |
+ out(list.keyword); |
+ out(" "); |
visitCommaSeparated(list.declarations, ASSIGNMENT, |
newInForInit: inForInit, newAtStatementBegin: false); |
} |
@@ -763,6 +780,10 @@ class Printer implements NodeVisitor { |
out("this"); |
} |
+ visitSuper(Super node) { |
+ out("super"); |
+ } |
+ |
visitVariableDeclaration(VariableDeclaration decl) { |
out(localNamer.getName(decl.name)); |
} |
@@ -789,11 +810,57 @@ class Printer implements NodeVisitor { |
return false; |
} |
} |
- // TODO(floitsch): normally we should also check that the field is not a |
- // reserved word. We don't generate fields with reserved word names except |
- // for 'super'. |
- if (field == '"super"') return false; |
- return true; |
+ |
+ if (options.avoidKeywordsInIdentifiers) { |
+ return !_isJsKeyword(field.substring(1, field.length - 1)); |
+ } else { |
+ // TODO(floitsch): normally we should also check that the field is not a |
+ // reserved word. We don't generate fields with reserved word names except |
+ // for 'super'. |
+ return field != '"super"'; |
+ } |
+ } |
+ |
+ static bool _isJsKeyword(String keyword) { |
+ switch (keyword) { |
+ case "break": |
+ case "case": |
+ case "catch": |
+ case "class": |
+ case "const": |
+ case "continue": |
+ case "debugger": |
+ case "default": |
+ case "delete": |
+ case "do": |
+ case "else": |
+ case "export": |
+ case "extends": |
+ case "finally": |
+ case "for": |
+ case "function": |
+ case "if": |
+ case "import": |
+ case "in": |
+ case "instanceof": |
+ case "let": |
+ case "new": |
+ case "return": |
+ case "static": |
+ case "super": |
+ case "switch": |
+ case "this": |
+ case "throw": |
+ case "try": |
+ case "typeof": |
+ case "var": |
+ case "void": |
+ case "while": |
+ case "with": |
+ case "yield": |
+ return true; |
+ } |
+ return false; |
} |
visitAccess(PropertyAccess access) { |
@@ -829,6 +896,27 @@ class Printer implements NodeVisitor { |
functionOut(fun, null, vars); |
} |
+ visitArrowFun(ArrowFun fun) { |
+ VarCollector vars = new VarCollector(); |
+ vars.visitArrowFun(fun); |
+ localNamer.enterScope(vars); |
+ out("("); |
+ if (fun.params != null) { |
+ visitCommaSeparated(fun.params, PRIMARY, |
+ newInForInit: false, newAtStatementBegin: false); |
+ } |
+ out(")"); |
+ spaceOut(); |
+ out("=>"); |
+ if (fun.body is Expression) { |
+ spaceOut(); |
+ fun.body.accept(this); |
+ } else { |
+ blockBody(fun.body, needsSeparation: false, needsNewline: false); |
+ } |
+ localNamer.leaveScope(); |
+ } |
+ |
visitLiteralBool(LiteralBool node) { |
out(node.value ? "true" : "false"); |
} |
@@ -883,20 +971,21 @@ class Printer implements NodeVisitor { |
List<Property> properties = node.properties; |
out("{"); |
indentMore(); |
+ |
+ var isOneLiner = !properties.any((p) => p.value is FunctionExpression); |
for (int i = 0; i < properties.length; i++) { |
- Expression value = properties[i].value; |
if (i != 0) { |
out(","); |
- if (node.isOneLiner) spaceOut(); |
+ if (isOneLiner) spaceOut(); |
} |
- if (!node.isOneLiner) { |
+ if (!isOneLiner) { |
forceLine(); |
indent(); |
} |
visit(properties[i]); |
} |
indentLess(); |
- if (!node.isOneLiner && !properties.isEmpty) { |
+ if (!isOneLiner) { |
lineOut(); |
indent(); |
} |
@@ -904,19 +993,7 @@ class Printer implements NodeVisitor { |
} |
visitProperty(Property node) { |
- if (node.name is LiteralString) { |
- LiteralString nameString = node.name; |
- String name = nameString.value; |
- if (isValidJavaScriptId(name)) { |
- out(name.substring(1, name.length - 1)); |
- } else { |
- out(name); |
- } |
- } else { |
- assert(node.name is LiteralNumber); |
- LiteralNumber nameNumber = node.name; |
- out(nameNumber.value); |
- } |
+ propertyNameOut(node.name); |
out(":"); |
spaceOut(); |
visitNestedExpression(node.value, ASSIGNMENT, |
@@ -927,6 +1004,117 @@ class Printer implements NodeVisitor { |
out(node.pattern); |
} |
+ visitTemplateString(TemplateString node) { |
+ out('`'); |
+ for (var element in node.elements) { |
+ if (element is String) { |
+ out(element); |
+ } else { |
+ out(r'${'); |
+ visit(element); |
+ out('}'); |
+ } |
+ } |
+ out('`'); |
+ } |
+ |
+ visitTaggedTemplate(TaggedTemplate node) { |
+ visit(node.tag); |
+ visit(node.template); |
+ } |
+ |
+ visitClassDeclaration(ClassDeclaration node) { |
+ indent(); |
+ visit(node.classExpr); |
+ lineOut(); |
+ } |
+ |
+ visitClassExpression(ClassExpression node) { |
+ out('class '); |
+ visit(node.name); |
+ if (node.heritage != null) { |
+ out(' extends '); |
+ visit(node.heritage); |
+ spaceOut(); |
+ } |
+ out('{'); |
+ lineOut(); |
+ indentMore(); |
+ for (var method in node.methods) { |
+ indent(); |
+ visit(method); |
+ lineOut(); |
+ } |
+ indentLess(); |
+ indent(); |
+ out('}'); |
+ } |
+ |
+ visitMethod(Method node) { |
+ if (node.isStatic) { |
+ out('static '); |
+ } |
+ if (node.isGetter) { |
+ out('get '); |
+ } else if (node.isSetter) { |
+ out('set '); |
+ } |
+ var vars = new VarCollector(); |
+ vars.visitMethod(node); |
+ |
+ propertyNameOut(node.name, inMethod: true); |
+ |
+ localNamer.enterScope(vars); |
+ out("("); |
+ var fun = node.function; |
+ if (fun.params != null) { |
+ visitCommaSeparated(fun.params, PRIMARY, |
+ newInForInit: false, newAtStatementBegin: false); |
+ } |
+ out(")"); |
+ // TODO(jmesserly): async modifiers |
+ if (fun.body.statements.isEmpty) { |
+ spaceOut(); |
+ out("{}"); |
+ } else { |
+ blockBody(fun.body, needsSeparation: false, needsNewline: false); |
+ } |
+ localNamer.leaveScope(); |
+ } |
+ |
+ visitPropertyName(PropertyName node) => propertyNameOut(node); |
+ |
+ void propertyNameOut(Expression node, {bool inMethod: false}) { |
+ inForInit = false; |
+ atStatementBegin = false; |
+ |
+ if (node is LiteralNumber) { |
+ LiteralNumber nameNumber = node; |
+ out(nameNumber.value); |
+ } else { |
+ String quotedName; |
+ if (node is PropertyName) { |
+ quotedName = "'${node.name}'"; |
+ } else if (node is LiteralString) { |
+ quotedName = node.value; |
+ } |
+ if (quotedName != null) { |
+ if (isValidJavaScriptId(quotedName)) { |
+ out(quotedName.substring(1, quotedName.length - 1)); |
+ } else { |
+ if (inMethod) out("["); |
+ out(quotedName); |
+ if (inMethod) out("]"); |
+ } |
+ } else { |
+ // ComputedPropertyName |
+ out("["); |
+ visit(node); |
+ out("]"); |
+ } |
+ } |
+ } |
+ |
visitLiteralExpression(LiteralExpression node) { |
String template = node.template; |
List<Expression> inputs = node.inputs; |
@@ -965,6 +1153,15 @@ class Printer implements NodeVisitor { |
visitInterpolatedSelector(InterpolatedSelector node) => |
visitInterpolatedNode(node); |
+ visitInterpolatedPropertyName(InterpolatedPropertyName node) => |
+ visitInterpolatedNode(node); |
+ |
+ visitInterpolatedMethod(InterpolatedMethod node) => |
+ visitInterpolatedNode(node); |
+ |
+ visitInterpolatedVariableDeclaration(InterpolatedVariableDeclaration node) => |
+ visitInterpolatedNode(node); |
+ |
visitInterpolatedStatement(InterpolatedStatement node) { |
outLn('#${node.nameOrPosition}'); |
} |
@@ -982,6 +1179,18 @@ class Printer implements NodeVisitor { |
} |
} |
+ void visitCommentExpression(CommentExpression node) { |
+ if (shouldCompressOutput) return; |
+ String comment = node.comment.trim(); |
+ if (comment.isEmpty) return; |
+ if (comment.startsWith('/*')) { |
+ out(comment); |
+ } else { |
+ out('/* $comment */'); |
+ } |
+ visit(node.expression); |
+ } |
+ |
void visitAwait(Await node) { |
out("await "); |
visit(node.expression); |
@@ -1021,7 +1230,7 @@ class VarCollector extends BaseVisitor { |
void forEachVar(void fn(String v)) => vars.forEach(fn); |
void forEachParam(void fn(String p)) => params.forEach(fn); |
- void collectVarsInFunction(Fun fun) { |
+ void collectVarsInFunction(FunctionExpression fun) { |
if (!nested) { |
nested = true; |
if (fun.params != null) { |
@@ -1029,7 +1238,7 @@ class VarCollector extends BaseVisitor { |
params.add(fun.params[i].name); |
} |
} |
- visitBlock(fun.body); |
+ fun.body.accept(this); |
nested = false; |
} |
} |
@@ -1044,10 +1253,19 @@ class VarCollector extends BaseVisitor { |
collectVarsInFunction(namedFunction.function); |
} |
+ void visitMethod(Method declaration) { |
+ // Note that we don't bother collecting the name of the function. |
+ collectVarsInFunction(declaration.function); |
+ } |
+ |
void visitFun(Fun fun) { |
collectVarsInFunction(fun); |
} |
+ void visitArrowFun(ArrowFun fun) { |
+ collectVarsInFunction(fun); |
+ } |
+ |
void visitThis(This node) {} |
void visitVariableDeclaration(VariableDeclaration decl) { |
@@ -1081,6 +1299,7 @@ class DanglingElseVisitor extends BaseVisitor<bool> { |
} |
bool visitFor(For node) => node.body.accept(this); |
bool visitForIn(ForIn node) => node.body.accept(this); |
+ bool visitForOf(ForOf node) => node.body.accept(this); |
bool visitWhile(While node) => node.body.accept(this); |
bool visitDo(Do node) => false; |
bool visitContinue(Continue node) => false; |
@@ -1102,6 +1321,7 @@ class DanglingElseVisitor extends BaseVisitor<bool> { |
bool visitLabeledStatement(LabeledStatement node) |
=> node.body.accept(this); |
bool visitLiteralStatement(LiteralStatement node) => true; |
+ bool visitClassDeclaration(ClassDeclaration) => false; |
bool visitExpression(Expression node) => false; |
} |