diff --git a/packages/babel-generator/src/node/parentheses.js b/packages/babel-generator/src/node/parentheses.js index cd3dc0943a12..56ae5d601824 100644 --- a/packages/babel-generator/src/node/parentheses.js +++ b/packages/babel-generator/src/node/parentheses.js @@ -293,7 +293,7 @@ function isFirstInStatement( let node = printStack[i]; i--; let parent = printStack[i]; - while (i > 0) { + while (i >= 0) { if ( t.isExpressionStatement(parent, { expression: node }) || (considerDefaultExports && diff --git a/packages/babel-generator/test/fixtures/parentheses/arrow-function-object-body/input.js b/packages/babel-generator/test/fixtures/parentheses/arrow-function-object-body/input.js index 762286ee9039..597909ffb33e 100644 --- a/packages/babel-generator/test/fixtures/parentheses/arrow-function-object-body/input.js +++ b/packages/babel-generator/test/fixtures/parentheses/arrow-function-object-body/input.js @@ -3,3 +3,5 @@ var foo = arr.map(v => ({ y: v.bar*2 })); var fn = () => ({}).key; +var fn2 = () => ({}); +var fn3 = () => ({a} = {}); diff --git a/packages/babel-generator/test/fixtures/parentheses/arrow-function-object-body/output.js b/packages/babel-generator/test/fixtures/parentheses/arrow-function-object-body/output.js index 880df494e86b..2a969b86aa00 100644 --- a/packages/babel-generator/test/fixtures/parentheses/arrow-function-object-body/output.js +++ b/packages/babel-generator/test/fixtures/parentheses/arrow-function-object-body/output.js @@ -3,4 +3,10 @@ var foo = arr.map(v => ({ y: v.bar * 2 })); -var fn = () => ({}).key; \ No newline at end of file +var fn = () => ({}).key; + +var fn2 = () => ({}); + +var fn3 = () => ({ + a +} = {}); \ No newline at end of file diff --git a/packages/babel-generator/test/fixtures/parentheses/assignment-expression/input.js b/packages/babel-generator/test/fixtures/parentheses/assignment-expression/input.js index 0144ab8ea036..4eb74b7e4cc9 100644 --- a/packages/babel-generator/test/fixtures/parentheses/assignment-expression/input.js +++ b/packages/babel-generator/test/fixtures/parentheses/assignment-expression/input.js @@ -4,3 +4,4 @@ a = a || (a = {}); (a = b)(); (a = b)?.(); +({a} = {}); diff --git a/packages/babel-generator/test/fixtures/parentheses/assignment-expression/output.js b/packages/babel-generator/test/fixtures/parentheses/assignment-expression/output.js index 85217e309303..d70614ff748a 100644 --- a/packages/babel-generator/test/fixtures/parentheses/assignment-expression/output.js +++ b/packages/babel-generator/test/fixtures/parentheses/assignment-expression/output.js @@ -2,4 +2,7 @@ 1 + (a += 2); a = a || (a = {}); (a = b)(); -(a = b)?.(); \ No newline at end of file +(a = b)?.(); +({ + a +} = {}); \ No newline at end of file diff --git a/packages/babel-generator/test/index.js b/packages/babel-generator/test/index.js index 8c6075431709..bf28092957ef 100644 --- a/packages/babel-generator/test/index.js +++ b/packages/babel-generator/test/index.js @@ -537,6 +537,169 @@ describe("programmatic generation", function () { expect(output).toBe("(number & boolean)?"); }); }); + + describe("object expressions", () => { + it("not wrapped in parentheses when standalone", () => { + const objectExpression = t.objectExpression([]); + const output = generate(objectExpression).code; + expect(output).toBe("{}"); + }); + + it("wrapped in parentheses in expression statement", () => { + const expressionStatement = t.expressionStatement(t.objectExpression([])); + const output = generate(expressionStatement).code; + expect(output).toBe("({});"); + }); + + it("wrapped in parentheses in arrow function", () => { + const arrowFunctionExpression = t.arrowFunctionExpression( + [], + t.objectExpression([]), + ); + const output = generate(arrowFunctionExpression).code; + expect(output).toBe("() => ({})"); + }); + + it("not wrapped in parentheses in conditional", () => { + const conditionalExpression = t.conditionalExpression( + t.objectExpression([]), + t.booleanLiteral(true), + t.booleanLiteral(false), + ); + const output = generate(conditionalExpression).code; + expect(output).toBe("{} ? true : false"); + }); + + it("wrapped in parentheses in conditional in expression statement", () => { + const expressionStatement = t.expressionStatement( + t.conditionalExpression( + t.objectExpression([]), + t.booleanLiteral(true), + t.booleanLiteral(false), + ), + ); + const output = generate(expressionStatement).code; + expect(output).toBe("({}) ? true : false;"); + }); + + it("wrapped in parentheses in conditional in arrow function", () => { + const arrowFunctionExpression = t.arrowFunctionExpression( + [], + t.conditionalExpression( + t.objectExpression([]), + t.booleanLiteral(true), + t.booleanLiteral(false), + ), + ); + const output = generate(arrowFunctionExpression).code; + expect(output).toBe("() => ({}) ? true : false"); + }); + + it("not wrapped in parentheses in binary expression", () => { + const binaryExpression = t.binaryExpression( + "+", + t.objectExpression([]), + t.numericLiteral(1), + ); + const output = generate(binaryExpression).code; + expect(output).toBe("{} + 1"); + }); + + it("wrapped in parentheses in binary expression in expression statement", () => { + const expressionStatement = t.expressionStatement( + t.binaryExpression("+", t.objectExpression([]), t.numericLiteral(1)), + ); + const output = generate(expressionStatement).code; + expect(output).toBe("({}) + 1;"); + }); + + it("wrapped in parentheses in binary expression in arrow function", () => { + const arrowFunctionExpression = t.arrowFunctionExpression( + [], + t.binaryExpression("+", t.objectExpression([]), t.numericLiteral(1)), + ); + const output = generate(arrowFunctionExpression).code; + expect(output).toBe("() => ({}) + 1"); + }); + + it("not wrapped in parentheses in sequence expression", () => { + const sequenceExpression = t.sequenceExpression([ + t.objectExpression([]), + t.numericLiteral(1), + ]); + const output = generate(sequenceExpression).code; + expect(output).toBe("{}, 1"); + }); + + it("wrapped in parentheses in sequence expression in expression statement", () => { + const expressionStatement = t.expressionStatement( + t.sequenceExpression([t.objectExpression([]), t.numericLiteral(1)]), + ); + const output = generate(expressionStatement).code; + expect(output).toBe("({}), 1;"); + }); + + it("wrapped in parentheses in sequence expression in arrow function", () => { + const arrowFunctionExpression = t.arrowFunctionExpression( + [], + t.sequenceExpression([t.objectExpression([]), t.numericLiteral(1)]), + ); + const output = generate(arrowFunctionExpression).code; + expect(output).toBe("() => (({}), 1)"); + }); + }); + + describe("function expressions", () => { + it("not wrapped in parentheses when standalone", () => { + const functionExpression = t.functionExpression( + null, + [], + t.blockStatement([]), + ); + const output = generate(functionExpression).code; + expect(output).toBe("function () {}"); + }); + + it("wrapped in parentheses in expression statement", () => { + const expressionStatement = t.expressionStatement( + t.functionExpression(null, [], t.blockStatement([])), + ); + const output = generate(expressionStatement).code; + expect(output).toBe("(function () {});"); + }); + + it("wrapped in parentheses in export default declaration", () => { + const exportDefaultDeclaration = t.exportDefaultDeclaration( + t.functionExpression(null, [], t.blockStatement([])), + ); + const output = generate(exportDefaultDeclaration).code; + expect(output).toBe("export default (function () {});"); + }); + }); + + describe("class expressions", () => { + it("not wrapped in parentheses when standalone", () => { + const classExpression = t.classExpression(null, null, t.classBody([])); + const output = generate(classExpression).code; + expect(output).toBe("class {}"); + }); + + it("wrapped in parentheses in expression statement", () => { + const expressionStatement = t.expressionStatement( + t.classExpression(null, null, t.classBody([])), + ); + const output = generate(expressionStatement).code; + expect(output).toBe("(class {});"); + }); + + it("wrapped in parentheses in export default declaration", () => { + const exportDefaultDeclaration = t.exportDefaultDeclaration( + t.classExpression(null, null, t.classBody([])), + ); + const output = generate(exportDefaultDeclaration).code; + expect(output).toBe("export default (class {});"); + }); + }); }); describe("CodeGenerator", function () {